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 | |
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')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 74 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 5 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 121 |
3 files changed, 105 insertions, 95 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 6447754ae56e..19b4530e3baf 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -2262,6 +2262,28 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec, | |||
2262 | return 0; | 2262 | return 0; |
2263 | } | 2263 | } |
2264 | 2264 | ||
2265 | /* | ||
2266 | * attach a new PCM stream | ||
2267 | */ | ||
2268 | static int __devinit | ||
2269 | snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) | ||
2270 | { | ||
2271 | struct hda_pcm_stream *info; | ||
2272 | int stream, err; | ||
2273 | |||
2274 | if (!pcm->name) | ||
2275 | return -EINVAL; | ||
2276 | for (stream = 0; stream < 2; stream++) { | ||
2277 | info = &pcm->stream[stream]; | ||
2278 | if (info->substreams) { | ||
2279 | err = set_pcm_default_values(codec, info); | ||
2280 | if (err < 0) | ||
2281 | return err; | ||
2282 | } | ||
2283 | } | ||
2284 | return codec->bus->ops.attach_pcm(codec, pcm); | ||
2285 | } | ||
2286 | |||
2265 | /** | 2287 | /** |
2266 | * snd_hda_build_pcms - build PCM information | 2288 | * snd_hda_build_pcms - build PCM information |
2267 | * @bus: the BUS | 2289 | * @bus: the BUS |
@@ -2290,10 +2312,24 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec, | |||
2290 | */ | 2312 | */ |
2291 | int __devinit snd_hda_build_pcms(struct hda_bus *bus) | 2313 | int __devinit snd_hda_build_pcms(struct hda_bus *bus) |
2292 | { | 2314 | { |
2315 | static const char *dev_name[HDA_PCM_NTYPES] = { | ||
2316 | "Audio", "SPDIF", "HDMI", "Modem" | ||
2317 | }; | ||
2318 | /* starting device index for each PCM type */ | ||
2319 | static int dev_idx[HDA_PCM_NTYPES] = { | ||
2320 | [HDA_PCM_TYPE_AUDIO] = 0, | ||
2321 | [HDA_PCM_TYPE_SPDIF] = 1, | ||
2322 | [HDA_PCM_TYPE_HDMI] = 3, | ||
2323 | [HDA_PCM_TYPE_MODEM] = 6 | ||
2324 | }; | ||
2325 | /* normal audio device indices; not linear to keep compatibility */ | ||
2326 | static int audio_idx[4] = { 0, 2, 4, 5 }; | ||
2293 | struct hda_codec *codec; | 2327 | struct hda_codec *codec; |
2328 | int num_devs[HDA_PCM_NTYPES]; | ||
2294 | 2329 | ||
2330 | memset(num_devs, 0, sizeof(num_devs)); | ||
2295 | list_for_each_entry(codec, &bus->codec_list, list) { | 2331 | list_for_each_entry(codec, &bus->codec_list, list) { |
2296 | unsigned int pcm, s; | 2332 | unsigned int pcm; |
2297 | int err; | 2333 | int err; |
2298 | if (!codec->patch_ops.build_pcms) | 2334 | if (!codec->patch_ops.build_pcms) |
2299 | continue; | 2335 | continue; |
@@ -2301,15 +2337,37 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus) | |||
2301 | if (err < 0) | 2337 | if (err < 0) |
2302 | return err; | 2338 | return err; |
2303 | for (pcm = 0; pcm < codec->num_pcms; pcm++) { | 2339 | for (pcm = 0; pcm < codec->num_pcms; pcm++) { |
2304 | for (s = 0; s < 2; s++) { | 2340 | struct hda_pcm *cpcm = &codec->pcm_info[pcm]; |
2305 | struct hda_pcm_stream *info; | 2341 | int type = cpcm->pcm_type; |
2306 | info = &codec->pcm_info[pcm].stream[s]; | 2342 | switch (type) { |
2307 | if (!info->substreams) | 2343 | case HDA_PCM_TYPE_AUDIO: |
2344 | if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { | ||
2345 | snd_printk(KERN_WARNING | ||
2346 | "Too many audio devices\n"); | ||
2347 | continue; | ||
2348 | } | ||
2349 | cpcm->device = audio_idx[num_devs[type]]; | ||
2350 | break; | ||
2351 | case HDA_PCM_TYPE_SPDIF: | ||
2352 | case HDA_PCM_TYPE_HDMI: | ||
2353 | case HDA_PCM_TYPE_MODEM: | ||
2354 | if (num_devs[type]) { | ||
2355 | snd_printk(KERN_WARNING | ||
2356 | "%s already defined\n", | ||
2357 | dev_name[type]); | ||
2308 | continue; | 2358 | continue; |
2309 | err = set_pcm_default_values(codec, info); | 2359 | } |
2310 | if (err < 0) | 2360 | cpcm->device = dev_idx[type]; |
2311 | return err; | 2361 | break; |
2362 | default: | ||
2363 | snd_printk(KERN_WARNING | ||
2364 | "Invalid PCM type %d\n", type); | ||
2365 | continue; | ||
2312 | } | 2366 | } |
2367 | num_devs[type]++; | ||
2368 | err = snd_hda_attach_pcm(codec, cpcm); | ||
2369 | if (err < 0) | ||
2370 | return err; | ||
2313 | } | 2371 | } |
2314 | } | 2372 | } |
2315 | return 0; | 2373 | return 0; |
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 60468f562400..70e8fa09273d 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -542,6 +542,8 @@ struct hda_bus_ops { | |||
542 | unsigned int (*get_response)(struct hda_codec *codec); | 542 | unsigned int (*get_response)(struct hda_codec *codec); |
543 | /* free the private data */ | 543 | /* free the private data */ |
544 | void (*private_free)(struct hda_bus *); | 544 | void (*private_free)(struct hda_bus *); |
545 | /* attach a PCM stream */ | ||
546 | int (*attach_pcm)(struct hda_codec *codec, struct hda_pcm *pcm); | ||
545 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 547 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
546 | /* notify power-up/down from codec to controller */ | 548 | /* notify power-up/down from codec to controller */ |
547 | void (*pm_notify)(struct hda_codec *codec); | 549 | void (*pm_notify)(struct hda_codec *codec); |
@@ -680,7 +682,8 @@ struct hda_pcm { | |||
680 | char *name; | 682 | char *name; |
681 | struct hda_pcm_stream stream[2]; | 683 | struct hda_pcm_stream stream[2]; |
682 | unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ | 684 | unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ |
683 | int device; /* assigned device number */ | 685 | int device; /* device number to assign */ |
686 | struct snd_pcm *pcm; /* assigned PCM instance */ | ||
684 | }; | 687 | }; |
685 | 688 | ||
686 | /* codec information */ | 689 | /* codec information */ |
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; |