~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to sound/soc/blackfin/bf5xx-tdm-pcm.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * File:         sound/soc/blackfin/bf5xx-tdm-pcm.c
 
3
 * Author:       Barry Song <Barry.Song@analog.com>
 
4
 *
 
5
 * Created:      Tue June 06 2009
 
6
 * Description:  DMA driver for tdm codec
 
7
 *
 
8
 * Modified:
 
9
 *               Copyright 2009 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/dma-mapping.h>
 
33
#include <linux/gfp.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-tdm-pcm.h"
 
43
#include "bf5xx-tdm.h"
 
44
#include "bf5xx-sport.h"
 
45
 
 
46
#define PCM_BUFFER_MAX  0x8000
 
47
#define FRAGMENT_SIZE_MIN  (4*1024)
 
48
#define FRAGMENTS_MIN  2
 
49
#define FRAGMENTS_MAX  32
 
50
 
 
51
static void bf5xx_dma_irq(void *data)
 
52
{
 
53
        struct snd_pcm_substream *pcm = data;
 
54
        snd_pcm_period_elapsed(pcm);
 
55
}
 
56
 
 
57
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
 
58
        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
 
59
                SNDRV_PCM_INFO_RESUME),
 
60
        .formats =          SNDRV_PCM_FMTBIT_S32_LE,
 
61
        .rates =            SNDRV_PCM_RATE_48000,
 
62
        .channels_min =     2,
 
63
        .channels_max =     8,
 
64
        .buffer_bytes_max = PCM_BUFFER_MAX,
 
65
        .period_bytes_min = FRAGMENT_SIZE_MIN,
 
66
        .period_bytes_max = PCM_BUFFER_MAX/2,
 
67
        .periods_min =      FRAGMENTS_MIN,
 
68
        .periods_max =      FRAGMENTS_MAX,
 
69
};
 
70
 
 
71
static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
 
72
        struct snd_pcm_hw_params *params)
 
73
{
 
74
        size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
 
75
        snd_pcm_lib_malloc_pages(substream, size * 4);
 
76
 
 
77
        return 0;
 
78
}
 
79
 
 
80
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
 
81
{
 
82
        snd_pcm_lib_free_pages(substream);
 
83
 
 
84
        return 0;
 
85
}
 
86
 
 
87
static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
 
88
{
 
89
        struct snd_pcm_runtime *runtime = substream->runtime;
 
90
        struct sport_device *sport = runtime->private_data;
 
91
        int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);
 
92
 
 
93
        fragsize_bytes /= runtime->channels;
 
94
        /* inflate the fragsize to match the dma width of SPORT */
 
95
        fragsize_bytes *= 8;
 
96
 
 
97
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
98
                sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
 
99
                sport_config_tx_dma(sport, runtime->dma_area,
 
100
                        runtime->periods, fragsize_bytes);
 
101
        } else {
 
102
                sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
 
103
                sport_config_rx_dma(sport, runtime->dma_area,
 
104
                        runtime->periods, fragsize_bytes);
 
105
        }
 
106
 
 
107
        return 0;
 
108
}
 
109
 
 
110
static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 
111
{
 
112
        struct snd_pcm_runtime *runtime = substream->runtime;
 
113
        struct sport_device *sport = runtime->private_data;
 
114
        int ret = 0;
 
115
 
 
116
        switch (cmd) {
 
117
        case SNDRV_PCM_TRIGGER_START:
 
118
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 
119
                        sport_tx_start(sport);
 
120
                else
 
121
                        sport_rx_start(sport);
 
122
                break;
 
123
        case SNDRV_PCM_TRIGGER_STOP:
 
124
        case SNDRV_PCM_TRIGGER_SUSPEND:
 
125
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 
126
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 
127
                        sport_tx_stop(sport);
 
128
                else
 
129
                        sport_rx_stop(sport);
 
130
                break;
 
131
        default:
 
132
                ret = -EINVAL;
 
133
        }
 
134
 
 
135
        return ret;
 
136
}
 
137
 
 
138
static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
 
139
{
 
140
        struct snd_pcm_runtime *runtime = substream->runtime;
 
141
        struct sport_device *sport = runtime->private_data;
 
142
        unsigned int diff;
 
143
        snd_pcm_uframes_t frames;
 
144
 
 
145
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
146
                diff = sport_curr_offset_tx(sport);
 
147
                frames = diff / (8*4); /* 32 bytes per frame */
 
148
        } else {
 
149
                diff = sport_curr_offset_rx(sport);
 
150
                frames = diff / (8*4);
 
151
        }
 
152
        return frames;
 
153
}
 
154
 
 
155
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
 
156
{
 
157
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 
158
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 
159
        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
 
160
        struct snd_pcm_runtime *runtime = substream->runtime;
 
161
        struct snd_dma_buffer *buf = &substream->dma_buffer;
 
162
 
 
163
        int ret = 0;
 
164
 
 
165
        snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
 
166
 
 
167
        ret = snd_pcm_hw_constraint_integer(runtime,
 
168
                SNDRV_PCM_HW_PARAM_PERIODS);
 
169
        if (ret < 0)
 
170
                goto out;
 
171
 
 
172
        if (sport_handle != NULL) {
 
173
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 
174
                        sport_handle->tx_buf = buf->area;
 
175
                else
 
176
                        sport_handle->rx_buf = buf->area;
 
177
 
 
178
                runtime->private_data = sport_handle;
 
179
        } else {
 
180
                pr_err("sport_handle is NULL\n");
 
181
                ret = -ENODEV;
 
182
        }
 
183
out:
 
184
        return ret;
 
185
}
 
186
 
 
187
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
 
188
        snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
 
189
{
 
190
        struct snd_pcm_runtime *runtime = substream->runtime;
 
191
        struct sport_device *sport = runtime->private_data;
 
192
        struct bf5xx_tdm_port *tdm_port = sport->private_data;
 
193
        unsigned int *src;
 
194
        unsigned int *dst;
 
195
        int i;
 
196
 
 
197
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
198
                src = buf;
 
199
                dst = (unsigned int *)substream->runtime->dma_area;
 
200
 
 
201
                dst += pos * 8;
 
202
                while (count--) {
 
203
                        for (i = 0; i < substream->runtime->channels; i++)
 
204
                                *(dst + tdm_port->tx_map[i]) = *src++;
 
205
                        dst += 8;
 
206
                }
 
207
        } else {
 
208
                src = (unsigned int *)substream->runtime->dma_area;
 
209
                dst = buf;
 
210
 
 
211
                src += pos * 8;
 
212
                while (count--) {
 
213
                        for (i = 0; i < substream->runtime->channels; i++)
 
214
                                *dst++ = *(src + tdm_port->rx_map[i]);
 
215
                        src += 8;
 
216
                }
 
217
        }
 
218
 
 
219
        return 0;
 
220
}
 
221
 
 
222
static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
 
223
        int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
 
224
{
 
225
        unsigned char *buf = substream->runtime->dma_area;
 
226
        buf += pos * 8 * 4;
 
227
        memset(buf, '\0', count * 8 * 4);
 
228
 
 
229
        return 0;
 
230
}
 
231
 
 
232
 
 
233
struct snd_pcm_ops bf5xx_pcm_tdm_ops = {
 
234
        .open           = bf5xx_pcm_open,
 
235
        .ioctl          = snd_pcm_lib_ioctl,
 
236
        .hw_params      = bf5xx_pcm_hw_params,
 
237
        .hw_free        = bf5xx_pcm_hw_free,
 
238
        .prepare        = bf5xx_pcm_prepare,
 
239
        .trigger        = bf5xx_pcm_trigger,
 
240
        .pointer        = bf5xx_pcm_pointer,
 
241
        .copy           = bf5xx_pcm_copy,
 
242
        .silence        = bf5xx_pcm_silence,
 
243
};
 
244
 
 
245
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
246
{
 
247
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 
248
        struct snd_dma_buffer *buf = &substream->dma_buffer;
 
249
        size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
 
250
 
 
251
        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 
252
        buf->dev.dev = pcm->card->dev;
 
253
        buf->private_data = NULL;
 
254
        buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
 
255
                &buf->addr, GFP_KERNEL);
 
256
        if (!buf->area) {
 
257
                pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
 
258
                return -ENOMEM;
 
259
        }
 
260
        buf->bytes = size;
 
261
 
 
262
        return 0;
 
263
}
 
264
 
 
265
static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
266
{
 
267
        struct snd_pcm_substream *substream;
 
268
        struct snd_dma_buffer *buf;
 
269
        int stream;
 
270
 
 
271
        for (stream = 0; stream < 2; stream++) {
 
272
                substream = pcm->streams[stream].substream;
 
273
                if (!substream)
 
274
                        continue;
 
275
 
 
276
                buf = &substream->dma_buffer;
 
277
                if (!buf->area)
 
278
                        continue;
 
279
                dma_free_coherent(NULL, buf->bytes, buf->area, 0);
 
280
                buf->area = NULL;
 
281
        }
 
282
}
 
283
 
 
284
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
 
285
 
 
286
static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd)
 
287
{
 
288
        struct snd_card *card = rtd->card->snd_card;
 
289
        struct snd_soc_dai *dai = rtd->cpu_dai;
 
290
        struct snd_pcm *pcm = rtd->pcm;
 
291
        int ret = 0;
 
292
 
 
293
        if (!card->dev->dma_mask)
 
294
                card->dev->dma_mask = &bf5xx_pcm_dmamask;
 
295
        if (!card->dev->coherent_dma_mask)
 
296
                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 
297
 
 
298
        if (dai->driver->playback.channels_min) {
 
299
                ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
 
300
                        SNDRV_PCM_STREAM_PLAYBACK);
 
301
                if (ret)
 
302
                        goto out;
 
303
        }
 
304
 
 
305
        if (dai->driver->capture.channels_min) {
 
306
                ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
 
307
                        SNDRV_PCM_STREAM_CAPTURE);
 
308
                if (ret)
 
309
                        goto out;
 
310
        }
 
311
out:
 
312
        return ret;
 
313
}
 
314
 
 
315
static struct snd_soc_platform_driver bf5xx_tdm_soc_platform = {
 
316
        .ops        = &bf5xx_pcm_tdm_ops,
 
317
        .pcm_new        = bf5xx_pcm_tdm_new,
 
318
        .pcm_free       = bf5xx_pcm_free_dma_buffers,
 
319
};
 
320
 
 
321
static int __devinit bf5xx_soc_platform_probe(struct platform_device *pdev)
 
322
{
 
323
        return snd_soc_register_platform(&pdev->dev, &bf5xx_tdm_soc_platform);
 
324
}
 
325
 
 
326
static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
 
327
{
 
328
        snd_soc_unregister_platform(&pdev->dev);
 
329
        return 0;
 
330
}
 
331
 
 
332
static struct platform_driver bfin_tdm_driver = {
 
333
        .driver = {
 
334
                        .name = "bfin-tdm-pcm-audio",
 
335
                        .owner = THIS_MODULE,
 
336
        },
 
337
 
 
338
        .probe = bf5xx_soc_platform_probe,
 
339
        .remove = __devexit_p(bf5xx_soc_platform_remove),
 
340
};
 
341
 
 
342
static int __init snd_bfin_tdm_init(void)
 
343
{
 
344
        return platform_driver_register(&bfin_tdm_driver);
 
345
}
 
346
module_init(snd_bfin_tdm_init);
 
347
 
 
348
static void __exit snd_bfin_tdm_exit(void)
 
349
{
 
350
        platform_driver_unregister(&bfin_tdm_driver);
 
351
}
 
352
module_exit(snd_bfin_tdm_exit);
 
353
 
 
354
MODULE_AUTHOR("Barry Song");
 
355
MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
 
356
MODULE_LICENSE("GPL");