diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a5697c3b30b8..4746afa25db8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2982,76 +2982,191 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) return 0; } -/* return 0 if no possible DAC is found, 1 if one or more found */ +/* 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) +{ + 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; +} + +#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 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 individual DAC for extra output */ + BAD_NO_EXTRA_DAC = 0x1000, + /* No individual DAC for extra surrounds */ + BAD_NO_EXTRA_SURR_DAC = 0x200, + /* Primary DAC shared with main surrounds */ + BAD_SHARED_SURROUND = 0x100, + /* Volume widget is shared */ + BAD_SHARED_VOL = 0x10, + /* Primary DAC shared with main CLFE */ + BAD_SHARED_CLFE = 0x10, + /* Primary DAC shared with extra surrounds */ + BAD_SHARED_EXTRA_SURROUND = 0x10, + /* No possible multi-ios */ + BAD_MULTI_IO = 0x1, +}; + +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; +} + +/* try to assign DACs to extra pins and return the resultant badness */ static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs, const hda_nid_t *pins, hda_nid_t *dacs) { + struct alc_spec *spec = codec->spec; int i; + int badness = 0; + hda_nid_t dac; if (num_outs && !dacs[0]) { - dacs[0] = alc_auto_look_for_dac(codec, pins[0]); - if (!dacs[0]) - return 0; + dac = dacs[0] = alc_auto_look_for_dac(codec, pins[0]); + if (!dacs[0]) { + dac = spec->private_dac_nids[0]; + if (!alc_auto_is_dac_reachable(codec, pins[0], dac)) + return BAD_NO_DAC; + badness += BAD_NO_EXTRA_DAC; + } + badness += eval_shared_vol_badness(codec, pins[0], dac); } for (i = 1; i < num_outs; i++) dacs[i] = get_dac_if_single(codec, pins[i]); for (i = 1; i < num_outs; i++) { - if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pins[i]); + dac = dacs[i]; + if (!dac) + dac = dacs[i] = alc_auto_look_for_dac(codec, pins[i]); + if (!dac) { + if (alc_auto_is_dac_reachable(codec, pins[i], dacs[0])) { + dac = dacs[0]; + badness += BAD_SHARED_EXTRA_SURROUND; + } else if (alc_auto_is_dac_reachable(codec, pins[i], + spec->private_dac_nids[0])) { + dac = spec->private_dac_nids[0]; + badness += BAD_NO_EXTRA_SURR_DAC; + } else + badness += BAD_NO_DAC; + } + if (dac) + badness += eval_shared_vol_badness(codec, pins[i], 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); /* 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) { 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) { + if (fill_hardwired) { 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]); + for (i = 0; i < cfg->hp_outs; i++) + spec->multiout.hp_out_nid[i] = + get_dac_if_single(codec, cfg->hp_pins[i]); + for (i = 0; i < cfg->speaker_outs; i++) + spec->multiout.extra_out_nid[i] = + get_dac_if_single(codec, cfg->speaker_pins[i]); } 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; + hda_nid_t dac; + if (!spec->private_dac_nids[i]) + spec->private_dac_nids[i] = + alc_auto_look_for_dac(codec, pin); + dac = spec->private_dac_nids[i]; + if (!dac) { + if (!i) + badness += BAD_NO_PRIMARY_DAC; + else if (alc_auto_is_dac_reachable(codec, pin, + spec->private_dac_nids[0])) { + if (i == 1) + badness += BAD_SHARED_SURROUND; + else + badness += BAD_SHARED_CLFE; + dac = spec->private_dac_nids[0]; + } else + badness += BAD_NO_DAC; } + if (dac) + badness += eval_shared_vol_badness(codec, pin, dac); } /* re-count num_dacs and squash invalid entries */ @@ -3071,26 +3186,114 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) /* 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, location, 0); + if (err < 0) + return err; + badness += err; } - 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_extra_dacs(codec, cfg->hp_outs, + cfg->hp_pins, + spec->multiout.hp_out_nid); + 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_extra_dacs(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid); + if (err < 0) + return err; + badness += err; + } + 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); + err = alc_auto_fill_multi_ios(codec, location, 1); + if (err < 0) + return err; + badness += err; + } + + 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]); + 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; + bool best_wired = 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); + if (badness < 0) + return badness; + debug_badness("==> lo_type=%d, wired=%d, badness=0x%x\n", + cfg->line_out_type, fill_hardwired, badness); + debug_show_configs(spec, cfg); + if (badness < best_badness) { + best_badness = badness; + *best_cfg = *cfg; + best_wired = fill_hardwired; + } + if (!badness) + break; + if (fill_hardwired) { + fill_hardwired = false; + 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, @@ -3101,48 +3304,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); } + debug_badness("==> Best config: lo_type=%d, wired=%d\n", + cfg->line_out_type, best_wired); + 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]); + + /* clear the bitmap flags for creating controls */ + clear_vol_marks(codec); + kfree(best_cfg); 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; -} - -#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) @@ -3539,6 +3739,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, 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 badness = 0; dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { @@ -3563,12 +3764,16 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, } if (!dac) dac = alc_auto_look_for_dac(codec, nid); - if (!dac) + if (!dac) { + badness += BAD_MULTI_IO; 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; + if (num_pins >= 2) + break; } } spec->multiout.num_dacs = dacs; @@ -3577,9 +3782,13 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, 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; + return badness; } - return num_pins; + + spec->multi_ios = num_pins; + spec->ext_channel_count = 2; + spec->multiout.num_dacs = num_pins + 1; + return 0; } static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,