Skip to content

Commit

Permalink
ALSA: hda/realtek - Parse input paths
Browse files Browse the repository at this point in the history
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: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Iwai committed Jan 10, 2013
1 parent 95e960c commit 36f0fd5
Showing 1 changed file with 73 additions and 30 deletions.
103 changes: 73 additions & 30 deletions sound/pci/hda/patch_realtek.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ enum {

#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];
* 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
Expand Down Expand Up @@ -196,6 +200,9 @@ struct alc_spec {
/* output paths */
struct snd_array out_path;

/* input paths */
struct snd_array in_path;

/* hooks */
void (*init_hook)(struct hda_codec *codec);
#ifdef CONFIG_PM
Expand Down Expand Up @@ -2428,6 +2435,7 @@ static void alc_free(struct hda_codec *codec)
alc_free_kctls(codec);
alc_free_bind_ctls(codec);
snd_array_free(&spec->out_path);
snd_array_free(&spec->in_path);
snd_hda_gen_free(&spec->gen);
kfree(spec);
snd_hda_detach_beep_device(codec);
Expand Down Expand Up @@ -2628,6 +2636,10 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int 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
/* add the powersave loopback-list entry */
static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
Expand Down Expand Up @@ -2666,6 +2678,28 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
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)
{
unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
Expand Down Expand Up @@ -2767,8 +2801,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
hda_nid_t cap = get_capsrc(spec, c);
idx = get_connection_index(codec, cap, pin);
if (idx >= 0) {
spec->imux_pins[imux->num_items] = pin;
snd_hda_add_imux_item(imux, label, idx, NULL);
err = new_capture_source(codec, c, pin, idx, label);
if (err < 0)
return err;
break;
}
}
Expand Down Expand Up @@ -2897,66 +2932,73 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
}

/* 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)
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, int depth)
{
struct alc_spec *spec = codec->spec;
hda_nid_t conn[8];
hda_nid_t conn[16];
int i, nums;

if (nid == spec->mixer_nid) {
if (to_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));
nums = snd_hda_get_connections(codec, to_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 (conn[i] != from_nid) {
/* special case: when from_nid is 0,
* try to find an empty DAC
*/
if (from_nid ||
get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
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)
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)
if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
type == AC_WID_PIN)
continue;
if (__parse_output_path(codec, conn[i], target_dac,
with_aa_mix, path, depth + 1))
if (__parse_nid_path(codec, from_nid, conn[i],
with_aa_mix, path, depth + 1))
goto found;
}
return false;

found:
path->path[path->depth] = conn[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->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
/* parse the widget path from the given nid to the target nid;
* 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 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,
hda_nid_t target_dac, int with_aa_mix,
struct nid_path *path)
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)
{
if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
path->path[path->depth] = nid;
if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
path->path[path->depth] = to_nid;
path->depth++;
#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->path[2], path->path[3], path->path[4]);
#endif
Expand Down Expand Up @@ -3034,7 +3076,7 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
if (!path)
return false;
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;
/* push back */
spec->out_path.used--;
Expand Down Expand Up @@ -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->bind_ctls, sizeof(struct hda_bind_ctls *), 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);
if (err < 0) {
Expand Down

0 comments on commit 36f0fd5

Please sign in to comment.