mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-02 12:06:42 +07:00
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;
|
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,
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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;
|
continue;
|
||||||
err = set_pcm_default_values(codec, info);
|
}
|
||||||
if (err < 0)
|
cpcm->device = audio_idx[num_devs[type]];
|
||||||
return err;
|
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;
|
return 0;
|
||||||
|
@ -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 {
|
|||||||
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)
|
|||||||
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,
|
|||||||
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 = {
|
|||||||
|
|
||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
||||||
|
Loading…
Reference in New Issue
Block a user