8067cfafa3a703ea5e0f9dacb30f4c13e882f0ff
[linux-2.6.git] / sound / soc / blackfin / bf5xx-ac97-pcm.c
1 /*
2  * File:         sound/soc/blackfin/bf5xx-ac97-pcm.c
3  * Author:       Cliff Cai <Cliff.Cai@analog.com>
4  *
5  * Created:      Tue June 06 2008
6  * Description:  DMA Driver for AC97 sound chip
7  *
8  * Modified:
9  *               Copyright 2008 Analog Devices Inc.
10  *
11  * Bugs:         Enter bugs at http://blackfin.uclinux.org/
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, see the file COPYING, or write
25  * to the Free Software Foundation, Inc.,
26  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
27  */
28
29 #include <linux/module.h>
30 #include <linux/init.h>
31 #include <linux/platform_device.h>
32 #include <linux/slab.h>
33 #include <linux/dma-mapping.h>
34
35 #include <sound/core.h>
36 #include <sound/pcm.h>
37 #include <sound/pcm_params.h>
38 #include <sound/soc.h>
39
40 #include <asm/dma.h>
41
42 #include "bf5xx-ac97-pcm.h"
43 #include "bf5xx-ac97.h"
44 #include "bf5xx-sport.h"
45
46 static unsigned int ac97_chan_mask[] = {
47         SP_FL, /* Mono */
48         SP_STEREO, /* Stereo */
49         SP_2DOT1, /* 2.1*/
50         SP_QUAD,/*Quadraquic*/
51         SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */
52         SP_5DOT1, /* 5.1 */
53 };
54
55 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
56 static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
57          snd_pcm_uframes_t count)
58 {
59         struct snd_pcm_runtime *runtime = substream->runtime;
60         struct sport_device *sport = runtime->private_data;
61         unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
62         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
63                 bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf +
64                 sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos *
65                 runtime->channels, count, chan_mask);
66                 sport->tx_pos += runtime->period_size;
67                 if (sport->tx_pos >= runtime->buffer_size)
68                         sport->tx_pos %= runtime->buffer_size;
69                 sport->tx_delay_pos = sport->tx_pos;
70         } else {
71                 bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf +
72                 sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos *
73                 runtime->channels, count);
74                 sport->rx_pos += runtime->period_size;
75                 if (sport->rx_pos >= runtime->buffer_size)
76                         sport->rx_pos %= runtime->buffer_size;
77         }
78 }
79 #endif
80
81 static void bf5xx_dma_irq(void *data)
82 {
83         struct snd_pcm_substream *pcm = data;
84 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
85         struct snd_pcm_runtime *runtime = pcm->runtime;
86         struct sport_device *sport = runtime->private_data;
87         bf5xx_mmap_copy(pcm, runtime->period_size);
88         if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) {
89                 if (sport->once == 0) {
90                         snd_pcm_period_elapsed(pcm);
91                         bf5xx_mmap_copy(pcm, runtime->period_size);
92                         sport->once = 1;
93                 }
94         }
95 #endif
96         snd_pcm_period_elapsed(pcm);
97 }
98
99 /* The memory size for pure pcm data is 128*1024 = 0x20000 bytes.
100  * The total rx/tx buffer is for ac97 frame to hold all pcm data
101  * is  0x20000 * sizeof(struct ac97_frame) / 4.
102  */
103 static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
104         .info                   = SNDRV_PCM_INFO_INTERLEAVED |
105 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
106                                    SNDRV_PCM_INFO_MMAP |
107                                    SNDRV_PCM_INFO_MMAP_VALID |
108 #endif
109                                    SNDRV_PCM_INFO_BLOCK_TRANSFER,
110
111         .formats                = SNDRV_PCM_FMTBIT_S16_LE,
112         .period_bytes_min       = 32,
113         .period_bytes_max       = 0x10000,
114         .periods_min            = 1,
115         .periods_max            = PAGE_SIZE/32,
116         .buffer_bytes_max       = 0x20000, /* 128 kbytes */
117         .fifo_size              = 16,
118 };
119
120 static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
121         struct snd_pcm_hw_params *params)
122 {
123         size_t size = bf5xx_pcm_hardware.buffer_bytes_max
124                         * sizeof(struct ac97_frame) / 4;
125
126         snd_pcm_lib_malloc_pages(substream, size);
127
128         return 0;
129 }
130
131 static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
132 {
133 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
134         struct snd_pcm_runtime *runtime = substream->runtime;
135         struct sport_device *sport = runtime->private_data;
136
137         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
138                 sport->once = 0;
139                 if (runtime->dma_area)
140                         memset(runtime->dma_area, 0, runtime->buffer_size);
141                 memset(sport->tx_dma_buf, 0, runtime->buffer_size *
142                         sizeof(struct ac97_frame));
143         } else
144                 memset(sport->rx_dma_buf, 0, runtime->buffer_size *
145                         sizeof(struct ac97_frame));
146 #endif
147         snd_pcm_lib_free_pages(substream);
148         return 0;
149 }
150
151 static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
152 {
153         struct snd_pcm_runtime *runtime = substream->runtime;
154         struct sport_device *sport = runtime->private_data;
155
156         /* An intermediate buffer is introduced for implementing mmap for
157          * SPORT working in TMD mode(include AC97).
158          */
159 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
160         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
161                 sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
162                 sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
163                         runtime->period_size * sizeof(struct ac97_frame));
164         } else {
165                 sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
166                 sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
167                         runtime->period_size * sizeof(struct ac97_frame));
168         }
169 #else
170         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
171                 sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
172                 sport_config_tx_dma(sport, runtime->dma_area, runtime->periods,
173                         runtime->period_size * sizeof(struct ac97_frame));
174         } else {
175                 sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
176                 sport_config_rx_dma(sport, runtime->dma_area, runtime->periods,
177                         runtime->period_size * sizeof(struct ac97_frame));
178         }
179 #endif
180         return 0;
181 }
182
183 static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
184 {
185         struct snd_pcm_runtime *runtime = substream->runtime;
186         struct sport_device *sport = runtime->private_data;
187         int ret = 0;
188
189         pr_debug("%s enter\n", __func__);
190         switch (cmd) {
191         case SNDRV_PCM_TRIGGER_START:
192                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
193 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
194                         bf5xx_mmap_copy(substream, runtime->period_size);
195                         sport->tx_delay_pos = 0;
196 #endif
197                         sport_tx_start(sport);
198                 } else
199                         sport_rx_start(sport);
200                 break;
201         case SNDRV_PCM_TRIGGER_STOP:
202         case SNDRV_PCM_TRIGGER_SUSPEND:
203         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
204                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
205 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
206                         sport->tx_pos = 0;
207 #endif
208                         sport_tx_stop(sport);
209                 } else {
210 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
211                         sport->rx_pos = 0;
212 #endif
213                         sport_rx_stop(sport);
214                 }
215                 break;
216         default:
217                 ret = -EINVAL;
218         }
219         return ret;
220 }
221
222 static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
223 {
224         struct snd_pcm_runtime *runtime = substream->runtime;
225         struct sport_device *sport = runtime->private_data;
226         unsigned int curr;
227
228 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
229         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
230                 curr = sport->tx_delay_pos;
231         else
232                 curr = sport->rx_pos;
233 #else
234
235         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
236                 curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame);
237         else
238                 curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame);
239
240 #endif
241         return curr;
242 }
243
244 static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
245 {
246         struct snd_pcm_runtime *runtime = substream->runtime;
247         int ret;
248
249         pr_debug("%s enter\n", __func__);
250         snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
251
252         ret = snd_pcm_hw_constraint_integer(runtime,
253                                             SNDRV_PCM_HW_PARAM_PERIODS);
254         if (ret < 0)
255                 goto out;
256
257         if (sport_handle != NULL)
258                 runtime->private_data = sport_handle;
259         else {
260                 pr_err("sport_handle is NULL\n");
261                 return -1;
262         }
263         return 0;
264
265  out:
266         return ret;
267 }
268
269 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
270 static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
271         struct vm_area_struct *vma)
272 {
273         struct snd_pcm_runtime *runtime = substream->runtime;
274         size_t size = vma->vm_end - vma->vm_start;
275         vma->vm_start = (unsigned long)runtime->dma_area;
276         vma->vm_end = vma->vm_start + size;
277         vma->vm_flags |=  VM_SHARED;
278         return 0 ;
279 }
280 #else
281 static  int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
282                     snd_pcm_uframes_t pos,
283                     void __user *buf, snd_pcm_uframes_t count)
284 {
285         struct snd_pcm_runtime *runtime = substream->runtime;
286         unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
287         pr_debug("%s copy pos:0x%lx count:0x%lx\n",
288                         substream->stream ? "Capture" : "Playback", pos, count);
289
290         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
291                 bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos,
292                         (__u16 *)buf, count, chan_mask);
293         else
294                 bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos,
295                         (__u16 *)buf, count);
296         return 0;
297 }
298 #endif
299
300 struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
301         .open           = bf5xx_pcm_open,
302         .ioctl          = snd_pcm_lib_ioctl,
303         .hw_params      = bf5xx_pcm_hw_params,
304         .hw_free        = bf5xx_pcm_hw_free,
305         .prepare        = bf5xx_pcm_prepare,
306         .trigger        = bf5xx_pcm_trigger,
307         .pointer        = bf5xx_pcm_pointer,
308 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
309         .mmap           = bf5xx_pcm_mmap,
310 #else
311         .copy           = bf5xx_pcm_copy,
312 #endif
313 };
314
315 static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
316 {
317         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
318         struct snd_dma_buffer *buf = &substream->dma_buffer;
319         size_t size = bf5xx_pcm_hardware.buffer_bytes_max
320                         * sizeof(struct ac97_frame) / 4;
321
322         buf->dev.type = SNDRV_DMA_TYPE_DEV;
323         buf->dev.dev = pcm->card->dev;
324         buf->private_data = NULL;
325         buf->area = dma_alloc_coherent(pcm->card->dev, size,
326                         &buf->addr, GFP_KERNEL);
327         if (!buf->area) {
328                 pr_err("Failed to allocate dma memory\n");
329                 pr_err("Please increase uncached DMA memory region\n");
330                 return -ENOMEM;
331         }
332         buf->bytes = size;
333
334         pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
335                         buf->area, buf->bytes);
336
337         if (stream == SNDRV_PCM_STREAM_PLAYBACK)
338                 sport_handle->tx_buf = buf->area;
339         else
340                 sport_handle->rx_buf = buf->area;
341
342 /*
343  * Need to allocate local buffer when enable
344  * MMAP for SPORT working in TMD mode (include AC97).
345  */
346 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
347         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
348                 if (!sport_handle->tx_dma_buf) {
349                         sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
350                                 size, &sport_handle->tx_dma_phy, GFP_KERNEL);
351                         if (!sport_handle->tx_dma_buf) {
352                                 pr_err("Failed to allocate memory for tx dma \
353                                         buf - Please increase uncached DMA \
354                                         memory region\n");
355                                 return -ENOMEM;
356                         } else
357                                 memset(sport_handle->tx_dma_buf, 0, size);
358                 } else
359                         memset(sport_handle->tx_dma_buf, 0, size);
360         } else {
361                 if (!sport_handle->rx_dma_buf) {
362                         sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
363                                 size, &sport_handle->rx_dma_phy, GFP_KERNEL);
364                         if (!sport_handle->rx_dma_buf) {
365                                 pr_err("Failed to allocate memory for rx dma \
366                                         buf - Please increase uncached DMA \
367                                         memory region\n");
368                                 return -ENOMEM;
369                         } else
370                                 memset(sport_handle->rx_dma_buf, 0, size);
371                 } else
372                         memset(sport_handle->rx_dma_buf, 0, size);
373         }
374 #endif
375         return 0;
376 }
377
378 static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
379 {
380         struct snd_pcm_substream *substream;
381         struct snd_dma_buffer *buf;
382         int stream;
383 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
384         size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
385                 sizeof(struct ac97_frame) / 4;
386 #endif
387         for (stream = 0; stream < 2; stream++) {
388                 substream = pcm->streams[stream].substream;
389                 if (!substream)
390                         continue;
391
392                 buf = &substream->dma_buffer;
393                 if (!buf->area)
394                         continue;
395                 dma_free_coherent(NULL, buf->bytes, buf->area, 0);
396                 buf->area = NULL;
397 #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
398         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
399                 if (sport_handle->tx_dma_buf)
400                         dma_free_coherent(NULL, size, \
401                                 sport_handle->tx_dma_buf, 0);
402                 sport_handle->tx_dma_buf = NULL;
403         } else {
404
405                 if (sport_handle->rx_dma_buf)
406                         dma_free_coherent(NULL, size, \
407                                 sport_handle->rx_dma_buf, 0);
408                 sport_handle->rx_dma_buf = NULL;
409         }
410 #endif
411         }
412         if (sport_handle)
413                 sport_done(sport_handle);
414 }
415
416 static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
417
418 int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
419         struct snd_pcm *pcm)
420 {
421         int ret = 0;
422
423         pr_debug("%s enter\n", __func__);
424         if (!card->dev->dma_mask)
425                 card->dev->dma_mask = &bf5xx_pcm_dmamask;
426         if (!card->dev->coherent_dma_mask)
427                 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
428
429         if (dai->playback.channels_min) {
430                 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
431                         SNDRV_PCM_STREAM_PLAYBACK);
432                 if (ret)
433                         goto out;
434         }
435
436         if (dai->capture.channels_min) {
437                 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
438                         SNDRV_PCM_STREAM_CAPTURE);
439                 if (ret)
440                         goto out;
441         }
442  out:
443         return ret;
444 }
445
446 struct snd_soc_platform bf5xx_ac97_soc_platform = {
447         .name           = "bf5xx-audio",
448         .pcm_ops        = &bf5xx_pcm_ac97_ops,
449         .pcm_new        = bf5xx_pcm_ac97_new,
450         .pcm_free       = bf5xx_pcm_free_dma_buffers,
451 };
452 EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
453
454 static int __init bfin_ac97_init(void)
455 {
456         return snd_soc_register_platform(&bf5xx_ac97_soc_platform);
457 }
458 module_init(bfin_ac97_init);
459
460 static void __exit bfin_ac97_exit(void)
461 {
462         snd_soc_unregister_platform(&bf5xx_ac97_soc_platform);
463 }
464 module_exit(bfin_ac97_exit);
465
466 MODULE_AUTHOR("Cliff Cai");
467 MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
468 MODULE_LICENSE("GPL");