ALSA: hda - Add mic-mute LED control for HP laptop
authorTakashi Iwai <tiwai@suse.de>
Fri, 14 Sep 2012 09:58:54 +0000 (11:58 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 14 Sep 2012 10:23:50 +0000 (12:23 +0200)
Some of new HP laptops have a LED for microphone (or recording) mute,
and it's controlled by GPIO pin 3.

Bind this with the capture switch to turn it on/off properly by the
mixer change.

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

index eef9c6c..bb6c50e 100644 (file)
@@ -103,6 +103,7 @@ enum {
        STAC_HP_ZEPHYR,
        STAC_92HD83XXX_HP_LED,
        STAC_92HD83XXX_HP_INV_LED,
+       STAC_92HD83XXX_HP_MIC_LED,
        STAC_92HD83XXX_MODELS
 };
 
@@ -215,6 +216,9 @@ struct sigmatel_spec {
        unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */
        unsigned int vref_led;
 
+       unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */
+       bool mic_mute_led_on; /* current mic mute state */
+
        /* stream */
        unsigned int stream_delay;
 
@@ -1679,6 +1683,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
        [STAC_HP_ZEPHYR] = "hp-zephyr",
        [STAC_92HD83XXX_HP_LED] = "hp-led",
        [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led",
+       [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led",
 };
 
 static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
@@ -1703,6 +1708,8 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
                          "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
                          "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df,
+                         "HP Folio", STAC_92HD83XXX_HP_MIC_LED),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
                          "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
@@ -2791,18 +2798,27 @@ stac_control_new(struct sigmatel_spec *spec,
        return knew;
 }
 
-static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
-                                    const struct snd_kcontrol_new *ktemp,
-                                    int idx, const char *name,
-                                    unsigned long val)
+static struct snd_kcontrol_new *
+add_control_temp(struct sigmatel_spec *spec,
+                const struct snd_kcontrol_new *ktemp,
+                int idx, const char *name,
+                unsigned long val)
 {
        struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
                                                         HDA_SUBDEV_AMP_FLAG);
        if (!knew)
-               return -ENOMEM;
+               return NULL;
        knew->index = idx;
        knew->private_value = val;
-       return 0;
+       return knew;
+}
+
+static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
+                                    const struct snd_kcontrol_new *ktemp,
+                                    int idx, const char *name,
+                                    unsigned long val)
+{
+       return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM;
 }
 
 static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec,
@@ -3245,18 +3261,56 @@ static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
        return 0;
 }
 
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+                         unsigned int dir_mask, unsigned int data);
+
+/* hook for controlling mic-mute LED GPIO */
+static int stac92xx_capture_sw_put_led(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       int err;
+       bool mute;
+
+       err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+       if (err <= 0)
+               return err;
+       mute = !(ucontrol->value.integer.value[0] &&
+                ucontrol->value.integer.value[1]);
+       if (spec->mic_mute_led_on != mute) {
+               spec->mic_mute_led_on = mute;
+               if (mute)
+                       spec->gpio_data |= spec->mic_mute_led_gpio;
+               else
+                       spec->gpio_data &= ~spec->mic_mute_led_gpio;
+               stac_gpio_set(codec, spec->gpio_mask,
+                             spec->gpio_dir, spec->gpio_data);
+       }
+       return err;
+}
+
 static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
                                    unsigned long sw, int idx)
 {
+       struct sigmatel_spec *spec = codec->spec;
+       struct snd_kcontrol_new *knew;
        int err;
+
        err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
                                       "Capture Volume", vol);
        if (err < 0)
                return err;
-       err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
-                                      "Capture Switch", sw);
-       if (err < 0)
-               return err;
+
+       knew = add_control_temp(spec,
+                               &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE],
+                               idx, "Capture Switch", sw);
+       if (!knew)
+               return -ENOMEM;
+       /* add a LED hook for some HP laptops */
+       if (spec->mic_mute_led_gpio)
+               knew->put = stac92xx_capture_sw_put_led;
+
        return 0;
 }
 
@@ -5579,6 +5633,9 @@ again:
        case STAC_92HD83XXX_HP_INV_LED:
                default_polarity = 1;
                break;
+       case STAC_92HD83XXX_HP_MIC_LED:
+               spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+               break;
        }
 
        if (find_mute_led_cfg(codec, default_polarity))
@@ -5597,6 +5654,13 @@ again:
                }
        }
 
+       if (spec->mic_mute_led_gpio) {
+               spec->gpio_mask |= spec->mic_mute_led_gpio;
+               spec->gpio_dir |= spec->mic_mute_led_gpio;
+               spec->mic_mute_led_on = true;
+               spec->gpio_data |= spec->mic_mute_led_gpio;
+       }
+
        err = stac92xx_parse_auto_config(codec);
        if (!err) {
                if (spec->board_config < 0) {