diff options
Diffstat (limited to 'firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c')
-rw-r--r-- | firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c | 292 |
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 */ | ||
30 | static 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] */ | ||
48 | static 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 */ | ||
59 | void fiq_handler(void) __attribute__((interrupt ("FIQ"))); | ||
60 | |||
61 | /* Mask the DMA interrupt */ | ||
62 | void 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 */ | ||
69 | void pcm_play_unlock(void) | ||
70 | { | ||
71 | if (--dma_play_lock.locked == 0) | ||
72 | s3c_regclr32(&INTMSK, dma_play_lock.state); | ||
73 | } | ||
74 | |||
75 | void 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 | |||
122 | void pcm_postinit(void) | ||
123 | { | ||
124 | audiohw_postinit(); | ||
125 | } | ||
126 | |||
127 | void 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 */ | ||
139 | static 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 */ | ||
164 | static 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 | |||
192 | void 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 */ | ||
221 | void pcm_play_dma_stop(void) | ||
222 | { | ||
223 | play_stop_pcm(); | ||
224 | |||
225 | /* Disconnect the IIS clock */ | ||
226 | s3c_regclr32(&CLKCON, 1<<17); | ||
227 | } | ||
228 | |||
229 | void 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 | |||
247 | void 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 | |||
280 | size_t pcm_get_bytes_waiting(void) | ||
281 | { | ||
282 | /* lie a little and only return full pairs */ | ||
283 | return (DSTAT2 & 0xFFFFE) * 2; | ||
284 | } | ||
285 | |||
286 | const 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 | } | ||