ALSA: hda - Fix PCM reconfigure
authorTakashi Iwai <tiwai@suse.de>
Thu, 27 Nov 2008 13:17:01 +0000 (14:17 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 27 Nov 2008 13:17:01 +0000 (14:17 +0100)
The reconfiguration of PCM affected all PCM streams on the bus, but
this this should be done rather only for the target codec.

This patch does the following:
- introduce bitmap indicating the PCM device usages on a hda_bus
- refactor the PCM build functions
- fix __devinit prefix in some fucntions
- add a proper ifdef around HDA-reconfig-specific functions

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

index 052a898a63ba68b43af99cd9b4539acfc37a696e..1cb85b73e19b8c81265a3f5f3b7eace34a329f24 100644 (file)
@@ -1206,6 +1206,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_RECONFIG
 /* Clear all controls assigned to the given codec */
 void snd_hda_ctls_clear(struct hda_codec *codec)
 {
@@ -1227,9 +1228,12 @@ void snd_hda_codec_reset(struct hda_codec *codec)
        snd_hda_ctls_clear(codec);
        /* relase PCMs */
        for (i = 0; i < codec->num_pcms; i++) {
-               if (codec->pcm_info[i].pcm)
+               if (codec->pcm_info[i].pcm) {
                        snd_device_free(codec->bus->card,
                                        codec->pcm_info[i].pcm);
+                       clear_bit(codec->pcm_info[i].device,
+                                 codec->bus->pcm_dev_bits);
+               }
        }
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
@@ -1240,6 +1244,7 @@ void snd_hda_codec_reset(struct hda_codec *codec)
        codec->pcm_info = NULL;
        codec->preset = NULL;
 }
+#endif /* CONFIG_SND_HDA_RECONFIG */
 
 /* create a virtual master control and add slaves */
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
@@ -2432,11 +2437,59 @@ static int set_pcm_default_values(struct hda_codec *codec,
        return 0;
 }
 
+/*
+ * get the empty PCM device number to assign
+ */
+static int get_empty_pcm_device(struct hda_bus *bus, int type)
+{
+       static const char *dev_name[HDA_PCM_NTYPES] = {
+               "Audio", "SPDIF", "HDMI", "Modem"
+       };
+       /* starting device index for each PCM type */
+       static int dev_idx[HDA_PCM_NTYPES] = {
+               [HDA_PCM_TYPE_AUDIO] = 0,
+               [HDA_PCM_TYPE_SPDIF] = 1,
+               [HDA_PCM_TYPE_HDMI] = 3,
+               [HDA_PCM_TYPE_MODEM] = 6
+       };
+       /* normal audio device indices; not linear to keep compatibility */
+       static int audio_idx[4] = { 0, 2, 4, 5 };
+       int i, dev;
+
+       switch (type) {
+       case HDA_PCM_TYPE_AUDIO:
+               for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
+                       dev = audio_idx[i];
+                       if (!test_bit(dev, bus->pcm_dev_bits))
+                               break;
+               }
+               if (i >= ARRAY_SIZE(audio_idx)) {
+                       snd_printk(KERN_WARNING "Too many audio devices\n");
+                       return -EAGAIN;
+               }
+               break;
+       case HDA_PCM_TYPE_SPDIF:
+       case HDA_PCM_TYPE_HDMI:
+       case HDA_PCM_TYPE_MODEM:
+               dev = dev_idx[type];
+               if (test_bit(dev, bus->pcm_dev_bits)) {
+                       snd_printk(KERN_WARNING "%s already defined\n",
+                                  dev_name[type]);
+                       return -EAGAIN;
+               }
+               break;
+       default:
+               snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
+               return -EINVAL;
+       }
+       set_bit(dev, bus->pcm_dev_bits);
+       return dev;
+}
+
 /*
  * attach a new PCM stream
  */
-static int __devinit
-snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
+static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
 {
        struct hda_bus *bus = codec->bus;
        struct hda_pcm_stream *info;
@@ -2455,6 +2508,39 @@ snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
        return bus->ops.attach_pcm(bus, codec, pcm);
 }
 
+/* assign all PCMs of the given codec */
+int snd_hda_codec_build_pcms(struct hda_codec *codec)
+{
+       unsigned int pcm;
+       int err;
+
+       if (!codec->num_pcms) {
+               if (!codec->patch_ops.build_pcms)
+                       return 0;
+               err = codec->patch_ops.build_pcms(codec);
+               if (err < 0)
+                       return err;
+       }
+       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+               struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+               int dev;
+
+               if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+                       return 0; /* no substreams assigned */
+
+               if (!cpcm->pcm) {
+                       dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
+                       if (dev < 0)
+                               return 0;
+                       cpcm->device = dev;
+                       err = snd_hda_attach_pcm(codec, cpcm);
+                       if (err < 0)
+                               return err;
+               }
+       }
+       return 0;
+}
+
 /**
  * snd_hda_build_pcms - build PCM information
  * @bus: the BUS
@@ -2481,76 +2567,14 @@ snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
  *
  * This function returns 0 if successfull, or a negative error code.
  */
-int snd_hda_build_pcms(struct hda_bus *bus)
+int __devinit snd_hda_build_pcms(struct hda_bus *bus)
 {
-       static const char *dev_name[HDA_PCM_NTYPES] = {
-               "Audio", "SPDIF", "HDMI", "Modem"
-       };
-       /* starting device index for each PCM type */
-       static int dev_idx[HDA_PCM_NTYPES] = {
-               [HDA_PCM_TYPE_AUDIO] = 0,
-               [HDA_PCM_TYPE_SPDIF] = 1,
-               [HDA_PCM_TYPE_HDMI] = 3,
-               [HDA_PCM_TYPE_MODEM] = 6
-       };
-       /* normal audio device indices; not linear to keep compatibility */
-       static int audio_idx[4] = { 0, 2, 4, 5 };
        struct hda_codec *codec;
-       int num_devs[HDA_PCM_NTYPES];
 
-       memset(num_devs, 0, sizeof(num_devs));
        list_for_each_entry(codec, &bus->codec_list, list) {
-               unsigned int pcm;
-               int err;
-               if (!codec->num_pcms) {
-                       if (!codec->patch_ops.build_pcms)
-                               continue;
-                       err = codec->patch_ops.build_pcms(codec);
-                       if (err < 0)
-                               return err;
-               }
-               for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-                       struct hda_pcm *cpcm = &codec->pcm_info[pcm];
-                       int type = cpcm->pcm_type;
-                       int dev;
-
-                       if (!cpcm->stream[0].substreams &&
-                           !cpcm->stream[1].substreams)
-                               continue; /* no substreams assigned */
-
-                       switch (type) {
-                       case HDA_PCM_TYPE_AUDIO:
-                               if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
-                                       snd_printk(KERN_WARNING
-                                                  "Too many audio devices\n");
-                                       continue;
-                               }
-                               dev = audio_idx[num_devs[type]];
-                               break;
-                       case HDA_PCM_TYPE_SPDIF:
-                       case HDA_PCM_TYPE_HDMI:
-                       case HDA_PCM_TYPE_MODEM:
-                               if (num_devs[type]) {
-                                       snd_printk(KERN_WARNING
-                                                  "%s already defined\n",
-                                                  dev_name[type]);
-                                       continue;
-                               }
-                               dev = dev_idx[type];
-                               break;
-                       default:
-                               snd_printk(KERN_WARNING
-                                          "Invalid PCM type %d\n", type);
-                               continue;
-                       }
-                       num_devs[type]++;
-                       if (!cpcm->pcm) {
-                               cpcm->device = dev;
-                               err = snd_hda_attach_pcm(codec, cpcm);
-                               if (err < 0)
-                                       return err;
-                       }
-               }
+               int err = snd_hda_codec_build_pcms(codec);
+               if (err < 0)
+                       return err;
        }
        return 0;
 }
index 4034625b5997ba43bf355bea455cd70113af157c..9fe0b67bb1e4f764bdbf68efe7829c27a211e60a 100644 (file)
@@ -617,6 +617,9 @@ struct hda_bus {
 
        struct snd_info_entry *proc;
 
+       /* assigned PCMs */
+       DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
+
        /* misc op flags */
        unsigned int needs_damn_long_delay :1;
        unsigned int shutdown :1;       /* being unloaded */
@@ -846,6 +849,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec);
  * PCM
  */
 int snd_hda_build_pcms(struct hda_bus *bus);
+int snd_hda_codec_build_pcms(struct hda_codec *codec);
 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
                                u32 stream_tag,
                                int channel_id, int format);
index 5868bbc131cd8a806071f78276d3855dc97659ea..173af489322fdb5668ba4d599d8e3fd26855549a 100644 (file)
@@ -168,7 +168,7 @@ static int reconfig_codec(struct hda_codec *codec)
        if (err < 0)
                return err;
        /* rebuild PCMs */
-       err = snd_hda_build_pcms(codec->bus);
+       err = snd_hda_codec_build_pcms(codec);
        if (err < 0)
                return err;
        /* rebuild mixers */