ALSA: hda - Add infrastructure for dynamic stream allocation
authorTakashi Iwai <tiwai@suse.de>
Wed, 30 Jul 2008 13:01:44 +0000 (15:01 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 13 Oct 2008 00:42:58 +0000 (02:42 +0200)
Added the infrastructure for dynamic stream allocation on HD-audio.

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

index 6447754..19b4530 100644 (file)
@@ -2262,6 +2262,28 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
        return 0;
 }
 
+/*
+ * attach a new PCM stream
+ */
+static int __devinit
+snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
+{
+       struct hda_pcm_stream *info;
+       int stream, err;
+
+       if (!pcm->name)
+               return -EINVAL;
+       for (stream = 0; stream < 2; stream++) {
+               info = &pcm->stream[stream];
+               if (info->substreams) {
+                       err = set_pcm_default_values(codec, info);
+                       if (err < 0)
+                               return err;
+               }
+       }
+       return codec->bus->ops.attach_pcm(codec, pcm);
+}
+
 /**
  * snd_hda_build_pcms - build PCM information
  * @bus: the BUS
@@ -2290,10 +2312,24 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
  */
 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, s;
+               unsigned int pcm;
                int err;
                if (!codec->patch_ops.build_pcms)
                        continue;
@@ -2301,15 +2337,37 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus)
                if (err < 0)
                        return err;
                for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-                       for (s = 0; s < 2; s++) {
-                               struct hda_pcm_stream *info;
-                               info = &codec->pcm_info[pcm].stream[s];
-                               if (!info->substreams)
+                       struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+                       int type = cpcm->pcm_type;
+                       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;
+                               }
+                               cpcm->device = 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;
-                               err = set_pcm_default_values(codec, info);
-                               if (err < 0)
-                                       return err;
+                               }
+                               cpcm->device = dev_idx[type];
+                               break;
+                       default:
+                               snd_printk(KERN_WARNING
+                                          "Invalid PCM type %d\n", type);
+                               continue;
                        }
+                       num_devs[type]++;
+                       err = snd_hda_attach_pcm(codec, cpcm);
+                       if (err < 0)
+                               return err;
                }
        }
        return 0;
index 60468f5..70e8fa0 100644 (file)
@@ -542,6 +542,8 @@ struct hda_bus_ops {
        unsigned int (*get_response)(struct hda_codec *codec);
        /* free the private data */
        void (*private_free)(struct hda_bus *);
+       /* attach a PCM stream */
+       int (*attach_pcm)(struct hda_codec *codec, struct hda_pcm *pcm);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        /* notify power-up/down from codec to controller */
        void (*pm_notify)(struct hda_codec *codec);
@@ -680,7 +682,8 @@ struct hda_pcm {
        char *name;
        struct hda_pcm_stream stream[2];
        unsigned int pcm_type;  /* HDA_PCM_TYPE_XXX */
-       int device;     /* assigned device number */
+       int device;             /* device number to assign */
+       struct snd_pcm *pcm;    /* assigned PCM instance */
 };
 
 /* codec information */
index 9f316c1..7b0abf0 100644 (file)
@@ -1180,6 +1180,7 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
        return 0;
 }
 
+static int azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm);
 
 /*
  * Codec initialization
@@ -1212,6 +1213,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
        bus_temp.pci = chip->pci;
        bus_temp.ops.command = azx_send_cmd;
        bus_temp.ops.get_response = azx_get_response;
+       bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        bus_temp.ops.pm_notify = azx_power_notify;
 #endif
@@ -1718,111 +1720,58 @@ static struct snd_pcm_ops azx_pcm_ops = {
 
 static void azx_pcm_free(struct snd_pcm *pcm)
 {
-       kfree(pcm->private_data);
+       struct azx_pcm *apcm = pcm->private_data;
+       if (apcm) {
+               apcm->chip->pcm[pcm->device] = NULL;
+               kfree(apcm);
+       }
 }
 
-static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
-                                     struct hda_pcm *cpcm)
+static int
+azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm)
 {
-       int err;
+       struct azx *chip = codec->bus->private_data;
        struct snd_pcm *pcm;
        struct azx_pcm *apcm;
+       int pcm_dev = cpcm->device;
+       int s, err;
 
-       /* if no substreams are defined for both playback and capture,
-        * it's just a placeholder.  ignore it.
-        */
-       if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
-               return 0;
-
-       if (snd_BUG_ON(!cpcm->name))
+       if (pcm_dev >= AZX_MAX_PCMS) {
+               snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+                          pcm_dev);
                return -EINVAL;
-
-       err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
-                         cpcm->stream[0].substreams,
-                         cpcm->stream[1].substreams,
+       }
+       if (chip->pcm[pcm_dev]) {
+               snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
+               return -EBUSY;
+       }
+       err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+                         cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+                         cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
                          &pcm);
        if (err < 0)
                return err;
        strcpy(pcm->name, cpcm->name);
-       apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
        if (apcm == NULL)
                return -ENOMEM;
        apcm->chip = chip;
        apcm->codec = codec;
-       apcm->hinfo[0] = &cpcm->stream[0];
-       apcm->hinfo[1] = &cpcm->stream[1];
        pcm->private_data = apcm;
        pcm->private_free = azx_pcm_free;
-       if (cpcm->stream[0].substreams)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
-       if (cpcm->stream[1].substreams)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+       if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+               pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+       chip->pcm[pcm_dev] = pcm;
+       cpcm->pcm = pcm;
+       for (s = 0; s < 2; s++) {
+               apcm->hinfo[s] = &cpcm->stream[s];
+               if (cpcm->stream[s].substreams)
+                       snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+       }
+       /* buffer pre-allocation */
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
                                              snd_dma_pci_data(chip->pci),
                                              1024 * 64, 32 * 1024 * 1024);
-       chip->pcm[cpcm->device] = pcm;
-       return 0;
-}
-
-static int __devinit azx_pcm_create(struct azx *chip)
-{
-       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 c, err;
-       int num_devs[HDA_PCM_NTYPES];
-
-       err = snd_hda_build_pcms(chip->bus);
-       if (err < 0)
-               return err;
-
-       /* create audio PCMs */
-       memset(num_devs, 0, sizeof(num_devs));
-       list_for_each_entry(codec, &chip->bus->codec_list, list) {
-               for (c = 0; c < codec->num_pcms; c++) {
-                       struct hda_pcm *cpcm = &codec->pcm_info[c];
-                       int type = cpcm->pcm_type;
-                       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;
-                               }
-                               cpcm->device = 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;
-                               }
-                               cpcm->device = dev_idx[type];
-                               break;
-                       default:
-                               snd_printk(KERN_WARNING
-                                          "Invalid PCM type %d\n", type);
-                               continue;
-                       }
-                       num_devs[type]++;
-                       err = create_codec_pcm(chip, codec, cpcm);
-                       if (err < 0)
-                               return err;
-               }
-       }
        return 0;
 }
 
@@ -2324,7 +2273,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
        }
 
        /* create PCM streams */
-       err = azx_pcm_create(chip);
+       err = snd_hda_build_pcms(chip->bus);
        if (err < 0) {
                snd_card_free(card);
                return err;