Commit | Line | Data |
---|---|---|
a6d77317 DB |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License version 2 as | |
4 | * published by the Free Software Foundation. | |
5 | */ | |
6 | ||
5a0e3ad6 | 7 | #include <linux/slab.h> |
a6d77317 DB |
8 | #include <linux/module.h> |
9 | #include <linux/dma-mapping.h> | |
d65a1458 | 10 | #include <linux/dmaengine.h> |
58ceb57e | 11 | #include <linux/dma/pxa-dma.h> |
a6d77317 DB |
12 | |
13 | #include <sound/core.h> | |
14 | #include <sound/pcm.h> | |
15 | #include <sound/pcm_params.h> | |
16 | #include <sound/pxa2xx-lib.h> | |
d65a1458 | 17 | #include <sound/dmaengine_pcm.h> |
a6d77317 | 18 | |
a6d77317 DB |
19 | #include "pxa2xx-pcm.h" |
20 | ||
21 | static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { | |
22 | .info = SNDRV_PCM_INFO_MMAP | | |
23 | SNDRV_PCM_INFO_MMAP_VALID | | |
24 | SNDRV_PCM_INFO_INTERLEAVED | | |
25 | SNDRV_PCM_INFO_PAUSE | | |
26 | SNDRV_PCM_INFO_RESUME, | |
27 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | |
28 | SNDRV_PCM_FMTBIT_S24_LE | | |
29 | SNDRV_PCM_FMTBIT_S32_LE, | |
30 | .period_bytes_min = 32, | |
31 | .period_bytes_max = 8192 - 32, | |
32 | .periods_min = 1, | |
58ceb57e | 33 | .periods_max = 256, |
a6d77317 DB |
34 | .buffer_bytes_max = 128 * 1024, |
35 | .fifo_size = 32, | |
36 | }; | |
37 | ||
38 | int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, | |
39 | struct snd_pcm_hw_params *params) | |
40 | { | |
58ceb57e DM |
41 | struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); |
42 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
43 | struct snd_dmaengine_dai_dma_data *dma_params; | |
44 | struct dma_slave_config config; | |
45 | int ret; | |
46 | ||
47 | dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | |
48 | if (!dma_params) | |
49 | return 0; | |
d65a1458 | 50 | |
58ceb57e DM |
51 | ret = snd_hwparams_to_dma_slave_config(substream, params, &config); |
52 | if (ret) | |
53 | return ret; | |
d65a1458 | 54 | |
58ceb57e DM |
55 | snd_dmaengine_pcm_set_config_from_dai_data(substream, |
56 | snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), | |
57 | &config); | |
a6d77317 | 58 | |
58ceb57e DM |
59 | ret = dmaengine_slave_config(chan, &config); |
60 | if (ret) | |
61 | return ret; | |
a6d77317 | 62 | |
58ceb57e | 63 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
a6d77317 DB |
64 | |
65 | return 0; | |
66 | } | |
67 | EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); | |
68 | ||
69 | int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) | |
70 | { | |
a6d77317 DB |
71 | snd_pcm_set_runtime_buffer(substream, NULL); |
72 | return 0; | |
73 | } | |
74 | EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); | |
75 | ||
76 | int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |
77 | { | |
58ceb57e | 78 | return snd_dmaengine_pcm_trigger(substream, cmd); |
a6d77317 DB |
79 | } |
80 | EXPORT_SYMBOL(pxa2xx_pcm_trigger); | |
81 | ||
82 | snd_pcm_uframes_t | |
83 | pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) | |
84 | { | |
58ceb57e | 85 | return snd_dmaengine_pcm_pointer(substream); |
a6d77317 DB |
86 | } |
87 | EXPORT_SYMBOL(pxa2xx_pcm_pointer); | |
88 | ||
89 | int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) | |
90 | { | |
a6d77317 DB |
91 | return 0; |
92 | } | |
93 | EXPORT_SYMBOL(__pxa2xx_pcm_prepare); | |
94 | ||
a6d77317 DB |
95 | int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) |
96 | { | |
58ceb57e | 97 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
a6d77317 | 98 | struct snd_pcm_runtime *runtime = substream->runtime; |
58ceb57e | 99 | struct snd_dmaengine_dai_dma_data *dma_params; |
a6d77317 DB |
100 | int ret; |
101 | ||
102 | runtime->hw = pxa2xx_pcm_hardware; | |
103 | ||
58ceb57e DM |
104 | dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
105 | if (!dma_params) | |
106 | return 0; | |
107 | ||
a6d77317 DB |
108 | /* |
109 | * For mysterious reasons (and despite what the manual says) | |
110 | * playback samples are lost if the DMA count is not a multiple | |
111 | * of the DMA burst size. Let's add a rule to enforce that. | |
112 | */ | |
113 | ret = snd_pcm_hw_constraint_step(runtime, 0, | |
114 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); | |
115 | if (ret) | |
58ceb57e | 116 | return ret; |
a6d77317 DB |
117 | |
118 | ret = snd_pcm_hw_constraint_step(runtime, 0, | |
119 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); | |
120 | if (ret) | |
58ceb57e | 121 | return ret; |
a6d77317 DB |
122 | |
123 | ret = snd_pcm_hw_constraint_integer(runtime, | |
124 | SNDRV_PCM_HW_PARAM_PERIODS); | |
125 | if (ret < 0) | |
58ceb57e | 126 | return ret; |
a6d77317 | 127 | |
58ceb57e DM |
128 | return snd_dmaengine_pcm_open_request_chan(substream, |
129 | pxad_filter_fn, | |
130 | dma_params->filter_data); | |
a6d77317 DB |
131 | } |
132 | EXPORT_SYMBOL(__pxa2xx_pcm_open); | |
133 | ||
134 | int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) | |
135 | { | |
58ceb57e | 136 | return snd_dmaengine_pcm_close_release_chan(substream); |
a6d77317 DB |
137 | } |
138 | EXPORT_SYMBOL(__pxa2xx_pcm_close); | |
139 | ||
140 | int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, | |
141 | struct vm_area_struct *vma) | |
142 | { | |
143 | struct snd_pcm_runtime *runtime = substream->runtime; | |
f6e45661 LR |
144 | return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, |
145 | runtime->dma_addr, runtime->dma_bytes); | |
a6d77317 DB |
146 | } |
147 | EXPORT_SYMBOL(pxa2xx_pcm_mmap); | |
148 | ||
149 | int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | |
150 | { | |
151 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | |
152 | struct snd_dma_buffer *buf = &substream->dma_buffer; | |
153 | size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; | |
154 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | |
155 | buf->dev.dev = pcm->card->dev; | |
156 | buf->private_data = NULL; | |
f6e45661 | 157 | buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); |
a6d77317 DB |
158 | if (!buf->area) |
159 | return -ENOMEM; | |
160 | buf->bytes = size; | |
161 | return 0; | |
162 | } | |
163 | EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); | |
164 | ||
165 | void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | |
166 | { | |
167 | struct snd_pcm_substream *substream; | |
168 | struct snd_dma_buffer *buf; | |
169 | int stream; | |
170 | ||
171 | for (stream = 0; stream < 2; stream++) { | |
172 | substream = pcm->streams[stream].substream; | |
173 | if (!substream) | |
174 | continue; | |
175 | buf = &substream->dma_buffer; | |
176 | if (!buf->area) | |
177 | continue; | |
f6e45661 | 178 | dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); |
a6d77317 DB |
179 | buf->area = NULL; |
180 | } | |
181 | } | |
182 | EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); | |
183 | ||
184 | MODULE_AUTHOR("Nicolas Pitre"); | |
185 | MODULE_DESCRIPTION("Intel PXA2xx sound library"); | |
186 | MODULE_LICENSE("GPL"); |