ALSA: hda - Headphone mic support for an Asus/Conexant device
authorDavid Henningsson <david.henningsson@canonical.com>
Tue, 16 Jul 2013 09:48:10 +0000 (11:48 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 16 Jul 2013 09:57:37 +0000 (11:57 +0200)
This Conexant codec has a single jack that can be used as either
headphone or mic (but not headset). The existing hp_mic functionality
does not apply here, because the mic and the HP are on separate pins.

Hence make a lighter version of what has been earlier done for Realtek
codecs.

BugLink: https://bugs.launchpad.net/bugs/1198030
Tested-by: Franz Hsieh <franz.hsieh@canonical.com>
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_conexant.c

index de00ce166470d5c73ac646c452f8a3ccd86e717a..4edd2d0f9a3ce66e625f7576b3b7053548397a51 100644 (file)
@@ -66,6 +66,8 @@ struct conexant_spec {
        hda_nid_t eapds[4];
        bool dynamic_eapd;
 
+       unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
 #ifdef ENABLE_CXT_STATIC_QUIRKS
        const struct snd_kcontrol_new *mixers[5];
        int num_mixers;
@@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec)
        snd_hda_gen_init(codec);
        if (!spec->dynamic_eapd)
                cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
+
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
        return 0;
 }
 
@@ -3224,6 +3229,8 @@ enum {
        CXT_PINCFG_LEMOTE_A1205,
        CXT_FIXUP_STEREO_DMIC,
        CXT_FIXUP_INC_MIC_BOOST,
+       CXT_FIXUP_HEADPHONE_MIC_PIN,
+       CXT_FIXUP_HEADPHONE_MIC,
 };
 
 static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
@@ -3246,6 +3253,59 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec,
                                  (0 << AC_AMPCAP_MUTE_SHIFT));
 }
 
+static void cxt_update_headset_mode(struct hda_codec *codec)
+{
+       /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
+       int i;
+       bool mic_mode = false;
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+       hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+
+       for (i = 0; i < cfg->num_inputs; i++)
+               if (cfg->inputs[i].pin == mux_pin) {
+                       mic_mode = !!cfg->inputs[i].is_headphone_mic;
+                       break;
+               }
+
+       if (mic_mode) {
+               snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
+               spec->gen.hp_jack_present = false;
+       } else {
+               snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
+               spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
+       }
+
+       snd_hda_gen_update_outputs(codec);
+}
+
+static void cxt_update_headset_mode_hook(struct hda_codec *codec,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       cxt_update_headset_mode(codec);
+}
+
+static void cxt_fixup_headphone_mic(struct hda_codec *codec,
+                                   const struct hda_fixup *fix, int action)
+{
+       struct conexant_spec *spec = codec->spec;
+
+       switch (action) {
+       case HDA_FIXUP_ACT_PRE_PROBE:
+               spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
+               break;
+       case HDA_FIXUP_ACT_PROBE:
+               spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
+               spec->gen.automute_hook = cxt_update_headset_mode;
+               break;
+       case HDA_FIXUP_ACT_INIT:
+               cxt_update_headset_mode(codec);
+               break;
+       }
+}
+
+
 /* ThinkPad X200 & co with cxt5051 */
 static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
        { 0x16, 0x042140ff }, /* HP (seq# overridden) */
@@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cxt5066_increase_mic_boost,
        },
+       [CXT_FIXUP_HEADPHONE_MIC_PIN] = {
+               .type = HDA_FIXUP_PINS,
+               .chained = true,
+               .chain_id = CXT_FIXUP_HEADPHONE_MIC,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+                       { }
+               }
+       },
+       [CXT_FIXUP_HEADPHONE_MIC] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cxt_fixup_headphone_mic,
+       },
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
 
 static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+       SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
@@ -3395,7 +3469,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
 
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
 
-       err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+       err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+                                      spec->parse_flags);
        if (err < 0)
                goto error;
 
@@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
                codec->bus->allow_bus_reset = 1;
        }
 
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
        return 0;
 
  error: