ALSA: hda - Add mic-mute LED control for HP laptop
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>
This commit is contained in:
parent
b35cc82258
commit
62cbde1868
|
@ -103,6 +103,7 @@ enum {
|
||||||
STAC_HP_ZEPHYR,
|
STAC_HP_ZEPHYR,
|
||||||
STAC_92HD83XXX_HP_LED,
|
STAC_92HD83XXX_HP_LED,
|
||||||
STAC_92HD83XXX_HP_INV_LED,
|
STAC_92HD83XXX_HP_INV_LED,
|
||||||
|
STAC_92HD83XXX_HP_MIC_LED,
|
||||||
STAC_92HD83XXX_MODELS
|
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_mute_led_nid; /* pin NID for mute-LED vref control */
|
||||||
unsigned int vref_led;
|
unsigned int vref_led;
|
||||||
|
|
||||||
|
unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */
|
||||||
|
bool mic_mute_led_on; /* current mic mute state */
|
||||||
|
|
||||||
/* stream */
|
/* stream */
|
||||||
unsigned int stream_delay;
|
unsigned int stream_delay;
|
||||||
|
|
||||||
|
@ -1679,6 +1683,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
|
||||||
[STAC_HP_ZEPHYR] = "hp-zephyr",
|
[STAC_HP_ZEPHYR] = "hp-zephyr",
|
||||||
[STAC_92HD83XXX_HP_LED] = "hp-led",
|
[STAC_92HD83XXX_HP_LED] = "hp-led",
|
||||||
[STAC_92HD83XXX_HP_INV_LED] = "hp-inv-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[] = {
|
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),
|
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
|
||||||
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
|
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
|
||||||
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
|
"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,
|
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
|
||||||
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
|
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
|
||||||
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
|
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
|
||||||
|
@ -2791,18 +2798,27 @@ stac_control_new(struct sigmatel_spec *spec,
|
||||||
return knew;
|
return knew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 NULL;
|
||||||
|
knew->index = idx;
|
||||||
|
knew->private_value = val;
|
||||||
|
return knew;
|
||||||
|
}
|
||||||
|
|
||||||
static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
|
static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
|
||||||
const struct snd_kcontrol_new *ktemp,
|
const struct snd_kcontrol_new *ktemp,
|
||||||
int idx, const char *name,
|
int idx, const char *name,
|
||||||
unsigned long val)
|
unsigned long val)
|
||||||
{
|
{
|
||||||
struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
|
return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM;
|
||||||
HDA_SUBDEV_AMP_FLAG);
|
|
||||||
if (!knew)
|
|
||||||
return -ENOMEM;
|
|
||||||
knew->index = idx;
|
|
||||||
knew->private_value = val;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec,
|
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;
|
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,
|
static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
|
||||||
unsigned long sw, int idx)
|
unsigned long sw, int idx)
|
||||||
{
|
{
|
||||||
|
struct sigmatel_spec *spec = codec->spec;
|
||||||
|
struct snd_kcontrol_new *knew;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
|
err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
|
||||||
"Capture Volume", vol);
|
"Capture Volume", vol);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
|
|
||||||
"Capture Switch", sw);
|
knew = add_control_temp(spec,
|
||||||
if (err < 0)
|
&stac92xx_control_templates[STAC_CTL_WIDGET_MUTE],
|
||||||
return err;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5579,6 +5633,9 @@ again:
|
||||||
case STAC_92HD83XXX_HP_INV_LED:
|
case STAC_92HD83XXX_HP_INV_LED:
|
||||||
default_polarity = 1;
|
default_polarity = 1;
|
||||||
break;
|
break;
|
||||||
|
case STAC_92HD83XXX_HP_MIC_LED:
|
||||||
|
spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (find_mute_led_cfg(codec, default_polarity))
|
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);
|
err = stac92xx_parse_auto_config(codec);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
if (spec->board_config < 0) {
|
if (spec->board_config < 0) {
|
||||||
|
|
Loading…
Reference in a new issue