diff options
author | Takashi Iwai <tiwai@suse.de> | 2008-07-30 09:01:44 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-10-12 20:42:58 -0400 |
commit | 176d5335fe66f379a339b0ab99cc7566e90ff1a9 (patch) | |
tree | 00f294bebf1270f65fe527141d3e6c1f440bba36 /sound/pci/hda/hda_intel.c | |
parent | 687cb98e893f492932abb3e92660d7d828bd44fb (diff) |
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>
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r-- | sound/pci/hda/hda_intel.c | 121 |
1 files changed, 35 insertions, 86 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9f316c1b2790..7b0abf08a583 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c | |||
@@ -1180,6 +1180,7 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) | |||
1180 | return 0; | 1180 | return 0; |
1181 | } | 1181 | } |
1182 | 1182 | ||
1183 | static int azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm); | ||
1183 | 1184 | ||
1184 | /* | 1185 | /* |
1185 | * Codec initialization | 1186 | * Codec initialization |
@@ -1212,6 +1213,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, | |||
1212 | bus_temp.pci = chip->pci; | 1213 | bus_temp.pci = chip->pci; |
1213 | bus_temp.ops.command = azx_send_cmd; | 1214 | bus_temp.ops.command = azx_send_cmd; |
1214 | bus_temp.ops.get_response = azx_get_response; | 1215 | bus_temp.ops.get_response = azx_get_response; |
1216 | bus_temp.ops.attach_pcm = azx_attach_pcm_stream; | ||
1215 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 1217 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
1216 | bus_temp.ops.pm_notify = azx_power_notify; | 1218 | bus_temp.ops.pm_notify = azx_power_notify; |
1217 | #endif | 1219 | #endif |
@@ -1718,111 +1720,58 @@ static struct snd_pcm_ops azx_pcm_ops = { | |||
1718 | 1720 | ||
1719 | static void azx_pcm_free(struct snd_pcm *pcm) | 1721 | static void azx_pcm_free(struct snd_pcm *pcm) |
1720 | { | 1722 | { |
1721 | kfree(pcm->private_data); | 1723 | struct azx_pcm *apcm = pcm->private_data; |
1724 | if (apcm) { | ||
1725 | apcm->chip->pcm[pcm->device] = NULL; | ||
1726 | kfree(apcm); | ||
1727 | } | ||
1722 | } | 1728 | } |
1723 | 1729 | ||
1724 | static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, | 1730 | static int |
1725 | struct hda_pcm *cpcm) | 1731 | azx_attach_pcm_stream(struct hda_codec *codec, struct hda_pcm *cpcm) |
1726 | { | 1732 | { |
1727 | int err; | 1733 | struct azx *chip = codec->bus->private_data; |
1728 | struct snd_pcm *pcm; | 1734 | struct snd_pcm *pcm; |
1729 | struct azx_pcm *apcm; | 1735 | struct azx_pcm *apcm; |
1736 | int pcm_dev = cpcm->device; | ||
1737 | int s, err; | ||
1730 | 1738 | ||
1731 | /* if no substreams are defined for both playback and capture, | 1739 | if (pcm_dev >= AZX_MAX_PCMS) { |
1732 | * it's just a placeholder. ignore it. | 1740 | snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n", |
1733 | */ | 1741 | pcm_dev); |
1734 | if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) | ||
1735 | return 0; | ||
1736 | |||
1737 | if (snd_BUG_ON(!cpcm->name)) | ||
1738 | return -EINVAL; | 1742 | return -EINVAL; |
1739 | 1743 | } | |
1740 | err = snd_pcm_new(chip->card, cpcm->name, cpcm->device, | 1744 | if (chip->pcm[pcm_dev]) { |
1741 | cpcm->stream[0].substreams, | 1745 | snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev); |
1742 | cpcm->stream[1].substreams, | 1746 | return -EBUSY; |
1747 | } | ||
1748 | err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, | ||
1749 | cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams, | ||
1750 | cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams, | ||
1743 | &pcm); | 1751 | &pcm); |
1744 | if (err < 0) | 1752 | if (err < 0) |
1745 | return err; | 1753 | return err; |
1746 | strcpy(pcm->name, cpcm->name); | 1754 | strcpy(pcm->name, cpcm->name); |
1747 | apcm = kmalloc(sizeof(*apcm), GFP_KERNEL); | 1755 | apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); |
1748 | if (apcm == NULL) | 1756 | if (apcm == NULL) |
1749 | return -ENOMEM; | 1757 | return -ENOMEM; |
1750 | apcm->chip = chip; | 1758 | apcm->chip = chip; |
1751 | apcm->codec = codec; | 1759 | apcm->codec = codec; |
1752 | apcm->hinfo[0] = &cpcm->stream[0]; | ||
1753 | apcm->hinfo[1] = &cpcm->stream[1]; | ||
1754 | pcm->private_data = apcm; | 1760 | pcm->private_data = apcm; |
1755 | pcm->private_free = azx_pcm_free; | 1761 | pcm->private_free = azx_pcm_free; |
1756 | if (cpcm->stream[0].substreams) | 1762 | if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM) |
1757 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); | 1763 | pcm->dev_class = SNDRV_PCM_CLASS_MODEM; |
1758 | if (cpcm->stream[1].substreams) | 1764 | chip->pcm[pcm_dev] = pcm; |
1759 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); | 1765 | cpcm->pcm = pcm; |
1766 | for (s = 0; s < 2; s++) { | ||
1767 | apcm->hinfo[s] = &cpcm->stream[s]; | ||
1768 | if (cpcm->stream[s].substreams) | ||
1769 | snd_pcm_set_ops(pcm, s, &azx_pcm_ops); | ||
1770 | } | ||
1771 | /* buffer pre-allocation */ | ||
1760 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, | 1772 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, |
1761 | snd_dma_pci_data(chip->pci), | 1773 | snd_dma_pci_data(chip->pci), |
1762 | 1024 * 64, 32 * 1024 * 1024); | 1774 | 1024 * 64, 32 * 1024 * 1024); |
1763 | chip->pcm[cpcm->device] = pcm; | ||
1764 | return 0; | ||
1765 | } | ||
1766 | |||
1767 | static int __devinit azx_pcm_create(struct azx *chip) | ||
1768 | { | ||
1769 | static const char *dev_name[HDA_PCM_NTYPES] = { | ||
1770 | "Audio", "SPDIF", "HDMI", "Modem" | ||
1771 | }; | ||
1772 | /* starting device index for each PCM type */ | ||
1773 | static int dev_idx[HDA_PCM_NTYPES] = { | ||
1774 | [HDA_PCM_TYPE_AUDIO] = 0, | ||
1775 | [HDA_PCM_TYPE_SPDIF] = 1, | ||
1776 | [HDA_PCM_TYPE_HDMI] = 3, | ||
1777 | [HDA_PCM_TYPE_MODEM] = 6 | ||
1778 | }; | ||
1779 | /* normal audio device indices; not linear to keep compatibility */ | ||
1780 | static int audio_idx[4] = { 0, 2, 4, 5 }; | ||
1781 | struct hda_codec *codec; | ||
1782 | int c, err; | ||
1783 | int num_devs[HDA_PCM_NTYPES]; | ||
1784 | |||
1785 | err = snd_hda_build_pcms(chip->bus); | ||
1786 | if (err < 0) | ||
1787 | return err; | ||
1788 | |||
1789 | /* create audio PCMs */ | ||
1790 | memset(num_devs, 0, sizeof(num_devs)); | ||
1791 | list_for_each_entry(codec, &chip->bus->codec_list, list) { | ||
1792 | for (c = 0; c < codec->num_pcms; c++) { | ||
1793 | struct hda_pcm *cpcm = &codec->pcm_info[c]; | ||
1794 | int type = cpcm->pcm_type; | ||
1795 | switch (type) { | ||
1796 | case HDA_PCM_TYPE_AUDIO: | ||
1797 | if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { | ||
1798 | snd_printk(KERN_WARNING | ||
1799 | "Too many audio devices\n"); | ||
1800 | continue; | ||
1801 | } | ||
1802 | cpcm->device = audio_idx[num_devs[type]]; | ||
1803 | break; | ||
1804 | case HDA_PCM_TYPE_SPDIF: | ||
1805 | case HDA_PCM_TYPE_HDMI: | ||
1806 | case HDA_PCM_TYPE_MODEM: | ||
1807 | if (num_devs[type]) { | ||
1808 | snd_printk(KERN_WARNING | ||
1809 | "%s already defined\n", | ||
1810 | dev_name[type]); | ||
1811 | continue; | ||
1812 | } | ||
1813 | cpcm->device = dev_idx[type]; | ||
1814 | break; | ||
1815 | default: | ||
1816 | snd_printk(KERN_WARNING | ||
1817 | "Invalid PCM type %d\n", type); | ||
1818 | continue; | ||
1819 | } | ||
1820 | num_devs[type]++; | ||
1821 | err = create_codec_pcm(chip, codec, cpcm); | ||
1822 | if (err < 0) | ||
1823 | return err; | ||
1824 | } | ||
1825 | } | ||
1826 | return 0; | 1775 | return 0; |
1827 | } | 1776 | } |
1828 | 1777 | ||
@@ -2324,7 +2273,7 @@ static int __devinit azx_probe(struct pci_dev *pci, | |||
2324 | } | 2273 | } |
2325 | 2274 | ||
2326 | /* create PCM streams */ | 2275 | /* create PCM streams */ |
2327 | err = azx_pcm_create(chip); | 2276 | err = snd_hda_build_pcms(chip->bus); |
2328 | if (err < 0) { | 2277 | if (err < 0) { |
2329 | snd_card_free(card); | 2278 | snd_card_free(card); |
2330 | return err; | 2279 | return err; |