hda-codec - add snd_hda_attach_pcm() function

Create snd_hda_attach_pcm() function to attach a new PCM stream
dynamically.  This is transmitted to the controller side via the new
attach_pcm callback.

diff -r 738095375f51 pci/hda/hda_codec.c
--- a/pci/hda/hda_codec.c	Fri Jul 27 17:09:53 2007 +0200
+++ b/pci/hda/hda_codec.c	Fri Jul 27 17:20:13 2007 +0200
@@ -1742,6 +1742,26 @@ static int __devinit set_pcm_default_val
 	return 0;
 }
 
+/*
+ * attach a new PCM stream
+ */
+static int __devinit
+snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm, int stream)
+{
+	struct hda_pcm_stream *info;
+	int err;
+
+	info = &pcm->stream[stream];
+	if (!info->substreams)
+		return 0;
+	if (!pcm->name)
+		return -EINVAL;
+	err = set_pcm_default_values(codec, info);
+	if (err < 0)
+		return err;
+	return codec->bus->ops.attach_pcm(codec, pcm, stream);
+}
+
 /**
  * snd_hda_build_pcms - build PCM information
  * @bus: the BUS
@@ -1771,7 +1791,10 @@ int __devinit snd_hda_build_pcms(struct 
 int __devinit snd_hda_build_pcms(struct hda_bus *bus)
 {
 	struct hda_codec *codec;
-
+	int dev_audio, dev_modem;
+
+	dev_audio = 0;
+	dev_modem = HDA_MAX_AUDIO_PCMS;
 	list_for_each_entry(codec, &bus->codec_list, list) {
 		unsigned int pcm, s;
 		int err;
@@ -1781,12 +1804,13 @@ int __devinit snd_hda_build_pcms(struct 
 		if (err < 0)
 			return err;
 		for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+			struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+			if (cpcm->is_modem)
+				cpcm->device = dev_modem++;
+			else
+				cpcm->device = dev_audio++;
 			for (s = 0; s < 2; s++) {
-				struct hda_pcm_stream *info;
-				info = &codec->pcm_info[pcm].stream[s];
-				if (!info->substreams)
-					continue;
-				err = set_pcm_default_values(codec, info);
+				err = snd_hda_attach_pcm(codec, cpcm, s);
 				if (err < 0)
 					return err;
 			}
diff -r 738095375f51 pci/hda/hda_codec.h
--- a/pci/hda/hda_codec.h	Fri Jul 27 17:09:53 2007 +0200
+++ b/pci/hda/hda_codec.h	Fri Jul 27 17:20:13 2007 +0200
@@ -390,6 +390,12 @@ enum {
 /* max. codec address */
 #define HDA_MAX_CODEC_ADDRESS	0x0f
 
+/* max number of audio and modem PCM streams per bus */
+#define HDA_MAX_AUDIO_PCMS	6
+#define HDA_MAX_MODEM_PCMS	2
+#define HDA_MAX_PCMS		(HDA_MAX_AUDIO_PCMS + HDA_MAX_MODEM_PCMS)
+
+
 /*
  * Structures
  */
@@ -412,6 +418,9 @@ 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,
+			  int stream);
 };
 
 /* template to pass to the bus constructor */
@@ -518,6 +527,7 @@ struct hda_pcm {
 	char *name;
 	struct hda_pcm_stream stream[2];
 	unsigned int is_modem;	/* modem codec? */
+	int device;	/* assigned device number */
 };
 
 /* codec information */
@@ -536,6 +546,7 @@ struct hda_codec {
 
 	/* detected preset */
 	const struct hda_codec_preset *preset;
+	const char *name;	/* override the default name */
 
 	/* set by patch */
 	struct hda_codec_ops patch_ops;
diff -r 738095375f51 pci/hda/hda_intel.c
--- a/pci/hda/hda_intel.c	Fri Jul 27 17:09:53 2007 +0200
+++ b/pci/hda/hda_intel.c	Fri Jul 27 17:20:13 2007 +0200
@@ -193,9 +193,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO
 /* max buffer size - no h/w limit, you can increase as you like */
 #define AZX_MAX_BUF_SIZE	(1024*1024*1024)
 /* max number of PCM devics per card */
-#define AZX_MAX_AUDIO_PCMS	6
-#define AZX_MAX_MODEM_PCMS	2
-#define AZX_MAX_PCMS		(AZX_MAX_AUDIO_PCMS + AZX_MAX_MODEM_PCMS)
+#define AZX_MAX_PCMS		8
 
 /* RIRB int mask: overrun[2], response[0] */
 #define RIRB_INT_RESPONSE	0x01
@@ -327,7 +325,6 @@ struct azx {
 	struct azx_dev *azx_dev;
 
 	/* PCM */
-	unsigned int pcm_devs;
 	struct snd_pcm *pcm[AZX_MAX_PCMS];
 
 	/* HD codec */
@@ -981,6 +978,8 @@ static int azx_setup_controller(struct a
 	return 0;
 }
 
+static int azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm,
+				 int stream);
 
 /*
  * Codec initialization
@@ -1007,6 +1006,7 @@ static int __devinit azx_codec_create(st
 	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;
 
 	err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
 	if (err < 0)
@@ -1302,106 +1302,64 @@ static struct snd_pcm_ops azx_pcm_ops = 
 
 static void azx_pcm_free(struct snd_pcm *pcm)
 {
-	kfree(pcm->private_data);
-}
-
-static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
-				      struct hda_pcm *cpcm, int pcm_dev)
-{
-	int err;
+	struct azx_pcm *apcm = pcm->private_data;
+	if (apcm) {
+		apcm->chip->pcm[pcm->device] = NULL;
+		kfree(apcm);
+	}
+}
+
+static int __devinit
+azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm, int stream)
+{
+	struct azx *chip = codec->bus->private_data; 
 	struct snd_pcm *pcm;
 	struct azx_pcm *apcm;
-
-	/* 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;
-
-	snd_assert(cpcm->name, return -EINVAL);
-
-	err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
-			  cpcm->stream[0].substreams,
-			  cpcm->stream[1].substreams,
-			  &pcm);
+	struct snd_pcm_substream *substream;
+	int pcm_dev = cpcm->device;
+	int err;
+
+	if (pcm_dev >= AZX_MAX_PCMS) {
+		snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+			   pcm_dev);
+		return -EINVAL;
+	}
+	pcm = chip->pcm[pcm_dev];
+	if (!pcm) {
+		err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, 0, 0, &pcm);
+		if (err < 0)
+			return err;
+		strcpy(pcm->name, cpcm->name);
+		apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+		if (apcm == NULL)
+			return -ENOMEM;
+		apcm->chip = chip;
+		apcm->codec = codec;
+		pcm->private_data = apcm;
+		pcm->private_free = azx_pcm_free;
+		chip->pcm[pcm_dev] = pcm;
+	} else {
+		apcm = pcm->private_data;
+		if (apcm->hinfo[stream]) {
+			snd_printk(KERN_ERR SFX "stream %d:%d already exists\n",
+				   pcm_dev, stream);
+			return -EBUSY;
+		}
+	}
+
+	err = snd_pcm_new_stream(pcm, stream, cpcm->stream[stream].substreams);
 	if (err < 0)
 		return err;
-	strcpy(pcm->name, cpcm->name);
-	apcm = kmalloc(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);
-	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+	apcm->hinfo[stream] = &cpcm->stream[stream];
+	if (cpcm->stream[stream].substreams)
+		snd_pcm_set_ops(pcm, stream, &azx_pcm_ops);
+	/* buffer pre-allocation */
+	for (substream = pcm->streams[stream].substream; substream;
+	     substream = substream->next)
+		snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
 					      snd_dma_pci_data(chip->pci),
 					      1024 * 64, 1024 * 1024);
-	chip->pcm[pcm_dev] = pcm;
-	if (chip->pcm_devs < pcm_dev + 1)
-		chip->pcm_devs = pcm_dev + 1;
-
-	return 0;
-}
-
-static int __devinit azx_pcm_create(struct azx *chip)
-{
-	struct list_head *p;
-	struct hda_codec *codec;
-	int c, err;
-	int pcm_dev;
-
-	err = snd_hda_build_pcms(chip->bus);
-	if (err < 0)
-		return err;
-
-	/* create audio PCMs */
-	pcm_dev = 0;
-	list_for_each(p, &chip->bus->codec_list) {
-		codec = list_entry(p, struct hda_codec, list);
-		for (c = 0; c < codec->num_pcms; c++) {
-			if (codec->pcm_info[c].is_modem)
-				continue; /* create later */
-			if (pcm_dev >= AZX_MAX_AUDIO_PCMS) {
-				snd_printk(KERN_ERR SFX
-					   "Too many audio PCMs\n");
-				return -EINVAL;
-			}
-			err = create_codec_pcm(chip, codec,
-					       &codec->pcm_info[c], pcm_dev);
-			if (err < 0)
-				return err;
-			pcm_dev++;
-		}
-	}
-
-	/* create modem PCMs */
-	pcm_dev = AZX_MAX_AUDIO_PCMS;
-	list_for_each(p, &chip->bus->codec_list) {
-		codec = list_entry(p, struct hda_codec, list);
-		for (c = 0; c < codec->num_pcms; c++) {
-			if (!codec->pcm_info[c].is_modem)
-				continue; /* already created */
-			if (pcm_dev >= AZX_MAX_PCMS) {
-				snd_printk(KERN_ERR SFX
-					   "Too many modem PCMs\n");
-				return -EINVAL;
-			}
-			err = create_codec_pcm(chip, codec,
-					       &codec->pcm_info[c], pcm_dev);
-			if (err < 0)
-				return err;
-			chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM;
-			pcm_dev++;
-		}
-	}
-	return 0;
+ 	return 0;
 }
 
 /*
@@ -1470,7 +1428,7 @@ static int azx_suspend(struct pci_dev *p
 	int i;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-	for (i = 0; i < chip->pcm_devs; i++)
+	for (i = 0; i < AZX_MAX_PCMS; i++)
 		snd_pcm_suspend_all(chip->pcm[i]);
 	snd_hda_suspend(chip->bus, state);
 	azx_free_cmd_io(chip);
@@ -1778,7 +1736,7 @@ static int __devinit azx_probe(struct pc
 	}
 
 	/* create PCM streams */
-	err = azx_pcm_create(chip);
+	err = snd_hda_build_pcms(chip->bus);
 	if (err < 0) {
 		snd_card_free(card);
 		return err;
