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: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2011-04-28 14:41:52 +02:00
parent 3b8510ce97
commit e6a5e1b709
1 changed files with 120 additions and 154 deletions

View File

@ -392,9 +392,12 @@ struct alc_spec {
/* for pin sensing */
unsigned int sense_updated: 1;
unsigned int jack_present: 1;
unsigned int line_jack_present:1;
unsigned int master_sw: 1;
unsigned int auto_mic:1;
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 */
unsigned int no_analog :1; /* digital I/O only */
@ -1074,54 +1077,96 @@ static int alc_init_jacks(struct hda_codec *codec)
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;
unsigned int mute;
hda_nid_t nid;
int i;
int i, present = 0;
if (!spec->automute)
return;
spec->jack_present = 0;
for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
nid = spec->autocfg.hp_pins[i];
for (i = 0; i < num_pins; i++) {
hda_nid_t nid = pins[i];
if (!nid)
break;
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;
/* Toggle internal speakers muting */
for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
nid = spec->autocfg.speaker_pins[i];
static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
bool mute)
{
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)
break;
switch (spec->automute_mode) {
case ALC_AUTOMUTE_PIN:
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
spec->jack_present ? 0 : PIN_OUT);
AC_VERB_SET_PIN_WIDGET_CONTROL,
pin_bits);
break;
case ALC_AUTOMUTE_AMP:
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
HDA_AMP_MUTE, mute);
HDA_AMP_MUTE, mute_bits);
break;
case ALC_AUTOMUTE_MIXER:
nid = spec->automute_mixer_nid[i];
if (!nid)
break;
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,
HDA_AMP_MUTE, mute);
HDA_AMP_MUTE, mute_bits);
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,
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:
alc_hp_automute(codec);
break;
case ALC880_FRONT_EVENT:
alc_line_automute(codec);
break;
case ALC880_MIC_EVENT:
alc_mic_automute(codec);
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)
{
alc_hp_automute(codec);
alc_line_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 },
};
/* toggle front-jack and RCA according to the hp-jack state */
static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
static void alc888_lenovo_ms7195_setup(struct hda_codec *codec)
{
unsigned int present = snd_hda_jack_detect(codec, 0x1b);
struct alc_spec *spec = codec->spec;
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);
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,
unsigned int res)
{
if ((res >> 26) == ALC880_HP_EVENT)
alc888_lenovo_ms7195_front_automute(codec);
if ((res >> 26) == ALC880_FRONT_EVENT)
alc888_lenovo_ms7195_rca_automute(codec);
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.line_out_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
spec->automute = 1;
spec->automute_mode = ALC_AUTOMUTE_AMP;
}
/* 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;
}
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;
struct alc_spec *spec = codec->spec;
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;
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,
unsigned int res)
{
if ((res >> 26) == ALC880_HP_EVENT)
alc883_lenovo_101e_all_automute(codec);
if ((res >> 26) == ALC880_FRONT_EVENT)
alc883_lenovo_101e_ispeaker_automute(codec);
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.line_out_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
spec->automute = 1;
spec->detect_line = 1;
spec->automute_lines = 1;
spec->automute_mode = ALC_AUTOMUTE_AMP;
}
/* 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),
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_lenovo_101e_capture_source,
.unsol_event = alc883_lenovo_101e_unsol_event,
.init_hook = alc883_lenovo_101e_all_automute,
.setup = alc883_lenovo_101e_setup,
.unsol_event = alc_sku_unsol_event,
.init_hook = alc_inithook,
},
[ALC883_LENOVO_NB0763] = {
.mixers = { alc883_lenovo_nb0763_mixer },
@ -10610,8 +10628,9 @@ static struct alc_config_preset alc882_presets[] = {
.channel_mode = alc883_3ST_6ch_modes,
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
.unsol_event = alc883_lenovo_ms7195_unsol_event,
.init_hook = alc888_lenovo_ms7195_front_automute,
.unsol_event = alc_sku_unsol_event,
.setup = alc888_lenovo_ms7195_setup,
.init_hook = alc_inithook,
},
[ALC883_HAIER_W66] = {
.mixers = { alc883_targa_2ch_mixer},
@ -18217,39 +18236,17 @@ static struct snd_kcontrol_new alc272_auto_capture_mixer[] = {
{ } /* 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;
struct alc_spec *spec = codec->spec;
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;
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,
unsigned int res)
{
if ((res >> 26) == ALC880_HP_EVENT)
alc662_lenovo_101e_all_automute(codec);
if ((res >> 26) == ALC880_FRONT_EVENT)
alc662_lenovo_101e_ispeaker_automute(codec);
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.line_out_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
spec->automute = 1;
spec->detect_line = 1;
spec->automute_lines = 1;
spec->automute_mode = ALC_AUTOMUTE_AMP;
}
/* unsolicited event for HP jack sensing */
@ -18439,53 +18436,21 @@ static void alc663_mode8_setup(struct hda_codec *codec)
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) {
case ALC880_HP_EVENT:
alc663_g71v_hp_automute(codec);
break;
case ALC880_FRONT_EVENT:
alc663_g71v_front_automute(codec);
break;
case ALC880_MIC_EVENT:
alc_mic_automute(codec);
break;
}
}
#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);
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x21;
spec->autocfg.line_out_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
spec->automute = 1;
spec->automute_mode = ALC_AUTOMUTE_AMP;
spec->detect_line = 1;
spec->automute_lines = 1;
spec->ext_mic.pin = 0x18;
spec->ext_mic.mux_idx = 0;
spec->int_mic.pin = 0x12;
spec->int_mic.mux_idx = 9;
spec->auto_mic = 1;
}
#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),
.channel_mode = alc662_3ST_2ch_modes,
.input_mux = &alc662_lenovo_101e_capture_source,
.unsol_event = alc662_lenovo_101e_unsol_event,
.init_hook = alc662_lenovo_101e_all_automute,
.unsol_event = alc_sku_unsol_event,
.setup = alc662_lenovo_101e_setup,
.init_hook = alc_inithook,
},
[ALC662_ASUS_EEEPC_P701] = {
.mixers = { alc662_eeepc_p701_mixer },
@ -18765,9 +18731,9 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(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,
.init_hook = alc663_g71v_inithook,
.init_hook = alc_inithook,
},
[ALC663_ASUS_H13] = {
.mixers = { alc663_m51va_mixer },