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

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

ALSA: hda - Add support of line-out automute for Realtek



Add the common helper function and flags to support the auto-mute
per line-out jack detection, and also the mute of line-out jacks.

A few model-specific implementations are replaced with the common
helpers.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 3b8510ce
Loading
Loading
Loading
Loading
+120 −154
Original line number Original line Diff line number Diff line
@@ -392,9 +392,12 @@ struct alc_spec {
	/* for pin sensing */
	/* for pin sensing */
	unsigned int sense_updated: 1;
	unsigned int sense_updated: 1;
	unsigned int jack_present: 1;
	unsigned int jack_present: 1;
	unsigned int line_jack_present:1;
	unsigned int master_sw: 1;
	unsigned int master_sw: 1;
	unsigned int auto_mic:1;
	unsigned int auto_mic:1;
	unsigned int automute:1;	/* HP automute enabled */
	unsigned int automute:1;	/* HP automute enabled */
	unsigned int detect_line:1;	/* Line-out detection enabled */
	unsigned int automute_lines:1;	/* automute line-out as well */
	/* other flags */
	/* other flags */
	unsigned int no_analog :1; /* digital I/O only */
	unsigned int no_analog :1; /* digital I/O only */
@@ -1074,54 +1077,96 @@ static int alc_init_jacks(struct hda_codec *codec)
	return 0;
	return 0;
}
}
static void alc_hp_automute(struct hda_codec *codec)
static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
{
{
	struct alc_spec *spec = codec->spec;
	int i, present = 0;
	unsigned int mute;
	hda_nid_t nid;
	int i;
	if (!spec->automute)
	for (i = 0; i < num_pins; i++) {
		return;
		hda_nid_t nid = pins[i];
	spec->jack_present = 0;
	for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
		nid = spec->autocfg.hp_pins[i];
		if (!nid)
		if (!nid)
			break;
			break;
		snd_hda_input_jack_report(codec, nid);
		snd_hda_input_jack_report(codec, nid);
		spec->jack_present |= snd_hda_jack_detect(codec, nid);
		present |= snd_hda_jack_detect(codec, nid);
	}
	return present;
}
}
	mute = spec->jack_present ? HDA_AMP_MUTE : 0;
static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
	/* Toggle internal speakers muting */
			bool mute)
	for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
{
		nid = spec->autocfg.speaker_pins[i];
	struct alc_spec *spec = codec->spec;
	unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0;
	unsigned int pin_bits = mute ? 0 : PIN_OUT;
	int i;
	for (i = 0; i < num_pins; i++) {
		hda_nid_t nid = pins[i];
		if (!nid)
		if (!nid)
			break;
			break;
		switch (spec->automute_mode) {
		switch (spec->automute_mode) {
		case ALC_AUTOMUTE_PIN:
		case ALC_AUTOMUTE_PIN:
			snd_hda_codec_write(codec, nid, 0,
			snd_hda_codec_write(codec, nid, 0,
					    AC_VERB_SET_PIN_WIDGET_CONTROL,
					    AC_VERB_SET_PIN_WIDGET_CONTROL,
				    spec->jack_present ? 0 : PIN_OUT);
					    pin_bits);
			break;
			break;
		case ALC_AUTOMUTE_AMP:
		case ALC_AUTOMUTE_AMP:
			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
					 HDA_AMP_MUTE, mute);
						 HDA_AMP_MUTE, mute_bits);
			break;
			break;
		case ALC_AUTOMUTE_MIXER:
		case ALC_AUTOMUTE_MIXER:
			nid = spec->automute_mixer_nid[i];
			nid = spec->automute_mixer_nid[i];
			if (!nid)
			if (!nid)
				break;
				break;
			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
						 HDA_AMP_MUTE, mute);
						 HDA_AMP_MUTE, mute_bits);
			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1,
			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1,
						 HDA_AMP_MUTE, mute);
						 HDA_AMP_MUTE, mute_bits);
			break;
			break;
		}
		}
	}
	}
}
}
/* Toggle internal speakers muting */
static void update_speakers(struct hda_codec *codec)
{
	struct alc_spec *spec = codec->spec;
	do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
		    spec->autocfg.speaker_pins,
		    spec->jack_present | spec->line_jack_present);
	/* toggle line-out mutes if needed, too */
	if (!spec->automute_lines)
		return;
	do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
		    spec->autocfg.line_out_pins,
		    spec->jack_present);
}
static void alc_hp_automute(struct hda_codec *codec)
{
	struct alc_spec *spec = codec->spec;
	if (!spec->automute)
		return;
	spec->jack_present =
		detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
			     spec->autocfg.hp_pins);
	update_speakers(codec);
}
static void alc_line_automute(struct hda_codec *codec)
{
	struct alc_spec *spec = codec->spec;
	if (!spec->automute || !spec->detect_line)
		return;
	spec->line_jack_present =
		detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
			     spec->autocfg.line_out_pins);
	update_speakers(codec);
}
static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
				hda_nid_t nid)
				hda_nid_t nid)
{
{
@@ -1219,6 +1264,9 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
	case ALC880_HP_EVENT:
	case ALC880_HP_EVENT:
		alc_hp_automute(codec);
		alc_hp_automute(codec);
		break;
		break;
	case ALC880_FRONT_EVENT:
		alc_line_automute(codec);
		break;
	case ALC880_MIC_EVENT:
	case ALC880_MIC_EVENT:
		alc_mic_automute(codec);
		alc_mic_automute(codec);
		break;
		break;
@@ -1228,6 +1276,7 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
static void alc_inithook(struct hda_codec *codec)
static void alc_inithook(struct hda_codec *codec)
{
{
	alc_hp_automute(codec);
	alc_hp_automute(codec);
	alc_line_automute(codec);
	alc_mic_automute(codec);
	alc_mic_automute(codec);
}
}
@@ -9551,33 +9600,15 @@ static struct hda_channel_mode alc888_3st_hp_modes[3] = {
	{ 6, alc888_3st_hp_6ch_init },
	{ 6, alc888_3st_hp_6ch_init },
};
};
/* toggle front-jack and RCA according to the hp-jack state */
static void alc888_lenovo_ms7195_setup(struct hda_codec *codec)
static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
{
 	unsigned int present = snd_hda_jack_detect(codec, 0x1b);
	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
/* toggle RCA according to the front-jack state */
static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
{
{
 	unsigned int present = snd_hda_jack_detect(codec, 0x14);
	struct alc_spec *spec = codec->spec;
	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
static void alc883_lenovo_ms7195_unsol_event(struct hda_codec *codec,
	spec->autocfg.hp_pins[0] = 0x1b;
					     unsigned int res)
	spec->autocfg.line_out_pins[0] = 0x14;
{
	spec->autocfg.speaker_pins[0] = 0x15;
	if ((res >> 26) == ALC880_HP_EVENT)
	spec->automute = 1;
		alc888_lenovo_ms7195_front_automute(codec);
	spec->automute_mode = ALC_AUTOMUTE_AMP;
	if ((res >> 26) == ALC880_FRONT_EVENT)
		alc888_lenovo_ms7195_rca_automute(codec);
}
}
/* toggle speaker-output according to the hp-jack state */
/* toggle speaker-output according to the hp-jack state */
@@ -9645,31 +9676,17 @@ static void alc883_haier_w66_setup(struct hda_codec *codec)
	spec->automute_mode = ALC_AUTOMUTE_AMP;
	spec->automute_mode = ALC_AUTOMUTE_AMP;
}
}
static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
static void alc883_lenovo_101e_setup(struct hda_codec *codec)
{
	int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
}
static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
{
{
	int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0;
	struct alc_spec *spec = codec->spec;
	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
}
static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
	spec->autocfg.hp_pins[0] = 0x1b;
					   unsigned int res)
	spec->autocfg.line_out_pins[0] = 0x14;
{
	spec->autocfg.speaker_pins[0] = 0x15;
	if ((res >> 26) == ALC880_HP_EVENT)
	spec->automute = 1;
		alc883_lenovo_101e_all_automute(codec);
	spec->detect_line = 1;
	if ((res >> 26) == ALC880_FRONT_EVENT)
	spec->automute_lines = 1;
		alc883_lenovo_101e_ispeaker_automute(codec);
	spec->automute_mode = ALC_AUTOMUTE_AMP;
}
}
/* toggle speaker-output according to the hp-jack state */
/* toggle speaker-output according to the hp-jack state */
@@ -10584,8 +10601,9 @@ static struct alc_config_preset alc882_presets[] = {
		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
		.channel_mode = alc883_3ST_2ch_modes,
		.channel_mode = alc883_3ST_2ch_modes,
		.input_mux = &alc883_lenovo_101e_capture_source,
		.input_mux = &alc883_lenovo_101e_capture_source,
		.unsol_event = alc883_lenovo_101e_unsol_event,
		.setup = alc883_lenovo_101e_setup,
		.init_hook = alc883_lenovo_101e_all_automute,
		.unsol_event = alc_sku_unsol_event,
		.init_hook = alc_inithook,
	},
	},
	[ALC883_LENOVO_NB0763] = {
	[ALC883_LENOVO_NB0763] = {
		.mixers = { alc883_lenovo_nb0763_mixer },
		.mixers = { alc883_lenovo_nb0763_mixer },
@@ -10610,8 +10628,9 @@ static struct alc_config_preset alc882_presets[] = {
		.channel_mode = alc883_3ST_6ch_modes,
		.channel_mode = alc883_3ST_6ch_modes,
		.need_dac_fix = 1,
		.need_dac_fix = 1,
		.input_mux = &alc883_capture_source,
		.input_mux = &alc883_capture_source,
		.unsol_event = alc883_lenovo_ms7195_unsol_event,
		.unsol_event = alc_sku_unsol_event,
		.init_hook = alc888_lenovo_ms7195_front_automute,
		.setup = alc888_lenovo_ms7195_setup,
		.init_hook = alc_inithook,
	},
	},
	[ALC883_HAIER_W66] = {
	[ALC883_HAIER_W66] = {
		.mixers = { alc883_targa_2ch_mixer},
		.mixers = { alc883_targa_2ch_mixer},
@@ -18217,39 +18236,17 @@ static struct snd_kcontrol_new alc272_auto_capture_mixer[] = {
	{ } /* end */
	{ } /* end */
};
};
static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
static void alc662_lenovo_101e_setup(struct hda_codec *codec)
{
	unsigned int present;
	unsigned char bits;
	present = snd_hda_jack_detect(codec, 0x14);
	bits = present ? HDA_AMP_MUTE : 0;
	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
}
static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
{
{
	unsigned int present;
	struct alc_spec *spec = codec->spec;
	unsigned char bits;
 	present = snd_hda_jack_detect(codec, 0x1b);
	bits = present ? HDA_AMP_MUTE : 0;
	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
}
static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
	spec->autocfg.hp_pins[0] = 0x1b;
					   unsigned int res)
	spec->autocfg.line_out_pins[0] = 0x14;
{
	spec->autocfg.speaker_pins[0] = 0x15;
	if ((res >> 26) == ALC880_HP_EVENT)
	spec->automute = 1;
		alc662_lenovo_101e_all_automute(codec);
	spec->detect_line = 1;
	if ((res >> 26) == ALC880_FRONT_EVENT)
	spec->automute_lines = 1;
		alc662_lenovo_101e_ispeaker_automute(codec);
	spec->automute_mode = ALC_AUTOMUTE_AMP;
}
}
/* unsolicited event for HP jack sensing */
/* unsolicited event for HP jack sensing */
@@ -18439,53 +18436,21 @@ static void alc663_mode8_setup(struct hda_codec *codec)
	spec->auto_mic = 1;
	spec->auto_mic = 1;
}
}
static void alc663_g71v_hp_automute(struct hda_codec *codec)
static void alc663_g71v_setup(struct hda_codec *codec)
{
	unsigned int present;
	unsigned char bits;
	present = snd_hda_jack_detect(codec, 0x21);
	bits = present ? HDA_AMP_MUTE : 0;
	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
}
static void alc663_g71v_front_automute(struct hda_codec *codec)
{
	unsigned int present;
	unsigned char bits;
	present = snd_hda_jack_detect(codec, 0x15);
	bits = present ? HDA_AMP_MUTE : 0;
	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
				 HDA_AMP_MUTE, bits);
}
static void alc663_g71v_unsol_event(struct hda_codec *codec,
					   unsigned int res)
{
{
	switch (res >> 26) {
	struct alc_spec *spec = codec->spec;
	case ALC880_HP_EVENT:
	spec->autocfg.hp_pins[0] = 0x21;
		alc663_g71v_hp_automute(codec);
	spec->autocfg.line_out_pins[0] = 0x15;
		break;
	spec->autocfg.speaker_pins[0] = 0x14;
	case ALC880_FRONT_EVENT:
	spec->automute = 1;
		alc663_g71v_front_automute(codec);
	spec->automute_mode = ALC_AUTOMUTE_AMP;
		break;
	spec->detect_line = 1;
	case ALC880_MIC_EVENT:
	spec->automute_lines = 1;
		alc_mic_automute(codec);
	spec->ext_mic.pin = 0x18;
		break;
	spec->ext_mic.mux_idx = 0;
	}
	spec->int_mic.pin = 0x12;
}
	spec->int_mic.mux_idx = 9;
	spec->auto_mic = 1;
#define alc663_g71v_setup	alc663_m51va_setup
static void alc663_g71v_inithook(struct hda_codec *codec)
{
	alc663_g71v_front_automute(codec);
	alc663_g71v_hp_automute(codec);
	alc_mic_automute(codec);
}
}
#define alc663_g50v_setup	alc663_m51va_setup
#define alc663_g50v_setup	alc663_m51va_setup
@@ -18697,8 +18662,9 @@ static struct alc_config_preset alc662_presets[] = {
		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
		.channel_mode = alc662_3ST_2ch_modes,
		.channel_mode = alc662_3ST_2ch_modes,
		.input_mux = &alc662_lenovo_101e_capture_source,
		.input_mux = &alc662_lenovo_101e_capture_source,
		.unsol_event = alc662_lenovo_101e_unsol_event,
		.unsol_event = alc_sku_unsol_event,
		.init_hook = alc662_lenovo_101e_all_automute,
		.setup = alc662_lenovo_101e_setup,
		.init_hook = alc_inithook,
	},
	},
	[ALC662_ASUS_EEEPC_P701] = {
	[ALC662_ASUS_EEEPC_P701] = {
		.mixers = { alc662_eeepc_p701_mixer },
		.mixers = { alc662_eeepc_p701_mixer },
@@ -18765,9 +18731,9 @@ static struct alc_config_preset alc662_presets[] = {
		.dig_out_nid = ALC662_DIGOUT_NID,
		.dig_out_nid = ALC662_DIGOUT_NID,
		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
		.channel_mode = alc662_3ST_2ch_modes,
		.channel_mode = alc662_3ST_2ch_modes,
		.unsol_event = alc663_g71v_unsol_event,
		.unsol_event = alc_sku_unsol_event,
		.setup = alc663_g71v_setup,
		.setup = alc663_g71v_setup,
		.init_hook = alc663_g71v_inithook,
		.init_hook = alc_inithook,
	},
	},
	[ALC663_ASUS_H13] = {
	[ALC663_ASUS_H13] = {
		.mixers = { alc663_m51va_mixer },
		.mixers = { alc663_m51va_mixer },