ALSA: hda - Add infrastructure for dynamic stream allocation
Added the infrastructure for dynamic stream allocation on HD-audio. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
687cb98e89
commit
176d5335fe
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue