Commit | Line | Data |
---|---|---|
f11a96d5 LG |
1 | /* |
2 | * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip | |
3 | * | |
4 | * Author: Nicolas Pitre | |
5 | * Created: Nov 30, 2004 | |
6 | * Copyright: (C) 2004 MontaVista Software, Inc. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
f11a96d5 | 13 | #include <linux/dma-mapping.h> |
da155d5b | 14 | #include <linux/module.h> |
d65a1458 | 15 | #include <linux/dmaengine.h> |
c529ca4a | 16 | #include <linux/of.h> |
f11a96d5 | 17 | |
b4528141 AB |
18 | #include <mach/dma.h> |
19 | ||
f11a96d5 | 20 | #include <sound/core.h> |
f11a96d5 | 21 | #include <sound/soc.h> |
a6d77317 | 22 | #include <sound/pxa2xx-lib.h> |
d65a1458 | 23 | #include <sound/dmaengine_pcm.h> |
f11a96d5 | 24 | |
a6d77317 | 25 | #include "../../arm/pxa2xx-pcm.h" |
f11a96d5 LG |
26 | |
27 | static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, | |
28 | struct snd_pcm_hw_params *params) | |
29 | { | |
30 | struct snd_pcm_runtime *runtime = substream->runtime; | |
31 | struct pxa2xx_runtime_data *prtd = runtime->private_data; | |
32 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
d65a1458 | 33 | struct snd_dmaengine_dai_dma_data *dma; |
f11a96d5 LG |
34 | int ret; |
35 | ||
f0fba2ad | 36 | dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
5f712b2b | 37 | |
a8f5d0a5 AJ |
38 | /* return if this is a bufferless transfer e.g. |
39 | * codec <--> BT codec or GSM modem -- lg FIXME */ | |
b32432e3 MB |
40 | if (!dma) |
41 | return 0; | |
a8f5d0a5 | 42 | |
f11a96d5 LG |
43 | /* this may get called several times by oss emulation |
44 | * with different params */ | |
45 | if (prtd->params == NULL) { | |
46 | prtd->params = dma; | |
d65a1458 | 47 | ret = pxa_request_dma("name", DMA_PRIO_LOW, |
f11a96d5 LG |
48 | pxa2xx_pcm_dma_irq, substream); |
49 | if (ret < 0) | |
50 | return ret; | |
51 | prtd->dma_ch = ret; | |
52 | } else if (prtd->params != dma) { | |
53 | pxa_free_dma(prtd->dma_ch); | |
54 | prtd->params = dma; | |
d65a1458 | 55 | ret = pxa_request_dma("name", DMA_PRIO_LOW, |
f11a96d5 LG |
56 | pxa2xx_pcm_dma_irq, substream); |
57 | if (ret < 0) | |
58 | return ret; | |
59 | prtd->dma_ch = ret; | |
60 | } | |
61 | ||
a6d77317 | 62 | return __pxa2xx_pcm_hw_params(substream, params); |
f11a96d5 LG |
63 | } |
64 | ||
65 | static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) | |
66 | { | |
67 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | |
68 | ||
a6d77317 | 69 | __pxa2xx_pcm_hw_free(substream); |
f11a96d5 | 70 | |
9bef6489 | 71 | if (prtd->dma_ch >= 0) { |
f11a96d5 | 72 | pxa_free_dma(prtd->dma_ch); |
9bef6489 | 73 | prtd->dma_ch = -1; |
97c96c07 | 74 | prtd->params = NULL; |
f11a96d5 LG |
75 | } |
76 | ||
77 | return 0; | |
78 | } | |
79 | ||
0e734ad5 | 80 | static struct snd_pcm_ops pxa2xx_pcm_ops = { |
a6d77317 DB |
81 | .open = __pxa2xx_pcm_open, |
82 | .close = __pxa2xx_pcm_close, | |
f11a96d5 LG |
83 | .ioctl = snd_pcm_lib_ioctl, |
84 | .hw_params = pxa2xx_pcm_hw_params, | |
85 | .hw_free = pxa2xx_pcm_hw_free, | |
a6d77317 | 86 | .prepare = __pxa2xx_pcm_prepare, |
f11a96d5 LG |
87 | .trigger = pxa2xx_pcm_trigger, |
88 | .pointer = pxa2xx_pcm_pointer, | |
89 | .mmap = pxa2xx_pcm_mmap, | |
90 | }; | |
91 | ||
552d1ef6 | 92 | static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) |
f11a96d5 | 93 | { |
552d1ef6 | 94 | struct snd_card *card = rtd->card->snd_card; |
552d1ef6 | 95 | struct snd_pcm *pcm = rtd->pcm; |
c9bd5e69 | 96 | int ret; |
f11a96d5 | 97 | |
c9bd5e69 RK |
98 | ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); |
99 | if (ret) | |
100 | return ret; | |
f11a96d5 | 101 | |
53dea36c | 102 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { |
f11a96d5 LG |
103 | ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, |
104 | SNDRV_PCM_STREAM_PLAYBACK); | |
105 | if (ret) | |
106 | goto out; | |
107 | } | |
108 | ||
53dea36c | 109 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { |
f11a96d5 LG |
110 | ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, |
111 | SNDRV_PCM_STREAM_CAPTURE); | |
112 | if (ret) | |
113 | goto out; | |
114 | } | |
115 | out: | |
116 | return ret; | |
117 | } | |
118 | ||
f0fba2ad LG |
119 | static struct snd_soc_platform_driver pxa2xx_soc_platform = { |
120 | .ops = &pxa2xx_pcm_ops, | |
8a33de9e | 121 | .pcm_new = pxa2xx_soc_pcm_new, |
f11a96d5 LG |
122 | .pcm_free = pxa2xx_pcm_free_dma_buffers, |
123 | }; | |
f11a96d5 | 124 | |
570f6fe1 | 125 | static int pxa2xx_soc_platform_probe(struct platform_device *pdev) |
958e792c | 126 | { |
f0fba2ad | 127 | return snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform); |
958e792c | 128 | } |
958e792c | 129 | |
570f6fe1 | 130 | static int pxa2xx_soc_platform_remove(struct platform_device *pdev) |
958e792c | 131 | { |
f0fba2ad LG |
132 | snd_soc_unregister_platform(&pdev->dev); |
133 | return 0; | |
134 | } | |
135 | ||
c529ca4a DM |
136 | #ifdef CONFIG_OF |
137 | static const struct of_device_id snd_soc_pxa_audio_match[] = { | |
138 | { .compatible = "mrvl,pxa-pcm-audio" }, | |
139 | { } | |
140 | }; | |
141 | #endif | |
142 | ||
f0fba2ad LG |
143 | static struct platform_driver pxa_pcm_driver = { |
144 | .driver = { | |
c529ca4a | 145 | .name = "pxa-pcm-audio", |
c529ca4a | 146 | .of_match_table = of_match_ptr(snd_soc_pxa_audio_match), |
f0fba2ad LG |
147 | }, |
148 | ||
149 | .probe = pxa2xx_soc_platform_probe, | |
570f6fe1 | 150 | .remove = pxa2xx_soc_platform_remove, |
f0fba2ad LG |
151 | }; |
152 | ||
2f702a19 | 153 | module_platform_driver(pxa_pcm_driver); |
958e792c | 154 | |
f11a96d5 LG |
155 | MODULE_AUTHOR("Nicolas Pitre"); |
156 | MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); | |
157 | MODULE_LICENSE("GPL"); |