ALSA: hda/realtek - Add conexant-style inverted dmic handling
authorTakashi Iwai <tiwai@suse.de>
Tue, 18 Dec 2012 16:24:25 +0000 (17:24 +0100)
committerTakashi Iwai <tiwai@suse.de>
Sat, 12 Jan 2013 07:30:43 +0000 (08:30 +0100)
To make the parser more generic, a few codes to handle the inverted
stereo dmic in a way Conexant parser does is added in this patch.

The caller should set spec->inv_dmic_split flag appropriately.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_realtek.c

index 6fb3922..fbdcbde 100644 (file)
@@ -184,6 +184,7 @@ struct alc_spec {
        int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
        hda_nid_t inv_dmic_pin;
        hda_nid_t shared_mic_vref_pin;
+       int inv_dmic_split_idx; /* used internally for inv_dmic_split */
 
        /* DAC list */
        int num_all_dacs;
@@ -222,6 +223,7 @@ struct alc_spec {
        unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
        unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
        unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
+       unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
 
        unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 
@@ -2550,6 +2552,8 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
        return 0;
 }
 
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs);
+
 static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
                              int idx, bool is_switch, unsigned int ctl)
 {
@@ -2557,17 +2561,37 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
        char tmpname[44];
        int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL;
        const char *sfx = is_switch ? "Switch" : "Volume";
+       unsigned int chs;
+       int err;
 
        if (!ctl)
                return 0;
 
+       if (idx == spec->inv_dmic_split_idx)
+               chs = 1;
+       else
+               chs = 3;
+
        if (label)
                snprintf(tmpname, sizeof(tmpname),
                         "%s Capture %s", label, sfx);
        else
                snprintf(tmpname, sizeof(tmpname),
                         "Capture %s", sfx);
-       return add_control(spec, type, tmpname, idx, ctl);
+       err = add_control(spec, type, tmpname, idx,
+                         amp_val_replace_channels(ctl, chs));
+       if (err < 0 || chs == 3)
+               return err;
+
+       /* Make independent right kcontrol */
+       if (label)
+               snprintf(tmpname, sizeof(tmpname),
+                        "Inverted %s Capture %s", label, sfx);
+       else
+               snprintf(tmpname, sizeof(tmpname),
+                        "Inverted Capture %s", sfx);
+       return add_control(spec, type, tmpname, idx,
+                          amp_val_replace_channels(ctl, 2));
 }
 
 /* create single (and simple) capture volume and switch controls */
@@ -2730,6 +2754,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
        if (num_adcs < 0)
                return 0;
 
+       spec->inv_dmic_split_idx = -1;
        for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t pin;
                const char *label;
@@ -2783,6 +2808,14 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
                                imux_added = true;
                        }
                }
+
+               if (spec->inv_dmic_split) {
+                       if (cfg->inputs[i].type == AUTO_PIN_MIC) {
+                               unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+                               if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT)
+                                       spec->inv_dmic_split_idx = i;
+                       }
+               }
        }
 
        return 0;