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 */
/* 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,
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]);
- 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)
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--) {
}
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;
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,