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

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
...@@ -2262,6 +2262,28 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec, ...@@ -2262,6 +2262,28 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
return 0; 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 * snd_hda_build_pcms - build PCM information
* @bus: the BUS * @bus: the BUS
...@@ -2290,10 +2312,24 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec, ...@@ -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) 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; 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) { list_for_each_entry(codec, &bus->codec_list, list) {
unsigned int pcm, s; unsigned int pcm;
int err; int err;
if (!codec->patch_ops.build_pcms) if (!codec->patch_ops.build_pcms)
continue; continue;
...@@ -2301,15 +2337,37 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus) ...@@ -2301,15 +2337,37 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus)
if (err < 0) if (err < 0)
return err; return err;
for (pcm = 0; pcm < codec->num_pcms; pcm++) { for (pcm = 0; pcm < codec->num_pcms; pcm++) {
for (s = 0; s < 2; s++) { struct hda_pcm *cpcm = &codec->pcm_info[pcm];
struct hda_pcm_stream *info; int type = cpcm->pcm_type;
info = &codec->pcm_info[pcm].stream[s]; switch (type) {
if (!info->substreams) 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; continue;
err = set_pcm_default_values(codec, info); }
if (err < 0) cpcm->device = dev_idx[type];
return err; 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; return 0;
......
...@@ -542,6 +542,8 @@ struct hda_bus_ops { ...@@ -542,6 +542,8 @@ struct hda_bus_ops {
unsigned int (*get_response)(struct hda_codec *codec); unsigned int (*get_response)(struct hda_codec *codec);
/* free the private data */ /* free the private data */
void (*private_free)(struct hda_bus *); 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 #ifdef CONFIG_SND_HDA_POWER_SAVE
/* notify power-up/down from codec to controller */ /* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_codec *codec); void (*pm_notify)(struct hda_codec *codec);
...@@ -680,7 +682,8 @@ struct hda_pcm { ...@@ -680,7 +682,8 @@ struct hda_pcm {
char *name; char *name;
struct hda_pcm_stream stream[2]; struct hda_pcm_stream stream[2];
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ 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 */ /* codec information */
......
...@@ -1180,6 +1180,7 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) ...@@ -1180,6 +1180,7 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
return 0; return 0;
} }
static int azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm);
/* /*
* Codec initialization * Codec initialization
...@@ -1212,6 +1213,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, ...@@ -1212,6 +1213,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
bus_temp.pci = chip->pci; bus_temp.pci = chip->pci;
bus_temp.ops.command = azx_send_cmd; bus_temp.ops.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response; bus_temp.ops.get_response = azx_get_response;
bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
bus_temp.ops.pm_notify = azx_power_notify; bus_temp.ops.pm_notify = azx_power_notify;
#endif #endif
...@@ -1718,111 +1720,58 @@ static struct snd_pcm_ops azx_pcm_ops = { ...@@ -1718,111 +1720,58 @@ static struct snd_pcm_ops azx_pcm_ops = {
static void azx_pcm_free(struct snd_pcm *pcm) 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, static int
struct hda_pcm *cpcm) 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 snd_pcm *pcm;
struct azx_pcm *apcm; struct azx_pcm *apcm;
int pcm_dev = cpcm->device;
int s, err;
/* if no substreams are defined for both playback and capture, if (pcm_dev >= AZX_MAX_PCMS) {
* it's just a placeholder. ignore it. snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
*/ pcm_dev);
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
return 0;
if (snd_BUG_ON(!cpcm->name))
return -EINVAL; return -EINVAL;
}
err = snd_pcm_new(chip->card, cpcm->name, cpcm->device, if (chip->pcm[pcm_dev]) {
cpcm->stream[0].substreams, snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
cpcm->stream[1].substreams, 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); &pcm);
if (err < 0) if (err < 0)
return err; return err;
strcpy(pcm->name, cpcm->name); strcpy(pcm->name, cpcm->name);
apcm = kmalloc(sizeof(*apcm), GFP_KERNEL); apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
if (apcm == NULL) if (apcm == NULL)
return -ENOMEM; return -ENOMEM;
apcm->chip = chip; apcm->chip = chip;
apcm->codec = codec; apcm->codec = codec;
apcm->hinfo[0] = &cpcm->stream[0];
apcm->hinfo[1] = &cpcm->stream[1];
pcm->private_data = apcm; pcm->private_data = apcm;
pcm->private_free = azx_pcm_free; pcm->private_free = azx_pcm_free;
if (cpcm->stream[0].substreams) if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
if (cpcm->stream[1].substreams) chip->pcm[pcm_dev] = pcm;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); 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_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci), snd_dma_pci_data(chip->pci),
1024 * 64, 32 * 1024 * 1024); 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; return 0;
} }
...@@ -2324,7 +2273,7 @@ static int __devinit azx_probe(struct pci_dev *pci, ...@@ -2324,7 +2273,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
} }
/* create PCM streams */ /* create PCM streams */
err = azx_pcm_create(chip); err = snd_hda_build_pcms(chip->bus);
if (err < 0) { if (err < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment