~ubuntu-branches/ubuntu/maverick/linux-backports-modules-2.6.32/maverick

« back to all changes in this revision

Viewing changes to updates/alsa-driver/alsa-kernel/soc/blackfin/bf5xx-tdm-pcm.c

  • Committer: Bazaar Package Importer
  • Author(s): Andy Whitcroft, Andy Whitcroft
  • Date: 2010-02-04 23:15:51 UTC
  • Revision ID: james.westby@ubuntu.com-20100204231551-vjz5pkvxclukjxm1
Tags: 2.6.32-12.1
[ Andy Whitcroft ]

* initial LBM for lucid
* drop generated files
* printchanges -- rebase tree does not have stable tags use changelog
* printenv -- add revisions to printenv output
* formally rename compat-wireless to linux-backports-modules-wireless
* Update to compat-wireless-2.6.33-rc5
* update nouveau to mainline 2.6.33-rc4
* add new LBM package for nouveau
* nouveau -- fix major numbers and proc entry names
* fix up firmware installs for -wireless
* clean up UPDATE-NOVEAU
* update Nouveau to v2.6.33-rc6

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/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-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_pcm_runtime *runtime = substream->runtime;
 
158
        int ret = 0;
 
159
 
 
160
        snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
 
161
 
 
162
        ret = snd_pcm_hw_constraint_integer(runtime,
 
163
                SNDRV_PCM_HW_PARAM_PERIODS);
 
164
        if (ret < 0)
 
165
                goto out;
 
166
 
 
167
        if (sport_handle != NULL)
 
168
                runtime->private_data = sport_handle;
 
169
        else {
 
170
                pr_err("sport_handle is NULL\n");
 
171
                ret = -ENODEV;
 
172
        }
 
173
out:
 
174
        return ret;
 
175
}
 
176
 
 
177
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
 
178
        snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
 
179
{
 
180
        struct snd_pcm_runtime *runtime = substream->runtime;
 
181
        struct sport_device *sport = runtime->private_data;
 
182
        struct bf5xx_tdm_port *tdm_port = sport->private_data;
 
183
        unsigned int *src;
 
184
        unsigned int *dst;
 
185
        int i;
 
186
 
 
187
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
188
                src = buf;
 
189
                dst = (unsigned int *)substream->runtime->dma_area;
 
190
 
 
191
                dst += pos * 8;
 
192
                while (count--) {
 
193
                        for (i = 0; i < substream->runtime->channels; i++)
 
194
                                *(dst + tdm_port->tx_map[i]) = *src++;
 
195
                        dst += 8;
 
196
                }
 
197
        } else {
 
198
                src = (unsigned int *)substream->runtime->dma_area;
 
199
                dst = buf;
 
200
 
 
201
                src += pos * 8;
 
202
                while (count--) {
 
203
                        for (i = 0; i < substream->runtime->channels; i++)
 
204
                                *dst++ = *(src + tdm_port->rx_map[i]);
 
205
                        src += 8;
 
206
                }
 
207
        }
 
208
 
 
209
        return 0;
 
210
}
 
211
 
 
212
static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
 
213
        int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
 
214
{
 
215
        unsigned char *buf = substream->runtime->dma_area;
 
216
        buf += pos * 8 * 4;
 
217
        memset(buf, '\0', count * 8 * 4);
 
218
 
 
219
        return 0;
 
220
}
 
221
 
 
222
 
 
223
struct snd_pcm_ops bf5xx_pcm_tdm_ops = {
 
224
        .open           = bf5xx_pcm_open,
 
225
        .ioctl          = snd_pcm_lib_ioctl,
 
226
        .hw_params      = bf5xx_pcm_hw_params,
 
227
        .hw_free        = bf5xx_pcm_hw_free,
 
228
        .prepare        = bf5xx_pcm_prepare,
 
229
        .trigger        = bf5xx_pcm_trigger,
 
230
        .pointer        = bf5xx_pcm_pointer,
 
231
        .copy           = bf5xx_pcm_copy,
 
232
        .silence        = bf5xx_pcm_silence,
 
233
};
 
234
 
 
235
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
236
{
 
237
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 
238
        struct snd_dma_buffer *buf = &substream->dma_buffer;
 
239
        size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
 
240
 
 
241
        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 
242
        buf->dev.dev = pcm->card->dev;
 
243
        buf->private_data = NULL;
 
244
        buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
 
245
                &buf->addr, GFP_KERNEL);
 
246
        if (!buf->area) {
 
247
                pr_err("Failed to allocate dma memory \
 
248
                        Please increase uncached DMA memory region\n");
 
249
                return -ENOMEM;
 
250
        }
 
251
        buf->bytes = size;
 
252
 
 
253
        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 
254
                sport_handle->tx_buf = buf->area;
 
255
        else
 
256
                sport_handle->rx_buf = buf->area;
 
257
 
 
258
        return 0;
 
259
}
 
260
 
 
261
static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
262
{
 
263
        struct snd_pcm_substream *substream;
 
264
        struct snd_dma_buffer *buf;
 
265
        int stream;
 
266
 
 
267
        for (stream = 0; stream < 2; stream++) {
 
268
                substream = pcm->streams[stream].substream;
 
269
                if (!substream)
 
270
                        continue;
 
271
 
 
272
                buf = &substream->dma_buffer;
 
273
                if (!buf->area)
 
274
                        continue;
 
275
                dma_free_coherent(NULL, buf->bytes, buf->area, 0);
 
276
                buf->area = NULL;
 
277
        }
 
278
        if (sport_handle)
 
279
                sport_done(sport_handle);
 
280
}
 
281
 
 
282
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
 
283
 
 
284
static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
 
285
        struct snd_pcm *pcm)
 
286
{
 
287
        int ret = 0;
 
288
 
 
289
        if (!card->dev->dma_mask)
 
290
                card->dev->dma_mask = &bf5xx_pcm_dmamask;
 
291
        if (!card->dev->coherent_dma_mask)
 
292
                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 
293
 
 
294
        if (dai->playback.channels_min) {
 
295
                ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
 
296
                        SNDRV_PCM_STREAM_PLAYBACK);
 
297
                if (ret)
 
298
                        goto out;
 
299
        }
 
300
 
 
301
        if (dai->capture.channels_min) {
 
302
                ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
 
303
                        SNDRV_PCM_STREAM_CAPTURE);
 
304
                if (ret)
 
305
                        goto out;
 
306
        }
 
307
out:
 
308
        return ret;
 
309
}
 
310
 
 
311
struct snd_soc_platform bf5xx_tdm_soc_platform = {
 
312
        .name           = "bf5xx-audio",
 
313
        .pcm_ops        = &bf5xx_pcm_tdm_ops,
 
314
        .pcm_new        = bf5xx_pcm_tdm_new,
 
315
        .pcm_free       = bf5xx_pcm_free_dma_buffers,
 
316
};
 
317
EXPORT_SYMBOL_GPL(bf5xx_tdm_soc_platform);
 
318
 
 
319
static int __init bfin_pcm_tdm_init(void)
 
320
{
 
321
        return snd_soc_register_platform(&bf5xx_tdm_soc_platform);
 
322
}
 
323
module_init(bfin_pcm_tdm_init);
 
324
 
 
325
static void __exit bfin_pcm_tdm_exit(void)
 
326
{
 
327
        snd_soc_unregister_platform(&bf5xx_tdm_soc_platform);
 
328
}
 
329
module_exit(bfin_pcm_tdm_exit);
 
330
 
 
331
MODULE_AUTHOR("Barry Song");
 
332
MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
 
333
MODULE_LICENSE("GPL");