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

Commit f567b788 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Move mic mute LED helper to the generic parser



Move the code for setting up and controlling the mic mute LED hook
from dell-wmi helper to the generic parser, so that it can be referred
from the multiple driver codes.

No functional change.

Tested-by: default avatarPali Rohár <pali.rohar@gmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 6c5e1ac0
Loading
Loading
Loading
Loading
+6 −110
Original line number Diff line number Diff line
@@ -6,111 +6,18 @@
#if IS_ENABLED(CONFIG_DELL_LAPTOP)
#include <linux/dell-led.h>

enum {
	MICMUTE_LED_ON,
	MICMUTE_LED_OFF,
	MICMUTE_LED_FOLLOW_CAPTURE,
	MICMUTE_LED_FOLLOW_MUTE,
};

static int dell_led_mode = MICMUTE_LED_FOLLOW_MUTE;
static int dell_capture;
static int dell_led_value;
static int (*dell_micmute_led_set_func)(int);
static void (*dell_old_cap_hook)(struct hda_codec *,
			         struct snd_kcontrol *,
				 struct snd_ctl_elem_value *);

static void call_micmute_led_update(void)
{
	int val;

	switch (dell_led_mode) {
	case MICMUTE_LED_ON:
		val = 1;
		break;
	case MICMUTE_LED_OFF:
		val = 0;
		break;
	case MICMUTE_LED_FOLLOW_CAPTURE:
		val = dell_capture;
		break;
	case MICMUTE_LED_FOLLOW_MUTE:
	default:
		val = !dell_capture;
		break;
	}

	if (val == dell_led_value)
		return;
	dell_led_value = val;
	dell_micmute_led_set_func(dell_led_value);
}

static void update_dell_wmi_micmute_led(struct hda_codec *codec,
				        struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	if (dell_old_cap_hook)
		dell_old_cap_hook(codec, kcontrol, ucontrol);

	if (!ucontrol || !dell_micmute_led_set_func)
		return;
	if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
		/* TODO: How do I verify if it's a mono or stereo here? */
		dell_capture = (ucontrol->value.integer.value[0] ||
				ucontrol->value.integer.value[1]);
		call_micmute_led_update();
	}
}

static int dell_mic_mute_led_mode_info(struct snd_kcontrol *kcontrol,
				       struct snd_ctl_elem_info *uinfo)
static void dell_micmute_update(struct hda_codec *codec)
{
	static const char * const texts[] = {
		"On", "Off", "Follow Capture", "Follow Mute",
	};

	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
	struct hda_gen_spec *spec = codec->spec;

static int dell_mic_mute_led_mode_get(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
{
	ucontrol->value.enumerated.item[0] = dell_led_mode;
	return 0;
	dell_micmute_led_set_func(spec->micmute_led.led_value);
}

static int dell_mic_mute_led_mode_put(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
{
	unsigned int mode;

	mode = ucontrol->value.enumerated.item[0];
	if (mode > MICMUTE_LED_FOLLOW_MUTE)
		mode = MICMUTE_LED_FOLLOW_MUTE;
	if (mode == dell_led_mode)
		return 0;
	dell_led_mode = mode;
	call_micmute_led_update();
	return 1;
}

static const struct snd_kcontrol_new dell_mic_mute_mode_ctls[] = {
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Mic Mute-LED Mode",
		.info = dell_mic_mute_led_mode_info,
		.get = dell_mic_mute_led_mode_get,
		.put = dell_mic_mute_led_mode_put,
	},
	{}
};

static void alc_fixup_dell_wmi(struct hda_codec *codec,
			       const struct hda_fixup *fix, int action)
{
	struct alc_spec *spec = codec->spec;
	bool removefunc = false;

	if (action == HDA_FIXUP_ACT_PROBE) {
@@ -121,25 +28,14 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec,
			return;
		}

		removefunc = true;
		if (dell_micmute_led_set_func(false) >= 0) {
			dell_led_value = 0;
			if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch)
				codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
			else {
				dell_old_cap_hook = spec->gen.cap_sync_hook;
				spec->gen.cap_sync_hook = update_dell_wmi_micmute_led;
				removefunc = false;
				add_mixer(spec, dell_mic_mute_mode_ctls);
			}
		}

		removefunc = (dell_micmute_led_set_func(false) < 0) ||
			(snd_hda_gen_add_micmute_led(codec,
						     dell_micmute_update) <= 0);
	}

	if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
		symbol_put(dell_micmute_led_set);
		dell_micmute_led_set_func = NULL;
		dell_old_cap_hook = NULL;
	}
}

+140 −0
Original line number Diff line number Diff line
@@ -3899,6 +3899,146 @@ static int parse_mic_boost(struct hda_codec *codec)
	return 0;
}

/*
 * mic mute LED hook helpers
 */
enum {
	MICMUTE_LED_ON,
	MICMUTE_LED_OFF,
	MICMUTE_LED_FOLLOW_CAPTURE,
	MICMUTE_LED_FOLLOW_MUTE,
};

static void call_micmute_led_update(struct hda_codec *codec)
{
	struct hda_gen_spec *spec = codec->spec;
	unsigned int val;

	switch (spec->micmute_led.led_mode) {
	case MICMUTE_LED_ON:
		val = 1;
		break;
	case MICMUTE_LED_OFF:
		val = 0;
		break;
	case MICMUTE_LED_FOLLOW_CAPTURE:
		val = spec->micmute_led.capture;
		break;
	case MICMUTE_LED_FOLLOW_MUTE:
	default:
		val = !spec->micmute_led.capture;
		break;
	}

	if (val == spec->micmute_led.led_value)
		return;
	spec->micmute_led.led_value = val;
	if (spec->micmute_led.update)
		spec->micmute_led.update(codec);
}

static void update_micmute_led(struct hda_codec *codec,
			       struct snd_kcontrol *kcontrol,
			       struct snd_ctl_elem_value *ucontrol)
{
	struct hda_gen_spec *spec = codec->spec;

	if (spec->micmute_led.old_hook)
		spec->micmute_led.old_hook(codec, kcontrol, ucontrol);

	if (!ucontrol)
		return;
	if (!strcmp("Capture Switch", ucontrol->id.name) &&
	    !ucontrol->id.index) {
		/* TODO: How do I verify if it's a mono or stereo here? */
		spec->micmute_led.capture = (ucontrol->value.integer.value[0] ||
					     ucontrol->value.integer.value[1]);
		call_micmute_led_update(codec);
	}
}

static int micmute_led_mode_info(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
{
	static const char * const texts[] = {
		"On", "Off", "Follow Capture", "Follow Mute",
	};

	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}

static int micmute_led_mode_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct hda_gen_spec *spec = codec->spec;

	ucontrol->value.enumerated.item[0] = spec->micmute_led.led_mode;
	return 0;
}

static int micmute_led_mode_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct hda_gen_spec *spec = codec->spec;
	unsigned int mode;

	mode = ucontrol->value.enumerated.item[0];
	if (mode > MICMUTE_LED_FOLLOW_MUTE)
		mode = MICMUTE_LED_FOLLOW_MUTE;
	if (mode == spec->micmute_led.led_mode)
		return 0;
	spec->micmute_led.led_mode = mode;
	call_micmute_led_update(codec);
	return 1;
}

static const struct snd_kcontrol_new micmute_led_mode_ctl = {
	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
	.name = "Mic Mute-LED Mode",
	.info = micmute_led_mode_info,
	.get = micmute_led_mode_get,
	.put = micmute_led_mode_put,
};

/**
 * snd_hda_gen_add_micmute_led - helper for setting up mic mute LED hook
 * @codec: the HDA codec
 * @hook: the callback for updating LED
 *
 * Called from the codec drivers for offering the mic mute LED controls.
 * Only valid for a single ADC (or a single input).  When established, it
 * sets up cap_sync_hook and triggers the callback at each time when the
 * capture mixer switch changes.  The callback is supposed to update the LED
 * accordingly.
 *
 * Returns 1 if the hook is established, 0 if skipped (no valid config), or
 * a negative error code.
 */
int snd_hda_gen_add_micmute_led(struct hda_codec *codec,
				void (*hook)(struct hda_codec *))
{
	struct hda_gen_spec *spec = codec->spec;

	if (spec->num_adc_nids > 1 && !spec->dyn_adc_switch) {
		codec_dbg(codec,
			  "Skipping micmute LED control due to several ADCs");
		return 0;
	}

	spec->micmute_led.led_mode = MICMUTE_LED_FOLLOW_MUTE;
	spec->micmute_led.capture = 0;
	spec->micmute_led.led_value = 0;
	spec->micmute_led.old_hook = spec->cap_sync_hook;
	spec->micmute_led.update = hook;
	spec->cap_sync_hook = update_micmute_led;
	if (!snd_hda_gen_add_kctl(spec, NULL, &micmute_led_mode_ctl))
		return -ENOMEM;
	return 1;
}
EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led);

/*
 * parse digital I/Os and set up NIDs in BIOS auto-parse mode
 */
+16 −0
Original line number Diff line number Diff line
@@ -86,6 +86,16 @@ struct badness_table {
extern const struct badness_table hda_main_out_badness;
extern const struct badness_table hda_extra_out_badness;

struct hda_micmute_hook {
	unsigned int led_mode;
	unsigned int capture;
	unsigned int led_value;
	void (*update)(struct hda_codec *codec);
	void (*old_hook)(struct hda_codec *codec,
			 struct snd_kcontrol *kcontrol,
			 struct snd_ctl_elem_value *ucontrol);
};

struct hda_gen_spec {
	char stream_name_analog[32];	/* analog PCM stream */
	const struct hda_pcm_stream *stream_analog_playback;
@@ -276,6 +286,9 @@ struct hda_gen_spec {
			      struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_value *ucontrol);

	/* mic mute LED hook; called via cap_sync_hook */
	struct hda_micmute_hook micmute_led;

	/* PCM hooks */
	void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
				  struct hda_codec *codec,
@@ -342,4 +355,7 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on);
int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin);

int snd_hda_gen_add_micmute_led(struct hda_codec *codec,
				void (*hook)(struct hda_codec *));

#endif /* __SOUND_HDA_GENERIC_H */