OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [tags/] [linux-2.6/] [linux-2.6.24_or32_unified_v2.3/] [sound/] [soc/] [sh/] [dma-sh7760.c] - Blame information for rev 8

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 xianfeng
/*
2
 * SH7760 ("camelot") DMABRG audio DMA unit support
3
 *
4
 * Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
5
 *  licensed under the terms outlined in the file COPYING at the root
6
 *  of the linux kernel sources.
7
 *
8
 * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
9
 * trigger an interrupt when one half of the programmed transfer size
10
 * has been xmitted.
11
 *
12
 * FIXME: little-endian only for now
13
 */
14
 
15
#include <linux/module.h>
16
#include <linux/init.h>
17
#include <linux/platform_device.h>
18
#include <linux/dma-mapping.h>
19
#include <sound/driver.h>
20
#include <sound/core.h>
21
#include <sound/pcm.h>
22
#include <sound/pcm_params.h>
23
#include <sound/soc.h>
24
#include <asm/dmabrg.h>
25
 
26
 
27
/* registers and bits */
28
#define BRGATXSAR       0x00
29
#define BRGARXDAR       0x04
30
#define BRGATXTCR       0x08
31
#define BRGARXTCR       0x0C
32
#define BRGACR          0x10
33
#define BRGATXTCNT      0x14
34
#define BRGARXTCNT      0x18
35
 
36
#define ACR_RAR         (1 << 18)
37
#define ACR_RDS         (1 << 17)
38
#define ACR_RDE         (1 << 16)
39
#define ACR_TAR         (1 << 2)
40
#define ACR_TDS         (1 << 1)
41
#define ACR_TDE         (1 << 0)
42
 
43
/* receiver/transmitter data alignment */
44
#define ACR_RAM_NONE    (0 << 24)
45
#define ACR_RAM_4BYTE   (1 << 24)
46
#define ACR_RAM_2WORD   (2 << 24)
47
#define ACR_TAM_NONE    (0 << 8)
48
#define ACR_TAM_4BYTE   (1 << 8)
49
#define ACR_TAM_2WORD   (2 << 8)
50
 
51
 
52
struct camelot_pcm {
53
        unsigned long mmio;  /* DMABRG audio channel control reg MMIO */
54
        unsigned int txid;    /* ID of first DMABRG IRQ for this unit */
55
 
56
        struct snd_pcm_substream *tx_ss;
57
        unsigned long tx_period_size;
58
        unsigned int  tx_period;
59
 
60
        struct snd_pcm_substream *rx_ss;
61
        unsigned long rx_period_size;
62
        unsigned int  rx_period;
63
 
64
} cam_pcm_data[2] = {
65
        {
66
                .mmio   =       0xFE3C0040,
67
                .txid   =       DMABRGIRQ_A0TXF,
68
        },
69
        {
70
                .mmio   =       0xFE3C0060,
71
                .txid   =       DMABRGIRQ_A1TXF,
72
        },
73
};
74
 
75
#define BRGREG(x)       (*(unsigned long *)(cam->mmio + (x)))
76
 
77
/*
78
 * set a minimum of 16kb per period, to avoid interrupt-"storm" and
79
 * resulting skipping. In general, the bigger the minimum size, the
80
 * better for overall system performance. (The SH7760 is a puny CPU
81
 * with a slow SDRAM interface and poor internal bus bandwidth,
82
 * *especially* when the LCDC is active).  The minimum for the DMAC
83
 * is 8 bytes; 16kbytes are enough to get skip-free playback of a
84
 * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
85
 * reasonable responsiveness in MPlayer.
86
 */
87
#define DMABRG_PERIOD_MIN               16 * 1024
88
#define DMABRG_PERIOD_MAX               0x03fffffc
89
#define DMABRG_PREALLOC_BUFFER          32 * 1024
90
#define DMABRG_PREALLOC_BUFFER_MAX      32 * 1024
91
 
92
/* support everything the SSI supports */
93
#define DMABRG_RATES    \
94
        SNDRV_PCM_RATE_8000_192000
95
 
96
#define DMABRG_FMTS     \
97
        (SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |  \
98
         SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |  \
99
         SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |  \
100
         SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |  \
101
         SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
102
 
103
static struct snd_pcm_hardware camelot_pcm_hardware = {
104
        .info = (SNDRV_PCM_INFO_MMAP |
105
                SNDRV_PCM_INFO_INTERLEAVED |
106
                SNDRV_PCM_INFO_BLOCK_TRANSFER |
107
                SNDRV_PCM_INFO_MMAP_VALID),
108
        .formats =      DMABRG_FMTS,
109
        .rates =        DMABRG_RATES,
110
        .rate_min =             8000,
111
        .rate_max =             192000,
112
        .channels_min =         2,
113
        .channels_max =         8,              /* max of the SSI */
114
        .buffer_bytes_max =     DMABRG_PERIOD_MAX,
115
        .period_bytes_min =     DMABRG_PERIOD_MIN,
116
        .period_bytes_max =     DMABRG_PERIOD_MAX / 2,
117
        .periods_min =          2,
118
        .periods_max =          2,
119
        .fifo_size =            128,
120
};
121
 
122
static void camelot_txdma(void *data)
123
{
124
        struct camelot_pcm *cam = data;
125
        cam->tx_period ^= 1;
126
        snd_pcm_period_elapsed(cam->tx_ss);
127
}
128
 
129
static void camelot_rxdma(void *data)
130
{
131
        struct camelot_pcm *cam = data;
132
        cam->rx_period ^= 1;
133
        snd_pcm_period_elapsed(cam->rx_ss);
134
}
135
 
136
static int camelot_pcm_open(struct snd_pcm_substream *substream)
137
{
138
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
139
        struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
140
        int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
141
        int ret, dmairq;
142
 
143
        snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
144
 
145
        /* DMABRG buffer half/full events */
146
        dmairq = (recv) ? cam->txid + 2 : cam->txid;
147
        if (recv) {
148
                cam->rx_ss = substream;
149
                ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
150
                if (unlikely(ret)) {
151
                        pr_debug("audio unit %d irqs already taken!\n",
152
                             rtd->dai->cpu_dai->id);
153
                        return -EBUSY;
154
                }
155
                (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
156
        } else {
157
                cam->tx_ss = substream;
158
                ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
159
                if (unlikely(ret)) {
160
                        pr_debug("audio unit %d irqs already taken!\n",
161
                             rtd->dai->cpu_dai->id);
162
                        return -EBUSY;
163
                }
164
                (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
165
        }
166
        return 0;
167
}
168
 
169
static int camelot_pcm_close(struct snd_pcm_substream *substream)
170
{
171
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
172
        struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
173
        int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
174
        int dmairq;
175
 
176
        dmairq = (recv) ? cam->txid + 2 : cam->txid;
177
 
178
        if (recv)
179
                cam->rx_ss = NULL;
180
        else
181
                cam->tx_ss = NULL;
182
 
183
        dmabrg_free_irq(dmairq + 1);
184
        dmabrg_free_irq(dmairq);
185
 
186
        return 0;
187
}
188
 
189
static int camelot_hw_params(struct snd_pcm_substream *substream,
190
                             struct snd_pcm_hw_params *hw_params)
191
{
192
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
193
        struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
194
        int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
195
        int ret;
196
 
197
        ret = snd_pcm_lib_malloc_pages(substream,
198
                                       params_buffer_bytes(hw_params));
199
        if (ret < 0)
200
                return ret;
201
 
202
        if (recv) {
203
                cam->rx_period_size = params_period_bytes(hw_params);
204
                cam->rx_period = 0;
205
        } else {
206
                cam->tx_period_size = params_period_bytes(hw_params);
207
                cam->tx_period = 0;
208
        }
209
        return 0;
210
}
211
 
212
static int camelot_hw_free(struct snd_pcm_substream *substream)
213
{
214
        return snd_pcm_lib_free_pages(substream);
215
}
216
 
217
static int camelot_prepare(struct snd_pcm_substream *substream)
218
{
219
        struct snd_pcm_runtime *runtime = substream->runtime;
220
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
221
        struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
222
 
223
        pr_debug("PCM data: addr 0x%08ulx len %d\n",
224
                 (u32)runtime->dma_addr, runtime->dma_bytes);
225
 
226
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
227
                BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
228
                BRGREG(BRGATXTCR) = runtime->dma_bytes;
229
        } else {
230
                BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
231
                BRGREG(BRGARXTCR) = runtime->dma_bytes;
232
        }
233
 
234
        return 0;
235
}
236
 
237
static inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
238
{
239
        unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
240
        /* start DMABRG engine: XFER start, auto-addr-reload */
241
        BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
242
}
243
 
244
static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
245
{
246
        unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
247
        /* forcibly terminate data transmission */
248
        BRGREG(BRGACR) = acr | ACR_TDS;
249
}
250
 
251
static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
252
{
253
        unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
254
        /* start DMABRG engine: recv start, auto-reload */
255
        BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
256
}
257
 
258
static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
259
{
260
        unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
261
        /* forcibly terminate data receiver */
262
        BRGREG(BRGACR) = acr | ACR_RDS;
263
}
264
 
265
static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
266
{
267
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
268
        struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
269
        int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
270
 
271
        switch (cmd) {
272
        case SNDRV_PCM_TRIGGER_START:
273
                if (recv)
274
                        dmabrg_rec_dma_start(cam);
275
                else
276
                        dmabrg_play_dma_start(cam);
277
                break;
278
        case SNDRV_PCM_TRIGGER_STOP:
279
                if (recv)
280
                        dmabrg_rec_dma_stop(cam);
281
                else
282
                        dmabrg_play_dma_stop(cam);
283
                break;
284
        default:
285
                return -EINVAL;
286
        }
287
 
288
        return 0;
289
}
290
 
291
static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
292
{
293
        struct snd_pcm_runtime *runtime = substream->runtime;
294
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
295
        struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
296
        int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
297
        unsigned long pos;
298
 
299
        /* cannot use the DMABRG pointer register: under load, by the
300
         * time ALSA comes around to read the register, it is already
301
         * far ahead (or worse, already done with the fragment) of the
302
         * position at the time the IRQ was triggered, which results in
303
         * fast-playback sound in my test application (ScummVM)
304
         */
305
        if (recv)
306
                pos = cam->rx_period ? cam->rx_period_size : 0;
307
        else
308
                pos = cam->tx_period ? cam->tx_period_size : 0;
309
 
310
        return bytes_to_frames(runtime, pos);
311
}
312
 
313
static struct snd_pcm_ops camelot_pcm_ops = {
314
        .open           = camelot_pcm_open,
315
        .close          = camelot_pcm_close,
316
        .ioctl          = snd_pcm_lib_ioctl,
317
        .hw_params      = camelot_hw_params,
318
        .hw_free        = camelot_hw_free,
319
        .prepare        = camelot_prepare,
320
        .trigger        = camelot_trigger,
321
        .pointer        = camelot_pos,
322
};
323
 
324
static void camelot_pcm_free(struct snd_pcm *pcm)
325
{
326
        snd_pcm_lib_preallocate_free_for_all(pcm);
327
}
328
 
329
static int camelot_pcm_new(struct snd_card *card,
330
                           struct snd_soc_codec_dai *dai,
331
                           struct snd_pcm *pcm)
332
{
333
        /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
334
         * in MMAP mode (i.e. aplay -M)
335
         */
336
        snd_pcm_lib_preallocate_pages_for_all(pcm,
337
                SNDRV_DMA_TYPE_CONTINUOUS,
338
                snd_dma_continuous_data(GFP_KERNEL),
339
                DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX);
340
 
341
        return 0;
342
}
343
 
344
struct snd_soc_platform sh7760_soc_platform = {
345
        .name           = "sh7760-pcm",
346
        .pcm_ops        = &camelot_pcm_ops,
347
        .pcm_new        = camelot_pcm_new,
348
        .pcm_free       = camelot_pcm_free,
349
};
350
EXPORT_SYMBOL_GPL(sh7760_soc_platform);
351
 
352
MODULE_LICENSE("GPL");
353
MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
354
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.