Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62cbde18 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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: default avatarTakashi Iwai <tiwai@suse.de>
parent b35cc822
Loading
Loading
Loading
Loading
+74 −10
Original line number Diff line number Diff line
@@ -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,7 +2798,8 @@ stac_control_new(struct sigmatel_spec *spec,
	return knew;
}

static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
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)
@@ -2799,10 +2807,18 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
	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) {