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

Commit eb06ed8f authored by Takashi Iwai's avatar Takashi Iwai Committed by Jaroslav Kysela
Browse files

[ALSA] hda-codec - Support multiple headphone pins



Some machines have multiple headpohne pins (usually on the lpatop
and on the docking station) while the current hda-codec driver
assumes a single headphone pin.  Now it supports multiple hp pins
(at least for detection).
The sigmatel 92xx code supports this new multiple hp pins.
It detects all hp pins for auto-muting, too.
Also, the driver checks speaker pins in addition.  In some cases,
all line-out, speaker and hp-pins coexist.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent 33ef7651
Loading
Loading
Loading
Loading
+15 −8
Original line number Original line Diff line number Diff line
@@ -2012,7 +2012,7 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
 * in the order of front, rear, CLFE, side, ...
 * in the order of front, rear, CLFE, side, ...
 *
 *
 * If more extra outputs (speaker and headphone) are found, the pins are
 * If more extra outputs (speaker and headphone) are found, the pins are
 * assisnged to hp_pin and speaker_pins[], respectively.  If no line-out jack
 * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
 * is detected, one of speaker of HP pins is assigned as the primary
 * is detected, one of speaker of HP pins is assigned as the primary
 * output, i.e. to line_out_pins[0].  So, line_outs is always positive
 * output, i.e. to line_out_pins[0].  So, line_outs is always positive
 * if any analog output exists.
 * if any analog output exists.
@@ -2074,7 +2074,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
			cfg->speaker_outs++;
			cfg->speaker_outs++;
			break;
			break;
		case AC_JACK_HP_OUT:
		case AC_JACK_HP_OUT:
			cfg->hp_pin = nid;
			if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
				continue;
			cfg->hp_pins[cfg->hp_outs] = nid;
			cfg->hp_outs++;
			break;
			break;
		case AC_JACK_MIC_IN:
		case AC_JACK_MIC_IN:
			if (loc == AC_JACK_LOC_FRONT)
			if (loc == AC_JACK_LOC_FRONT)
@@ -2147,8 +2150,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
		   cfg->speaker_outs, cfg->speaker_pins[0],
		   cfg->speaker_outs, cfg->speaker_pins[0],
		   cfg->speaker_pins[1], cfg->speaker_pins[2],
		   cfg->speaker_pins[1], cfg->speaker_pins[2],
		   cfg->speaker_pins[3], cfg->speaker_pins[4]);
		   cfg->speaker_pins[3], cfg->speaker_pins[4]);
	snd_printd("   hp=0x%x, dig_out=0x%x, din_in=0x%x\n",
	snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
		   cfg->hp_pin, cfg->dig_out_pin, cfg->dig_in_pin);
		   cfg->hp_outs, cfg->hp_pins[0],
		   cfg->hp_pins[1], cfg->hp_pins[2],
		   cfg->hp_pins[3], cfg->hp_pins[4]);
	snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
	snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
		   " cd=0x%x, aux=0x%x\n",
		   " cd=0x%x, aux=0x%x\n",
		   cfg->input_pins[AUTO_PIN_MIC],
		   cfg->input_pins[AUTO_PIN_MIC],
@@ -2169,10 +2174,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
			       sizeof(cfg->speaker_pins));
			       sizeof(cfg->speaker_pins));
			cfg->speaker_outs = 0;
			cfg->speaker_outs = 0;
			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
		} else if (cfg->hp_pin) {
		} else if (cfg->hp_outs) {
			cfg->line_outs = 1;
			cfg->line_outs = cfg->hp_outs;
			cfg->line_out_pins[0] = cfg->hp_pin;
			memcpy(cfg->line_out_pins, cfg->hp_pins,
			cfg->hp_pin = 0;
			       sizeof(cfg->hp_pins));
			cfg->hp_outs = 0;
			memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
		}
		}
	}
	}


+2 −1
Original line number Original line Diff line number Diff line
@@ -229,7 +229,8 @@ struct auto_pin_cfg {
	hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */
	hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */
	int speaker_outs;
	int speaker_outs;
	hda_nid_t speaker_pins[5];
	hda_nid_t speaker_pins[5];
	hda_nid_t hp_pin;
	int hp_outs;
	hda_nid_t hp_pins[5];
	hda_nid_t input_pins[AUTO_PIN_LAST];
	hda_nid_t input_pins[AUTO_PIN_LAST];
	hda_nid_t dig_out_pin;
	hda_nid_t dig_out_pin;
	hda_nid_t dig_in_pin;
	hda_nid_t dig_in_pin;
+2 −2
Original line number Original line Diff line number Diff line
@@ -2471,7 +2471,7 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
	pin = spec->autocfg.speaker_pins[0];
	pin = spec->autocfg.speaker_pins[0];
	if (pin) /* connect to front */
	if (pin) /* connect to front */
		ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
		ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
	pin = spec->autocfg.hp_pin;
	pin = spec->autocfg.hp_pins[0];
	if (pin) /* connect to front */
	if (pin) /* connect to front */
		ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
		ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
}
}
@@ -2523,7 +2523,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
	    (err = ad1988_auto_create_extra_out(codec,
	    (err = ad1988_auto_create_extra_out(codec,
						spec->autocfg.speaker_pins[0],
						spec->autocfg.speaker_pins[0],
						"Speaker")) < 0 ||
						"Speaker")) < 0 ||
	    (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pin,
	    (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
						"Headphone")) < 0 ||
						"Headphone")) < 0 ||
	    (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
	    (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
		return err;
		return err;
+9 −9
Original line number Original line Diff line number Diff line
@@ -2753,7 +2753,7 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
	pin = spec->autocfg.speaker_pins[0];
	pin = spec->autocfg.speaker_pins[0];
	if (pin) /* connect to front */
	if (pin) /* connect to front */
		alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
		alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
	pin = spec->autocfg.hp_pin;
	pin = spec->autocfg.hp_pins[0];
	if (pin) /* connect to front */
	if (pin) /* connect to front */
		alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
		alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
}
}
@@ -2794,7 +2794,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
	    (err = alc880_auto_create_extra_out(spec,
	    (err = alc880_auto_create_extra_out(spec,
						spec->autocfg.speaker_pins[0],
						spec->autocfg.speaker_pins[0],
						"Speaker")) < 0 ||
						"Speaker")) < 0 ||
	    (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pin,
	    (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
						"Headphone")) < 0 ||
						"Headphone")) < 0 ||
	    (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
	    (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
		return err;
		return err;
@@ -3736,7 +3736,7 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
			return err;
			return err;
	}
	}


	nid = cfg->hp_pin;
	nid = cfg->hp_pins[0];
	if (nid) {
	if (nid) {
		err = alc260_add_playback_controls(spec, nid, "Headphone");
		err = alc260_add_playback_controls(spec, nid, "Headphone");
		if (err < 0)
		if (err < 0)
@@ -3806,7 +3806,7 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
	if (nid)
	if (nid)
		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);


	nid = spec->autocfg.hp_pin;
	nid = spec->autocfg.hp_pins[0];
	if (nid)
	if (nid)
		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
}	
}	
@@ -4526,7 +4526,7 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
	struct alc_spec *spec = codec->spec;
	struct alc_spec *spec = codec->spec;
	hda_nid_t pin;
	hda_nid_t pin;


	pin = spec->autocfg.hp_pin;
	pin = spec->autocfg.hp_pins[0];
	if (pin) /* connect to front */
	if (pin) /* connect to front */
		alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
		alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
}
}
@@ -5207,7 +5207,7 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec)
	struct alc_spec *spec = codec->spec;
	struct alc_spec *spec = codec->spec;
	hda_nid_t pin;
	hda_nid_t pin;


	pin = spec->autocfg.hp_pin;
	pin = spec->autocfg.hp_pins[0];
	if (pin) /* connect to front */
	if (pin) /* connect to front */
		/* use dac 0 */
		/* use dac 0 */
		alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
		alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
@@ -5630,7 +5630,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct
				return err;
				return err;
		}
		}
	}
	}
	nid = cfg->hp_pin;
	nid = cfg->hp_pins[0];
	if (nid) {
	if (nid) {
		/* spec->multiout.hp_nid = 2; */
		/* spec->multiout.hp_nid = 2; */
		if (nid == 0x16) {
		if (nid == 0x16) {
@@ -6630,7 +6630,7 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
	struct alc_spec *spec = codec->spec;
	struct alc_spec *spec = codec->spec;
	hda_nid_t pin;
	hda_nid_t pin;


	pin = spec->autocfg.hp_pin;
	pin = spec->autocfg.hp_pins[0];
	if (pin) /* connect to front */
	if (pin) /* connect to front */
		alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
		alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
}
}
@@ -6665,7 +6665,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)


	if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
	if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
	    (err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
	    (err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
	    (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 ||
	    (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0])) < 0 ||
	    (err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
	    (err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
		return err;
		return err;


+136 −66
Original line number Original line Diff line number Diff line
@@ -1011,11 +1011,29 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
	return 0;
	return 0;
}
}


/* create volume control/switch for the given prefx type */
static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
{
	char name[32];
	int err;

	sprintf(name, "%s Playback Volume", pfx);
	err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
				   HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
	if (err < 0)
		return err;
	sprintf(name, "%s Playback Switch", pfx);
	err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
				   HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
	if (err < 0)
		return err;
	return 0;
}

/* add playback controls from the parsed DAC table */
/* add playback controls from the parsed DAC table */
static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec,
static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec,
					       const struct auto_pin_cfg *cfg)
					       const struct auto_pin_cfg *cfg)
{
{
	char name[32];
	static const char *chname[4] = {
	static const char *chname[4] = {
		"Front", "Surround", NULL /*CLFE*/, "Side"
		"Front", "Surround", NULL /*CLFE*/, "Side"
	};
	};
@@ -1030,26 +1048,15 @@ static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec,


		if (i == 2) {
		if (i == 2) {
			/* Center/LFE */
			/* Center/LFE */
			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Center Playback Volume",
			err = create_controls(spec, "Center", nid, 1);
					       HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
			if (err < 0)
				return err;
			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "LFE Playback Volume",
					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
				return err;
			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Center Playback Switch",
					       HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
				return err;
				return err;
			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "LFE Playback Switch",
			err = create_controls(spec, "LFE", nid, 2);
					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
			if (err < 0)
				return err;
				return err;
		} else {
		} else {
			sprintf(name, "%s Playback Volume", chname[i]);
			err = create_controls(spec, chname[i], nid, 3);
			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
			if (err < 0)
					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
				return err;
			sprintf(name, "%s Playback Switch", chname[i]);
			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
				return err;
				return err;
		}
		}
	}
	}
@@ -1065,39 +1072,85 @@ static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec,
	return 0;
	return 0;
}
}


/* add playback controls for HP output */
static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin_cfg *cfg)
{
{
	struct sigmatel_spec *spec = codec->spec;
	int i;
	hda_nid_t pin = cfg->hp_pin;
	hda_nid_t nid;
	int i, err;
	unsigned int wid_caps;


	if (! pin)
	for (i = 0; i < spec->multiout.num_dacs; i++) {
		if (spec->multiout.dac_nids[i] == nid)
			return 1;
	}
	if (spec->multiout.hp_nid == nid)
		return 1;
	return 0;
	return 0;
}


	wid_caps = get_wcaps(codec, pin);
static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
{
	if (!spec->multiout.hp_nid)
		spec->multiout.hp_nid = nid;
	else if (spec->multiout.num_dacs > 4) {
		printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
		return 1;
	} else {
		spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
		spec->multiout.num_dacs++;
	}
	return 0;
}

/* add playback controls for Speaker and HP outputs */
static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
					struct auto_pin_cfg *cfg)
{
	struct sigmatel_spec *spec = codec->spec;
	hda_nid_t nid;
	int i, old_num_dacs, err;

	old_num_dacs = spec->multiout.num_dacs;
	for (i = 0; i < cfg->hp_outs; i++) {
		unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
		if (wid_caps & AC_WCAP_UNSOL_CAP)
		if (wid_caps & AC_WCAP_UNSOL_CAP)
			spec->hp_detect = 1;
			spec->hp_detect = 1;

		nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
	nid = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
	for (i = 0; i < cfg->line_outs; i++) {
		if (check_in_dac_nids(spec, nid))
		if (! spec->multiout.dac_nids[i])
			nid = 0;
		if (! nid)
			continue;
			continue;
		if (spec->multiout.dac_nids[i] == nid)
		add_spec_dacs(spec, nid);
			return 0;
	}
	for (i = 0; i < cfg->speaker_outs; i++) {
		nid = snd_hda_codec_read(codec, cfg->speaker_pins[0], 0,
					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
		if (check_in_dac_nids(spec, nid))
			nid = 0;
		if (check_in_dac_nids(spec, nid))
			nid = 0;
		if (! nid)
			continue;
		add_spec_dacs(spec, nid);
	}
	}


	spec->multiout.hp_nid = nid;
	for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {

		static const char *pfxs[] = {
	/* control HP volume/switch on the output mixer amp */
			"Speaker", "External Speaker", "Speaker2",
	if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Headphone Playback Volume",
		};
					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
		err = create_controls(spec, pfxs[i - old_num_dacs],
				      spec->multiout.dac_nids[i], 3);
		if (err < 0)
			return err;
			return err;
	if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
	}
					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
	if (spec->multiout.hp_nid) {
		const char *pfx;
		if (old_num_dacs == spec->multiout.num_dacs)
			pfx = "Master";
		else
			pfx = "Headphone";
		err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
		if (err < 0)
			return err;
			return err;
	}


	return 0;
	return 0;
}
}
@@ -1160,12 +1213,21 @@ static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
{
{
	struct sigmatel_spec *spec = codec->spec;
	struct sigmatel_spec *spec = codec->spec;
	hda_nid_t pin;
	int i;


	pin = spec->autocfg.hp_pin;
	for (i = 0; i < spec->autocfg.hp_outs; i++) {
		hda_nid_t pin;
		pin = spec->autocfg.hp_pins[i];
		if (pin) /* connect to front */
		if (pin) /* connect to front */
			stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
			stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
	}
	}
	for (i = 0; i < spec->autocfg.speaker_outs; i++) {
		hda_nid_t pin;
		pin = spec->autocfg.speaker_pins[i];
		if (pin) /* connect to front */
			stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
	}
}


static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
{
{
@@ -1210,7 +1272,7 @@ static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
					struct auto_pin_cfg *cfg)
					struct auto_pin_cfg *cfg)
{
{
	struct sigmatel_spec *spec = codec->spec;
	struct sigmatel_spec *spec = codec->spec;
	hda_nid_t pin = cfg->hp_pin;
	hda_nid_t pin = cfg->hp_pins[0];
	unsigned int wid_caps;
	unsigned int wid_caps;


	if (! pin)
	if (! pin)
@@ -1266,16 +1328,7 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
	}
	}


	if (lfe_pin) {
	if (lfe_pin) {
		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
		err = create_controls(spec, "LFE", lfe_pin, 1);
					   "LFE Playback Volume",
					   HDA_COMPOSE_AMP_VAL(lfe_pin, 1, 0,
							       HDA_OUTPUT));
		if (err < 0)
			return err;
		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
					   "LFE Playback Switch",
					   HDA_COMPOSE_AMP_VAL(lfe_pin, 1, 0,
							       HDA_OUTPUT));
		if (err < 0)
		if (err < 0)
			return err;
			return err;
	}
	}
@@ -1363,7 +1416,9 @@ static int stac92xx_init(struct hda_codec *codec)
	/* set up pins */
	/* set up pins */
	if (spec->hp_detect) {
	if (spec->hp_detect) {
		/* Enable unsolicited responses on the HP widget */
		/* Enable unsolicited responses on the HP widget */
		snd_hda_codec_write(codec, cfg->hp_pin, 0,
		for (i = 0; i < cfg->hp_outs; i++)
			if (get_wcaps(codec, cfg->hp_pins[i]) & AC_WCAP_UNSOL_CAP)
				snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
						    AC_VERB_SET_UNSOLICITED_ENABLE,
						    AC_VERB_SET_UNSOLICITED_ENABLE,
						    STAC_UNSOL_ENABLE);
						    STAC_UNSOL_ENABLE);
		/* fake event to set up pins */
		/* fake event to set up pins */
@@ -1447,21 +1502,36 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
	if ((res >> 26) != STAC_HP_EVENT)
	if ((res >> 26) != STAC_HP_EVENT)
		return;
		return;


	presence = snd_hda_codec_read(codec, cfg->hp_pin, 0,
	presence = 0;
			AC_VERB_GET_PIN_SENSE, 0x00) >> 31;
	for (i = 0; i < cfg->hp_outs; i++) {
		int p = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
					   AC_VERB_GET_PIN_SENSE, 0x00);
		if (p & (1 << 31))
			presence++;
	}


	if (presence) {
	if (presence) {
		/* disable lineouts, enable hp */
		/* disable lineouts, enable hp */
		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);
		stac92xx_set_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN);
		for (i = 0; i < cfg->speaker_outs; i++)
			stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
						AC_PINCTL_OUT_EN);
		for (i = 0; i < cfg->hp_outs; i++)
			stac92xx_set_pinctl(codec, cfg->hp_pins[i],
					    AC_PINCTL_OUT_EN);
	} else {
	} else {
		/* enable lineouts, disable hp */
		/* enable lineouts, disable hp */
		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);
		stac92xx_reset_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN);
		for (i = 0; i < cfg->speaker_outs; i++)
			stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
						AC_PINCTL_OUT_EN);
		for (i = 0; i < cfg->hp_outs; i++)
			stac92xx_reset_pinctl(codec, cfg->hp_pins[i],
					      AC_PINCTL_OUT_EN);
	}
	}
} 
}