ALSA: hda - proc - add support for dynamic controls to mixer<->NID mapping
[deliverable/linux.git] / sound / pci / hda / patch_analog.c
index 215e72a8711306567bf3c898b5ef02caeb5ef5a4..2d345606265b22e83270af2ccc77aefc89bc3cad 100644 (file)
@@ -159,7 +159,7 @@ static void ad198x_free_kctls(struct hda_codec *codec);
 /* additional beep mixers; the actual parameters are overwritten at build */
 static struct snd_kcontrol_new ad_beep_mixer[] = {
        HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
        { } /* end */
 };
 
@@ -202,7 +202,9 @@ static int ad198x_build_controls(struct hda_codec *codec)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->private_value = spec->beep_amp;
-                       err = snd_hda_ctl_add(codec, kctl);
+                       err = snd_hda_ctl_add(codec,
+                                               get_amp_nid_(spec->beep_amp),
+                                               kctl);
                        if (err < 0)
                                return err;
                }
@@ -2569,6 +2571,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (! knew->name)
                return -ENOMEM;
+       if (get_amp_nid_(val))
+               knew->subdevice = (1<<31)|get_amp_nid_(val);
        knew->private_value = val;
        return 0;
 }
@@ -4031,6 +4035,127 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ * HP Touchsmart
+ * port-A (0x11)      - front hp-out
+ * port-B (0x14)      - unused
+ * port-C (0x15)      - unused
+ * port-D (0x12)      - rear line out
+ * port-E (0x1c)      - front mic-in
+ * port-F (0x16)      - Internal speakers
+ * digital-mic (0x17) - Internal mic
+ */
+
+static struct hda_verb ad1984a_touchsmart_verbs[] = {
+       /* DACs; unmute as default */
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+       /* Port-A (HP) mixer - route only from analog mixer */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-A pin */
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       /* Port-A (HP) pin - always unmuted */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Port-E (int speaker) mixer - route only from analog mixer */
+       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
+       /* Port-E pin */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       /* Port-F (int speaker) mixer - route only from analog mixer */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-F pin */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Analog mixer; mute as default */
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+       /* Analog Mix output amp */
+       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* capture sources */
+       /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* unsolicited event for pin-sense */
+       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+       /* allow to touch GPIO1 (for mute control) */
+       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
+       /* internal mic - dmic */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       /* set magic COEFs for dmic */
+       {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
+       {0x01, AC_VERB_SET_PROC_COEF, 0x08},
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+/*     HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_hda_mixer_amp_switch_info,
+               .get = snd_hda_mixer_amp_switch_get,
+               .put = ad1884a_mobile_master_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+       },
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
+/* switch to external mic if plugged */
+static void ad1984a_touchsmart_automic(struct hda_codec *codec)
+{
+       if (snd_hda_codec_read(codec, 0x1c, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) {
+               snd_hda_codec_write(codec, 0x0c, 0,
+                                    AC_VERB_SET_CONNECT_SEL, 0x4);
+       } else {
+               snd_hda_codec_write(codec, 0x0c, 0,
+                                    AC_VERB_SET_CONNECT_SEL, 0x5);
+       }
+}
+
+
+/* unsolicited event for HP jack sensing */
+static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
+       unsigned int res)
+{
+       switch (res >> 26) {
+       case AD1884A_HP_EVENT:
+               ad1884a_hp_automute(codec);
+               break;
+       case AD1884A_MIC_EVENT:
+               ad1984a_touchsmart_automic(codec);
+               break;
+       }
+}
+
+/* initialize jack-sensing, too */
+static int ad1984a_touchsmart_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1884a_hp_automute(codec);
+       ad1984a_touchsmart_automic(codec);
+       return 0;
+}
+
+
 /*
  */
 
@@ -4039,6 +4164,7 @@ enum {
        AD1884A_LAPTOP,
        AD1884A_MOBILE,
        AD1884A_THINKPAD,
+       AD1984A_TOUCHSMART,
        AD1884A_MODELS
 };
 
@@ -4047,6 +4173,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
        [AD1884A_LAPTOP]        = "laptop",
        [AD1884A_MOBILE]        = "mobile",
        [AD1884A_THINKPAD]      = "thinkpad",
+       [AD1984A_TOUCHSMART]    = "touchsmart",
 };
 
 static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
@@ -4059,6 +4186,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
        SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
        SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
        SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
+       SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
        {}
 };
 
@@ -4142,6 +4270,21 @@ static int patch_ad1884a(struct hda_codec *codec)
                codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
                codec->patch_ops.init = ad1984a_thinkpad_init;
                break;
+       case AD1984A_TOUCHSMART:
+               spec->mixers[0] = ad1984a_touchsmart_mixers;
+               spec->init_verbs[0] = ad1984a_touchsmart_verbs;
+               spec->multiout.dig_out_nid = 0;
+               codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
+               codec->patch_ops.init = ad1984a_touchsmart_init;
+               /* set the upper-limit for mixer amp to 0dB for avoiding the
+                * possible damage by overloading
+                */
+               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
+               break;
        }
 
        return 0;
This page took 0.025923 seconds and 5 git commands to generate.