At Mon, 23 Aug 2010 21:25:55 +0200, David Henningsson wrote:
I admit that the best thing would be to make the driver support all three mics, but at this point, the alsa driver can only support two, one called "mic" and one called "front mic". I believe the best choice is to select the internal mic as "front mic" and the mic jack on the laptop as "mic", thus leaving the docking station mic jack non-functional.
The behavior without this patch is to select the mic jack on the laptop as "mic" and the mic on the docking station as "front mic", thus leaving the internal mic non-functional in all cases, and that regardless of you have a docking station or not.
OK, here is a test patch for fixing more intensively. (I just did build-test, totally untested with real machines :)
It basically extends the config parser to allow multiple input pins assigned for a single type. The old input_pins field is kept for other drivers, so that it won't give regressions. It'll be removed in future.
Takashi
--- diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3827092..5472a47 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4372,6 +4372,16 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, }
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, + int type) +{ + if (cfg->num_inputs < AUTO_CFG_MAX_INS) { + cfg->inputs[cfg->num_inputs].pin = nid; + cfg->inputs[cfg->num_inputs].type = type; + cfg->num_inputs++; + } +} + /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -4398,6 +4408,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; + int i;
memset(cfg, 0, sizeof(*cfg));
@@ -4482,19 +4493,26 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->input_pins[preferred] = nid; else if (!cfg->input_pins[alt]) cfg->input_pins[alt] = nid; + add_auto_cfg_input_pin(cfg, nid, preferred); break; } - case AC_JACK_LINE_IN: + case AC_JACK_LINE_IN: { + int type; if (loc == AC_JACK_LOC_FRONT) - cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid; + type = AUTO_PIN_FRONT_LINE; else - cfg->input_pins[AUTO_PIN_LINE] = nid; + type = AUTO_PIN_LINE; + cfg->input_pins[type] = nid; + add_auto_cfg_input_pin(cfg, nid, type); break; + } case AC_JACK_CD: cfg->input_pins[AUTO_PIN_CD] = nid; + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); break; case AC_JACK_AUX: cfg->input_pins[AUTO_PIN_AUX] = nid; + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); break; case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: @@ -4621,14 +4639,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, if (cfg->dig_outs) snd_printd(" dig-out=0x%x/0x%x\n", cfg->dig_out_pins[0], cfg->dig_out_pins[1]); - snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," - " cd=0x%x, aux=0x%x\n", - cfg->input_pins[AUTO_PIN_MIC], - cfg->input_pins[AUTO_PIN_FRONT_MIC], - cfg->input_pins[AUTO_PIN_LINE], - cfg->input_pins[AUTO_PIN_FRONT_LINE], - cfg->input_pins[AUTO_PIN_CD], - cfg->input_pins[AUTO_PIN_AUX]); + snd_printd(" inputs:"); + for (i = 0; i < cfg->num_inputs; i++) { + snd_printdd(" %s=0x%x", + auto_pin_cfg_labels[cfg->inputs[i].type], + cfg->inputs[i].pin); + } + snd_printd("\n"); if (cfg->dig_in_pin) snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
@@ -4636,12 +4653,35 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
-/* labels for input pins */ +/* labels for input pins - for obsoleted config stuff */ const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = { "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" }; EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
+static const char *input_labels[AUTO_PIN_LAST][4] = { + { "Mic", "Mic 2", "Mic 3", "Mic 4" }, + { "Front Mic", "Front Mic 2", "Front Mic 3", "Front Mic 4" }, + { "Line", "Line 2", "Line 3", "Line 4" }, + { "Front Line", "Front Line 2", "Front Line 3", "Front Line 4" }, + { "CD", "CD 2", "CD 3", "CD 4" }, + { "Aux", "Aux 2", "Aux 3", "Aux 4" }, +}; + +const char *snd_hda_get_input_pin_label(const struct auto_pin_cfg *cfg, + int input) +{ + int type = cfg->inputs[input].type; + int idx; + + for (idx = 0; idx < 3 && --input >= 0; idx++) { + if (type != cfg->inputs[input].type) + break; + } + return input_labels[type][idx]; +} +EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_label); +
#ifdef CONFIG_PM /* diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 28ab4ae..fb56174 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -383,6 +383,16 @@ enum { extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
#define AUTO_CFG_MAX_OUTS 5 +#define AUTO_CFG_MAX_INS 8 + +struct auto_pin_cfg_item { + hda_nid_t pin; + int type; +}; + +struct auto_pin_cfg; +const char *snd_hda_get_input_pin_label(const struct auto_pin_cfg *cfg, + int input);
struct auto_pin_cfg { int line_outs; @@ -393,7 +403,9 @@ struct auto_pin_cfg { int hp_outs; int line_out_type; /* AUTO_PIN_XXX_OUT */ hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; - hda_nid_t input_pins[AUTO_PIN_LAST]; + hda_nid_t input_pins[AUTO_PIN_LAST]; /* old config; to be deprecated */ + int num_inputs; + struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; int dig_outs; hda_nid_t dig_out_pins[2]; hda_nid_t dig_in_pin; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 95148e5..71088f6 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1180,14 +1180,11 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } - for (i = 0; i < AUTO_PIN_LAST; i++) { - nid = cfg->input_pins[i]; - if (nid) { - err = stac92xx_add_jack(codec, nid, - SND_JACK_MICROPHONE); - if (err < 0) - return err; - } + for (i = 0; i < cfg->num_inputs; i++) { + nid = cfg->inputs[i].pin; + err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE); + if (err < 0) + return err; }
return 0; @@ -2821,13 +2818,18 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec) struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; unsigned int pincap; + int i;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT) return 0; - nid = cfg->input_pins[AUTO_PIN_LINE]; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_OUT) - return nid; + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type == AUTO_PIN_LINE) { + nid = cfg->inputs[i].pin; + pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_OUT) + return nid; + } + } return 0; }
@@ -2837,13 +2839,14 @@ static hda_nid_t check_mic_out_switch(struct hda_codec *codec) struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int def_conf, pincap; - unsigned int mic_pin; + int i;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT) return 0; - mic_pin = AUTO_PIN_MIC; - for (;;) { - hda_nid_t nid = cfg->input_pins[mic_pin]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (cfg->inputs[i].type > AUTO_PIN_FRONT_MIC) + break; def_conf = snd_hda_codec_get_pincfg(codec, nid); /* some laptops have an internal analog microphone * which can't be used as a output */ @@ -2852,10 +2855,6 @@ static hda_nid_t check_mic_out_switch(struct hda_codec *codec) if (pincap & AC_PINCAP_OUT) return nid; } - if (mic_pin == AUTO_PIN_MIC) - mic_pin = AUTO_PIN_FRONT_MIC; - else - break; } return 0; } @@ -3202,13 +3201,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, return err; }
- for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) { - nid = cfg->input_pins[idx]; - if (nid) { - err = stac92xx_add_jack_mode_control(codec, nid, idx); - if (err < 0) - return err; - } + for (idx = 0; idx < cfg->num_inputs; idx++) { + if (cfg->inputs[idx].type > AUTO_PIN_FRONT_LINE) + break; + nid = cfg->inputs[idx].pin; + err = stac92xx_add_jack_mode_control(codec, nid, idx); + if (err < 0) + return err; }
return 0; @@ -3415,7 +3414,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, /* create a volume assigned to the given pin (only if supported) */ /* return 1 if the volume control is created */ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, - const char *label, int direction) + const char *label, int idx, int direction) { unsigned int caps, nums; char name[32]; @@ -3432,8 +3431,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, if (!nums) return 0; snprintf(name, sizeof(name), "%s Capture Volume", label); - err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); if (err < 0) return err; return 1; @@ -3485,11 +3484,11 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, else label = stac92xx_dmic_labels[dimux->num_items];
- err = create_elem_capture_vol(codec, nid, label, HDA_INPUT); + err = create_elem_capture_vol(codec, nid, label, 0, HDA_INPUT); if (err < 0) return err; if (!err) { - err = create_elem_capture_vol(codec, nid, label, + err = create_elem_capture_vol(codec, nid, label, 0, HDA_OUTPUT); if (err < 0) return err; @@ -3540,10 +3539,11 @@ static int set_mic_route(struct hda_codec *codec, int i;
mic->pin = pin; - for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) - if (pin == cfg->input_pins[i]) + for (i = 0; i < cfg->num_inputs; i++) { + if (pin == cfg->inputs[i].pin) break; - if (i <= AUTO_PIN_FRONT_MIC) { + } + if (i < cfg->num_inputs && cfg->inputs[i].type <= AUTO_PIN_FRONT_MIC) { /* analog pin */ i = get_connection_index(codec, spec->mux_nids[0], pin); if (i < 0) @@ -3577,13 +3577,13 @@ static int stac_check_auto_mic(struct hda_codec *codec) hda_nid_t fixed, ext; int i;
- for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) { - if (cfg->input_pins[i]) + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type >= AUTO_PIN_LINE) return 0; /* must be exclusively mics */ } fixed = ext = 0; - for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) - if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext)) + for (i = 0; i < cfg->num_inputs; i++) + if (check_mic_pin(codec, cfg->inputs[i].pin, &fixed, &ext)) return 0; for (i = 0; i < spec->num_dmics; i++) if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext)) @@ -3603,14 +3603,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const { struct sigmatel_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux; - int i, j; + int i, j, type_idx = 0;
- for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = cfg->input_pins[i]; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; int index, err;
- if (!nid) - continue; index = -1; for (j = 0; j < spec->num_muxes; j++) { index = get_connection_index(codec, spec->mux_nids[j], @@ -3621,13 +3619,18 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const if (index < 0) continue;
+ if (i > 0 && cfg->inputs[i].type == cfg->inputs[i - 1].type) + type_idx++; + else + type_idx = 0; err = create_elem_capture_vol(codec, nid, - auto_pin_cfg_labels[i], + auto_pin_cfg_labels[i], type_idx, HDA_INPUT); if (err < 0) return err;
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; + imux->items[imux->num_items].label = + snd_hda_get_input_pin_label(cfg, i); imux->items[imux->num_items].index = index; imux->num_items++; } @@ -4304,37 +4307,34 @@ static int stac92xx_init(struct hda_codec *codec) if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) stac_issue_unsol_event(codec, spec->ext_mic.pin); } - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = cfg->input_pins[i]; - if (nid) { - unsigned int pinctl, conf; - if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { - /* for mic pins, force to initialize */ - pinctl = stac92xx_get_default_vref(codec, nid); + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int type = cfg->inputs[i].type; + unsigned int pinctl, conf; + if (type == AUTO_PIN_MIC || type == AUTO_PIN_FRONT_MIC) { + /* for mic pins, force to initialize */ + pinctl = stac92xx_get_default_vref(codec, nid); + pinctl |= AC_PINCTL_IN_EN; + stac92xx_auto_set_pinctl(codec, nid, pinctl); + } else { + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* if PINCTL already set then skip */ + /* Also, if both INPUT and OUTPUT are set, + * it must be a BIOS bug; need to override, too + */ + if (!(pinctl & AC_PINCTL_IN_EN) || + (pinctl & AC_PINCTL_OUT_EN)) { + pinctl &= ~AC_PINCTL_OUT_EN; pinctl |= AC_PINCTL_IN_EN; stac92xx_auto_set_pinctl(codec, nid, pinctl); - } else { - pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - /* if PINCTL already set then skip */ - /* Also, if both INPUT and OUTPUT are set, - * it must be a BIOS bug; need to override, too - */ - if (!(pinctl & AC_PINCTL_IN_EN) || - (pinctl & AC_PINCTL_OUT_EN)) { - pinctl &= ~AC_PINCTL_OUT_EN; - pinctl |= AC_PINCTL_IN_EN; - stac92xx_auto_set_pinctl(codec, nid, - pinctl); - } - } - conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { - if (enable_pin_detect(codec, nid, - STAC_INSERT_EVENT)) - stac_issue_unsol_event(codec, nid); } } + conf = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { + if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT)) + stac_issue_unsol_event(codec, nid); + } } for (i = 0; i < spec->num_dmics; i++) stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],