Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 176d5335 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Add infrastructure for dynamic stream allocation



Added the infrastructure for dynamic stream allocation on HD-audio.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 687cb98e
Loading
Loading
Loading
Loading
+66 −8
Original line number Diff line number Diff line
@@ -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,17 +2337,39 @@ 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;
				err = set_pcm_default_values(codec, info);
				}
				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 = snd_hda_attach_pcm(codec, cpcm);
			if (err < 0)
				return err;
		}
	}
	}
	return 0;
}

+4 −1
Original line number Diff line number Diff line
@@ -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 */
+35 −86
Original line number Diff line number Diff line
@@ -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;