Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21a4dc43 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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: default avatarTakashi Iwai <tiwai@suse.de>
parent e5f14248
Loading
Loading
Loading
Loading
+198 −133
Original line number Diff line number Diff line
@@ -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;
	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;