diff options
Diffstat (limited to 'sound/soc/blackfin/bf5xx-i2s-pcm.c')
-rw-r--r-- | sound/soc/blackfin/bf5xx-i2s-pcm.c | 183 |
1 files changed, 118 insertions, 65 deletions
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 262c1de364d8..9cb4a80df98e 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c | |||
@@ -39,8 +39,8 @@ | |||
39 | 39 | ||
40 | #include <asm/dma.h> | 40 | #include <asm/dma.h> |
41 | 41 | ||
42 | #include "bf5xx-i2s-pcm.h" | ||
43 | #include "bf5xx-sport.h" | 42 | #include "bf5xx-sport.h" |
43 | #include "bf5xx-i2s-pcm.h" | ||
44 | 44 | ||
45 | static void bf5xx_dma_irq(void *data) | 45 | static void bf5xx_dma_irq(void *data) |
46 | { | 46 | { |
@@ -50,7 +50,6 @@ static void bf5xx_dma_irq(void *data) | |||
50 | 50 | ||
51 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | 51 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { |
52 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 52 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
53 | SNDRV_PCM_INFO_MMAP | | ||
54 | SNDRV_PCM_INFO_MMAP_VALID | | 53 | SNDRV_PCM_INFO_MMAP_VALID | |
55 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | 54 | SNDRV_PCM_INFO_BLOCK_TRANSFER, |
56 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | 55 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
@@ -67,10 +66,16 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | |||
67 | static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, | 66 | static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, |
68 | struct snd_pcm_hw_params *params) | 67 | struct snd_pcm_hw_params *params) |
69 | { | 68 | { |
70 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max; | 69 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
71 | snd_pcm_lib_malloc_pages(substream, size); | 70 | unsigned int buffer_size = params_buffer_bytes(params); |
71 | struct bf5xx_i2s_pcm_data *dma_data; | ||
72 | 72 | ||
73 | return 0; | 73 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
74 | |||
75 | if (dma_data->tdm_mode) | ||
76 | buffer_size = buffer_size / params_channels(params) * 8; | ||
77 | |||
78 | return snd_pcm_lib_malloc_pages(substream, buffer_size); | ||
74 | } | 79 | } |
75 | 80 | ||
76 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | 81 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) |
@@ -82,9 +87,16 @@ static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | |||
82 | 87 | ||
83 | static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) | 88 | static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) |
84 | { | 89 | { |
90 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
85 | struct snd_pcm_runtime *runtime = substream->runtime; | 91 | struct snd_pcm_runtime *runtime = substream->runtime; |
86 | struct sport_device *sport = runtime->private_data; | 92 | struct sport_device *sport = runtime->private_data; |
87 | int period_bytes = frames_to_bytes(runtime, runtime->period_size); | 93 | int period_bytes = frames_to_bytes(runtime, runtime->period_size); |
94 | struct bf5xx_i2s_pcm_data *dma_data; | ||
95 | |||
96 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
97 | |||
98 | if (dma_data->tdm_mode) | ||
99 | period_bytes = period_bytes / runtime->channels * 8; | ||
88 | 100 | ||
89 | pr_debug("%s enter\n", __func__); | 101 | pr_debug("%s enter\n", __func__); |
90 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 102 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
@@ -131,10 +143,15 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
131 | 143 | ||
132 | static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | 144 | static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) |
133 | { | 145 | { |
146 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
134 | struct snd_pcm_runtime *runtime = substream->runtime; | 147 | struct snd_pcm_runtime *runtime = substream->runtime; |
135 | struct sport_device *sport = runtime->private_data; | 148 | struct sport_device *sport = runtime->private_data; |
136 | unsigned int diff; | 149 | unsigned int diff; |
137 | snd_pcm_uframes_t frames; | 150 | snd_pcm_uframes_t frames; |
151 | struct bf5xx_i2s_pcm_data *dma_data; | ||
152 | |||
153 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
154 | |||
138 | pr_debug("%s enter\n", __func__); | 155 | pr_debug("%s enter\n", __func__); |
139 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 156 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
140 | diff = sport_curr_offset_tx(sport); | 157 | diff = sport_curr_offset_tx(sport); |
@@ -151,6 +168,8 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | |||
151 | diff = 0; | 168 | diff = 0; |
152 | 169 | ||
153 | frames = bytes_to_frames(substream->runtime, diff); | 170 | frames = bytes_to_frames(substream->runtime, diff); |
171 | if (dma_data->tdm_mode) | ||
172 | frames = frames * runtime->channels / 8; | ||
154 | 173 | ||
155 | return frames; | 174 | return frames; |
156 | } | 175 | } |
@@ -162,11 +181,18 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) | |||
162 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); | 181 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); |
163 | struct snd_pcm_runtime *runtime = substream->runtime; | 182 | struct snd_pcm_runtime *runtime = substream->runtime; |
164 | struct snd_dma_buffer *buf = &substream->dma_buffer; | 183 | struct snd_dma_buffer *buf = &substream->dma_buffer; |
184 | struct bf5xx_i2s_pcm_data *dma_data; | ||
165 | int ret; | 185 | int ret; |
166 | 186 | ||
187 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
188 | |||
167 | pr_debug("%s enter\n", __func__); | 189 | pr_debug("%s enter\n", __func__); |
168 | 190 | ||
169 | snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); | 191 | snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); |
192 | if (dma_data->tdm_mode) | ||
193 | runtime->hw.buffer_bytes_max /= 4; | ||
194 | else | ||
195 | runtime->hw.info |= SNDRV_PCM_INFO_MMAP; | ||
170 | 196 | ||
171 | ret = snd_pcm_hw_constraint_integer(runtime, | 197 | ret = snd_pcm_hw_constraint_integer(runtime, |
172 | SNDRV_PCM_HW_PARAM_PERIODS); | 198 | SNDRV_PCM_HW_PARAM_PERIODS); |
@@ -202,6 +228,88 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, | |||
202 | return 0 ; | 228 | return 0 ; |
203 | } | 229 | } |
204 | 230 | ||
231 | static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, | ||
232 | snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) | ||
233 | { | ||
234 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
235 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
236 | unsigned int sample_size = runtime->sample_bits / 8; | ||
237 | struct bf5xx_i2s_pcm_data *dma_data; | ||
238 | unsigned int i; | ||
239 | void *src, *dst; | ||
240 | |||
241 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
242 | |||
243 | if (dma_data->tdm_mode) { | ||
244 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
245 | src = buf; | ||
246 | dst = runtime->dma_area; | ||
247 | dst += pos * sample_size * 8; | ||
248 | |||
249 | while (count--) { | ||
250 | for (i = 0; i < runtime->channels; i++) { | ||
251 | memcpy(dst + dma_data->map[i] * | ||
252 | sample_size, src, sample_size); | ||
253 | src += sample_size; | ||
254 | } | ||
255 | dst += 8 * sample_size; | ||
256 | } | ||
257 | } else { | ||
258 | src = runtime->dma_area; | ||
259 | src += pos * sample_size * 8; | ||
260 | dst = buf; | ||
261 | |||
262 | while (count--) { | ||
263 | for (i = 0; i < runtime->channels; i++) { | ||
264 | memcpy(dst, src + dma_data->map[i] * | ||
265 | sample_size, sample_size); | ||
266 | dst += sample_size; | ||
267 | } | ||
268 | src += 8 * sample_size; | ||
269 | } | ||
270 | } | ||
271 | } else { | ||
272 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
273 | src = buf; | ||
274 | dst = runtime->dma_area; | ||
275 | dst += frames_to_bytes(runtime, pos); | ||
276 | } else { | ||
277 | src = runtime->dma_area; | ||
278 | src += frames_to_bytes(runtime, pos); | ||
279 | dst = buf; | ||
280 | } | ||
281 | |||
282 | memcpy(dst, src, frames_to_bytes(runtime, count)); | ||
283 | } | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, | ||
289 | int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) | ||
290 | { | ||
291 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
292 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
293 | unsigned int sample_size = runtime->sample_bits / 8; | ||
294 | void *buf = runtime->dma_area; | ||
295 | struct bf5xx_i2s_pcm_data *dma_data; | ||
296 | unsigned int offset, size; | ||
297 | |||
298 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
299 | |||
300 | if (dma_data->tdm_mode) { | ||
301 | offset = pos * 8 * sample_size; | ||
302 | size = count * 8 * sample_size; | ||
303 | } else { | ||
304 | offset = frames_to_bytes(runtime, pos); | ||
305 | size = frames_to_bytes(runtime, count); | ||
306 | } | ||
307 | |||
308 | snd_pcm_format_set_silence(runtime->format, buf + offset, size); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
205 | static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { | 313 | static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { |
206 | .open = bf5xx_pcm_open, | 314 | .open = bf5xx_pcm_open, |
207 | .ioctl = snd_pcm_lib_ioctl, | 315 | .ioctl = snd_pcm_lib_ioctl, |
@@ -211,57 +319,16 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { | |||
211 | .trigger = bf5xx_pcm_trigger, | 319 | .trigger = bf5xx_pcm_trigger, |
212 | .pointer = bf5xx_pcm_pointer, | 320 | .pointer = bf5xx_pcm_pointer, |
213 | .mmap = bf5xx_pcm_mmap, | 321 | .mmap = bf5xx_pcm_mmap, |
322 | .copy = bf5xx_pcm_copy, | ||
323 | .silence = bf5xx_pcm_silence, | ||
214 | }; | 324 | }; |
215 | 325 | ||
216 | static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
217 | { | ||
218 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
219 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
220 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max; | ||
221 | |||
222 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
223 | buf->dev.dev = pcm->card->dev; | ||
224 | buf->private_data = NULL; | ||
225 | buf->area = dma_alloc_coherent(pcm->card->dev, size, | ||
226 | &buf->addr, GFP_KERNEL); | ||
227 | if (!buf->area) { | ||
228 | pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n"); | ||
229 | return -ENOMEM; | ||
230 | } | ||
231 | buf->bytes = size; | ||
232 | |||
233 | pr_debug("%s, area:%p, size:0x%08lx\n", __func__, | ||
234 | buf->area, buf->bytes); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
240 | { | ||
241 | struct snd_pcm_substream *substream; | ||
242 | struct snd_dma_buffer *buf; | ||
243 | int stream; | ||
244 | |||
245 | for (stream = 0; stream < 2; stream++) { | ||
246 | substream = pcm->streams[stream].substream; | ||
247 | if (!substream) | ||
248 | continue; | ||
249 | |||
250 | buf = &substream->dma_buffer; | ||
251 | if (!buf->area) | ||
252 | continue; | ||
253 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); | ||
254 | buf->area = NULL; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); | 326 | static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); |
259 | 327 | ||
260 | static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) | 328 | static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) |
261 | { | 329 | { |
262 | struct snd_card *card = rtd->card->snd_card; | 330 | struct snd_card *card = rtd->card->snd_card; |
263 | struct snd_pcm *pcm = rtd->pcm; | 331 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max; |
264 | int ret = 0; | ||
265 | 332 | ||
266 | pr_debug("%s enter\n", __func__); | 333 | pr_debug("%s enter\n", __func__); |
267 | if (!card->dev->dma_mask) | 334 | if (!card->dev->dma_mask) |
@@ -269,27 +336,13 @@ static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) | |||
269 | if (!card->dev->coherent_dma_mask) | 336 | if (!card->dev->coherent_dma_mask) |
270 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | 337 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); |
271 | 338 | ||
272 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { | 339 | return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, |
273 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | 340 | SNDRV_DMA_TYPE_DEV, card->dev, size, size); |
274 | SNDRV_PCM_STREAM_PLAYBACK); | ||
275 | if (ret) | ||
276 | goto out; | ||
277 | } | ||
278 | |||
279 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { | ||
280 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | ||
281 | SNDRV_PCM_STREAM_CAPTURE); | ||
282 | if (ret) | ||
283 | goto out; | ||
284 | } | ||
285 | out: | ||
286 | return ret; | ||
287 | } | 341 | } |
288 | 342 | ||
289 | static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = { | 343 | static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = { |
290 | .ops = &bf5xx_pcm_i2s_ops, | 344 | .ops = &bf5xx_pcm_i2s_ops, |
291 | .pcm_new = bf5xx_pcm_i2s_new, | 345 | .pcm_new = bf5xx_pcm_i2s_new, |
292 | .pcm_free = bf5xx_pcm_free_dma_buffers, | ||
293 | }; | 346 | }; |
294 | 347 | ||
295 | static int bfin_i2s_soc_platform_probe(struct platform_device *pdev) | 348 | static int bfin_i2s_soc_platform_probe(struct platform_device *pdev) |