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

Commit 3af9ee6b authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Check hard-wired DACs at first for ALC662 & co



Some Realtek codecs have the output pins hardwired with certain DACs.
These DACs have to be assigned at first and assign the rest for
multi-DAC pins so that all DACs can be assigned properly.

Without such an optimization, speaker outputs may be assigned to the
same DAC as the headphone or others.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent cb053a82
Loading
Loading
Loading
Loading
+81 −31
Original line number Diff line number Diff line
@@ -1760,6 +1760,15 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
	return 0;
}
static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
{
	int i;
	for (i = 0; i < nums; i++)
		if (list[i] == nid)
			return true;
	return false;
}
/* check subsystem ID and set up device-specific initialization;
 * return 1 if initialized, 0 if invalid SSID
 */
@@ -1869,8 +1878,8 @@ static int alc_subsystem_id(struct hda_codec *codec,
			nid = porti;
		else
			return 1;
		for (i = 0; i < spec->autocfg.line_outs; i++)
			if (spec->autocfg.line_out_pins[i] == nid)
		if (found_in_nid_list(nid, spec->autocfg.line_out_pins,
				      spec->autocfg.line_outs))
			return 1;
		spec->autocfg.hp_pins[0] = nid;
	}
@@ -15839,7 +15848,7 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
{
	struct alc_spec *spec = codec->spec;
	hda_nid_t mix, srcs[5];
	int i, j, num;
	int i, num;
	if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
		return 0;
@@ -15851,10 +15860,8 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
		type = get_wcaps_type(get_wcaps(codec, srcs[i]));
		if (type != AC_WID_AUD_OUT)
			continue;
		for (j = 0; j < spec->multiout.num_dacs; j++)
			if (spec->multiout.dac_nids[j] == srcs[i])
				break;
		if (j >= spec->multiout.num_dacs)
		if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids,
				       spec->multiout.num_dacs))
			return srcs[i];
	}
	return 0;
@@ -18748,7 +18755,7 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
{
	struct alc_spec *spec = codec->spec;
	hda_nid_t srcs[5];
	int i, j, num;
	int i, num;
	pin = alc_go_down_to_selector(codec, pin);
	num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
@@ -18756,31 +18763,78 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
		hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
		if (!nid)
			continue;
		for (j = 0; j < spec->multiout.num_dacs; j++)
			if (spec->multiout.dac_nids[j] == nid)
				break;
		if (j >= spec->multiout.num_dacs)
		if (found_in_nid_list(nid, spec->multiout.dac_nids,
				      spec->multiout.num_dacs))
			continue;
		if (spec->multiout.hp_nid == nid)
			continue;
		if (found_in_nid_list(nid, spec->multiout.extra_out_nid,
				      ARRAY_SIZE(spec->multiout.extra_out_nid)))
		    continue;
		return nid;
	}
	return 0;
}
static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
{
	hda_nid_t sel = alc_go_down_to_selector(codec, pin);
	if (snd_hda_get_conn_list(codec, sel, NULL) == 1)
		return alc_auto_look_for_dac(codec, pin);
	return 0;
}
/* fill in the dac_nids table from the parsed pin configuration */
static int alc662_auto_fill_dac_nids(struct hda_codec *codec)
{
	struct alc_spec *spec = codec->spec;
	const struct auto_pin_cfg *cfg = &spec->autocfg;
	bool redone;
	int i;
	hda_nid_t dac;
	spec->multiout.dac_nids = spec->private_dac_nids;
 again:
	spec->multiout.num_dacs = 0;
	spec->multiout.hp_nid = 0;
	spec->multiout.extra_out_nid[0] = 0;
	memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
	spec->multiout.dac_nids = spec->private_dac_nids;
	/* fill hard-wired DACs first */
	if (!redone) {
		for (i = 0; i < cfg->line_outs; i++)
			spec->private_dac_nids[i] =
				get_dac_if_single(codec, cfg->line_out_pins[i]);
		if (cfg->hp_outs)
			spec->multiout.hp_nid =
				get_dac_if_single(codec, cfg->hp_pins[0]);
		if (cfg->speaker_outs)
			spec->multiout.extra_out_nid[0] =
				get_dac_if_single(codec, cfg->speaker_pins[0]);
	}
	for (i = 0; i < cfg->line_outs; i++) {
		dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]);
		if (!dac)
		hda_nid_t pin = cfg->line_out_pins[i];
		if (spec->private_dac_nids[i])
			continue;
		spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
		spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin);
		if (!spec->private_dac_nids[i] && !redone) {
			/* if we can't find primary DACs, re-probe without
			 * checking the hard-wired DACs
			 */
			redone = true;
			goto again;
		}
	}
	for (i = 0; i < cfg->line_outs; i++) {
		if (spec->private_dac_nids[i])
			spec->multiout.num_dacs++;
		else
			memmove(spec->private_dac_nids + i,
				spec->private_dac_nids + i + 1,
				sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
	}
	return 0;
}
@@ -18860,18 +18914,16 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
}
/* add playback controls for speaker and HP outputs */
/* return DAC nid if any new DAC is assigned */
static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
					const char *pfx)
					hda_nid_t dac, const char *pfx)
{
	struct alc_spec *spec = codec->spec;
	hda_nid_t nid, mix;
	hda_nid_t mix;
	int err;
	if (!pin)
		return 0;
	nid = alc_auto_look_for_dac(codec, pin);
	if (!nid) {
	if (!dac) {
		/* the corresponding DAC is already occupied */
		if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
			return 0; /* no way */
@@ -18880,16 +18932,16 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
				   HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
	}
	mix = alc_auto_dac_to_mix(codec, pin, nid);
	mix = alc_auto_dac_to_mix(codec, pin, dac);
	if (!mix)
		return 0;
	err = alc662_add_vol_ctl(spec, pfx, nid, 3);
	err = alc662_add_vol_ctl(spec, pfx, dac, 3);
	if (err < 0)
		return err;
	err = alc662_add_sw_ctl(spec, pfx, mix, 3);
	if (err < 0)
		return err;
	return nid;
	return 0;
}
/* create playback/capture controls for input pins */
@@ -19146,17 +19198,15 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
		return err;
	err = alc662_auto_create_extra_out(codec,
					   spec->autocfg.speaker_pins[0],
					   spec->multiout.extra_out_nid[0],
					   "Speaker");
	if (err < 0)
		return err;
	if (err)
		spec->multiout.extra_out_nid[0] = err;
	err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
					   spec->multiout.hp_nid,
					   "Headphone");
	if (err < 0)
		return err;
	if (err)
		spec->multiout.hp_nid = err;
	err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
	if (err < 0)
		return err;