ALSA: hda/realtek - Parse input paths
authorTakashi Iwai <tiwai@suse.de>
Wed, 12 Dec 2012 16:25:00 +0000 (17:25 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 10 Jan 2013 09:34:28 +0000 (10:34 +0100)
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>
sound/pci/hda/patch_realtek.c

index 434856376bf00a1022d41e4279145d42640ca8d0..fbc4a97ea8f6daaf55324cbcf4a20a251d9e2ad8 100644 (file)
@@ -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
@@ -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
@@ -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);
@@ -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)
@@ -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);
@@ -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;
                        }
                }
@@ -2897,40 +2932,45 @@ 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;
@@ -2938,25 +2978,27 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
  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
@@ -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--;
@@ -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) {