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

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

ALSA: hda - Fix IDT/STAC multiple HP detection

Due to the recent change for multiple HP as line-out switch, only
one of the multiple headphons (usually a wrong one) is toggled
and the other pins are still disabled.  This causes the silent output
problem on some Dell laptops.

Also, the hp_switch check is screwed up when a line-in or a mic-in
jack exists.  This is added as an additional output, but hp_switch
check doesn't take it into account.

This patch fixes these issues: simplify hp_switch check by using
the NID instead of bool, and clean up / fix the toggle of HP pins
in unsol event handler code.

Reference: Novell bnc#443267
	https://bugzilla.novell.com/show_bug.cgi?id=443267



Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 4f1e6bc3
Loading
Loading
Loading
Loading
+45 −14
Original line number Original line Diff line number Diff line
@@ -212,7 +212,7 @@ struct sigmatel_spec {
	/* i/o switches */
	/* i/o switches */
	unsigned int io_switch[2];
	unsigned int io_switch[2];
	unsigned int clfe_swap;
	unsigned int clfe_swap;
	unsigned int hp_switch;
	unsigned int hp_switch; /* NID of HP as line-out */
	unsigned int aloopback;
	unsigned int aloopback;


	struct hda_pcm pcm_rec[2];	/* PCM information */
	struct hda_pcm pcm_rec[2];	/* PCM information */
@@ -2443,7 +2443,7 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct sigmatel_spec *spec = codec->spec;
	struct sigmatel_spec *spec = codec->spec;


	ucontrol->value.integer.value[0] = spec->hp_switch;
	ucontrol->value.integer.value[0] = !!spec->hp_switch;
	return 0;
	return 0;
}
}


@@ -2452,8 +2452,9 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
{
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct sigmatel_spec *spec = codec->spec;
	struct sigmatel_spec *spec = codec->spec;
	int nid = kcontrol->private_value;
 
 
	spec->hp_switch = ucontrol->value.integer.value[0];
	spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0;


	/* check to be sure that the ports are upto date with
	/* check to be sure that the ports are upto date with
	 * switch changes
	 * switch changes
@@ -2862,7 +2863,8 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
	if (cfg->hp_outs > 1) {
	if (cfg->hp_outs > 1) {
		err = stac92xx_add_control(spec,
		err = stac92xx_add_control(spec,
			STAC_CTL_WIDGET_HP_SWITCH,
			STAC_CTL_WIDGET_HP_SWITCH,
			"Headphone as Line Out Switch", 0);
			"Headphone as Line Out Switch",
			cfg->hp_pins[cfg->hp_outs - 1]);
		if (err < 0)
		if (err < 0)
			return err;
			return err;
	}
	}
@@ -3786,11 +3788,30 @@ static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
	return 0;
	return 0;
}
}


/* return non-zero if the hp-pin of the given array index isn't
 * a jack-detection target
 */
static int no_hp_sensing(struct sigmatel_spec *spec, int i)
{
	struct auto_pin_cfg *cfg = &spec->autocfg;

	/* ignore sensing of shared line and mic jacks */
	if (spec->line_switch &&
	    cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE])
		return 1;
	if (spec->mic_switch &&
	    cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC])
		return 1;
	/* ignore if the pin is set as line-out */
	if (cfg->hp_pins[i] == spec->hp_switch)
		return 1;
	return 0;
}

static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
{
{
	struct sigmatel_spec *spec = codec->spec;
	struct sigmatel_spec *spec = codec->spec;
	struct auto_pin_cfg *cfg = &spec->autocfg;
	struct auto_pin_cfg *cfg = &spec->autocfg;
	int nid = cfg->hp_pins[cfg->hp_outs - 1];
	int i, presence;
	int i, presence;


	presence = 0;
	presence = 0;
@@ -3801,15 +3822,16 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
	for (i = 0; i < cfg->hp_outs; i++) {
	for (i = 0; i < cfg->hp_outs; i++) {
		if (presence)
		if (presence)
			break;
			break;
		if (spec->hp_switch && cfg->hp_pins[i] == nid)
		if (no_hp_sensing(spec, i))
			break;
			continue;
		presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
		presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
	}
	}


	if (presence) {
	if (presence) {
		/* disable lineouts, enable hp */
		/* disable lineouts */
		if (spec->hp_switch)
		if (spec->hp_switch)
			stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN);
			stac92xx_reset_pinctl(codec, spec->hp_switch,
					      AC_PINCTL_OUT_EN);
		for (i = 0; i < cfg->line_outs; i++)
		for (i = 0; i < cfg->line_outs; i++)
			stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
			stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
						AC_PINCTL_OUT_EN);
						AC_PINCTL_OUT_EN);
@@ -3821,9 +3843,10 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
				spec->gpio_dir, spec->gpio_data &
				spec->gpio_dir, spec->gpio_data &
				~spec->eapd_mask);
				~spec->eapd_mask);
	} else {
	} else {
		/* enable lineouts, disable hp */
		/* enable lineouts */
		if (spec->hp_switch)
		if (spec->hp_switch)
			stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
			stac92xx_set_pinctl(codec, spec->hp_switch,
					    AC_PINCTL_OUT_EN);
		for (i = 0; i < cfg->line_outs; i++)
		for (i = 0; i < cfg->line_outs; i++)
			stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
			stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
						AC_PINCTL_OUT_EN);
						AC_PINCTL_OUT_EN);
@@ -3835,8 +3858,16 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
				spec->gpio_dir, spec->gpio_data |
				spec->gpio_dir, spec->gpio_data |
				spec->eapd_mask);
				spec->eapd_mask);
	}
	}
	if (!spec->hp_switch && cfg->hp_outs > 1 && presence)
	/* toggle hp outs */
		stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
	for (i = 0; i < cfg->hp_outs; i++) {
		unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
		if (no_hp_sensing(spec, i))
			continue;
		if (presence)
			stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
		else
			stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val);
	}
} 
} 


static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
static void stac92xx_pin_sense(struct hda_codec *codec, int idx)