|
@@ -147,6 +147,33 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
|
|
|
}
|
|
|
EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
|
|
|
|
|
|
+/* get the index number corresponding to the path instance;
|
|
|
+ * the index starts from 1, for easier checking the invalid value
|
|
|
+ */
|
|
|
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
|
|
|
+{
|
|
|
+ struct hda_gen_spec *spec = codec->spec;
|
|
|
+ struct nid_path *array = spec->paths.list;
|
|
|
+ ssize_t idx;
|
|
|
+
|
|
|
+ if (!spec->paths.used)
|
|
|
+ return 0;
|
|
|
+ idx = path - array;
|
|
|
+ if (idx < 0 || idx >= spec->paths.used)
|
|
|
+ return 0;
|
|
|
+ return idx + 1;
|
|
|
+}
|
|
|
+
|
|
|
+/* get the path instance corresponding to the given index number */
|
|
|
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
|
|
|
+{
|
|
|
+ struct hda_gen_spec *spec = codec->spec;
|
|
|
+
|
|
|
+ if (idx <= 0 || idx > spec->paths.used)
|
|
|
+ return NULL;
|
|
|
+ return snd_array_elem(&spec->paths, idx - 1);
|
|
|
+}
|
|
|
+
|
|
|
/* check whether the given DAC is already found in any existing paths */
|
|
|
static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
|
|
|
{
|
|
@@ -836,6 +863,7 @@ static struct badness_table extra_out_badness = {
|
|
|
/* try to assign DACs to pins and return the resultant badness */
|
|
|
static int try_assign_dacs(struct hda_codec *codec, int num_outs,
|
|
|
const hda_nid_t *pins, hda_nid_t *dacs,
|
|
|
+ int *path_idx,
|
|
|
const struct badness_table *bad)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
@@ -862,6 +890,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
|
|
|
if (is_reachable_path(codec, dacs[j], pin)) {
|
|
|
dacs[0] = dacs[j];
|
|
|
dacs[j] = 0;
|
|
|
+ path_idx[j] = 0;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -898,6 +927,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
|
|
|
else {
|
|
|
print_nid_path("output", path);
|
|
|
path->active = true;
|
|
|
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
|
|
|
}
|
|
|
if (dac)
|
|
|
badness += assign_out_path_ctls(codec, pin, dac);
|
|
@@ -1025,6 +1055,8 @@ static int fill_multi_ios(struct hda_codec *codec,
|
|
|
print_nid_path("multiio", path);
|
|
|
spec->multi_io[spec->multi_ios].pin = nid;
|
|
|
spec->multi_io[spec->multi_ios].dac = dac;
|
|
|
+ spec->out_paths[cfg->line_outs + spec->multi_ios] =
|
|
|
+ snd_hda_get_path_idx(codec, path);
|
|
|
spec->multi_ios++;
|
|
|
if (spec->multi_ios >= 2)
|
|
|
break;
|
|
@@ -1056,7 +1088,7 @@ static int fill_multi_ios(struct hda_codec *codec,
|
|
|
|
|
|
/* map DACs for all pins in the list if they are single connections */
|
|
|
static bool map_singles(struct hda_codec *codec, int outs,
|
|
|
- const hda_nid_t *pins, hda_nid_t *dacs)
|
|
|
+ const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
int i;
|
|
@@ -1077,6 +1109,7 @@ static bool map_singles(struct hda_codec *codec, int outs,
|
|
|
found = true;
|
|
|
print_nid_path("output", path);
|
|
|
path->active = true;
|
|
|
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
|
|
|
}
|
|
|
}
|
|
|
return found;
|
|
@@ -1107,13 +1140,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
|
|
|
do {
|
|
|
mapped = map_singles(codec, cfg->line_outs,
|
|
|
cfg->line_out_pins,
|
|
|
- spec->private_dac_nids);
|
|
|
+ spec->private_dac_nids,
|
|
|
+ spec->out_paths);
|
|
|
mapped |= map_singles(codec, cfg->hp_outs,
|
|
|
cfg->hp_pins,
|
|
|
- spec->multiout.hp_out_nid);
|
|
|
+ spec->multiout.hp_out_nid,
|
|
|
+ spec->hp_paths);
|
|
|
mapped |= map_singles(codec, cfg->speaker_outs,
|
|
|
cfg->speaker_pins,
|
|
|
- spec->multiout.extra_out_nid);
|
|
|
+ spec->multiout.extra_out_nid,
|
|
|
+ spec->speaker_paths);
|
|
|
if (fill_mio_first && cfg->line_outs == 1 &&
|
|
|
cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
|
|
|
err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
|
|
@@ -1124,7 +1160,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
|
|
|
}
|
|
|
|
|
|
badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
|
|
|
- spec->private_dac_nids,
|
|
|
+ spec->private_dac_nids, spec->out_paths,
|
|
|
&main_out_badness);
|
|
|
|
|
|
/* re-count num_dacs and squash invalid entries */
|
|
@@ -1152,6 +1188,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
|
|
|
if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
|
|
|
err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
|
|
|
spec->multiout.hp_out_nid,
|
|
|
+ spec->hp_paths,
|
|
|
&extra_out_badness);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
@@ -1161,7 +1198,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
|
|
|
err = try_assign_dacs(codec, cfg->speaker_outs,
|
|
|
cfg->speaker_pins,
|
|
|
spec->multiout.extra_out_nid,
|
|
|
- &extra_out_badness);
|
|
|
+ spec->speaker_paths,
|
|
|
+ &extra_out_badness);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
badness += err;
|
|
@@ -1336,9 +1374,7 @@ static int parse_output_paths(struct hda_codec *codec)
|
|
|
|
|
|
if (cfg->line_out_pins[0]) {
|
|
|
struct nid_path *path;
|
|
|
- path = snd_hda_get_nid_path(codec,
|
|
|
- spec->multiout.dac_nids[0],
|
|
|
- cfg->line_out_pins[0]);
|
|
|
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
|
|
|
if (path)
|
|
|
spec->vmaster_nid = look_for_out_vol_nid(codec, path);
|
|
|
}
|
|
@@ -1361,22 +1397,20 @@ static int create_multi_out_ctls(struct hda_codec *codec,
|
|
|
for (i = 0; i < noutputs; i++) {
|
|
|
const char *name;
|
|
|
int index;
|
|
|
- hda_nid_t dac, pin;
|
|
|
+ hda_nid_t dac;
|
|
|
struct nid_path *path;
|
|
|
|
|
|
dac = spec->multiout.dac_nids[i];
|
|
|
if (!dac)
|
|
|
continue;
|
|
|
if (i >= cfg->line_outs) {
|
|
|
- pin = spec->multi_io[i - cfg->line_outs].pin;
|
|
|
index = 0;
|
|
|
name = channel_name[i];
|
|
|
} else {
|
|
|
- pin = cfg->line_out_pins[i];
|
|
|
name = get_line_out_pfx(spec, i, true, &index);
|
|
|
}
|
|
|
|
|
|
- path = snd_hda_get_nid_path(codec, dac, pin);
|
|
|
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
|
|
|
if (!path)
|
|
|
continue;
|
|
|
if (!name || !strcmp(name, "CLFE")) {
|
|
@@ -1406,12 +1440,13 @@ static int create_multi_out_ctls(struct hda_codec *codec,
|
|
|
}
|
|
|
|
|
|
static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
|
|
|
- hda_nid_t dac, const char *pfx, int cidx)
|
|
|
+ hda_nid_t dac, int path_idx,
|
|
|
+ const char *pfx, int cidx)
|
|
|
{
|
|
|
struct nid_path *path;
|
|
|
int err;
|
|
|
|
|
|
- path = snd_hda_get_nid_path(codec, dac, pin);
|
|
|
+ path = snd_hda_get_path_from_idx(codec, path_idx);
|
|
|
if (!path)
|
|
|
return 0;
|
|
|
/* bind volume control will be created in the case of dac = 0 */
|
|
@@ -1429,7 +1464,7 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
|
|
|
/* add playback controls for speaker and HP outputs */
|
|
|
static int create_extra_outs(struct hda_codec *codec, int num_pins,
|
|
|
const hda_nid_t *pins, const hda_nid_t *dacs,
|
|
|
- const char *pfx)
|
|
|
+ const int *paths, const char *pfx)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
struct hda_bind_ctls *ctl;
|
|
@@ -1443,7 +1478,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
|
|
|
hda_nid_t dac = *dacs;
|
|
|
if (!dac)
|
|
|
dac = spec->multiout.dac_nids[0];
|
|
|
- return create_extra_out(codec, *pins, dac, pfx, 0);
|
|
|
+ return create_extra_out(codec, *pins, dac, paths[0], pfx, 0);
|
|
|
}
|
|
|
|
|
|
for (i = 0; i < num_pins; i++) {
|
|
@@ -1453,14 +1488,16 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
|
|
|
else
|
|
|
dac = 0;
|
|
|
if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
|
|
|
- err = create_extra_out(codec, pins[i], dac,
|
|
|
+ err = create_extra_out(codec, pins[i], dac, paths[i],
|
|
|
"Bass Speaker", 0);
|
|
|
} else if (num_pins >= 3) {
|
|
|
snprintf(name, sizeof(name), "%s %s",
|
|
|
pfx, channel_name[i]);
|
|
|
- err = create_extra_out(codec, pins[i], dac, name, 0);
|
|
|
+ err = create_extra_out(codec, pins[i], dac, paths[i],
|
|
|
+ name, 0);
|
|
|
} else {
|
|
|
- err = create_extra_out(codec, pins[i], dac, pfx, i);
|
|
|
+ err = create_extra_out(codec, pins[i], dac, paths[i],
|
|
|
+ pfx, i);
|
|
|
}
|
|
|
if (err < 0)
|
|
|
return err;
|
|
@@ -1478,7 +1515,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
|
|
|
struct nid_path *path;
|
|
|
if (!pins[i] || !dacs[i])
|
|
|
continue;
|
|
|
- path = snd_hda_get_nid_path(codec, dacs[i], pins[i]);
|
|
|
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
|
|
|
if (!path)
|
|
|
continue;
|
|
|
vol = look_for_out_vol_nid(codec, path);
|
|
@@ -1501,6 +1538,7 @@ static int create_hp_out_ctls(struct hda_codec *codec)
|
|
|
return create_extra_outs(codec, spec->autocfg.hp_outs,
|
|
|
spec->autocfg.hp_pins,
|
|
|
spec->multiout.hp_out_nid,
|
|
|
+ spec->hp_paths,
|
|
|
"Headphone");
|
|
|
}
|
|
|
|
|
@@ -1510,6 +1548,7 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
|
|
|
return create_extra_outs(codec, spec->autocfg.speaker_outs,
|
|
|
spec->autocfg.speaker_pins,
|
|
|
spec->multiout.extra_out_nid,
|
|
|
+ spec->speaker_paths,
|
|
|
"Speaker");
|
|
|
}
|
|
|
|
|
@@ -1615,13 +1654,21 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static inline struct nid_path *
|
|
|
+get_multiio_path(struct hda_codec *codec, int idx)
|
|
|
+{
|
|
|
+ struct hda_gen_spec *spec = codec->spec;
|
|
|
+ return snd_hda_get_path_from_idx(codec,
|
|
|
+ spec->out_paths[spec->autocfg.line_outs + idx]);
|
|
|
+}
|
|
|
+
|
|
|
static int set_multi_io(struct hda_codec *codec, int idx, bool output)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
hda_nid_t nid = spec->multi_io[idx].pin;
|
|
|
struct nid_path *path;
|
|
|
|
|
|
- path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid);
|
|
|
+ path = get_multiio_path(codec, idx);
|
|
|
if (!path)
|
|
|
return -EINVAL;
|
|
|
|
|
@@ -1775,8 +1822,8 @@ static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
|
|
|
#endif
|
|
|
|
|
|
/* create input playback/capture controls for the given pin */
|
|
|
-static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
|
|
|
- const char *ctlname, int ctlidx,
|
|
|
+static int new_analog_input(struct hda_codec *codec, int input_idx,
|
|
|
+ hda_nid_t pin, const char *ctlname, int ctlidx,
|
|
|
hda_nid_t mix_nid)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
@@ -1792,6 +1839,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
|
|
|
if (!path)
|
|
|
return -EINVAL;
|
|
|
print_nid_path("loopback", path);
|
|
|
+ spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
|
|
|
|
|
|
idx = path->idx[path->depth - 1];
|
|
|
if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
|
|
@@ -1944,7 +1992,7 @@ static int create_input_ctls(struct hda_codec *codec)
|
|
|
|
|
|
if (mixer) {
|
|
|
if (is_reachable_path(codec, pin, mixer)) {
|
|
|
- err = new_analog_input(codec, pin,
|
|
|
+ err = new_analog_input(codec, i, pin,
|
|
|
label, type_idx, mixer);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
@@ -2445,6 +2493,7 @@ static void parse_digital(struct hda_codec *codec)
|
|
|
continue;
|
|
|
print_nid_path("digout", path);
|
|
|
path->active = true;
|
|
|
+ spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
|
|
|
if (!nums) {
|
|
|
spec->multiout.dig_out_nid = dig_nid;
|
|
|
spec->dig_out_type = spec->autocfg.dig_out_type[0];
|
|
@@ -3575,12 +3624,12 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
|
|
|
|
|
|
/* configure the path from the given dac to the pin as the proper output */
|
|
|
static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
|
|
|
- int pin_type, hda_nid_t dac)
|
|
|
+ int pin_type, int path_idx)
|
|
|
{
|
|
|
struct nid_path *path;
|
|
|
|
|
|
snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
|
|
|
- path = snd_hda_get_nid_path(codec, dac, pin);
|
|
|
+ path = snd_hda_get_path_from_idx(codec, path_idx);
|
|
|
if (!path)
|
|
|
return;
|
|
|
snd_hda_activate_path(codec, path, path->active, true);
|
|
@@ -3591,7 +3640,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
|
|
|
static void init_multi_out(struct hda_codec *codec)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
- hda_nid_t nid, dac;
|
|
|
+ hda_nid_t nid;
|
|
|
int pin_type;
|
|
|
int i;
|
|
|
|
|
@@ -3602,35 +3651,24 @@ static void init_multi_out(struct hda_codec *codec)
|
|
|
|
|
|
for (i = 0; i < spec->autocfg.line_outs; i++) {
|
|
|
nid = spec->autocfg.line_out_pins[i];
|
|
|
- if (nid) {
|
|
|
- dac = spec->multiout.dac_nids[i];
|
|
|
- if (!dac)
|
|
|
- dac = spec->multiout.dac_nids[0];
|
|
|
- set_output_and_unmute(codec, nid, pin_type, dac);
|
|
|
- }
|
|
|
+ if (nid)
|
|
|
+ set_output_and_unmute(codec, nid, pin_type,
|
|
|
+ spec->out_paths[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
static void __init_extra_out(struct hda_codec *codec, int num_outs,
|
|
|
- hda_nid_t *pins, hda_nid_t *dacs, int type)
|
|
|
+ hda_nid_t *pins, int *paths, int type)
|
|
|
{
|
|
|
- struct hda_gen_spec *spec = codec->spec;
|
|
|
int i;
|
|
|
- hda_nid_t pin, dac;
|
|
|
+ hda_nid_t pin;
|
|
|
|
|
|
for (i = 0; i < num_outs; i++) {
|
|
|
pin = pins[i];
|
|
|
if (!pin)
|
|
|
break;
|
|
|
- dac = dacs[i];
|
|
|
- if (!dac) {
|
|
|
- if (i > 0 && dacs[0])
|
|
|
- dac = dacs[0];
|
|
|
- else
|
|
|
- dac = spec->multiout.dac_nids[0];
|
|
|
- }
|
|
|
- set_output_and_unmute(codec, pin, type, dac);
|
|
|
+ set_output_and_unmute(codec, pin, type, paths[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -3642,11 +3680,11 @@ static void init_extra_out(struct hda_codec *codec)
|
|
|
if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
|
|
|
__init_extra_out(codec, spec->autocfg.hp_outs,
|
|
|
spec->autocfg.hp_pins,
|
|
|
- spec->multiout.hp_out_nid, PIN_HP);
|
|
|
+ spec->hp_paths, PIN_HP);
|
|
|
if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
|
|
|
__init_extra_out(codec, spec->autocfg.speaker_outs,
|
|
|
spec->autocfg.speaker_pins,
|
|
|
- spec->multiout.extra_out_nid, PIN_OUT);
|
|
|
+ spec->speaker_paths, PIN_OUT);
|
|
|
}
|
|
|
|
|
|
/* initialize multi-io paths */
|
|
@@ -3658,7 +3696,7 @@ static void init_multi_io(struct hda_codec *codec)
|
|
|
for (i = 0; i < spec->multi_ios; i++) {
|
|
|
hda_nid_t pin = spec->multi_io[i].pin;
|
|
|
struct nid_path *path;
|
|
|
- path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin);
|
|
|
+ path = get_multiio_path(codec, i);
|
|
|
if (!path)
|
|
|
continue;
|
|
|
if (!spec->multi_io[i].ctl_in)
|
|
@@ -3694,7 +3732,7 @@ static void init_analog_input(struct hda_codec *codec)
|
|
|
/* init loopback inputs */
|
|
|
if (spec->mixer_nid) {
|
|
|
struct nid_path *path;
|
|
|
- path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid);
|
|
|
+ path = snd_hda_get_path_from_idx(codec, spec->loopback_paths[i]);
|
|
|
if (path)
|
|
|
snd_hda_activate_path(codec, path,
|
|
|
path->active, false);
|
|
@@ -3746,7 +3784,8 @@ static void init_digital(struct hda_codec *codec)
|
|
|
pin = spec->autocfg.dig_out_pins[i];
|
|
|
if (!pin)
|
|
|
continue;
|
|
|
- set_output_and_unmute(codec, pin, PIN_OUT, 0);
|
|
|
+ set_output_and_unmute(codec, pin, PIN_OUT,
|
|
|
+ spec->digout_paths[i]);
|
|
|
}
|
|
|
pin = spec->autocfg.dig_in_pin;
|
|
|
if (pin)
|