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

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

ALSA: hda - Introduce snd_hda_set_pin_ctl*() helper functions



For setting the pin-control values more safely to match with the
actual pin capability bits, a copule of new helper functions,
snd_hda_set_pin_ctl() and snd_hda_set_pin_ctl_cache(), are
introduced.  These are simple replacement of the codec verb write with
AC_VERB_SET_PIN_WIDGET but do more sanity checks and filter out
superfluous pin-control bits if they don't fit with the corresponding
pin capabilities.

Some codecs are screwed up or ignore the command when such a wrong bit
is set.  These helpers will avoid such secret errors.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent d3980110
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -4795,6 +4795,32 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);

int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
			 unsigned int val, bool cached)
{
	if (val) {
		unsigned int cap = snd_hda_query_pin_caps(codec, pin);
		if (val & AC_PINCTL_OUT_EN) {
			if (!(cap & AC_PINCAP_OUT))
				val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
			else if ((val & AC_PINCTL_HP_EN) &&
				 !(cap & AC_PINCAP_HP_DRV))
				val &= ~AC_PINCTL_HP_EN;
		}
		if (val & AC_PINCTL_IN_EN) {
			if (!(cap & AC_PINCAP_IN))
				val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
		}
	}
	if (cached)
		return snd_hda_codec_update_cache(codec, pin, 0,
				AC_VERB_SET_PIN_WIDGET_CONTROL, val);
	else
		return snd_hda_codec_write(codec, pin, 0,
					   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
}
EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl);

/*
 * Helper for automatic pin configuration
 */
+38 −0
Original line number Diff line number Diff line
@@ -502,6 +502,44 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
#define PIN_HP			(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP		(AC_PINCTL_HP_EN)

int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
			 unsigned int val, bool cached);

/**
 * _snd_hda_set_pin_ctl - Set a pin-control value safely
 * @codec: the codec instance
 * @pin: the pin NID to set the control
 * @val: the pin-control value (AC_PINCTL_* bits)
 *
 * This function sets the pin-control value to the given pin, but
 * filters out the invalid pin-control bits when the pin has no such
 * capabilities.  For example, when PIN_HP is passed but the pin has no
 * HP-drive capability, the HP bit is omitted.
 *
 * The function doesn't check the input VREF capability bits, though.
 * Also, this function is only for analog pins, not for HDMI pins.
 */
static inline int
snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
{
	return _snd_hda_set_pin_ctl(codec, pin, val, false);
}

/**
 * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
 * @codec: the codec instance
 * @pin: the pin NID to set the control
 * @val: the pin-control value (AC_PINCTL_* bits)
 *
 * Just like snd_hda_set_pin_ctl() but write to cache as well.
 */
static inline int
snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
			  unsigned int val)
{
	return _snd_hda_set_pin_ctl(codec, pin, val, true);
}

/*
 * get widget capabilities
 */
+3 −5
Original line number Diff line number Diff line
@@ -1742,9 +1742,7 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
	if (! ad198x_eapd_put(kcontrol, ucontrol))
		return 0;
	/* change speaker pin appropriately */
	snd_hda_codec_write(codec, 0x05, 0,
			    AC_VERB_SET_PIN_WIDGET_CONTROL,
			    spec->cur_eapd ? PIN_OUT : 0);
	snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
	/* toggle HP mute appropriately */
	snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE,
@@ -3103,7 +3101,7 @@ static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
					      int dac_idx)
{
	/* set as output */
	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
	snd_hda_set_pin_ctl(codec, nid, pin_type);
	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
	switch (nid) {
	case 0x11: /* port-A - DAC 03 */
@@ -3165,7 +3163,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
			snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
			break;
		}
		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
		snd_hda_set_pin_ctl(codec, nid,
				    type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
		if (nid != AD1988_PIN_CD_NID)
			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+2 −4
Original line number Diff line number Diff line
@@ -341,8 +341,7 @@ static int ca0110_build_pcms(struct hda_codec *codec)
static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
{
	if (pin) {
		snd_hda_codec_write(codec, pin, 0,
				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
		snd_hda_set_pin_ctl(codec, pin, PIN_HP);
		if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
			snd_hda_codec_write(codec, pin, 0,
					    AC_VERB_SET_AMP_GAIN_MUTE,
@@ -356,8 +355,7 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
{
	if (pin) {
		snd_hda_codec_write(codec, pin, 0,
				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
		snd_hda_set_pin_ctl(codec, pin, PIN_VREF80);
		if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
			snd_hda_codec_write(codec, pin, 0,
					    AC_VERB_SET_AMP_GAIN_MUTE,
+2 −5
Original line number Diff line number Diff line
@@ -239,8 +239,7 @@ enum get_set {
static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
{
	if (pin) {
		snd_hda_codec_write(codec, pin, 0,
				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
		snd_hda_set_pin_ctl(codec, pin, PIN_HP);
		if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
			snd_hda_codec_write(codec, pin, 0,
					    AC_VERB_SET_AMP_GAIN_MUTE,
@@ -254,9 +253,7 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
{
	if (pin) {
		snd_hda_codec_write(codec, pin, 0,
				    AC_VERB_SET_PIN_WIDGET_CONTROL,
				    PIN_VREF80);
		snd_hda_set_pin_ctl(codec, pin, PIN_VREF80);
		if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
			snd_hda_codec_write(codec, pin, 0,
					    AC_VERB_SET_AMP_GAIN_MUTE,
Loading