ALSA: hda - Try to find an empty control index when it's occupied
authorTakashi Iwai <tiwai@suse.de>
Thu, 23 Dec 2010 09:17:52 +0000 (10:17 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 23 Dec 2010 09:22:55 +0000 (10:22 +0100)
When a mixer control element was already created with the given name,
try to find another index for avoiding conflicts, instead of breaking
with an error.  This makes the driver more robust.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_codec.c

index 644e3f14f8ca5aea7af2e465450caab86d2f90cb..98b6d02a36c9c0600a7b26dffbbf1f4f378fccd8 100644 (file)
@@ -1919,6 +1919,16 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
+static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name)
+{
+       int idx;
+       for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */
+               if (!_snd_hda_find_mixer_ctl(codec, name, idx))
+                       return idx;
+       }
+       return -EBUSY;
+}
+
 /**
  * snd_hda_ctl_add - Add a control element and assign to the codec
  * @codec: HD-audio codec
@@ -2654,8 +2664,6 @@ static struct snd_kcontrol_new dig_mixes[] = {
        { } /* end */
 };
 
-#define SPDIF_MAX_IDX  4       /* 4 instances should be enough to probe */
-
 /**
  * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
  * @codec: the HDA codec
@@ -2673,12 +2681,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
        struct snd_kcontrol_new *dig_mix;
        int idx;
 
-       for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
-               if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch",
-                                            idx))
-                       break;
-       }
-       if (idx >= SPDIF_MAX_IDX) {
+       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch");
+       if (idx < 0) {
                printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
                return -EBUSY;
        }
@@ -2829,12 +2833,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
        struct snd_kcontrol_new *dig_mix;
        int idx;
 
-       for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
-               if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch",
-                                            idx))
-                       break;
-       }
-       if (idx >= SPDIF_MAX_IDX) {
+       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch");
+       if (idx < 0) {
                printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
                return -EBUSY;
        }
@@ -3808,21 +3808,32 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 
        for (; knew->name; knew++) {
                struct snd_kcontrol *kctl;
+               int addr = 0, idx = 0;
                if (knew->iface == -1)  /* skip this codec private value */
                        continue;
-               kctl = snd_ctl_new1(knew, codec);
-               if (!kctl)
-                       return -ENOMEM;
-               err = snd_hda_ctl_add(codec, 0, kctl);
-               if (err < 0) {
-                       if (!codec->addr)
-                               return err;
+               for (;;) {
                        kctl = snd_ctl_new1(knew, codec);
                        if (!kctl)
                                return -ENOMEM;
-                       kctl->id.device = codec->addr;
+                       if (addr > 0)
+                               kctl->id.device = addr;
+                       if (idx > 0)
+                               kctl->id.index = idx;
                        err = snd_hda_ctl_add(codec, 0, kctl);
-                       if (err < 0)
+                       if (!err)
+                               break;
+                       /* try first with another device index corresponding to
+                        * the codec addr; if it still fails (or it's the
+                        * primary codec), then try another control index
+                        */
+                       if (!addr && codec->addr)
+                               addr = codec->addr;
+                       else if (!idx && !knew->index) {
+                               idx = find_empty_mixer_ctl_idx(codec,
+                                                              knew->name);
+                               if (idx <= 0)
+                                       return err;
+                       } else
                                return err;
                }
        }