Commit 7085ec12 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Fix / improve ALC66x parser

The auto-parser for ALC662/663/272 codecs doesn't work properly when
a speaker is connected to mono NID 0x17, and doesn't handle the dynamic
DAC assignment properly.

This patch fixes the issues and also improves the assignment of DACs
so that HP and speakers can have independent volume controls.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 02d33322
...@@ -17146,70 +17146,145 @@ static struct alc_config_preset alc662_presets[] = { ...@@ -17146,70 +17146,145 @@ static struct alc_config_preset alc662_presets[] = {
* BIOS auto configuration * BIOS auto configuration
*/ */
/* convert from MIX nid to DAC */
static inline hda_nid_t alc662_mix_to_dac(hda_nid_t nid)
{
if (nid == 0x0f)
return 0x02;
else if (nid >= 0x0c && nid <= 0x0e)
return nid - 0x0c + 0x02;
else
return 0;
}
/* get MIX nid connected to the given pin targeted to DAC */
static hda_nid_t alc662_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
hda_nid_t dac)
{
hda_nid_t mix[4];
int i, num;
num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
for (i = 0; i < num; i++) {
if (alc662_mix_to_dac(mix[i]) == dac)
return mix[i];
}
return 0;
}
/* look for an empty DAC slot */
static hda_nid_t alc662_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;
num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
if (num < 0)
return 0;
for (i = 0; i < num; i++) {
hda_nid_t nid = alc662_mix_to_dac(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)
return nid;
}
return 0;
}
/* fill in the dac_nids table from the parsed pin configuration */
static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
struct alc_spec *spec = codec->spec;
int i;
hda_nid_t dac;
spec->multiout.dac_nids = spec->private_dac_nids;
for (i = 0; i < cfg->line_outs; i++) {
dac = alc662_look_for_dac(codec, cfg->line_out_pins[i]);
if (!dac)
continue;
spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
}
return 0;
}
static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
char name[32];
sprintf(name, "%s Playback Volume", pfx);
return add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
}
static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
hda_nid_t nid, unsigned int chs)
{
char name[32];
sprintf(name, "%s Playback Switch", pfx);
return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
}
#define alc662_add_stereo_vol(spec, pfx, nid) \
alc662_add_vol_ctl(spec, pfx, nid, 3)
#define alc662_add_stereo_sw(spec, pfx, nid) \
alc662_add_sw_ctl(spec, pfx, nid, 3)
/* add playback controls from the parsed DAC table */ /* add playback controls from the parsed DAC table */
static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec, static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
char name[32]; struct alc_spec *spec = codec->spec;
static const char *chname[4] = { static const char *chname[4] = {
"Front", "Surround", NULL /*CLFE*/, "Side" "Front", "Surround", NULL /*CLFE*/, "Side"
}; };
hda_nid_t nid; hda_nid_t nid, mix;
int i, err; int i, err;
for (i = 0; i < cfg->line_outs; i++) { for (i = 0; i < cfg->line_outs; i++) {
if (!spec->multiout.dac_nids[i]) nid = spec->multiout.dac_nids[i];
if (!nid)
continue;
mix = alc662_dac_to_mix(codec, cfg->line_out_pins[i], nid);
if (!mix)
continue; continue;
nid = alc880_idx_to_dac(i);
if (i == 2) { if (i == 2) {
/* Center/LFE */ /* Center/LFE */
err = add_control(spec, ALC_CTL_WIDGET_VOL, err = alc662_add_vol_ctl(spec, "Center", nid, 1);
"Center Playback Volume",
HDA_COMPOSE_AMP_VAL(nid, 1, 0,
HDA_OUTPUT));
if (err < 0) if (err < 0)
return err; return err;
err = add_control(spec, ALC_CTL_WIDGET_VOL, err = alc662_add_vol_ctl(spec, "LFE", nid, 2);
"LFE Playback Volume",
HDA_COMPOSE_AMP_VAL(nid, 2, 0,
HDA_OUTPUT));
if (err < 0) if (err < 0)
return err; return err;
err = add_control(spec, ALC_CTL_WIDGET_MUTE, err = alc662_add_sw_ctl(spec, "Center", mix, 1);
"Center Playback Switch",
HDA_COMPOSE_AMP_VAL(0x0e, 1, 0,
HDA_INPUT));
if (err < 0) if (err < 0)
return err; return err;
err = add_control(spec, ALC_CTL_WIDGET_MUTE, err = alc662_add_sw_ctl(spec, "LFE", mix, 2);
"LFE Playback Switch",
HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
HDA_INPUT));
if (err < 0) if (err < 0)
return err; return err;
} else { } else {
const char *pfx; const char *pfx;
if (cfg->line_outs == 1 && if (cfg->line_outs == 1 &&
cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
if (!cfg->hp_pins) if (cfg->hp_outs)
pfx = "Speaker"; pfx = "Speaker";
else else
pfx = "PCM"; pfx = "PCM";
} else } else
pfx = chname[i]; pfx = chname[i];
sprintf(name, "%s Playback Volume", pfx); err = alc662_add_vol_ctl(spec, pfx, nid, 3);
err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 0,
HDA_OUTPUT));
if (err < 0) if (err < 0)
return err; return err;
if (cfg->line_outs == 1 && if (cfg->line_outs == 1 &&
cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
pfx = "Speaker"; pfx = "Speaker";
sprintf(name, "%s Playback Switch", pfx); err = alc662_add_sw_ctl(spec, pfx, mix, 3);
err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i),
3, 0, HDA_INPUT));
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -17218,54 +17293,38 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec, ...@@ -17218,54 +17293,38 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
} }
/* add playback controls for speaker and HP outputs */ /* add playback controls for speaker and HP outputs */
static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, /* 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) const char *pfx)
{ {
hda_nid_t nid; struct alc_spec *spec = codec->spec;
hda_nid_t nid, mix;
int err; int err;
char name[32];
if (!pin) if (!pin)
return 0; return 0;
nid = alc662_look_for_dac(codec, pin);
if (pin == 0x17) { if (!nid) {
/* ALC663 has a mono output pin on 0x17 */ char name[32];
/* the corresponding DAC is already occupied */
if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
return 0; /* no way */
/* create a switch only */
sprintf(name, "%s Playback Switch", pfx); sprintf(name, "%s Playback Switch", pfx);
err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
HDA_COMPOSE_AMP_VAL(pin, 2, 0, HDA_OUTPUT)); HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
return err;
} }
if (alc880_is_fixed_pin(pin)) { mix = alc662_dac_to_mix(codec, pin, nid);
nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); if (!mix)
/* printk(KERN_DEBUG "DAC nid=%x\n",nid); */ return 0;
/* specify the DAC as the extra output */ err = alc662_add_vol_ctl(spec, pfx, nid, 3);
if (!spec->multiout.hp_nid) if (err < 0)
spec->multiout.hp_nid = nid; return err;
else err = alc662_add_sw_ctl(spec, pfx, mix, 3);
spec->multiout.extra_out_nid[0] = nid; if (err < 0)
/* control HP volume/switch on the output mixer amp */ return err;
nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); return nid;
sprintf(name, "%s Playback Volume", pfx);
err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", pfx);
err = add_control(spec, ALC_CTL_BIND_MUTE, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
if (err < 0)
return err;
} else if (alc880_is_multi_pin(pin)) {
/* set manual connection */
/* we have only a switch on HP-out PIN */
sprintf(name, "%s Playback Switch", pfx);
err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
if (err < 0)
return err;
}
return 0;
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
...@@ -17274,30 +17333,35 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, ...@@ -17274,30 +17333,35 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid, int pin_type, hda_nid_t nid, int pin_type,
int dac_idx) hda_nid_t dac)
{ {
int i, num;
hda_nid_t srcs[4];
alc_set_pin_output(codec, nid, pin_type); alc_set_pin_output(codec, nid, pin_type);
/* need the manual connection? */ /* need the manual connection? */
if (alc880_is_multi_pin(nid)) { num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
struct alc_spec *spec = codec->spec; if (num <= 1)
int idx = alc880_multi_pin_idx(nid); return;
snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, for (i = 0; i < num; i++) {
AC_VERB_SET_CONNECT_SEL, if (alc662_mix_to_dac(srcs[i]) != dac)
alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); continue;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
return;
} }
} }
static void alc662_auto_init_multi_out(struct hda_codec *codec) static void alc662_auto_init_multi_out(struct hda_codec *codec)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
int pin_type = get_pin_type(spec->autocfg.line_out_type);
int i; int i;
for (i = 0; i <= HDA_SIDE; i++) { for (i = 0; i <= HDA_SIDE; i++) {
hda_nid_t nid = spec->autocfg.line_out_pins[i]; hda_nid_t nid = spec->autocfg.line_out_pins[i];
int pin_type = get_pin_type(spec->autocfg.line_out_type);
if (nid) if (nid)
alc662_auto_set_output_and_unmute(codec, nid, pin_type, alc662_auto_set_output_and_unmute(codec, nid, pin_type,
i); spec->multiout.dac_nids[i]);
} }
} }
...@@ -17307,12 +17371,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) ...@@ -17307,12 +17371,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
hda_nid_t pin; hda_nid_t pin;
pin = spec->autocfg.hp_pins[0]; pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */ if (pin)
/* use dac 0 */ alc662_auto_set_output_and_unmute(codec, pin, PIN_HP,
alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); spec->multiout.hp_nid);
pin = spec->autocfg.speaker_pins[0]; pin = spec->autocfg.speaker_pins[0];
if (pin) if (pin)
alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT,
spec->multiout.extra_out_nid[0]);
} }
#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID #define ALC662_PIN_CD_NID ALC880_PIN_CD_NID
...@@ -17350,21 +17415,25 @@ static int alc662_parse_auto_config(struct hda_codec *codec) ...@@ -17350,21 +17415,25 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
if (!spec->autocfg.line_outs) if (!spec->autocfg.line_outs)
return 0; /* can't find valid BIOS pin config */ return 0; /* can't find valid BIOS pin config */
err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
err = alc662_auto_create_multi_out_ctls(spec, &spec->autocfg); err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
err = alc662_auto_create_extra_out(spec, err = alc662_auto_create_extra_out(codec,
spec->autocfg.speaker_pins[0], spec->autocfg.speaker_pins[0],
"Speaker"); "Speaker");
if (err < 0) if (err < 0)
return err; return err;
err = alc662_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], if (err)
spec->multiout.extra_out_nid[0] = err;
err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
"Headphone"); "Headphone");
if (err < 0) if (err < 0)
return err; return err;
if (err)
spec->multiout.hp_nid = err;
err = alc662_auto_create_input_ctls(codec, &spec->autocfg); err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment