aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/blackfin/bf5xx-i2s-pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/blackfin/bf5xx-i2s-pcm.c')
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c183
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
45static void bf5xx_dma_irq(void *data) 45static void bf5xx_dma_irq(void *data)
46{ 46{
@@ -50,7 +50,6 @@ static void bf5xx_dma_irq(void *data)
50 50
51static const struct snd_pcm_hardware bf5xx_pcm_hardware = { 51static 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 = {
67static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, 66static 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
76static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) 81static 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
83static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) 88static 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
132static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) 144static 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
231static 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
288static 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
205static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { 313static 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
216static 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
239static 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
258static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); 326static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
259 327
260static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) 328static 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
289static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = { 343static 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
295static int bfin_i2s_soc_platform_probe(struct platform_device *pdev) 348static int bfin_i2s_soc_platform_probe(struct platform_device *pdev)