From 21a4dc43acdd547335f59ad52efeef5160247736 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Jul 2009 12:55:46 +0200 Subject: [PATCH] ALSA: hda - Fix cirrus codec parsing The parser wasn't called in the proper order. Split now the parser to be called in patch_cirrus(), and the rest are just for building PCMs and controls. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cirrus.c | 331 +++++++++++++++++++++-------------- 1 file changed, 198 insertions(+), 133 deletions(-) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index c4dc12e85732..051302e78345 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -56,7 +56,6 @@ struct cs_spec { unsigned int hp_detect:1; unsigned int mic_detect:1; - unsigned int built_up:1; }; #define HP_EVENT 1 @@ -213,7 +212,6 @@ static int cs_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nid[spec->cur_input]; codec->num_pcms++; @@ -242,6 +240,10 @@ static int cs_build_pcms(struct hda_codec *codec) return 0; } +/* + * parse codec topology + */ + static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin) { hda_nid_t dac; @@ -252,6 +254,169 @@ static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin) return dac; } +static int is_ext_mic(struct hda_codec *codec, unsigned int idx) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t pin = cfg->input_pins[idx]; + unsigned int val = snd_hda_query_pin_caps(codec, pin); + if (!(val & AC_PINCAP_PRES_DETECT)) + return 0; + val = snd_hda_codec_get_pincfg(codec, pin); + return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX); +} + +static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, + unsigned int *idxp) +{ + int i; + hda_nid_t nid; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + hda_nid_t pins[2]; + unsigned int type; + int j, nums; + type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) + >> AC_WCAP_TYPE_SHIFT; + if (type != AC_WID_AUD_IN) + continue; + nums = snd_hda_get_connections(codec, nid, pins, + ARRAY_SIZE(pins)); + if (nums <= 0) + continue; + for (j = 0; j < nums; j++) { + if (pins[j] == pin) { + *idxp = j; + return nid; + } + } + } + return 0; +} + +static int parse_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err, extra_nids; + hda_nid_t dac; + + for (i = 0; i < cfg->line_outs; i++) { + dac = get_dac(codec, cfg->line_out_pins[i]); + if (!dac) + break; + spec->dac_nid[i] = dac; + } + spec->multiout.num_dacs = i; + spec->multiout.dac_nids = spec->dac_nid; + spec->multiout.max_channels = i * 2; + + /* add HP and speakers */ + extra_nids = 0; + for (i = 0; i < cfg->hp_outs; i++) { + dac = get_dac(codec, cfg->hp_pins[i]); + if (!dac) + break; + if (!i) + spec->multiout.hp_nid = dac; + else + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + for (i = 0; i < cfg->speaker_outs; i++) { + dac = get_dac(codec, cfg->speaker_pins[i]); + if (!dac) + break; + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = 0; + } + + return 0; +} + +static int parse_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, n, err; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t pin = cfg->input_pins[i]; + struct snd_kcontrol *kctl; + if (!pin) + continue; + spec->input_idx[spec->num_inputs] = i; + spec->capsrc_idx[i] = spec->num_inputs++; + spec->cur_input = i; + spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); + } + if (!spec->num_inputs) + return 0; + + /* check whether the automatic mic switch is available */ + if (spec->num_inputs == 2 && + spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) { + if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) { + if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { + spec->mic_detect = 1; + spec->automic_idx = AUTO_PIN_FRONT_MIC; + } + } else { + if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { + spec->mic_detect = 1; + spec->automic_idx = AUTO_PIN_MIC; + } + } + } + return 0; +} + + +static int parse_digital_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + int err; + + if (!cfg->dig_outs) + return 0; + if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1) + return 0; + spec->multiout.dig_out_nid = nid; + spec->multiout.share_spdif = 1; + if (cfg->dig_outs > 1 && + snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) { + spec->slave_dig_outs[0] = nid; + codec->slave_dig_outs = spec->slave_dig_outs; + } + return 0; +} + +static int parse_digital_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int idx; + + if (!cfg->dig_in_pin) + return 0; + spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx); + if (!spec->dig_in) + return 0; + return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); +} + +/* + * create mixer controls + */ + static const char *dir_sfx[2] = { "Playback", "Capture" }; static int add_mute(struct hda_codec *codec, const char *name, int index, @@ -376,55 +541,26 @@ static int build_output(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err, extra_nids; - hda_nid_t dac; + int i, err; for (i = 0; i < cfg->line_outs; i++) { - dac = get_dac(codec, cfg->line_out_pins[i]); - if (!dac) - break; - spec->dac_nid[i] = dac; - err = add_output(codec, dac, i, cfg->line_outs, - cfg->line_out_type); + err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]), + i, cfg->line_outs, cfg->line_out_type); if (err < 0) return err; } - spec->multiout.num_dacs = i; - spec->multiout.dac_nids = spec->dac_nid; - spec->multiout.max_channels = i * 2; - - /* add HP and speakers */ - extra_nids = 0; for (i = 0; i < cfg->hp_outs; i++) { - dac = get_dac(codec, cfg->hp_pins[i]); - if (!dac) - break; - if (!i) - spec->multiout.hp_nid = dac; - else - spec->multiout.extra_out_nid[extra_nids++] = dac; - err = add_output(codec, dac, i, cfg->hp_outs, AUTO_PIN_HP_OUT); + err = add_output(codec, get_dac(codec, cfg->hp_pins[i]), + i, cfg->hp_outs, AUTO_PIN_HP_OUT); if (err < 0) return err; } for (i = 0; i < cfg->speaker_outs; i++) { - dac = get_dac(codec, cfg->speaker_pins[i]); - if (!dac) - break; - spec->multiout.extra_out_nid[extra_nids++] = dac; - err = add_output(codec, dac, i, cfg->speaker_outs, - AUTO_PIN_SPEAKER_OUT); + err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]), + i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT); if (err < 0) return err; } - - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = 0; - } - return 0; } @@ -506,48 +642,6 @@ static struct snd_kcontrol_new cs_capture_source = { .put = cs_capture_source_put, }; - -static int is_ext_mic(struct hda_codec *codec, unsigned int idx) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t pin = cfg->input_pins[idx]; - unsigned int val = snd_hda_query_pin_caps(codec, pin); - if (!(val & AC_PINCAP_PRES_DETECT)) - return 0; - val = snd_hda_codec_get_pincfg(codec, pin); - return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX); -} - -static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, - unsigned int *idxp) -{ - int i; - hda_nid_t nid; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - hda_nid_t pins[2]; - unsigned int type; - int j, nums; - type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) - >> AC_WCAP_TYPE_SHIFT; - if (type != AC_WID_AUD_IN) - continue; - nums = snd_hda_get_connections(codec, nid, pins, - ARRAY_SIZE(pins)); - if (nums <= 0) - continue; - for (j = 0; j < nums; j++) { - if (pins[j] == pin) { - *idxp = j; - return nid; - } - } - } - return 0; -} - static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, struct hda_ctl_ops *ops) { @@ -574,38 +668,11 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, static int build_input(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, n, err; + int i, err; - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t pin = cfg->input_pins[i]; - struct snd_kcontrol *kctl; - if (!pin) - continue; - spec->input_idx[spec->num_inputs] = i; - spec->capsrc_idx[i] = spec->num_inputs++; - spec->cur_input = i; - spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); - } if (!spec->num_inputs) return 0; - /* check whether the automatic mic switch is available */ - if (spec->num_inputs == 2 && - spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) { - if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) { - if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { - spec->mic_detect = 1; - spec->automic_idx = AUTO_PIN_FRONT_MIC; - } - } else { - if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { - spec->mic_detect = 1; - spec->automic_idx = AUTO_PIN_MIC; - } - } - } - /* make bind-capture */ spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); @@ -632,47 +699,35 @@ static int build_input(struct hda_codec *codec) return 0; } +/* + */ + static int build_digital_output(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; int err; - if (!cfg->dig_outs) - return 0; - if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1) - return 0; - spec->multiout.dig_out_nid = nid; err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) return err; err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); if (err < 0) return err; - spec->multiout.share_spdif = 1; - if (cfg->dig_outs > 1 && - snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) { - spec->slave_dig_outs[0] = nid; - codec->slave_dig_outs = spec->slave_dig_outs; - } return 0; } static int build_digital_input(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int idx; - - if (!cfg->dig_in_pin) - return 0; - spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx); - if (!spec->dig_in) - return 0; - return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (spec->dig_in) + return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + return 0; } +/* + * auto-mute and auto-mic switching + */ + static void cs_automute(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; @@ -810,8 +865,6 @@ static int cs_init(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - if (!spec->built_up) - return 0; init_output(codec); init_input(codec); return 0; @@ -834,7 +887,6 @@ static int cs_build_controls(struct hda_codec *codec) err = build_digital_input(codec); if (err < 0) return err; - spec->built_up = 1; return cs_init(codec); } @@ -892,6 +944,19 @@ static int patch_cs420x(struct hda_codec *codec) if (err < 0) goto error; + err = parse_output(codec); + if (err < 0) + goto error; + err = parse_input(codec); + if (err < 0) + goto error; + err = parse_digital_output(codec); + if (err < 0) + goto error; + err = parse_digital_input(codec); + if (err < 0) + goto error; + codec->patch_ops = cs_patch_ops; return 0;