diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-08-10 12:47:44 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-08-10 12:47:44 -0400 |
commit | 6c8194922739138f046a4d0924519dd43b48e1f3 (patch) | |
tree | d22057966b05273af33881a46a33567c24fe966e /sound/pci/hda/patch_realtek.c | |
parent | d5c9c8912a25550f2b6a0974667eb968fe6a316e (diff) |
ALSA: hda - Add auto-mic support for Realtek codecs
Added the support for automatic mic selection via plugging for
Realtek codecs (in auto-probing mode). The auto-mic mode is enabled
only when one internal mic and one external mic are present.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 177 |
1 files changed, 135 insertions, 42 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b0d30fced3f5..678c2d7b7f98 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -258,6 +258,14 @@ enum { | |||
258 | ALC_INIT_GPIO3, | 258 | ALC_INIT_GPIO3, |
259 | }; | 259 | }; |
260 | 260 | ||
261 | struct alc_mic_route { | ||
262 | hda_nid_t pin; | ||
263 | unsigned char mux_idx; | ||
264 | unsigned char amix_idx; | ||
265 | }; | ||
266 | |||
267 | #define MUX_IDX_UNDEF ((unsigned char)-1) | ||
268 | |||
261 | struct alc_spec { | 269 | struct alc_spec { |
262 | /* codec parameterization */ | 270 | /* codec parameterization */ |
263 | struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ | 271 | struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ |
@@ -300,6 +308,8 @@ struct alc_spec { | |||
300 | unsigned int num_mux_defs; | 308 | unsigned int num_mux_defs; |
301 | const struct hda_input_mux *input_mux; | 309 | const struct hda_input_mux *input_mux; |
302 | unsigned int cur_mux[3]; | 310 | unsigned int cur_mux[3]; |
311 | struct alc_mic_route ext_mic; | ||
312 | struct alc_mic_route int_mic; | ||
303 | 313 | ||
304 | /* channel model */ | 314 | /* channel model */ |
305 | const struct hda_channel_mode *channel_mode; | 315 | const struct hda_channel_mode *channel_mode; |
@@ -327,6 +337,7 @@ struct alc_spec { | |||
327 | unsigned int sense_updated: 1; | 337 | unsigned int sense_updated: 1; |
328 | unsigned int jack_present: 1; | 338 | unsigned int jack_present: 1; |
329 | unsigned int master_sw: 1; | 339 | unsigned int master_sw: 1; |
340 | unsigned int auto_mic:1; | ||
330 | 341 | ||
331 | /* other flags */ | 342 | /* other flags */ |
332 | unsigned int no_analog :1; /* digital I/O only */ | 343 | unsigned int no_analog :1; /* digital I/O only */ |
@@ -963,30 +974,79 @@ static void alc_automute_pin(struct hda_codec *codec) | |||
963 | } | 974 | } |
964 | } | 975 | } |
965 | 976 | ||
966 | #if 0 /* it's broken in some cases -- temporarily disabled */ | 977 | static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, |
978 | hda_nid_t nid) | ||
979 | { | ||
980 | hda_nid_t conn[HDA_MAX_NUM_INPUTS]; | ||
981 | int i, nums; | ||
982 | |||
983 | nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); | ||
984 | for (i = 0; i < nums; i++) | ||
985 | if (conn[i] == nid) | ||
986 | return i; | ||
987 | return -1; | ||
988 | } | ||
989 | |||
990 | static int set_mic_mux_idx(struct hda_codec *codec, hda_nid_t cap, | ||
991 | struct alc_mic_route *mic) | ||
992 | { | ||
993 | int idx = get_connection_index(codec, cap, mic->pin); | ||
994 | if (idx < 0) | ||
995 | return 1; /* invalid */ | ||
996 | mic->mux_idx = idx; | ||
997 | return 0; | ||
998 | } | ||
999 | |||
967 | static void alc_mic_automute(struct hda_codec *codec) | 1000 | static void alc_mic_automute(struct hda_codec *codec) |
968 | { | 1001 | { |
969 | struct alc_spec *spec = codec->spec; | 1002 | struct alc_spec *spec = codec->spec; |
970 | unsigned int present; | 1003 | struct alc_mic_route *dead, *alive; |
971 | unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; | 1004 | unsigned int present, type; |
972 | unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; | 1005 | hda_nid_t cap_nid; |
973 | unsigned int mix_nid = spec->capsrc_nids[0]; | 1006 | |
974 | unsigned int capsrc_idx_mic, capsrc_idx_fmic; | 1007 | if (!spec->int_mic.pin || !spec->ext_mic.pin) |
975 | 1008 | return; | |
976 | capsrc_idx_mic = mic_nid - 0x18; | 1009 | if (snd_BUG_ON(!spec->adc_nids)) |
977 | capsrc_idx_fmic = fmic_nid - 0x18; | 1010 | return; |
978 | present = snd_hda_codec_read(codec, mic_nid, 0, | 1011 | |
979 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | 1012 | cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; |
980 | snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, | 1013 | |
981 | 0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80)); | 1014 | present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0, |
982 | snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, | 1015 | AC_VERB_GET_PIN_SENSE, 0); |
983 | 0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0)); | 1016 | present &= AC_PINSENSE_PRESENCE; |
984 | snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic, | 1017 | if (present) { |
985 | HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); | 1018 | alive = &spec->ext_mic; |
1019 | dead = &spec->int_mic; | ||
1020 | } else { | ||
1021 | alive = &spec->int_mic; | ||
1022 | dead = &spec->ext_mic; | ||
1023 | } | ||
1024 | |||
1025 | if (alive->mux_idx == MUX_IDX_UNDEF && | ||
1026 | set_mic_mux_idx(codec, cap_nid, alive)) | ||
1027 | return; | ||
1028 | if (dead->mux_idx == MUX_IDX_UNDEF && | ||
1029 | set_mic_mux_idx(codec, cap_nid, dead)) | ||
1030 | return; | ||
1031 | |||
1032 | type = get_wcaps_type(get_wcaps(codec, cap_nid)); | ||
1033 | if (type == AC_WID_AUD_MIX) { | ||
1034 | /* Matrix-mixer style (e.g. ALC882) */ | ||
1035 | snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, | ||
1036 | alive->mux_idx, | ||
1037 | HDA_AMP_MUTE, 0); | ||
1038 | snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, | ||
1039 | dead->mux_idx, | ||
1040 | HDA_AMP_MUTE, HDA_AMP_MUTE); | ||
1041 | } else { | ||
1042 | /* MUX style (e.g. ALC880) */ | ||
1043 | snd_hda_codec_write_cache(codec, cap_nid, 0, | ||
1044 | AC_VERB_SET_CONNECT_SEL, | ||
1045 | alive->mux_idx); | ||
1046 | } | ||
1047 | |||
1048 | /* FIXME: analog mixer */ | ||
986 | } | 1049 | } |
987 | #else | ||
988 | #define alc_mic_automute(codec) do {} while(0) /* NOP */ | ||
989 | #endif /* disabled */ | ||
990 | 1050 | ||
991 | /* unsolicited event for HP jack sensing */ | 1051 | /* unsolicited event for HP jack sensing */ |
992 | static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) | 1052 | static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) |
@@ -1142,6 +1202,55 @@ static void alc_init_auto_hp(struct hda_codec *codec) | |||
1142 | spec->unsol_event = alc_sku_unsol_event; | 1202 | spec->unsol_event = alc_sku_unsol_event; |
1143 | } | 1203 | } |
1144 | 1204 | ||
1205 | static void alc_init_auto_mic(struct hda_codec *codec) | ||
1206 | { | ||
1207 | struct alc_spec *spec = codec->spec; | ||
1208 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
1209 | hda_nid_t fixed, ext; | ||
1210 | int i; | ||
1211 | |||
1212 | /* there must be only two mic inputs exclusively */ | ||
1213 | for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) | ||
1214 | if (cfg->input_pins[i]) | ||
1215 | return; | ||
1216 | |||
1217 | fixed = ext = 0; | ||
1218 | for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) { | ||
1219 | hda_nid_t nid = cfg->input_pins[i]; | ||
1220 | unsigned int defcfg; | ||
1221 | if (!nid) | ||
1222 | return; | ||
1223 | defcfg = snd_hda_codec_get_pincfg(codec, nid); | ||
1224 | switch (get_defcfg_connect(defcfg)) { | ||
1225 | case AC_JACK_PORT_FIXED: | ||
1226 | if (fixed) | ||
1227 | return; /* already occupied */ | ||
1228 | fixed = nid; | ||
1229 | break; | ||
1230 | case AC_JACK_PORT_COMPLEX: | ||
1231 | if (ext) | ||
1232 | return; /* already occupied */ | ||
1233 | ext = nid; | ||
1234 | break; | ||
1235 | default: | ||
1236 | return; /* invalid entry */ | ||
1237 | } | ||
1238 | } | ||
1239 | if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP)) | ||
1240 | return; /* no unsol support */ | ||
1241 | snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n", | ||
1242 | ext, fixed); | ||
1243 | spec->ext_mic.pin = ext; | ||
1244 | spec->int_mic.pin = fixed; | ||
1245 | spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ | ||
1246 | spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ | ||
1247 | spec->auto_mic = 1; | ||
1248 | snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0, | ||
1249 | AC_VERB_SET_UNSOLICITED_ENABLE, | ||
1250 | AC_USRSP_EN | ALC880_MIC_EVENT); | ||
1251 | spec->unsol_event = alc_sku_unsol_event; | ||
1252 | } | ||
1253 | |||
1145 | /* check subsystem ID and set up device-specific initialization; | 1254 | /* check subsystem ID and set up device-specific initialization; |
1146 | * return 1 if initialized, 0 if invalid SSID | 1255 | * return 1 if initialized, 0 if invalid SSID |
1147 | */ | 1256 | */ |
@@ -1243,6 +1352,7 @@ do_sku: | |||
1243 | } | 1352 | } |
1244 | 1353 | ||
1245 | alc_init_auto_hp(codec); | 1354 | alc_init_auto_hp(codec); |
1355 | alc_init_auto_mic(codec); | ||
1246 | return 1; | 1356 | return 1; |
1247 | } | 1357 | } |
1248 | 1358 | ||
@@ -1255,6 +1365,7 @@ static void alc_ssid_check(struct hda_codec *codec, | |||
1255 | "Enable default setup for auto mode as fallback\n"); | 1365 | "Enable default setup for auto mode as fallback\n"); |
1256 | spec->init_amp = ALC_INIT_DEFAULT; | 1366 | spec->init_amp = ALC_INIT_DEFAULT; |
1257 | alc_init_auto_hp(codec); | 1367 | alc_init_auto_hp(codec); |
1368 | alc_init_auto_mic(codec); | ||
1258 | } | 1369 | } |
1259 | } | 1370 | } |
1260 | 1371 | ||
@@ -4572,7 +4683,8 @@ static void set_capture_mixer(struct alc_spec *spec) | |||
4572 | }; | 4683 | }; |
4573 | if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { | 4684 | if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { |
4574 | int mux; | 4685 | int mux; |
4575 | if (spec->input_mux && spec->input_mux->num_items > 1) | 4686 | if (spec->input_mux && spec->input_mux->num_items > 1 && |
4687 | !spec->auto_mic) | ||
4576 | mux = 1; | 4688 | mux = 1; |
4577 | else | 4689 | else |
4578 | mux = 0; | 4690 | mux = 0; |
@@ -17118,25 +17230,6 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, | |||
17118 | return 0; | 17230 | return 0; |
17119 | } | 17231 | } |
17120 | 17232 | ||
17121 | /* return the index of the src widget from the connection list of the nid. | ||
17122 | * return -1 if not found | ||
17123 | */ | ||
17124 | static int alc662_input_pin_idx(struct hda_codec *codec, hda_nid_t nid, | ||
17125 | hda_nid_t src) | ||
17126 | { | ||
17127 | hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; | ||
17128 | int i, conns; | ||
17129 | |||
17130 | conns = snd_hda_get_connections(codec, nid, conn_list, | ||
17131 | ARRAY_SIZE(conn_list)); | ||
17132 | if (conns < 0) | ||
17133 | return -1; | ||
17134 | for (i = 0; i < conns; i++) | ||
17135 | if (conn_list[i] == src) | ||
17136 | return i; | ||
17137 | return -1; | ||
17138 | } | ||
17139 | |||
17140 | static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid) | 17233 | static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid) |
17141 | { | 17234 | { |
17142 | unsigned int pincap = snd_hda_query_pin_caps(codec, nid); | 17235 | unsigned int pincap = snd_hda_query_pin_caps(codec, nid); |
@@ -17153,7 +17246,7 @@ static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec, | |||
17153 | 17246 | ||
17154 | for (i = 0; i < AUTO_PIN_LAST; i++) { | 17247 | for (i = 0; i < AUTO_PIN_LAST; i++) { |
17155 | if (alc662_is_input_pin(codec, cfg->input_pins[i])) { | 17248 | if (alc662_is_input_pin(codec, cfg->input_pins[i])) { |
17156 | idx = alc662_input_pin_idx(codec, 0x0b, | 17249 | idx = get_connection_index(codec, 0x0b, |
17157 | cfg->input_pins[i]); | 17250 | cfg->input_pins[i]); |
17158 | if (idx >= 0) { | 17251 | if (idx >= 0) { |
17159 | err = new_analog_input(spec, cfg->input_pins[i], | 17252 | err = new_analog_input(spec, cfg->input_pins[i], |
@@ -17162,7 +17255,7 @@ static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec, | |||
17162 | if (err < 0) | 17255 | if (err < 0) |
17163 | return err; | 17256 | return err; |
17164 | } | 17257 | } |
17165 | idx = alc662_input_pin_idx(codec, 0x22, | 17258 | idx = get_connection_index(codec, 0x22, |
17166 | cfg->input_pins[i]); | 17259 | cfg->input_pins[i]); |
17167 | if (idx >= 0) { | 17260 | if (idx >= 0) { |
17168 | imux->items[imux->num_items].label = | 17261 | imux->items[imux->num_items].label = |