summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c')
-rw-r--r--firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
new file mode 100644
index 0000000000..237bf264f5
--- /dev/null
+++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
@@ -0,0 +1,292 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdlib.h>
22#include "system.h"
23#include "kernel.h"
24#include "logf.h"
25#include "audio.h"
26#include "sound.h"
27#include "file.h"
28
29/* PCM interrupt routine lockout */
30static struct
31{
32 int locked;
33 unsigned long state;
34} dma_play_lock =
35{
36 .locked = 0,
37 .state = 0,
38};
39
40#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
41
42/* Setup for the DMA controller */
43#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
44
45#ifdef HAVE_UDA1341
46/* for PCLK = 50 MHz, frame size = 32 */
47/* [prescaler, master clock rate] */
48static const unsigned char pcm_freq_parms[HW_NUM_FREQ][2] =
49{
50 [HW_FREQ_64] = { 2, IISMOD_MASTER_CLOCK_256FS },
51 [HW_FREQ_44] = { 3, IISMOD_MASTER_CLOCK_384FS },
52 [HW_FREQ_22] = { 8, IISMOD_MASTER_CLOCK_256FS },
53 [HW_FREQ_11] = { 17, IISMOD_MASTER_CLOCK_256FS },
54};
55#endif
56
57/* DMA count has hit zero - no more data */
58/* Get more data from the callback and top off the FIFO */
59void fiq_handler(void) __attribute__((interrupt ("FIQ")));
60
61/* Mask the DMA interrupt */
62void pcm_play_lock(void)
63{
64 if (++dma_play_lock.locked == 1)
65 s3c_regset32(&INTMSK, DMA2_MASK);
66}
67
68/* Unmask the DMA interrupt if enabled */
69void pcm_play_unlock(void)
70{
71 if (--dma_play_lock.locked == 0)
72 s3c_regclr32(&INTMSK, dma_play_lock.state);
73}
74
75void pcm_play_dma_init(void)
76{
77 /* There seem to be problems when changing the IIS interface configuration
78 * when a clock is not present.
79 */
80 s3c_regset32(&CLKCON, 1<<17);
81
82#ifdef HAVE_UDA1341
83 /* master, transmit mode, 16 bit samples, BCLK 32fs, PCLK */
84 IISMOD = IISMOD_MASTER_CLOCK_PCLK | IISMOD_MASTER_MODE | IISMOD_TRANSMIT_MODE
85 | IISMOD_16_BIT | IISMOD_MASTER_CLOCK_256FS | IISMOD_BIT_CLOCK_32FS;
86
87 /* TX idle, enable prescaler */
88 IISCON |= IISCON_TX_IDLE | IISCON_IIS_PRESCALER_ENABLE;
89#else
90 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
91 BCLK 32fs */
92 IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0);
93
94 /* RX,TX off,on */
95 IISCON |= (1<<3) | (1<<2);
96#endif
97
98 s3c_regclr32(&CLKCON, 1<<17);
99
100 audiohw_init();
101
102 /* init GPIO */
103#ifdef GIGABEAT_F
104/* GPCCON = (GPCCON & ~(3<<14)) | (1<<14); */
105 S3C244_GPIO_CONFIG (GPCCON, 7, GPIO_OUTPUT);
106 GPCDAT |= (1<<7);
107#endif
108
109 /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
110 GPECON = (GPECON & ~0x3ff) | 0x2aa;
111
112 /* Do not service DMA requests, yet */
113
114 /* clear any pending int and mask it */
115 s3c_regset32(&INTMSK, DMA2_MASK);
116 SRCPND = DMA2_MASK;
117
118 /* connect to FIQ */
119 s3c_regset32(&INTMOD, DMA2_MASK);
120}
121
122void pcm_postinit(void)
123{
124 audiohw_postinit();
125}
126
127void pcm_dma_apply_settings(void)
128{
129#ifdef HAVE_UDA1341
130 /* set prescaler and master clock rate according to freq */
131 IISPSR = (pcm_freq_parms [pcm_fsel][0] * IISPSR_PRESCALER_A) | pcm_freq_parms [pcm_fsel][0];
132 IISMOD |= ~IISMOD_MASTER_CLOCK_384FS | pcm_freq_parms [pcm_fsel][1] ;
133#endif
134
135 audiohw_set_frequency(pcm_fsel);
136}
137
138/* Connect the DMA and start filling the FIFO */
139static void play_start_pcm(void)
140{
141 /* clear pending DMA interrupt */
142 SRCPND = DMA2_MASK;
143
144 /* Flush any pending writes */
145 clean_dcache_range((char*)DISRC2-0x30000000, (DCON2 & 0xFFFFF) * 2);
146
147 /* unmask DMA interrupt when unlocking */
148 dma_play_lock.state = DMA2_MASK;
149
150 /* turn on the request */
151 IISCON |= (1<<5);
152
153 /* Activate the channel */
154 DMASKTRIG2 = 0x2;
155
156 /* turn off the idle */
157 IISCON &= ~(1<<3);
158
159 /* start the IIS */
160 IISCON |= (1<<0);
161}
162
163/* Disconnect the DMA and wait for the FIFO to clear */
164static void play_stop_pcm(void)
165{
166 /* Mask DMA interrupt */
167 s3c_regset32(&INTMSK, DMA2_MASK);
168
169 /* De-Activate the DMA channel */
170 DMASKTRIG2 = 0x4;
171
172 /* are we playing? wait for the chunk to finish */
173 if (dma_play_lock.state != 0)
174 {
175 /* wait for the FIFO to empty and DMA to stop */
176 while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
177 }
178
179 /* Keep interrupt masked when unlocking */
180 dma_play_lock.state = 0;
181
182 /* turn off the request */
183 IISCON &= ~(1<<5);
184
185 /* turn on the idle */
186 IISCON |= (1<<3);
187
188 /* stop the IIS */
189 IISCON &= ~(1<<0);
190}
191
192void pcm_play_dma_start(const void *addr, size_t size)
193{
194 /* Enable the IIS clock */
195 s3c_regset32(&CLKCON, 1<<17);
196
197 /* stop any DMA in progress - idle IIS */
198 play_stop_pcm();
199
200 /* connect DMA to the FIFO and enable the FIFO */
201 IISFCON = (1<<15) | (1<<13);
202
203 /* set DMA dest */
204 DIDST2 = (unsigned int)&IISFIFO;
205
206 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
207 DIDSTC2 = 0x03;
208
209 /* set DMA source and options */
210 DISRC2 = (unsigned int)addr + 0x30000000;
211 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
212 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
213 /* no auto-reload, half-word (16bit) */
214 DCON2 = DMA_CONTROL_SETUP | (size / 2);
215 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
216
217 play_start_pcm();
218}
219
220/* Promptly stop DMA transfers and stop IIS */
221void pcm_play_dma_stop(void)
222{
223 play_stop_pcm();
224
225 /* Disconnect the IIS clock */
226 s3c_regclr32(&CLKCON, 1<<17);
227}
228
229void pcm_play_dma_pause(bool pause)
230{
231 if (pause)
232 {
233 /* pause playback on current buffer */
234 play_stop_pcm();
235 }
236 else
237 {
238 /* restart playback on current buffer */
239 /* make sure we're aligned on left channel - skip any right
240 channel sample left waiting */
241 DISRC2 = (DCSRC2 + 2) & ~0x3;
242 DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
243 play_start_pcm();
244 }
245}
246
247void fiq_handler(void)
248{
249 static unsigned char *start;
250 static size_t size;
251 register pcm_more_callback_type get_more; /* No stack for this */
252
253 /* clear any pending interrupt */
254 SRCPND = DMA2_MASK;
255
256 /* Buffer empty. Try to get more. */
257 get_more = pcm_callback_for_more;
258 size = 0;
259
260 if (get_more == NULL || (get_more(&start, &size), size == 0))
261 {
262 /* Callback missing or no more DMA to do */
263 pcm_play_dma_stop();
264 pcm_play_dma_stopped_callback();
265 }
266 else
267 {
268 /* Flush any pending cache writes */
269 clean_dcache_range(start, size);
270
271 /* set the new DMA values */
272 DCON2 = DMA_CONTROL_SETUP | (size >> 1);
273 DISRC2 = (unsigned int)start + 0x30000000;
274
275 /* Re-Activate the channel */
276 DMASKTRIG2 = 0x2;
277 }
278}
279
280size_t pcm_get_bytes_waiting(void)
281{
282 /* lie a little and only return full pairs */
283 return (DSTAT2 & 0xFFFFE) * 2;
284}
285
286const void * pcm_play_dma_get_peak_buffer(int *count)
287{
288 unsigned long addr = DCSRC2;
289 int cnt = DSTAT2;
290 *count = (cnt & 0xFFFFF) >> 1;
291 return (void *)((addr + 2) & ~3);
292}