diff options
-rwxr-xr-x | firmware/target/arm/s5l8700/dma-s5l8700.c | 119 | ||||
-rwxr-xr-x | firmware/target/arm/s5l8700/dma-target.h | 41 | ||||
-rw-r--r-- | firmware/target/arm/s5l8700/pcm-s5l8700.c | 276 |
3 files changed, 436 insertions, 0 deletions
diff --git a/firmware/target/arm/s5l8700/dma-s5l8700.c b/firmware/target/arm/s5l8700/dma-s5l8700.c new file mode 100755 index 0000000000..f766cef98a --- /dev/null +++ b/firmware/target/arm/s5l8700/dma-s5l8700.c | |||
@@ -0,0 +1,119 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright © 2009 Bertrik Sikken | ||
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 | |||
22 | #include "config.h" | ||
23 | |||
24 | #include "s5l8700.h" | ||
25 | #include "dma-target.h" | ||
26 | #include "panic.h" | ||
27 | #include "system.h" | ||
28 | |||
29 | /* Driver for the IODMA part of the s5l8700 | ||
30 | |||
31 | When requesting a DMA transfer the supplied callback is stored and called | ||
32 | upon completion of the DMA transfer (callback runs in interrupt context). | ||
33 | */ | ||
34 | |||
35 | |||
36 | #define DMAC_BASE 0x38400000 | ||
37 | |||
38 | #define DMABASE(c) (*(volatile unsigned int*)(DMAC_BASE+0x00+(0x20*c))) | ||
39 | #define DMACON(c) (*(volatile unsigned int*)(DMAC_BASE+0x04+(0x20*c))) | ||
40 | #define DMATCNT(c) (*(volatile unsigned int*)(DMAC_BASE+0x08+(0x20*c))) | ||
41 | #define DMACADDR(c) (*(volatile unsigned int*)(DMAC_BASE+0x0C+(0x20*c))) | ||
42 | #define DMACTCNT(c) (*(volatile unsigned int*)(DMAC_BASE+0x10+(0x20*c))) | ||
43 | #define DMACOM(c) (*(volatile unsigned int*)(DMAC_BASE+0x14+(0x20*c))) | ||
44 | #define DMANOFF(c) (*(volatile unsigned int*)(DMAC_BASE+0x18+(0x20*c))) | ||
45 | |||
46 | #define DMACOM_HOLD 2 /* only allowed on channel 0 */ | ||
47 | #define DMACOM_SKIP 3 /* only allowed on channel 0 */ | ||
48 | #define DMACOM_CHAN_ON 4 | ||
49 | #define DMACOM_CHAN_OFF 5 | ||
50 | #define DMACOM_CLEAR_HCOM 6 | ||
51 | #define DMACOM_CLEAR_HCOM_WCOM 7 | ||
52 | |||
53 | |||
54 | /* one completion callback for each channel */ | ||
55 | static void (*dma_callback[4])(void); | ||
56 | |||
57 | |||
58 | void dma_init(void) | ||
59 | { | ||
60 | int i; | ||
61 | |||
62 | for (i = 0; i < 4; i++) { | ||
63 | dma_callback[i] = NULL; | ||
64 | dma_disable_channel(i); | ||
65 | } | ||
66 | |||
67 | INTMSK |= (1 << 10); | ||
68 | } | ||
69 | |||
70 | /* setup a DMA transfer, but do not start it yet */ | ||
71 | void dma_setup_channel(int channel, int sel, int dir, int dsize, int blen, | ||
72 | void *addr, size_t size, void (*callback)(void)) | ||
73 | { | ||
74 | dma_callback[channel] = callback; | ||
75 | |||
76 | DMACON(channel) = (sel << 30) | /* DEVSEL */ | ||
77 | (dir << 29) | /* DIR */ | ||
78 | (0 << 24) | /* SCHCNT */ | ||
79 | (dsize << 22) | /* DSIZE */ | ||
80 | (blen << 19) | /* BLEN */ | ||
81 | (0 << 18) | /* RELOAD */ | ||
82 | (0 << 17) | /* HCOMINT */ | ||
83 | (1 << 16) | /* WCOMINT */ | ||
84 | (0 << 0); /* OFFSET */ | ||
85 | DMABASE(channel) = (unsigned int)addr; | ||
86 | DMATCNT(channel) = size; | ||
87 | } | ||
88 | |||
89 | void dma_enable_channel(int channel) | ||
90 | { | ||
91 | DMACOM(channel) = DMACOM_CHAN_ON; | ||
92 | } | ||
93 | |||
94 | void dma_disable_channel(int channel) | ||
95 | { | ||
96 | DMACOM(channel) = DMACOM_CHAN_OFF; | ||
97 | } | ||
98 | |||
99 | /* interrupt handler for all DMA channels */ | ||
100 | void INT_DMA(void) | ||
101 | { | ||
102 | unsigned int mask; | ||
103 | int channel; | ||
104 | |||
105 | mask = (1 << 0) | /* WCOMx interrupt bit */ | ||
106 | (1 << 1); /* HCOMx interrupt bit */ | ||
107 | for (channel = 0; channel < 4; channel++) { | ||
108 | if (DMAALLST & mask) { | ||
109 | /* clear half and whole completion bits */ | ||
110 | DMACOM(channel) = DMACOM_CLEAR_HCOM_WCOM; | ||
111 | |||
112 | if (dma_callback[channel]) { | ||
113 | dma_callback[channel](); | ||
114 | } | ||
115 | } | ||
116 | mask <<= 4; | ||
117 | } | ||
118 | } | ||
119 | |||
diff --git a/firmware/target/arm/s5l8700/dma-target.h b/firmware/target/arm/s5l8700/dma-target.h new file mode 100755 index 0000000000..6bb3a92bbd --- /dev/null +++ b/firmware/target/arm/s5l8700/dma-target.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright © 2009 Bertrik Sikken | ||
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 | |||
22 | #include <stdlib.h> | ||
23 | |||
24 | /* generic DMA definitions */ | ||
25 | #define DMA_IO_TO_MEM 0 | ||
26 | #define DMA_MEM_TO_IO 1 | ||
27 | |||
28 | /* IISOUT DMA settings */ | ||
29 | #define DMA_IISOUT_CHANNEL 0 | ||
30 | #define DMA_IISOUT_SELECT 0 | ||
31 | #define DMA_IISOUT_DSIZE 1 /* 1 = 16-bit/transfer */ | ||
32 | #define DMA_IISOUT_BLEN 0 /* 0 = 1 transfer/burst */ | ||
33 | |||
34 | void dma_init(void); | ||
35 | |||
36 | void dma_setup_channel(int channel, int sel, int dir, int dsize, int blen, | ||
37 | void *addr, size_t size, void (*callback)(void)); | ||
38 | |||
39 | void dma_enable_channel(int channel); | ||
40 | void dma_disable_channel(int channel); | ||
41 | |||
diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c new file mode 100644 index 0000000000..38b581b80c --- /dev/null +++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c | |||
@@ -0,0 +1,276 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright © 2009 Bertrik Sikken | ||
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 <string.h> | ||
22 | |||
23 | #include "config.h" | ||
24 | #include "system.h" | ||
25 | #include "audio.h" | ||
26 | #include "s5l8700.h" | ||
27 | #include "panic.h" | ||
28 | #include "audiohw.h" | ||
29 | #include "pcm.h" | ||
30 | #include "pcm_sampr.h" | ||
31 | #include "dma-target.h" | ||
32 | |||
33 | /* Driver for the IIS/PCM part of the s5l8700 using DMA | ||
34 | |||
35 | Notes: | ||
36 | - not all possible PCM sample rates are enabled (no support in codec driver) | ||
37 | - pcm_play_dma_pause is untested, not sure if implemented the right way | ||
38 | - pcm_play_dma_stop is untested, not sure if implemented the right way | ||
39 | - pcm_play_dma_get_peak_buffer is not implemented | ||
40 | - recording is not implemented | ||
41 | */ | ||
42 | |||
43 | static void dma_callback(void); | ||
44 | static volatile int locked = 0; | ||
45 | |||
46 | /* table of recommended PLL/MCLK dividers for mode 256Fs from the datasheet */ | ||
47 | static const struct div_entry { | ||
48 | int pdiv, mdiv, sdiv, cdiv; | ||
49 | } div_table[HW_NUM_FREQ] = { | ||
50 | [HW_FREQ_11] = { 26, 189, 3, 8}, | ||
51 | [HW_FREQ_22] = { 50, 98, 2, 8}, | ||
52 | [HW_FREQ_44] = { 37, 151, 1, 9}, | ||
53 | [HW_FREQ_88] = { 50, 98, 1, 4}, | ||
54 | #if 0 /* disabled because the codec driver does not support it (yet) */ | ||
55 | [HW_FREQ_8 ] = { 28, 192, 3, 12} | ||
56 | [HW_FREQ_16] = { 28, 192, 3, 6}, | ||
57 | [HW_FREQ_32] = { 28, 192, 2, 6}, | ||
58 | |||
59 | [HW_FREQ_12] = { 28, 192, 3, 8}, | ||
60 | [HW_FREQ_24] = { 28, 192, 2, 8}, | ||
61 | [HW_FREQ_48] = { 28, 192, 2, 4}, | ||
62 | [HW_FREQ_96] = { 28, 192, 1, 4}, | ||
63 | #endif | ||
64 | }; | ||
65 | |||
66 | /* Mask the DMA interrupt */ | ||
67 | void pcm_play_lock(void) | ||
68 | { | ||
69 | if (locked++ == 0) { | ||
70 | INTMSK &= ~(1 << 10); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /* Unmask the DMA interrupt if enabled */ | ||
75 | void pcm_play_unlock(void) | ||
76 | { | ||
77 | if (--locked == 0) { | ||
78 | INTMSK |= ~(1 << 10); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void play_next(void *addr, size_t size) | ||
83 | { | ||
84 | /* setup DMA */ | ||
85 | dma_setup_channel(DMA_IISOUT_CHANNEL, DMA_IISOUT_SELECT, DMA_MEM_TO_IO, | ||
86 | DMA_IISOUT_DSIZE, DMA_IISOUT_BLEN, addr, size / 2, | ||
87 | dma_callback); | ||
88 | |||
89 | /* DMA channel on */ | ||
90 | dma_enable_channel(DMA_IISOUT_CHANNEL); | ||
91 | } | ||
92 | |||
93 | static void dma_callback(void) | ||
94 | { | ||
95 | unsigned char *dma_start_addr; | ||
96 | size_t dma_size; | ||
97 | |||
98 | register pcm_more_callback_type get_more = pcm_callback_for_more; | ||
99 | if (get_more) { | ||
100 | get_more(&dma_start_addr, &dma_size); | ||
101 | |||
102 | if (dma_size == 0) { | ||
103 | pcm_play_dma_stop(); | ||
104 | pcm_play_dma_stopped_callback(); | ||
105 | } | ||
106 | else { | ||
107 | play_next(dma_start_addr, dma_size); | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | void pcm_play_dma_start(const void *addr, size_t size) | ||
113 | { | ||
114 | /* S1: DMA channel 0 set */ | ||
115 | dma_setup_channel(DMA_IISOUT_CHANNEL, DMA_IISOUT_SELECT, DMA_MEM_TO_IO, | ||
116 | DMA_IISOUT_DSIZE, DMA_IISOUT_BLEN, (void *)addr, size / 2, | ||
117 | dma_callback); | ||
118 | |||
119 | /* S2: IIS Tx mode set */ | ||
120 | I2STXCON = (DMA_IISOUT_BLEN << 16) | /* burst length */ | ||
121 | (0 << 15) | /* 0 = falling edge */ | ||
122 | (0 << 13) | /* 0 = basic I2S format */ | ||
123 | (0 << 12) | /* 0 = MSB first */ | ||
124 | (0 << 11) | /* 0 = left channel for low polarity */ | ||
125 | (3 << 8) | /* MCLK divider */ | ||
126 | (0 << 5) | /* 0 = 16-bit */ | ||
127 | (0 << 3) | /* bit clock per frame */ | ||
128 | (1 << 0); /* channel index */ | ||
129 | |||
130 | /* S3: DMA channel 0 on */ | ||
131 | dma_enable_channel(DMA_IISOUT_CHANNEL); | ||
132 | |||
133 | /* S4: IIS Tx clock on */ | ||
134 | I2SCLKCON = (1 << 0); /* 1 = power on */ | ||
135 | |||
136 | /* S5: IIS Tx on */ | ||
137 | I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ | ||
138 | (1 << 2) | /* 1 = I2S interface enable */ | ||
139 | (1 << 1) | /* 1 = DMA request enable */ | ||
140 | (0 << 0); /* 0 = LRCK on */ | ||
141 | } | ||
142 | |||
143 | void pcm_play_dma_stop(void) | ||
144 | { | ||
145 | /* DMA channel off */ | ||
146 | dma_disable_channel(DMA_IISOUT_CHANNEL); | ||
147 | |||
148 | /* TODO Some time wait */ | ||
149 | /* LRCK half cycle wait */ | ||
150 | |||
151 | /* IIS Tx off */ | ||
152 | I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ | ||
153 | (0 << 2) | /* 1 = I2S interface enable */ | ||
154 | (1 << 1) | /* 1 = DMA request enable */ | ||
155 | (0 << 0); /* 0 = LRCK on */ | ||
156 | } | ||
157 | |||
158 | /* pause playback by disabling further DMA requests */ | ||
159 | void pcm_play_dma_pause(bool pause) | ||
160 | { | ||
161 | if (pause) { | ||
162 | I2STXCOM &= ~(1 << 1); /* DMA request enable */ | ||
163 | } | ||
164 | else { | ||
165 | I2STXCOM |= (1 << 1); /* DMA request enable */ | ||
166 | } | ||
167 | } | ||
168 | |||
169 | void pcm_play_dma_init(void) | ||
170 | { | ||
171 | /* configure IIS pins */ | ||
172 | PCON7 = (PCON7 & ~(0x0FFFFF00)) | 0x02222200; | ||
173 | |||
174 | /* enable clock to the IIS module */ | ||
175 | PWRCON &= ~(1 << 6); | ||
176 | |||
177 | audiohw_preinit(); | ||
178 | } | ||
179 | |||
180 | void pcm_postinit(void) | ||
181 | { | ||
182 | audiohw_postinit(); | ||
183 | } | ||
184 | |||
185 | /* set the configured PCM frequency */ | ||
186 | void pcm_dma_apply_settings(void) | ||
187 | { | ||
188 | audiohw_set_frequency(pcm_sampr); | ||
189 | |||
190 | struct div_entry div = div_table[pcm_fsel]; | ||
191 | |||
192 | /* configure PLL1 and MCLK for the desired sample rate */ | ||
193 | PLL1PMS = (div.pdiv << 16) | | ||
194 | (div.mdiv << 8) | | ||
195 | (div.sdiv << 0); | ||
196 | PLL1LCNT = 7500; /* no idea what to put here */ | ||
197 | |||
198 | /* enable PLL1 and wait for lock */ | ||
199 | PLLCON |= (1 << 1); | ||
200 | while ((PLLLOCK & (1 << 1)) == 0); | ||
201 | |||
202 | /* configure MCLK */ | ||
203 | CLKCON = (CLKCON & ~(0xFF)) | | ||
204 | (0 << 7) | /* MCLK_MASK */ | ||
205 | (2 << 5) | /* MCLK_SEL = PLL1 */ | ||
206 | (1 << 4) | /* MCLK_DIV_ON */ | ||
207 | (div.cdiv - 1); /* MCLK_DIV_VAL */ | ||
208 | } | ||
209 | |||
210 | size_t pcm_get_bytes_waiting(void) | ||
211 | { | ||
212 | return DMATCNT0 * 2; | ||
213 | } | ||
214 | |||
215 | const void * pcm_play_dma_get_peak_buffer(int *count) | ||
216 | { | ||
217 | /* currently not supported */ | ||
218 | *count = 0; | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | #ifdef HAVE_PCM_DMA_ADDRESS | ||
223 | void * pcm_dma_addr(void *addr) | ||
224 | { | ||
225 | if (addr != NULL) | ||
226 | addr = UNCACHED_ADDR(addr); | ||
227 | return addr; | ||
228 | } | ||
229 | #endif | ||
230 | |||
231 | |||
232 | /**************************************************************************** | ||
233 | ** Recording DMA transfer | ||
234 | **/ | ||
235 | #ifdef HAVE_RECORDING | ||
236 | void pcm_rec_lock(void) | ||
237 | { | ||
238 | } | ||
239 | |||
240 | void pcm_rec_unlock(void) | ||
241 | { | ||
242 | } | ||
243 | |||
244 | void pcm_record_more(void *start, size_t size) | ||
245 | { | ||
246 | (void)start; | ||
247 | (void)size; | ||
248 | } | ||
249 | |||
250 | void pcm_rec_dma_stop(void) | ||
251 | { | ||
252 | } | ||
253 | |||
254 | void pcm_rec_dma_start(void *addr, size_t size) | ||
255 | { | ||
256 | (void)addr; | ||
257 | (void)size; | ||
258 | } | ||
259 | |||
260 | void pcm_rec_dma_close(void) | ||
261 | { | ||
262 | } | ||
263 | |||
264 | |||
265 | void pcm_rec_dma_init(void) | ||
266 | { | ||
267 | } | ||
268 | |||
269 | |||
270 | const void * pcm_rec_dma_get_peak_buffer(int *count) | ||
271 | { | ||
272 | (void)count; | ||
273 | } | ||
274 | |||
275 | #endif /* HAVE_RECORDING */ | ||
276 | |||