[alsa-devel] [PATCH 4/5] ALSA: hda - Implement shared front mic/hp jack for HP Z220
Takashi Iwai
tiwai at suse.de
Thu Nov 29 16:21:28 CET 2012
HP Z220 with ALC221 codec has a front jack that should work as both
the front mic and the secondary headphone. This patch adds a new
mixer enum to change the jack behavior.
In the headphone mode, the auto-mic switching is disabled and the
input is fixed to the rear line-in.
Signed-off-by: Takashi Iwai <tiwai at suse.de>
---
sound/pci/hda/patch_realtek.c | 147 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 147 insertions(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index c5cc47b..95c5bc7 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -213,6 +213,10 @@ 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 */
+ /* front-mic / HP sharing for HP Z220 */
+ unsigned int shared_fmic_hp_mode:1;
+ hda_nid_t shared_fmic_hp_nid;
+
/* auto-mute control */
int automute_mode;
hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS];
@@ -5959,6 +5963,143 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec,
spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
}
+/* allow switching HP/front-mic on HP Z220 */
+static int alc221_shared_fmic_hp_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[] = {
+ "Mic", "Headphone"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= 2)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int alc221_shared_fmic_hp_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->shared_fmic_hp_mode;
+ return 0;
+}
+
+static void alc221_shared_fmic_hp_mode_update(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_jack_tbl *jack;
+ hda_nid_t fmic_nid = spec->shared_fmic_hp_nid;
+ unsigned int pinctl, mute, idx;
+ hda_jack_callback callback;
+
+ if (!fmic_nid)
+ return;
+
+ /* utterly hackish: replace the secondary hp pin, enable/disable the
+ * automic on the fly
+ */
+ if (spec->shared_fmic_hp_mode) {
+ spec->autocfg.hp_outs = 2;
+ spec->autocfg.hp_pins[1] = fmic_nid;
+ callback = alc_hp_automute;
+ pinctl = PIN_HP;
+ mute = AMP_OUT_UNMUTE;
+ spec->auto_mic = 0;
+ alc_mux_select(codec, 0, spec->am_entry[0].idx, false);
+ /* choose the same DAC as the primary HP output */
+ idx = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ snd_hda_codec_write(codec, fmic_nid, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+ } else {
+ spec->autocfg.hp_outs = 1;
+ spec->autocfg.hp_pins[1] = 0;
+ callback = alc_mic_automute;
+ pinctl = PIN_VREF80;
+ mute = AMP_OUT_MUTE;
+ spec->auto_mic = 1;
+ }
+
+ snd_hda_codec_write(codec, fmic_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+ snd_hda_codec_write(codec, fmic_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, mute);
+
+ jack = snd_hda_jack_tbl_get(codec, fmic_nid);
+ if (jack)
+ jack->callback = callback;
+ else
+ snd_hda_jack_detect_enable_callback(codec, fmic_nid,
+ ALC_HP_EVENT, callback);
+ alc_hp_automute(codec, NULL);
+ alc_mic_automute(codec, NULL);
+}
+
+static int alc221_shared_fmic_hp_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->shared_fmic_hp_mode == ucontrol->value.enumerated.item[0])
+ return 0;
+ spec->shared_fmic_hp_mode = ucontrol->value.enumerated.item[0];
+ alc221_shared_fmic_hp_mode_update(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new alc221_shared_fmic_hp_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Mic Jack Mode",
+ .info = alc221_shared_fmic_hp_mode_info,
+ .get = alc221_shared_fmic_hp_mode_get,
+ .put = alc221_shared_fmic_hp_mode_put,
+};
+
+static int alc221_add_shared_fmic_hp_mode(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+ char name[22];
+
+ if (spec->autocfg.hp_outs != 1 || spec->am_num_entries != 2)
+ return -EINVAL;
+
+ snd_hda_get_pin_label(codec, spec->am_entry[1].pin, &spec->autocfg,
+ name, sizeof(name), NULL);
+ strlcat(name, " Jack Mode", sizeof(name));
+
+ knew = alc_kcontrol_new(spec);
+ if (!knew)
+ return -ENOMEM;
+ *knew = alc221_shared_fmic_hp_mode_enum;
+ knew->name = kstrdup(name, GFP_KERNEL);
+ if (!knew->name)
+ return -ENOMEM;
+ spec->shared_fmic_hp_nid = spec->am_entry[1].pin;
+ spec->shared_fmic_hp_mode = 0;
+ return 0;
+}
+
+static void alc221_fixup_shared_fmic_hp(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ switch (action) {
+ case ALC_FIXUP_ACT_PROBE:
+ if (alc221_add_shared_fmic_hp_mode(codec) < 0)
+ return;
+ break;
+ case ALC_FIXUP_ACT_INIT:
+ alc221_shared_fmic_hp_mode_update(codec);
+ break;
+ }
+}
+
static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
const struct alc_fixup *fix, int action)
{
@@ -6055,6 +6196,7 @@ enum {
ALC269_FIXUP_MIC2_MUTE_LED,
ALC269_FIXUP_INV_DMIC,
ALC269_FIXUP_LENOVO_DOCK,
+ ALC221_FIXUP_SHARED_FMIC_HP,
ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
ALC271_FIXUP_AMIC_MIC2,
ALC271_FIXUP_HP_GATE_MIC_JACK,
@@ -6198,6 +6340,10 @@ static const struct alc_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT
},
+ [ALC221_FIXUP_SHARED_FMIC_HP] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc221_fixup_shared_fmic_hp,
+ },
[ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
.type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_pincfg_no_hp_to_lineout,
@@ -6224,6 +6370,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x1791, "HP Z220", ALC221_FIXUP_SHARED_FMIC_HP),
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
--
1.8.0.1
More information about the Alsa-devel
mailing list