Commit 36f0fd54 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda/realtek - Parse input paths

Just like the output paths, parse the whole paths for inputs as well
and store in a path list.  For that purpose, rewrite the output parser
code to be generically usable.

The input path list is not referred at all in this patch.  It'll be
used to replace the fixed adc/capsrc array in later patches for more
flexible input path selections.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 95e960ce
...@@ -101,7 +101,11 @@ enum { ...@@ -101,7 +101,11 @@ enum {
#define MAX_NID_PATH_DEPTH 5 #define MAX_NID_PATH_DEPTH 5
/* output-path: DAC -> ... -> pin /* Widget connection path
*
* For output, stored in the order of DAC -> ... -> pin,
* for input, pin -> ... -> ADC.
*
* idx[i] contains the source index number to select on of the widget path[i]; * idx[i] contains the source index number to select on of the widget path[i];
* e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
* multi[] indicates whether it's a selector widget with multi-connectors * multi[] indicates whether it's a selector widget with multi-connectors
...@@ -196,6 +200,9 @@ struct alc_spec { ...@@ -196,6 +200,9 @@ struct alc_spec {
/* output paths */ /* output paths */
struct snd_array out_path; struct snd_array out_path;
/* input paths */
struct snd_array in_path;
/* hooks */ /* hooks */
void (*init_hook)(struct hda_codec *codec); void (*init_hook)(struct hda_codec *codec);
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -2428,6 +2435,7 @@ static void alc_free(struct hda_codec *codec) ...@@ -2428,6 +2435,7 @@ static void alc_free(struct hda_codec *codec)
alc_free_kctls(codec); alc_free_kctls(codec);
alc_free_bind_ctls(codec); alc_free_bind_ctls(codec);
snd_array_free(&spec->out_path); snd_array_free(&spec->out_path);
snd_array_free(&spec->in_path);
snd_hda_gen_free(&spec->gen); snd_hda_gen_free(&spec->gen);
kfree(spec); kfree(spec);
snd_hda_detach_beep_device(codec); snd_hda_detach_beep_device(codec);
...@@ -2628,6 +2636,10 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, ...@@ -2628,6 +2636,10 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
return channel_name[ch]; return channel_name[ch];
} }
static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
hda_nid_t to_nid, int with_aa_mix,
struct nid_path *path);
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* add the powersave loopback-list entry */ /* add the powersave loopback-list entry */
static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
...@@ -2666,6 +2678,28 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, ...@@ -2666,6 +2678,28 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
return 0; return 0;
} }
static int new_capture_source(struct hda_codec *codec, int adc_idx,
hda_nid_t pin, int idx, const char *label)
{
struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
struct nid_path *path;
path = snd_array_new(&spec->in_path);
if (!path)
return -ENOMEM;
memset(path, 0, sizeof(*path));
if (!parse_nid_path(codec, pin, spec->adc_nids[adc_idx], 2, path)) {
snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n",
pin, spec->adc_nids[adc_idx]);
return -EINVAL;
}
spec->imux_pins[imux->num_items] = pin;
snd_hda_add_imux_item(imux, label, idx, NULL);
return 0;
}
static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
{ {
unsigned int pincap = snd_hda_query_pin_caps(codec, nid); unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
...@@ -2767,8 +2801,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) ...@@ -2767,8 +2801,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
hda_nid_t cap = get_capsrc(spec, c); hda_nid_t cap = get_capsrc(spec, c);
idx = get_connection_index(codec, cap, pin); idx = get_connection_index(codec, cap, pin);
if (idx >= 0) { if (idx >= 0) {
spec->imux_pins[imux->num_items] = pin; err = new_capture_source(codec, c, pin, idx, label);
snd_hda_add_imux_item(imux, label, idx, NULL); if (err < 0)
return err;
break; break;
} }
} }
...@@ -2897,40 +2932,45 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) ...@@ -2897,40 +2932,45 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
} }
/* called recursively */ /* called recursively */
static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, static bool __parse_nid_path(struct hda_codec *codec,
hda_nid_t target_dac, int with_aa_mix, hda_nid_t from_nid, hda_nid_t to_nid,
struct nid_path *path, int depth) int with_aa_mix, struct nid_path *path, int depth)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
hda_nid_t conn[8]; hda_nid_t conn[16];
int i, nums; int i, nums;
if (nid == spec->mixer_nid) { if (to_nid == spec->mixer_nid) {
if (!with_aa_mix) if (!with_aa_mix)
return false; return false;
with_aa_mix = 2; /* mark aa-mix is included */ with_aa_mix = 2; /* mark aa-mix is included */
} }
nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn));
for (i = 0; i < nums; i++) { for (i = 0; i < nums; i++) {
if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) if (conn[i] != from_nid) {
continue; /* special case: when from_nid is 0,
if (conn[i] == target_dac || * try to find an empty DAC
(!target_dac && !alc_is_dac_already_used(codec, conn[i]))) { */
/* aa-mix is requested but not included? */ if (from_nid ||
if (!(spec->mixer_nid && with_aa_mix == 1)) get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
goto found; alc_is_dac_already_used(codec, conn[i]))
continue;
} }
/* aa-mix is requested but not included? */
if (!(spec->mixer_nid && with_aa_mix == 1))
goto found;
} }
if (depth >= MAX_NID_PATH_DEPTH) if (depth >= MAX_NID_PATH_DEPTH)
return false; return false;
for (i = 0; i < nums; i++) { for (i = 0; i < nums; i++) {
unsigned int type; unsigned int type;
type = get_wcaps_type(get_wcaps(codec, conn[i])); type = get_wcaps_type(get_wcaps(codec, conn[i]));
if (type == AC_WID_AUD_OUT) if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
type == AC_WID_PIN)
continue; continue;
if (__parse_output_path(codec, conn[i], target_dac, if (__parse_nid_path(codec, from_nid, conn[i],
with_aa_mix, path, depth + 1)) with_aa_mix, path, depth + 1))
goto found; goto found;
} }
return false; return false;
...@@ -2938,25 +2978,27 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, ...@@ -2938,25 +2978,27 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
found: found:
path->path[path->depth] = conn[i]; path->path[path->depth] = conn[i];
path->idx[path->depth + 1] = i; path->idx[path->depth + 1] = i;
if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
path->multi[path->depth + 1] = 1; path->multi[path->depth + 1] = 1;
path->depth++; path->depth++;
return true; return true;
} }
/* parse the output path from the given nid to the target DAC; /* parse the widget path from the given nid to the target nid;
* when target_dac is 0, try to find an empty DAC; * when @from_nid is 0, try to find an empty DAC;
* when with_aa_mix is 0, paths with spec->mixer_nid are excluded * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded.
* when @with_aa_mix is 1, paths without spec->mixer_nid are excluded.
* when @with_aa_mix is 2, no special handling about spec->mixer_nid.
*/ */
static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
hda_nid_t target_dac, int with_aa_mix, hda_nid_t to_nid, int with_aa_mix,
struct nid_path *path) struct nid_path *path)
{ {
if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
path->path[path->depth] = nid; path->path[path->depth] = to_nid;
path->depth++; path->depth++;
#if 0 #if 0
snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
path->depth, path->path[0], path->path[1], path->depth, path->path[0], path->path[1],
path->path[2], path->path[3], path->path[4]); path->path[2], path->path[3], path->path[4]);
#endif #endif
...@@ -3034,7 +3076,7 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, ...@@ -3034,7 +3076,7 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
if (!path) if (!path)
return false; return false;
memset(path, 0, sizeof(*path)); memset(path, 0, sizeof(*path));
if (parse_output_path(codec, pin, dac, 0, path)) if (parse_nid_path(codec, dac, pin, 0, path))
return true; return true;
/* push back */ /* push back */
spec->out_path.used--; spec->out_path.used--;
...@@ -4529,6 +4571,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) ...@@ -4529,6 +4571,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); 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->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); snd_array_init(&spec->out_path, sizeof(struct nid_path), 8);
snd_array_init(&spec->in_path, sizeof(struct nid_path), 8);
err = alc_codec_rename_from_preset(codec); err = alc_codec_rename_from_preset(codec);
if (err < 0) { if (err < 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment