#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD
+#define MAX_NID_PATH_DEPTH 5
+
+/* output-path: DAC -> ... -> pin
+ * idx[] contains the source index number of the next widget;
+ * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+struct nid_path {
+ int depth;
+ hda_nid_t path[MAX_NID_PATH_DEPTH];
+ unsigned char idx[MAX_NID_PATH_DEPTH];
+ unsigned char multi[MAX_NID_PATH_DEPTH];
+ unsigned int ctls[2]; /* 0 = volume, 1 = mute */
+};
+
struct alc_spec {
struct hda_gen_spec gen;
int num_all_dacs;
hda_nid_t all_dacs[16];
+ /* output paths */
+ struct snd_array out_path;
+
/* hooks */
void (*init_hook)(struct hda_codec *codec);
#ifdef CONFIG_PM
alc_free_kctls(codec);
alc_free_bind_ctls(codec);
+ snd_array_free(&spec->out_path);
snd_hda_gen_free(&spec->gen);
kfree(spec);
snd_hda_detach_beep_device(codec);
{
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)
+
+ for (i = 0; i < spec->out_path.used; i++) {
+ struct nid_path *path = snd_array_elem(&spec->out_path, i);
+ if (path->path[0] == nid)
return true;
}
return false;
return 0;
}
+/* called recursively */
+static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t target_dac, int with_aa_mix,
+ struct nid_path *path, int depth)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t conn[8];
+ int i, nums;
+
+ if (nid == spec->mixer_nid) {
+ if (!with_aa_mix)
+ return false;
+ with_aa_mix = 2; /* mark aa-mix is included */
+ }
+
+ nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++) {
+ if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
+ continue;
+ if (conn[i] == target_dac ||
+ (!target_dac && !alc_is_dac_already_used(codec, conn[i]))) {
+ /* aa-mix is requested but not included? */
+ if (!(spec->mixer_nid && with_aa_mix == 1))
+ goto found;
+ }
+ }
+ if (depth >= MAX_NID_PATH_DEPTH)
+ return false;
+ for (i = 0; i < nums; i++) {
+ unsigned int type;
+ type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_AUD_OUT)
+ continue;
+ if (__parse_output_path(codec, conn[i], target_dac,
+ with_aa_mix, path, depth + 1))
+ goto found;
+ }
+ return false;
+
+ found:
+ path->path[path->depth] = conn[i];
+ path->idx[path->depth] = i;
+ if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
+ path->multi[path->depth] = 1;
+ path->depth++;
+ return true;
+}
+
+/* parse the output path from the given nid to the target DAC;
+ * when target_dac is 0, try to find an empty DAC;
+ * when with_aa_mix is 0, paths with spec->mixer_nid are excluded
+ */
+static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t target_dac, int with_aa_mix,
+ struct nid_path *path)
+{
+ if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
+ path->path[path->depth] = nid;
+ path->depth++;
+#if 0
+ snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
+ path->depth, path->path[0], path->path[1],
+ path->path[2], path->path[3], path->path[4]);
+#endif
+ return true;
+ }
+ return false;
+}
+
static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
{
struct alc_spec *spec = codec->spec;
static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
hda_nid_t pin, hda_nid_t dac);
+static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t dac)
+{
+ struct alc_spec *spec = codec->spec;
+ struct nid_path *path;
+
+ path = snd_array_new(&spec->out_path);
+ if (!path)
+ return false;
+ memset(path, 0, sizeof(*path));
+ if (parse_output_path(codec, pin, dac, 0, path))
+ return true;
+ /* push back */
+ spec->out_path.used--;
+ return false;
+}
+
static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
hda_nid_t dac)
{
else
badness += bad->no_dac;
}
+ if (!add_new_out_path(codec, pin, dac))
+ dac = dacs[i] = 0;
if (dac)
badness += eval_shared_vol_badness(codec, pin, dac);
}
int i;
bool found = false;
for (i = 0; i < outs; i++) {
+ hda_nid_t dac;
if (dacs[i])
continue;
- dacs[i] = get_dac_if_single(codec, pins[i]);
- if (dacs[i])
+ dac = get_dac_if_single(codec, pins[i]);
+ if (!dac)
+ continue;
+ if (add_new_out_path(codec, pins[i], dac)) {
+ dacs[i] = dac;
found = true;
+ }
}
return found;
}
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;
+ snd_array_free(&spec->out_path);
clear_vol_marks(codec);
badness = 0;
badness++;
continue;
}
+ if (!add_new_out_path(codec, nid, dac)) {
+ badness++;
+ continue;
+ }
spec->multi_io[spec->multi_ios].pin = nid;
spec->multi_io[spec->multi_ios].dac = dac;
spec->multi_ios++;
return badness; /* no badness if nothing found */
}
if (!hardwired && spec->multi_ios < 2) {
+ /* cancel newly assigned paths */
+ spec->out_path.used -= spec->multi_ios - old_pins;
spec->multi_ios = old_pins;
return badness;
}
snd_hda_gen_init(&spec->gen);
snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
+ snd_array_init(&spec->out_path, sizeof(struct nid_path), 8);
err = alc_codec_rename_from_preset(codec);
if (err < 0) {