aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda/patch_realtek.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
-rw-r--r--sound/pci/hda/patch_realtek.c4144
1 files changed, 2034 insertions, 2110 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 30eeb304351..7ed47f66ddd 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -208,12 +208,6 @@ enum {
ALC885_MBP3,
ALC885_MB5,
ALC885_IMAC24,
- ALC882_AUTO,
- ALC882_MODEL_LAST,
-};
-
-/* ALC883 models */
-enum {
ALC883_3ST_2ch_DIG,
ALC883_3ST_6ch_DIG,
ALC883_3ST_6ch,
@@ -226,6 +220,7 @@ enum {
ALC888_ACER_ASPIRE_4930G,
ALC888_ACER_ASPIRE_6530G,
ALC888_ACER_ASPIRE_8930G,
+ ALC888_ACER_ASPIRE_7730G,
ALC883_MEDION,
ALC883_MEDION_MD2,
ALC883_LAPTOP_EAPD,
@@ -237,17 +232,20 @@ enum {
ALC888_3ST_HP,
ALC888_6ST_DELL,
ALC883_MITAC,
+ ALC883_CLEVO_M540R,
ALC883_CLEVO_M720,
ALC883_FUJITSU_PI2515,
ALC888_FUJITSU_XA3530,
ALC883_3ST_6ch_INTEL,
+ ALC889A_INTEL,
+ ALC889_INTEL,
ALC888_ASUS_M90V,
ALC888_ASUS_EEE1601,
ALC889A_MB31,
ALC1200_ASUS_P5Q,
ALC883_SONY_VAIO_TT,
- ALC883_AUTO,
- ALC883_MODEL_LAST,
+ ALC882_AUTO,
+ ALC882_MODEL_LAST,
};
/* for GPIO Poll */
@@ -262,6 +260,14 @@ enum {
ALC_INIT_GPIO3,
};
+struct alc_mic_route {
+ hda_nid_t pin;
+ unsigned char mux_idx;
+ unsigned char amix_idx;
+};
+
+#define MUX_IDX_UNDEF ((unsigned char)-1)
+
struct alc_spec {
/* codec parameterization */
struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
@@ -304,6 +310,8 @@ struct alc_spec {
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
unsigned int cur_mux[3];
+ struct alc_mic_route ext_mic;
+ struct alc_mic_route int_mic;
/* channel model */
const struct hda_channel_mode *channel_mode;
@@ -320,6 +328,8 @@ struct alc_spec {
struct snd_array kctls;
struct hda_input_mux private_imux[3];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
/* hooks */
void (*init_hook)(struct hda_codec *codec);
@@ -329,6 +339,7 @@ struct alc_spec {
unsigned int sense_updated: 1;
unsigned int jack_present: 1;
unsigned int master_sw: 1;
+ unsigned int auto_mic:1;
/* other flags */
unsigned int no_analog :1; /* digital I/O only */
@@ -370,6 +381,7 @@ struct alc_config_preset {
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
void (*unsol_event)(struct hda_codec *, unsigned int);
+ void (*setup)(struct hda_codec *);
void (*init_hook)(struct hda_codec *);
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_amp_list *loopbacks;
@@ -417,7 +429,7 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
imux = &spec->input_mux[mux_idx];
- type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ type = get_wcaps_type(get_wcaps(codec, nid));
if (type == AC_WID_AUD_MIX) {
/* Matrix-mixer style (e.g. ALC882) */
unsigned int *cur_val = &spec->cur_mux[adc_idx];
@@ -842,9 +854,10 @@ static void print_realtek_coef(struct snd_info_buffer *buffer,
/*
* set up from the preset table
*/
-static void setup_preset(struct alc_spec *spec,
+static void setup_preset(struct hda_codec *codec,
const struct alc_config_preset *preset)
{
+ struct alc_spec *spec = codec->spec;
int i;
for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
@@ -886,6 +899,9 @@ static void setup_preset(struct alc_spec *spec,
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = preset->loopbacks;
#endif
+
+ if (preset->setup)
+ preset->setup(codec);
}
/* Enable GPIO mask and set output */
@@ -965,30 +981,64 @@ static void alc_automute_pin(struct hda_codec *codec)
}
}
-#if 0 /* it's broken in some cases -- temporarily disabled */
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid)
+{
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ int i, nums;
+
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ return -1;
+}
+
static void alc_mic_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present;
- unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- unsigned int mix_nid = spec->capsrc_nids[0];
- unsigned int capsrc_idx_mic, capsrc_idx_fmic;
-
- capsrc_idx_mic = mic_nid - 0x18;
- capsrc_idx_fmic = fmic_nid - 0x18;
- present = snd_hda_codec_read(codec, mic_nid, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0));
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ struct alc_mic_route *dead, *alive;
+ unsigned int present, type;
+ hda_nid_t cap_nid;
+
+ if (!spec->auto_mic)
+ return;
+ if (!spec->int_mic.pin || !spec->ext_mic.pin)
+ return;
+ if (snd_BUG_ON(!spec->adc_nids))
+ return;
+
+ cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
+
+ present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ present &= AC_PINSENSE_PRESENCE;
+ if (present) {
+ alive = &spec->ext_mic;
+ dead = &spec->int_mic;
+ } else {
+ alive = &spec->int_mic;
+ dead = &spec->ext_mic;
+ }
+
+ type = get_wcaps_type(get_wcaps(codec, cap_nid));
+ if (type == AC_WID_AUD_MIX) {
+ /* Matrix-mixer style (e.g. ALC882) */
+ snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+ alive->mux_idx,
+ HDA_AMP_MUTE, 0);
+ snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+ dead->mux_idx,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* MUX style (e.g. ALC880) */
+ snd_hda_codec_write_cache(codec, cap_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ alive->mux_idx);
+ }
+
+ /* FIXME: analog mixer */
}
-#else
-#define alc_mic_automute(codec) do {} while(0) /* NOP */
-#endif /* disabled */
/* unsolicited event for HP jack sensing */
static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -1031,6 +1081,16 @@ static void alc888_coef_init(struct hda_codec *codec)
AC_VERB_SET_PROC_COEF, 0x3030);
}
+static void alc889_coef_init(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+ tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010);
+}
+
static void alc_auto_init_amp(struct hda_codec *codec, int type)
{
unsigned int tmp;
@@ -1088,15 +1148,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
case 0x10ec0885:
case 0x10ec0887:
case 0x10ec0889:
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x20, 0,
- AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF,
- tmp | 0x2010);
+ alc889_coef_init(codec);
break;
case 0x10ec0888:
alc888_coef_init(codec);
@@ -1142,6 +1194,55 @@ static void alc_init_auto_hp(struct hda_codec *codec)
spec->unsol_event = alc_sku_unsol_event;
}
+static void alc_init_auto_mic(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t fixed, ext;
+ int i;
+
+ /* there must be only two mic inputs exclusively */
+ for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
+ if (cfg->input_pins[i])
+ return;
+
+ fixed = ext = 0;
+ for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
+ hda_nid_t nid = cfg->input_pins[i];
+ unsigned int defcfg;
+ if (!nid)
+ return;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ switch (get_defcfg_connect(defcfg)) {
+ case AC_JACK_PORT_FIXED:
+ if (fixed)
+ return; /* already occupied */
+ fixed = nid;
+ break;
+ case AC_JACK_PORT_COMPLEX:
+ if (ext)
+ return; /* already occupied */
+ ext = nid;
+ break;
+ default:
+ return; /* invalid entry */
+ }
+ }
+ if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
+ return; /* no unsol support */
+ snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
+ ext, fixed);
+ spec->ext_mic.pin = ext;
+ spec->int_mic.pin = fixed;
+ spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+ spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+ spec->auto_mic = 1;
+ snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | ALC880_MIC_EVENT);
+ spec->unsol_event = alc_sku_unsol_event;
+}
+
/* check subsystem ID and set up device-specific initialization;
* return 1 if initialized, 0 if invalid SSID
*/
@@ -1243,6 +1344,7 @@ do_sku:
}
alc_init_auto_hp(codec);
+ alc_init_auto_mic(codec);
return 1;
}
@@ -1255,6 +1357,7 @@ static void alc_ssid_check(struct hda_codec *codec,
"Enable default setup for auto mode as fallback\n");
spec->init_amp = ALC_INIT_DEFAULT;
alc_init_auto_hp(codec);
+ alc_init_auto_mic(codec);
}
}
@@ -1436,7 +1539,25 @@ static void alc_automute_amp_unsol_event(struct hda_codec *codec,
alc_automute_amp(codec);
}
-static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
+static void alc889_automute_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->autocfg.hp_pins[0] = 0x15;
+ spec->autocfg.speaker_pins[0] = 0x14;
+ spec->autocfg.speaker_pins[1] = 0x16;
+ spec->autocfg.speaker_pins[2] = 0x17;
+ spec->autocfg.speaker_pins[3] = 0x19;
+ spec->autocfg.speaker_pins[4] = 0x1a;
+}
+
+static void alc889_intel_init_hook(struct hda_codec *codec)
+{
+ alc889_coef_init(codec);
+ alc_automute_amp(codec);
+}
+
+static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1444,7 +1565,6 @@ static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
spec->autocfg.hp_pins[1] = 0x1b; /* hp */
spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
spec->autocfg.speaker_pins[1] = 0x15; /* bass */
- alc_automute_amp(codec);
}
/*
@@ -1643,16 +1763,15 @@ static struct snd_kcontrol_new alc888_base_mixer[] = {
{ } /* end */
};
-static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_amp(codec);
}
-static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1660,10 +1779,9 @@ static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x16;
spec->autocfg.speaker_pins[2] = 0x17;
- alc_automute_amp(codec);
}
-static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
+static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1671,7 +1789,6 @@ static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x16;
spec->autocfg.speaker_pins[2] = 0x1b;
- alc_automute_amp(codec);
}
/*
@@ -2651,13 +2768,17 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
}
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x16;
+}
+
+static void alc880_uniwill_init_hook(struct hda_codec *codec)
+{
alc_automute_amp(codec);
alc880_uniwill_mic_automute(codec);
}
@@ -2678,13 +2799,12 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec,
}
}
-static void alc880_uniwill_p53_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_p53_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
- alc_automute_amp(codec);
}
static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -2947,13 +3067,12 @@ static struct hda_verb alc880_lg_init_verbs[] = {
};
/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_init_hook(struct hda_codec *codec)
+static void alc880_lg_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x17;
- alc_automute_amp(codec);
}
/*
@@ -3032,13 +3151,12 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = {
};
/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_init_hook(struct hda_codec *codec)
+static void alc880_lg_lw_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_amp(codec);
}
static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
@@ -3104,13 +3222,12 @@ static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
alc880_medion_rim_automute(codec);
}
-static void alc880_medion_rim_init_hook(struct hda_codec *codec)
+static void alc880_medion_rim_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x1b;
- alc880_medion_rim_automute(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -3977,7 +4094,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_2_jack_modes,
.input_mux = &alc880_f1734_capture_source,
.unsol_event = alc880_uniwill_p53_unsol_event,
- .init_hook = alc880_uniwill_p53_init_hook,
+ .setup = alc880_uniwill_p53_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_ASUS] = {
.mixers = { alc880_asus_mixer },
@@ -4054,6 +4172,7 @@ static struct alc_config_preset alc880_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc880_capture_source,
.unsol_event = alc880_uniwill_unsol_event,
+ .setup = alc880_uniwill_setup,
.init_hook = alc880_uniwill_init_hook,
},
[ALC880_UNIWILL_P53] = {
@@ -4066,7 +4185,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_threestack_modes,
.input_mux = &alc880_capture_source,
.unsol_event = alc880_uniwill_p53_unsol_event,
- .init_hook = alc880_uniwill_p53_init_hook,
+ .setup = alc880_uniwill_p53_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_FUJITSU] = {
.mixers = { alc880_fujitsu_mixer },
@@ -4080,7 +4200,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_2_jack_modes,
.input_mux = &alc880_capture_source,
.unsol_event = alc880_uniwill_p53_unsol_event,
- .init_hook = alc880_uniwill_p53_init_hook,
+ .setup = alc880_uniwill_p53_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_CLEVO] = {
.mixers = { alc880_three_stack_mixer },
@@ -4106,7 +4227,8 @@ static struct alc_config_preset alc880_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc880_lg_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc880_lg_init_hook,
+ .setup = alc880_lg_setup,
+ .init_hook = alc_automute_amp,
#ifdef CONFIG_SND_HDA_POWER_SAVE
.loopbacks = alc880_lg_loopbacks,
#endif
@@ -4122,7 +4244,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_lg_lw_modes,
.input_mux = &alc880_lg_lw_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc880_lg_lw_init_hook,
+ .setup = alc880_lg_lw_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_MEDION_RIM] = {
.mixers = { alc880_medion_rim_mixer },
@@ -4136,7 +4259,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_2_jack_modes,
.input_mux = &alc880_medion_rim_capture_source,
.unsol_event = alc880_medion_rim_unsol_event,
- .init_hook = alc880_medion_rim_init_hook,
+ .setup = alc880_medion_rim_setup,
+ .init_hook = alc880_medion_rim_automute,
},
#ifdef CONFIG_SND_DEBUG
[ALC880_TEST] = {
@@ -4189,8 +4313,6 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
#define alc880_multi_pin_idx(nid) ((nid) - 0x18)
-#define alc880_is_input_pin(nid) ((nid) >= 0x18)
-#define alc880_input_pin_idx(nid) ((nid) - 0x18)
#define alc880_idx_to_dac(nid) ((nid) + 0x02)
#define alc880_dac_to_idx(nid) ((nid) - 0x02)
#define alc880_idx_to_mixer(nid) ((nid) + 0x0c)
@@ -4278,13 +4400,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
if (err < 0)
return err;
} else {
- sprintf(name, "%s Playback Volume", chname[i]);
+ const char *pfx;
+ if (cfg->line_outs == 1 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ else
+ pfx = chname[i];
+ sprintf(name, "%s Playback Volume", pfx);
err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", chname[i]);
+ sprintf(name, "%s Playback Switch", pfx);
err = add_control(spec, ALC_CTL_BIND_MUTE, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 2,
HDA_INPUT));
@@ -4358,31 +4486,61 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
return 0;
}
+static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+ return (pincap & AC_PINCAP_IN) != 0;
+}
+
/* create playback/capture controls for input pins */
-static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int alc_auto_create_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ hda_nid_t mixer,
+ hda_nid_t cap1, hda_nid_t cap2)
{
+ struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx;
for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (alc880_is_input_pin(cfg->input_pins[i])) {
- idx = alc880_input_pin_idx(cfg->input_pins[i]);
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i],
- idx, 0x0b);
- if (err < 0)
- return err;
+ hda_nid_t pin;
+
+ pin = cfg->input_pins[i];
+ if (!alc_is_input_pin(codec, pin))
+ continue;
+
+ if (mixer) {
+ idx = get_connection_index(codec, mixer, pin);
+ if (idx >= 0) {
+ err = new_analog_input(spec, pin,
+ auto_pin_cfg_labels[i],
+ idx, mixer);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ if (!cap1)
+ continue;
+ idx = get_connection_index(codec, cap1, pin);
+ if (idx < 0 && cap2)
+ idx = get_connection_index(codec, cap2, pin);
+ if (idx >= 0) {
imux->items[imux->num_items].label =
auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index =
- alc880_input_pin_idx(cfg->input_pins[i]);
+ imux->items[imux->num_items].index = idx;
imux->num_items++;
}
}
return 0;
}
+static int alc880_auto_create_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09);
+}
+
static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
unsigned int pin_type)
{
@@ -4448,7 +4606,7 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec)
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (alc880_is_input_pin(nid)) {
+ if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC880_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
@@ -4491,7 +4649,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
"Headphone");
if (err < 0)
return err;
- err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc880_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4505,12 +4663,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
&dig_nid, 1);
if (err < 0)
continue;
- if (dig_nid > 0x7f) {
- printk(KERN_ERR "alc880_auto: invalid dig_nid "
- "connection 0x%x for NID 0x%x\n", dig_nid,
- spec->autocfg.dig_out_pins[i]);
- continue;
- }
if (!i)
spec->multiout.dig_out_nid = dig_nid;
else {
@@ -4547,8 +4699,42 @@ static void alc880_auto_init(struct hda_codec *codec)
alc_inithook(codec);
}
-static void set_capture_mixer(struct alc_spec *spec)
+/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
+ * one of two digital mic pins, e.g. on ALC272
+ */
+static void fixup_automic_adc(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ hda_nid_t cap = spec->capsrc_nids ?
+ spec->capsrc_nids[i] : spec->adc_nids[i];
+ int iidx, eidx;
+
+ iidx = get_connection_index(codec, cap, spec->int_mic.pin);
+ if (iidx < 0)
+ continue;
+ eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
+ if (eidx < 0)
+ continue;
+ spec->int_mic.mux_idx = iidx;
+ spec->ext_mic.mux_idx = eidx;
+ if (spec->capsrc_nids)
+ spec->capsrc_nids += i;
+ spec->adc_nids += i;
+ spec->num_adc_nids = 1;
+ return;
+ }
+ snd_printd(KERN_INFO "hda_codec: %s: "
+ "No ADC/MUX containing both 0x%x and 0x%x pins\n",
+ codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
+ spec->auto_mic = 0; /* disable auto-mic to be sure */
+}
+
+static void set_capture_mixer(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
static struct snd_kcontrol_new *caps[2][3] = {
{ alc_capture_mixer_nosrc1,
alc_capture_mixer_nosrc2,
@@ -4559,7 +4745,10 @@ static void set_capture_mixer(struct alc_spec *spec)
};
if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
int mux;
- if (spec->input_mux && spec->input_mux->num_items > 1)
+ if (spec->auto_mic) {
+ mux = 0;
+ fixup_automic_adc(codec);
+ } else if (spec->input_mux && spec->input_mux->num_items > 1)
mux = 1;
else
mux = 0;
@@ -4590,8 +4779,8 @@ static int patch_alc880(struct hda_codec *codec)
alc880_models,
alc880_cfg_tbl);
if (board_config < 0) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC880_AUTO;
}
@@ -4616,7 +4805,7 @@ static int patch_alc880(struct hda_codec *codec)
}
if (board_config != ALC880_AUTO)
- setup_preset(spec, &alc880_presets[board_config]);
+ setup_preset(codec, &alc880_presets[board_config]);
spec->stream_analog_playback = &alc880_pcm_analog_playback;
spec->stream_analog_capture = &alc880_pcm_analog_capture;
@@ -4629,7 +4818,7 @@ static int patch_alc880(struct hda_codec *codec)
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
/* get type */
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ wcap = get_wcaps_type(wcap);
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc880_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
@@ -4638,7 +4827,7 @@ static int patch_alc880(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
}
}
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c;
@@ -5830,7 +6019,14 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = cfg->line_out_pins[0];
if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Front", &vols);
+ const char *pfx;
+ if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
+ pfx = "Master";
+ else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ else
+ pfx = "Front";
+ err = alc260_add_playback_controls(spec, nid, pfx, &vols);
if (err < 0)
return err;
}
@@ -5853,39 +6049,10 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
}
/* create playback/capture controls for input pins */
-static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc260_auto_create_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (cfg->input_pins[i] >= 0x12) {
- idx = cfg->input_pins[i] - 0x12;
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i], idx,
- 0x07);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- if (cfg->input_pins[i] >= 0x0f && cfg->input_pins[i] <= 0x10){
- idx = cfg->input_pins[i] - 0x09;
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i], idx,
- 0x07);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- }
- return 0;
+ return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05);
}
static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
@@ -5999,7 +6166,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
return err;
if (!spec->kctls.list)
return 0; /* can't find valid BIOS pin config */
- err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc260_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -6234,8 +6401,7 @@ static int patch_alc260(struct hda_codec *codec)
alc260_models,
alc260_cfg_tbl);
if (board_config < 0) {
- snd_printd(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n",
+ snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
codec->chip_name);
board_config = ALC260_AUTO;
}
@@ -6261,7 +6427,7 @@ static int patch_alc260(struct hda_codec *codec)
}
if (board_config != ALC260_AUTO)
- setup_preset(spec, &alc260_presets[board_config]);
+ setup_preset(codec, &alc260_presets[board_config]);
spec->stream_analog_playback = &alc260_pcm_analog_playback;
spec->stream_analog_capture = &alc260_pcm_analog_capture;
@@ -6272,7 +6438,7 @@ static int patch_alc260(struct hda_codec *codec)
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x04 is valid */
unsigned int wcap = get_wcaps(codec, 0x04);
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ wcap = get_wcaps_type(wcap);
/* get type */
if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
spec->adc_nids = alc260_adc_nids_alt;
@@ -6282,7 +6448,7 @@ static int patch_alc260(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
}
}
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x08;
@@ -6301,7 +6467,7 @@ static int patch_alc260(struct hda_codec *codec)
/*
- * ALC882 support
+ * ALC882/883/885/888/889 support
*
* ALC882 is almost identical with ALC880 but has cleaner and more flexible
* configuration. Each pin widget can choose any input DACs and a mixer.
@@ -6313,22 +6479,35 @@ static int patch_alc260(struct hda_codec *codec)
*/
#define ALC882_DIGOUT_NID 0x06
#define ALC882_DIGIN_NID 0x0a
+#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID
+#define ALC883_DIGIN_NID ALC882_DIGIN_NID
+#define ALC1200_DIGOUT_NID 0x10
+
static struct hda_channel_mode alc882_ch_modes[1] = {
{ 8, NULL }
};
+/* DACs */
static hda_nid_t alc882_dac_nids[4] = {
/* front, rear, clfe, rear_surr */
0x02, 0x03, 0x04, 0x05
};
+#define alc883_dac_nids alc882_dac_nids
-/* identical with ALC880 */
+/* ADCs */
#define alc882_adc_nids alc880_adc_nids
#define alc882_adc_nids_alt alc880_adc_nids_alt
+#define alc883_adc_nids alc882_adc_nids_alt
+static hda_nid_t alc883_adc_nids_alt[1] = { 0x08 };
+static hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 };
+#define alc889_adc_nids alc880_adc_nids
static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
+#define alc883_capsrc_nids alc882_capsrc_nids_alt
+static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
+#define alc889_capsrc_nids alc882_capsrc_nids
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
@@ -6343,6 +6522,17 @@ static struct hda_input_mux alc882_capture_source = {
},
};
+#define alc883_capture_source alc882_capture_source
+
+static struct hda_input_mux alc889_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Front Mic", 0x0 },
+ { "Mic", 0x3 },
+ { "Line", 0x2 },
+ },
+};
+
static struct hda_input_mux mb5_capture_source = {
.num_items = 3,
.items = {
@@ -6352,6 +6542,77 @@ static struct hda_input_mux mb5_capture_source = {
},
};
+static struct hda_input_mux alc883_3stack_6ch_intel = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x1 },
+ { "Front Mic", 0x0 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc883_lenovo_101e_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x1 },
+ { "Line", 0x2 },
+ },
+};
+
+static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "iMic", 0x1 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ { "Int Mic", 0x1 },
+ },
+};
+
+static struct hda_input_mux alc883_lenovo_sky_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc883_asus_eee1601_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ { "Line", 0x2 },
+ },
+};
+
+static struct hda_input_mux alc889A_mb31_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ /* Front Mic (0x01) unused */
+ { "Line", 0x2 },
+ /* Line 2 (0x03) unused */
+ /* CD (0x04) unsused? */
+ },
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
+ { 2, NULL }
+};
+
/*
* 2ch mode
*/
@@ -6364,6 +6625,18 @@ static struct hda_verb alc882_3ST_ch2_init[] = {
};
/*
+ * 4ch mode
+ */
+static struct hda_verb alc882_3ST_ch4_init[] = {
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
* 6ch mode
*/
static struct hda_verb alc882_3ST_ch6_init[] = {
@@ -6376,11 +6649,60 @@ static struct hda_verb alc882_3ST_ch6_init[] = {
{ } /* end */
};
-static struct hda_channel_mode alc882_3ST_6ch_modes[2] = {
+static struct hda_channel_mode alc882_3ST_6ch_modes[3] = {
{ 2, alc882_3ST_ch2_init },
+ { 4, alc882_3ST_ch4_init },
{ 6, alc882_3ST_ch6_init },
};
+#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_clevo_init[] = {
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_clevo_init[] = {
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_clevo_init[] = {
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = {
+ { 2, alc883_3ST_ch2_clevo_init },
+ { 4, alc883_3ST_ch4_clevo_init },
+ { 6, alc883_3ST_ch6_clevo_init },
+};
+
+
/*
* 6ch mode
*/
@@ -6468,6 +6790,189 @@ static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
{ 6, alc885_mb5_ch6_init },
};
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_4ST_ch2_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_4ST_ch4_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VER