summaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_realtek.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
-rw-r--r--sound/pci/hda/patch_realtek.c1840
1 files changed, 1219 insertions, 621 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 22c73b78ac6f..8ea2fd654327 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -198,8 +198,11 @@ struct alc_spec {
/* for virtual master */
hda_nid_t vmaster_nid;
+ struct hda_vmaster_mute_hook vmaster_mute;
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
+ int num_loopbacks;
+ struct hda_amp_list loopback_list[8];
#endif
/* for PLL fix */
@@ -220,8 +223,6 @@ struct alc_spec {
struct snd_array bind_ctls;
};
-#define ALC_MODEL_AUTO 0 /* common for all chips */
-
static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
int dir, unsigned int bits)
{
@@ -300,6 +301,9 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
int i, type, num_conns;
hda_nid_t nid;
+ if (!spec->input_mux)
+ return 0;
+
mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
imux = &spec->input_mux[mux_idx];
if (!imux->num_items && mux_idx > 0)
@@ -651,15 +655,51 @@ static void alc_exec_unsol_event(struct hda_codec *codec, int action)
snd_hda_jack_report_sync(codec);
}
+/* update the master volume per volume-knob's unsol event */
+static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int val;
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value *uctl;
+
+ kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume");
+ if (!kctl)
+ return;
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return;
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+ val &= HDA_AMP_VOLMASK;
+ uctl->value.integer.value[0] = val;
+ uctl->value.integer.value[1] = val;
+ kctl->put(kctl, uctl);
+ kfree(uctl);
+}
+
/* unsolicited event for HP jack sensing */
static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
{
+ int action;
+
if (codec->vendor_id == 0x10ec0880)
res >>= 28;
else
res >>= 26;
- res = snd_hda_jack_get_action(codec, res);
- alc_exec_unsol_event(codec, res);
+ action = snd_hda_jack_get_action(codec, res);
+ if (action == ALC_DCVOL_EVENT) {
+ /* Execute the dc-vol event here as it requires the NID
+ * but we don't pass NID to alc_exec_unsol_event().
+ * Once when we convert all static quirks to the auto-parser,
+ * this can be integerated into there.
+ */
+ struct hda_jack_tbl *jack;
+ jack = snd_hda_jack_tbl_get_from_tag(codec, res);
+ if (jack)
+ alc_update_knob_master(codec, jack->nid);
+ return;
+ }
+ alc_exec_unsol_event(codec, action);
}
/* call init functions of standard auto-mute helpers */
@@ -1033,45 +1073,6 @@ static bool alc_check_dyn_adc_switch(struct hda_codec *codec)
return true;
}
-/* rebuild imux for matching with the given auto-mic pins (if not yet) */
-static bool alc_rebuild_imux_for_auto_mic(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux;
- static char * const texts[3] = {
- "Mic", "Internal Mic", "Dock Mic"
- };
- int i;
-
- if (!spec->auto_mic)
- return false;
- imux = &spec->private_imux[0];
- if (spec->input_mux == imux)
- return true;
- spec->imux_pins[0] = spec->ext_mic_pin;
- spec->imux_pins[1] = spec->int_mic_pin;
- spec->imux_pins[2] = spec->dock_mic_pin;
- for (i = 0; i < 3; i++) {
- strcpy(imux->items[i].label, texts[i]);
- if (spec->imux_pins[i]) {
- hda_nid_t pin = spec->imux_pins[i];
- int c;
- for (c = 0; c < spec->num_adc_nids; c++) {
- hda_nid_t cap = get_capsrc(spec, c);
- int idx = get_connection_index(codec, cap, pin);
- if (idx >= 0) {
- imux->items[i].index = idx;
- break;
- }
- }
- imux->num_items = i + 1;
- }
- }
- spec->num_mux_defs = 1;
- spec->input_mux = imux;
- return true;
-}
-
/* check whether all auto-mic pins are valid; setup indices if OK */
static bool alc_auto_mic_check_imux(struct hda_codec *codec)
{
@@ -1441,6 +1442,7 @@ enum {
ALC_FIXUP_ACT_PRE_PROBE,
ALC_FIXUP_ACT_PROBE,
ALC_FIXUP_ACT_INIT,
+ ALC_FIXUP_ACT_BUILD,
};
static void alc_apply_fixup(struct hda_codec *codec, int action)
@@ -1520,6 +1522,13 @@ static void alc_pick_fixup(struct hda_codec *codec,
int id = -1;
const char *name = NULL;
+ /* when model=nofixup is given, don't pick up any fixups */
+ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+ spec->fixup_list = NULL;
+ spec->fixup_id = -1;
+ return;
+ }
+
if (codec->modelname && models) {
while (models->name) {
if (!strcmp(codec->modelname, models->name)) {
@@ -1847,36 +1856,10 @@ DEFINE_CAPMIX_NOSRC(3);
/*
* slave controls for virtual master
*/
-static const char * const alc_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- "Mono Playback Volume",
- "Line Out Playback Volume",
- "CLFE Playback Volume",
- "Bass Speaker Playback Volume",
- "PCM Playback Volume",
- NULL,
-};
-
-static const char * const alc_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "Mono Playback Switch",
- "IEC958 Playback Switch",
- "Line Out Playback Switch",
- "CLFE Playback Switch",
- "Bass Speaker Playback Switch",
- "PCM Playback Switch",
+static const char * const alc_slave_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", "Side",
+ "Headphone", "Speaker", "Mono", "Line Out",
+ "CLFE", "Bass Speaker", "PCM",
NULL,
};
@@ -1967,14 +1950,17 @@ static int __alc_build_controls(struct hda_codec *codec)
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
HDA_OUTPUT, vmaster_tlv);
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, alc_slave_vols);
+ vmaster_tlv, alc_slave_pfxs,
+ "Playback Volume");
if (err < 0)
return err;
}
if (!spec->no_analog &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, alc_slave_sws);
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, alc_slave_pfxs,
+ "Playback Switch",
+ true, &spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
}
@@ -2059,7 +2045,11 @@ static int alc_build_controls(struct hda_codec *codec)
int err = __alc_build_controls(codec);
if (err < 0)
return err;
- return snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
+ return 0;
}
@@ -2068,15 +2058,15 @@ static int alc_build_controls(struct hda_codec *codec)
*/
static void alc_init_special_input_src(struct hda_codec *codec);
-static int alc269_fill_coef(struct hda_codec *codec);
+static void alc_auto_init_std(struct hda_codec *codec);
static int alc_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
unsigned int i;
- if (codec->vendor_id == 0x10ec0269)
- alc269_fill_coef(codec);
+ if (spec->init_hook)
+ spec->init_hook(codec);
alc_fix_pll(codec);
alc_auto_init_amp(codec, spec->init_amp);
@@ -2084,9 +2074,7 @@ static int alc_init(struct hda_codec *codec)
for (i = 0; i < spec->num_init_verbs; i++)
snd_hda_sequence_write(codec, spec->init_verbs[i]);
alc_init_special_input_src(codec);
-
- if (spec->init_hook)
- spec->init_hook(codec);
+ alc_auto_init_std(codec);
alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
@@ -2675,6 +2663,25 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
return channel_name[ch];
}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+/* add the powersave loopback-list entry */
+static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
+{
+ struct hda_amp_list *list;
+
+ if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
+ return;
+ list = spec->loopback_list + spec->num_loopbacks;
+ list->nid = mix;
+ list->dir = HDA_INPUT;
+ list->idx = idx;
+ spec->num_loopbacks++;
+ spec->loopback.amplist = spec->loopback_list;
+}
+#else
+#define add_loopback_list(spec, mix, idx) /* NOP */
+#endif
+
/* create input playback/capture controls for the given pin */
static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
const char *ctlname, int ctlidx,
@@ -2690,6 +2697,7 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
+ add_loopback_list(spec, mix_nid, idx);
return 0;
}
@@ -2954,10 +2962,27 @@ static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin,
return 0;
}
+static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+ if (found_in_nid_list(nid, spec->multiout.dac_nids,
+ ARRAY_SIZE(spec->private_dac_nids)) ||
+ found_in_nid_list(nid, spec->multiout.hp_out_nid,
+ ARRAY_SIZE(spec->multiout.hp_out_nid)) ||
+ found_in_nid_list(nid, spec->multiout.extra_out_nid,
+ ARRAY_SIZE(spec->multiout.extra_out_nid)))
+ return true;
+ for (i = 0; i < spec->multi_ios; i++) {
+ if (spec->multi_io[i].dac == nid)
+ return true;
+ }
+ return false;
+}
+
/* look for an empty DAC slot */
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, num;
@@ -2967,16 +2992,8 @@ 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;
- if (found_in_nid_list(nid, spec->multiout.dac_nids,
- ARRAY_SIZE(spec->private_dac_nids)))
- continue;
- if (found_in_nid_list(nid, spec->multiout.hp_out_nid,
- ARRAY_SIZE(spec->multiout.hp_out_nid)))
- continue;
- if (found_in_nid_list(nid, spec->multiout.extra_out_nid,
- ARRAY_SIZE(spec->multiout.extra_out_nid)))
- continue;
- return nid;
+ if (!alc_is_dac_already_used(codec, nid))
+ return nid;
}
return 0;
}
@@ -2988,6 +3005,8 @@ static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
hda_nid_t srcs[5];
int i, num;
+ if (!pin || !dac)
+ return false;
pin = alc_go_down_to_selector(codec, pin);
num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
for (i = 0; i < num; i++) {
@@ -3000,83 +3019,260 @@ static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
{
+ struct alc_spec *spec = codec->spec;
hda_nid_t sel = alc_go_down_to_selector(codec, pin);
- if (snd_hda_get_conn_list(codec, sel, NULL) == 1)
+ hda_nid_t nid, nid_found, srcs[5];
+ int i, num = snd_hda_get_connections(codec, sel, srcs,
+ ARRAY_SIZE(srcs));
+ if (num == 1)
return alc_auto_look_for_dac(codec, pin);
- return 0;
+ nid_found = 0;
+ for (i = 0; i < num; i++) {
+ if (srcs[i] == spec->mixer_nid)
+ continue;
+ nid = alc_auto_mix_to_dac(codec, srcs[i]);
+ if (nid && !alc_is_dac_already_used(codec, nid)) {
+ if (nid_found)
+ return 0;
+ nid_found = nid;
+ }
+ }
+ return nid_found;
}
-/* return 0 if no possible DAC is found, 1 if one or more found */
-static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs,
- const hda_nid_t *pins, hda_nid_t *dacs)
+/* mark up volume and mute control NIDs: used during badness parsing and
+ * at creating actual controls
+ */
+static inline unsigned int get_ctl_pos(unsigned int data)
{
- int i;
+ hda_nid_t nid = get_amp_nid_(data);
+ unsigned int dir;
+ if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
+ return 0;
+ dir = get_amp_direction_(data);
+ return (nid << 1) | dir;
+}
- if (num_outs && !dacs[0]) {
- dacs[0] = alc_auto_look_for_dac(codec, pins[0]);
- if (!dacs[0])
- return 0;
- }
+#define is_ctl_used(bits, data) \
+ test_bit(get_ctl_pos(data), bits)
+#define mark_ctl_usage(bits, data) \
+ set_bit(get_ctl_pos(data), bits)
- for (i = 1; i < num_outs; i++)
- dacs[i] = get_dac_if_single(codec, pins[i]);
- for (i = 1; i < num_outs; i++) {
+static void clear_vol_marks(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls));
+ memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls));
+}
+
+/* badness definition */
+enum {
+ /* No primary DAC is found for the main output */
+ BAD_NO_PRIMARY_DAC = 0x10000,
+ /* No DAC is found for the extra output */
+ BAD_NO_DAC = 0x4000,
+ /* No possible multi-ios */
+ BAD_MULTI_IO = 0x103,
+ /* No individual DAC for extra output */
+ BAD_NO_EXTRA_DAC = 0x102,
+ /* No individual DAC for extra surrounds */
+ BAD_NO_EXTRA_SURR_DAC = 0x101,
+ /* Primary DAC shared with main surrounds */
+ BAD_SHARED_SURROUND = 0x100,
+ /* Primary DAC shared with main CLFE */
+ BAD_SHARED_CLFE = 0x10,
+ /* Primary DAC shared with extra surrounds */
+ BAD_SHARED_EXTRA_SURROUND = 0x10,
+ /* Volume widget is shared */
+ BAD_SHARED_VOL = 0x10,
+};
+
+static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac);
+static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac);
+
+static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t dac)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid;
+ unsigned int val;
+ int badness = 0;
+
+ nid = alc_look_for_out_vol_nid(codec, pin, dac);
+ if (nid) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (is_ctl_used(spec->vol_ctls, nid))
+ badness += BAD_SHARED_VOL;
+ else
+ mark_ctl_usage(spec->vol_ctls, val);
+ } else
+ badness += BAD_SHARED_VOL;
+ nid = alc_look_for_out_mute_nid(codec, pin, dac);
+ if (nid) {
+ unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
+ if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT)
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+ if (is_ctl_used(spec->sw_ctls, val))
+ badness += BAD_SHARED_VOL;
+ else
+ mark_ctl_usage(spec->sw_ctls, val);
+ } else
+ badness += BAD_SHARED_VOL;
+ return badness;
+}
+
+struct badness_table {
+ int no_primary_dac; /* no primary DAC */
+ int no_dac; /* no secondary DACs */
+ int shared_primary; /* primary DAC is shared with main output */
+ int shared_surr; /* secondary DAC shared with main or primary */
+ int shared_clfe; /* third DAC shared with main or primary */
+ int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
+};
+
+static struct badness_table main_out_badness = {
+ .no_primary_dac = BAD_NO_PRIMARY_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_PRIMARY_DAC,
+ .shared_surr = BAD_SHARED_SURROUND,
+ .shared_clfe = BAD_SHARED_CLFE,
+ .shared_surr_main = BAD_SHARED_SURROUND,
+};
+
+static struct badness_table extra_out_badness = {
+ .no_primary_dac = BAD_NO_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_EXTRA_DAC,
+ .shared_surr = BAD_SHARED_EXTRA_SURROUND,
+ .shared_clfe = BAD_SHARED_EXTRA_SURROUND,
+ .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
+};
+
+/* try to assign DACs to pins and return the resultant badness */
+static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins, hda_nid_t *dacs,
+ const struct badness_table *bad)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, j;
+ int badness = 0;
+ hda_nid_t dac;
+
+ if (!num_outs)
+ return 0;
+
+ for (i = 0; i < num_outs; i++) {
+ hda_nid_t pin = pins[i];
if (!dacs[i])
- dacs[i] = alc_auto_look_for_dac(codec, pins[i]);
+ dacs[i] = alc_auto_look_for_dac(codec, pin);
+ if (!dacs[i] && !i) {
+ for (j = 1; j < num_outs; j++) {
+ if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) {
+ dacs[0] = dacs[j];
+ dacs[j] = 0;
+ break;
+ }
+ }
+ }
+ dac = dacs[i];
+ if (!dac) {
+ if (alc_auto_is_dac_reachable(codec, pin, dacs[0]))
+ dac = dacs[0];
+ else if (cfg->line_outs > i &&
+ alc_auto_is_dac_reachable(codec, pin,
+ spec->private_dac_nids[i]))
+ dac = spec->private_dac_nids[i];
+ if (dac) {
+ if (!i)
+ badness += bad->shared_primary;
+ else if (i == 1)
+ badness += bad->shared_surr;
+ else
+ badness += bad->shared_clfe;
+ } else if (alc_auto_is_dac_reachable(codec, pin,
+ spec->private_dac_nids[0])) {
+ dac = spec->private_dac_nids[0];
+ badness += bad->shared_surr_main;
+ } else if (!i)
+ badness += bad->no_primary_dac;
+ else
+ badness += bad->no_dac;
+ }
+ if (dac)
+ badness += eval_shared_vol_badness(codec, pin, dac);
}
- return 1;
+
+ return badness;
}
static int alc_auto_fill_multi_ios(struct hda_codec *codec,
- unsigned int location, int offset);
-static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
- hda_nid_t pin, hda_nid_t dac);
+ hda_nid_t reference_pin,
+ bool hardwired, int offset);
+
+static bool alc_map_singles(struct hda_codec *codec, int outs,
+ const hda_nid_t *pins, hda_nid_t *dacs)
+{
+ int i;
+ bool found = false;
+ for (i = 0; i < outs; i++) {
+ if (dacs[i])
+ continue;
+ dacs[i] = get_dac_if_single(codec, pins[i]);
+ if (dacs[i])
+ found = true;
+ }
+ return found;
+}
/* fill in the dac_nids table from the parsed pin configuration */
-static int alc_auto_fill_dac_nids(struct hda_codec *codec)
+static int fill_and_eval_dacs(struct hda_codec *codec,
+ bool fill_hardwired,
+ bool fill_mio_first)
{
struct alc_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int location, defcfg;
- int num_pins;
- bool redone = false;
- int i;
+ int i, err, badness;
- again:
/* set num_dacs once to full for alc_auto_look_for_dac() */
spec->multiout.num_dacs = cfg->line_outs;
- spec->multiout.hp_out_nid[0] = 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;
+ memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
+ memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
+ memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
spec->multi_ios = 0;
+ clear_vol_marks(codec);
+ badness = 0;
/* 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_out_nid[0] =
- 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]);
+ if (fill_hardwired) {
+ bool mapped;
+ do {
+ mapped = alc_map_singles(codec, cfg->line_outs,
+ cfg->line_out_pins,
+ spec->private_dac_nids);
+ mapped |= alc_map_singles(codec, cfg->hp_outs,
+ cfg->hp_pins,
+ spec->multiout.hp_out_nid);
+ mapped |= alc_map_singles(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid);
+ if (fill_mio_first && cfg->line_outs == 1 &&
+ cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0);
+ if (!err)
+ mapped = true;
+ }
+ } while (mapped);
}
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t pin = cfg->line_out_pins[i];
- if (spec->private_dac_nids[i])
- continue;
- 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;
- }
- }
+ badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->private_dac_nids,
+ &main_out_badness);
/* re-count num_dacs and squash invalid entries */
spec->multiout.num_dacs = 0;
@@ -3091,30 +3287,144 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
}
}
- if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ if (fill_mio_first &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
/* try to fill multi-io first */
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
- location = get_defcfg_location(defcfg);
-
- num_pins = alc_auto_fill_multi_ios(codec, location, 0);
- if (num_pins > 0) {
- spec->multi_ios = num_pins;
- spec->ext_channel_count = 2;
- spec->multiout.num_dacs = num_pins + 1;
- }
+ err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+ if (err < 0)
+ return err;
+ /* we don't count badness at this stage yet */
}
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- alc_auto_fill_extra_dacs(codec, cfg->hp_outs, cfg->hp_pins,
- spec->multiout.hp_out_nid);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ &extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- int err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs,
- cfg->speaker_pins,
- spec->multiout.extra_out_nid);
- /* if no speaker volume is assigned, try again as the primary
- * output
- */
- if (!err && cfg->speaker_outs > 0 &&
+ err = alc_auto_fill_dacs(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ &extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ /* try multi-ios with HP + inputs */
+ int offset = 0;
+ if (cfg->line_outs >= 3)
+ offset = 1;
+ err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false,
+ offset);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+
+ if (spec->multi_ios == 2) {
+ for (i = 0; i < 2; i++)
+ spec->private_dac_nids[spec->multiout.num_dacs++] =
+ spec->multi_io[i].dac;
+ spec->ext_channel_count = 2;
+ } else if (spec->multi_ios) {
+ spec->multi_ios = 0;
+ badness += BAD_MULTI_IO;
+ }
+
+ return badness;
+}
+
+#define DEBUG_BADNESS
+
+#ifdef DEBUG_BADNESS
+#define debug_badness snd_printdd
+#else
+#define debug_badness(...)
+#endif
+
+static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg)
+{
+ debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[2],
+ spec->multiout.dac_nids[0],
+ spec->multiout.dac_nids[1],
+ spec->multiout.dac_nids[2],
+ spec->multiout.dac_nids[3]);
+ if (spec->multi_ios > 0)
+ debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
+ spec->multi_ios,
+ spec->multi_io[0].pin, spec->multi_io[1].pin,
+ spec->multi_io[0].dac, spec->multi_io[1].dac);
+ debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->hp_pins[0], cfg->hp_pins[1],
+ cfg->hp_pins[2], cfg->hp_pins[2],
+ spec->multiout.hp_out_nid[0],
+ spec->multiout.hp_out_nid[1],
+ spec->multiout.hp_out_nid[2],
+ spec->multiout.hp_out_nid[3]);
+ debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->speaker_pins[0], cfg->speaker_pins[1],
+ cfg->speaker_pins[2], cfg->speaker_pins[3],
+ spec->multiout.extra_out_nid[0],
+ spec->multiout.extra_out_nid[1],
+ spec->multiout.extra_out_nid[2],
+ spec->multiout.extra_out_nid[3]);
+}
+
+static int alc_auto_fill_dac_nids(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *best_cfg;
+ int best_badness = INT_MAX;
+ int badness;
+ bool fill_hardwired = true, fill_mio_first = true;
+ bool best_wired = true, best_mio = true;
+ bool hp_spk_swapped = false;
+
+ best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
+ if (!best_cfg)
+ return -ENOMEM;
+ *best_cfg = *cfg;
+
+ for (;;) {
+ badness = fill_and_eval_dacs(codec, fill_hardwired,
+ fill_mio_first);
+ if (badness < 0)
+ return badness;
+ debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
+ cfg->line_out_type, fill_hardwired, fill_mio_first,
+ badness);
+ debug_show_configs(spec, cfg);
+ if (badness < best_badness) {
+ best_badness = badness;
+ *best_cfg = *cfg;
+ best_wired = fill_hardwired;
+ best_mio = fill_mio_first;
+ }
+ if (!badness)
+ break;
+ fill_mio_first = !fill_mio_first;
+ if (!fill_mio_first)
+ continue;
+ fill_hardwired = !fill_hardwired;
+ if (!fill_hardwired)
+ continue;
+ if (hp_spk_swapped)
+ break;
+ hp_spk_swapped = true;
+ if (cfg->speaker_outs > 0 &&
cfg->line_out_type == AUTO_PIN_HP_OUT) {
cfg->hp_outs = cfg->line_outs;
memcpy(cfg->hp_pins, cfg->line_out_pins,
@@ -3125,48 +3435,45 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
cfg->speaker_outs = 0;
memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
- redone = false;
- goto again;
- }
+ fill_hardwired = true;
+ continue;
+ }
+ if (cfg->hp_outs > 0 &&
+ 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 = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ break;
}
- if (!spec->multi_ios &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
- cfg->hp_outs) {
- /* try multi-ios with HP + inputs */
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]);
- location = get_defcfg_location(defcfg);
-
- num_pins = alc_auto_fill_multi_ios(codec, location, 1);
- if (num_pins > 0) {
- spec->multi_ios = num_pins;
- spec->ext_channel_count = 2;
- spec->multiout.num_dacs = num_pins + 1;
- }
+ if (badness) {
+ *cfg = *best_cfg;
+ fill_and_eval_dacs(codec, best_wired, best_mio);
}
+ debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
+ cfg->line_out_type, best_wired, best_mio);
+ debug_show_configs(spec, cfg);
if (cfg->line_out_pins[0])
spec->vmaster_nid =
alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0],
spec->multiout.dac_nids[0]);
- return 0;
-}
-static inline unsigned int get_ctl_pos(unsigned int data)
-{
- hda_nid_t nid = get_amp_nid_(data);
- unsigned int dir;
- if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
- return 0;
- dir = get_amp_direction_(data);
- return (nid << 1) | dir;
+ /* clear the bitmap flags for creating controls */
+ clear_vol_marks(codec);
+ kfree(best_cfg);
+ return 0;
}
-#define is_ctl_used(bits, data) \
- test_bit(get_ctl_pos(data), bits)
-#define mark_ctl_usage(bits, data) \
- set_bit(get_ctl_pos(data), bits)
-
static int alc_auto_add_vol_ctl(struct hda_codec *codec,
const char *pfx, int cidx,
hda_nid_t nid, unsigned int chs)
@@ -3278,14 +3585,17 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
dac = spec->multiout.dac_nids[i];
if (!dac)
continue;
- if (i >= cfg->line_outs)
+ if (i >= cfg->line_outs) {
pin = spec->multi_io[i - 1].pin;
- else
+ index = 0;
+ name = channel_name[i];
+ } else {
pin = cfg->line_out_pins[i];
+ name = alc_get_line_out_pfx(spec, i, true, &index);
+ }
sw = alc_look_for_out_mute_nid(codec, pin, dac);
vol = alc_look_for_out_vol_nid(codec, pin, dac);
- name = alc_get_line_out_pfx(spec, i, true, &index);
if (!name || !strcmp(name, "CLFE")) {
/* Center/LFE */
err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1);
@@ -3382,41 +3692,31 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins,
return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0);
}
- if (dacs[num_pins - 1]) {
- /* OK, we have a multi-output system with individual volumes */
- for (i = 0; i < num_pins; i++) {
- if (num_pins >= 3) {
- snprintf(name, sizeof(name), "%s %s",
- pfx, channel_name[i]);
- err = alc_auto_create_extra_out(codec, pins[i], dacs[i],
- name, 0);
- } else {
- err = alc_auto_create_extra_out(codec, pins[i], dacs[i],
- pfx, i);
- }
- if (err < 0)
- return err;
- }
- return 0;
- }
-
- /* Let's create a bind-controls */
- ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_sw);
- if (!ctl)
- return -ENOMEM;
- n = 0;
for (i = 0; i < num_pins; i++) {
- if (get_wcaps(codec, pins[i]) & AC_WCAP_OUT_AMP)
- ctl->values[n++] =
- HDA_COMPOSE_AMP_VAL(pins[i], 3, 0, HDA_OUTPUT);
- }
- if (n) {
- snprintf(name, sizeof(name), "%s Playback Switch", pfx);
- err = add_control(spec, ALC_CTL_BIND_SW, name, 0, (long)ctl);
+ hda_nid_t dac;
+ if (dacs[num_pins - 1])
+ dac = dacs[i]; /* with individual volumes */
+ else
+ dac = 0;
+ if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
+ err = alc_auto_create_extra_out(codec, pins[i], dac,
+ "Bass Speaker", 0);
+ } else if (num_pins >= 3) {
+ snprintf(name, sizeof(name), "%s %s",
+ pfx, channel_name[i]);
+ err = alc_auto_create_extra_out(codec, pins[i], dac,
+ name, 0);
+ } else {
+ err = alc_auto_create_extra_out(codec, pins[i], dac,
+ pfx, i);
+ }
if (err < 0)
return err;
}
+ if (dacs[num_pins - 1])
+ return 0;
+ /* Let's create a bind-controls for volumes */
ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol);
if (!ctl)
return -ENOMEM;
@@ -3552,58 +3852,111 @@ static void alc_auto_init_extra_out(struct hda_codec *codec)
}
}
+/* check whether the given pin can be a multi-io pin */
+static bool can_be_multiio_pin(struct hda_codec *codec,
+ unsigned int location, hda_nid_t nid)
+{
+ unsigned int defcfg, caps;
+
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
+ return false;
+ if (location && get_defcfg_location(defcfg) != location)
+ return false;
+ caps = snd_hda_query_pin_caps(codec, nid);
+ if (!(caps & AC_PINCAP_OUT))
+ return false;
+ return true;
+}
+
/*
* multi-io helper
+ *
+ * When hardwired is set, try to fill ony hardwired pins, and returns
+ * zero if any pins are filled, non-zero if nothing found.
+ * When hardwired is off, try to fill possible input pins, and returns
+ * the badness value.
*/
static int alc_auto_fill_multi_ios(struct hda_codec *codec,
- unsigned int location,
- int offset)
+ hda_nid_t reference_pin,
+ bool hardwired, int offset)
{
struct alc_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t prime_dac = spec->private_dac_nids[0];
- int type, i, dacs, num_pins = 0;
+ int type, i, j, dacs, num_pins, old_pins;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int badness = 0;
+
+ old_pins = spec->multi_ios;
+ if (old_pins >= 2)
+ goto end_fill;
+
+ num_pins = 0;
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != type)
+ continue;
+ if (can_be_multiio_pin(codec, location,
+ cfg->inputs[i].pin))
+ num_pins++;
+ }
+ }
+ if (num_pins < 2)
+ goto end_fill;
dacs = spec->multiout.num_dacs;
for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->inputs[i].pin;
hda_nid_t dac = 0;
- unsigned int defcfg, caps;
+
if (cfg->inputs[i].type != type)
continue;
- defcfg = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
- continue;
- if (location && get_defcfg_location(defcfg) != location)
+ if (!can_be_multiio_pin(codec, location, nid))
continue;
- caps = snd_hda_query_pin_caps(codec, nid);
- if (!(caps & AC_PINCAP_OUT))
+ for (j = 0; j < spec->multi_ios; j++) {
+ if (nid == spec->multi_io[j].pin)
+ break;
+ }
+ if (j < spec->multi_ios)
continue;
- if (offset && offset + num_pins < dacs) {
- dac = spec->private_dac_nids[offset + num_pins];
+
+ if (offset && offset + spec->multi_ios < dacs) {
+ dac = spec->private_dac_nids[offset + spec->multi_ios];
if (!alc_auto_is_dac_reachable(codec, nid, dac))
dac = 0;
}
- if (!dac)
+ if (hardwired)
+ dac = get_dac_if_single(codec, nid);
+ else if (!dac)
dac = alc_auto_look_for_dac(codec, nid);
- if (!dac)
+ if (!dac) {
+ badness++;
continue;
- spec->multi_io[num_pins].pin = nid;
- spec->multi_io[num_pins].dac = dac;
- num_pins++;
- spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
+ }
+ spec->multi_io[spec->multi_ios].pin = nid;
+ spec->multi_io[spec->multi_ios].dac = dac;
+ spec->multi_ios++;
+ if (spec->multi_ios >= 2)
+ break;
}
}
- spec->multiout.num_dacs = dacs;
- if (num_pins < 2) {
- /* clear up again */
- memset(spec->private_dac_nids + dacs, 0,
- sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - dacs));
- spec->private_dac_nids[0] = prime_dac;
- return 0;
+ end_fill:
+ if (badness)
+ badness = BAD_MULTI_IO;
+ if (old_pins == spec->multi_ios) {
+ if (hardwired)
+ return 1; /* nothing found */
+ else
+ return badness; /* no badness if nothing found */
+ }