diff options
author | Michael Sparmann <theseven@rockbox.org> | 2010-11-13 21:08:15 +0000 |
---|---|---|
committer | Michael Sparmann <theseven@rockbox.org> | 2010-11-13 21:08:15 +0000 |
commit | 23fd886c4fb69ddc0f7ccf41621b36fd92033226 (patch) | |
tree | 4c7856ca8ab83c2da7e9a4f8453892cb848fddfc /firmware/target/arm/s5l8700 | |
parent | 9156dbaef5b97c673b95de1745ac33daf6518145 (diff) | |
download | rockbox-23fd886c4fb69ddc0f7ccf41621b36fd92033226.tar.gz rockbox-23fd886c4fb69ddc0f7ccf41621b36fd92033226.zip |
iPod Nano 2G: Finally FIQ-less and glitch-free PCM. This should allow for IRQ latencies high enough to allow re-locking PLLs and changing and stabilizing Vcore when switching CPU frequency.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28580 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/s5l8700')
-rw-r--r-- | firmware/target/arm/s5l8700/pcm-s5l8700.c | 274 |
1 files changed, 107 insertions, 167 deletions
diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c index 0819088c02..75afbd3dc8 100644 --- a/firmware/target/arm/s5l8700/pcm-s5l8700.c +++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c | |||
@@ -34,19 +34,19 @@ | |||
34 | /* Driver for the IIS/PCM part of the s5l8700 using DMA | 34 | /* Driver for the IIS/PCM part of the s5l8700 using DMA |
35 | 35 | ||
36 | Notes: | 36 | Notes: |
37 | - not all possible PCM sample rates are enabled (no support in codec driver) | ||
38 | - pcm_play_dma_pause is untested, not sure if implemented the right way | 37 | - pcm_play_dma_pause is untested, not sure if implemented the right way |
39 | - pcm_play_dma_stop is untested, not sure if implemented the right way | 38 | - pcm_play_dma_stop is untested, not sure if implemented the right way |
40 | - recording is not implemented | 39 | - recording is not implemented |
41 | */ | 40 | */ |
42 | 41 | ||
43 | static volatile int locked = 0; | 42 | static volatile int locked = 0; |
43 | static const int zerosample = 0; | ||
44 | static unsigned char dblbuf[1024]; | ||
45 | static const unsigned char* queuedbuf; | ||
46 | static size_t queuedsize; | ||
47 | static const unsigned char* nextbuf; | ||
44 | static size_t nextsize; | 48 | static size_t nextsize; |
45 | static size_t dblbufsize; | ||
46 | static int dmamode; | ||
47 | static const unsigned char* dblbuf; | ||
48 | 49 | ||
49 | /* table of recommended PLL/MCLK dividers for mode 256Fs from the datasheet */ | ||
50 | static const struct div_entry { | 50 | static const struct div_entry { |
51 | int pdiv, mdiv, sdiv, cdiv; | 51 | int pdiv, mdiv, sdiv, cdiv; |
52 | } div_table[HW_NUM_FREQ] = { | 52 | } div_table[HW_NUM_FREQ] = { |
@@ -55,7 +55,6 @@ static const struct div_entry { | |||
55 | [HW_FREQ_22] = { 2, 41, 4, 4}, | 55 | [HW_FREQ_22] = { 2, 41, 4, 4}, |
56 | [HW_FREQ_44] = { 2, 41, 3, 4}, | 56 | [HW_FREQ_44] = { 2, 41, 3, 4}, |
57 | [HW_FREQ_88] = { 2, 41, 2, 4}, | 57 | [HW_FREQ_88] = { 2, 41, 2, 4}, |
58 | #if 0 /* disabled because the codec driver does not support it (yet) */ | ||
59 | [HW_FREQ_8 ] = { 2, 12, 3, 9}, | 58 | [HW_FREQ_8 ] = { 2, 12, 3, 9}, |
60 | [HW_FREQ_16] = { 2, 12, 2, 9}, | 59 | [HW_FREQ_16] = { 2, 12, 2, 9}, |
61 | [HW_FREQ_32] = { 2, 12, 1, 9}, | 60 | [HW_FREQ_32] = { 2, 12, 1, 9}, |
@@ -63,8 +62,8 @@ static const struct div_entry { | |||
63 | [HW_FREQ_24] = { 2, 12, 3, 3}, | 62 | [HW_FREQ_24] = { 2, 12, 3, 3}, |
64 | [HW_FREQ_48] = { 2, 12, 2, 3}, | 63 | [HW_FREQ_48] = { 2, 12, 2, 3}, |
65 | [HW_FREQ_96] = { 2, 12, 1, 3}, | 64 | [HW_FREQ_96] = { 2, 12, 1, 3}, |
66 | #endif | ||
67 | #else | 65 | #else |
66 | /* table of recommended PLL/MCLK dividers for mode 256Fs from the datasheet */ | ||
68 | [HW_FREQ_11] = { 26, 189, 3, 8}, | 67 | [HW_FREQ_11] = { 26, 189, 3, 8}, |
69 | [HW_FREQ_22] = { 50, 98, 2, 8}, | 68 | [HW_FREQ_22] = { 50, 98, 2, 8}, |
70 | [HW_FREQ_44] = { 37, 151, 1, 9}, | 69 | [HW_FREQ_44] = { 37, 151, 1, 9}, |
@@ -97,146 +96,50 @@ void pcm_play_unlock(void) | |||
97 | } | 96 | } |
98 | } | 97 | } |
99 | 98 | ||
100 | static const void* dma_callback(void) ICODE_ATTR __attribute__((used)); | 99 | void INT_DMA(void) ICODE_ATTR; |
101 | static const void* dma_callback(void) | 100 | void INT_DMA(void) |
102 | { | 101 | { |
103 | if (dmamode) | 102 | DMACOM0 = 7; |
103 | while (!(DMACON0 & (1 << 18))) | ||
104 | { | 104 | { |
105 | void *dma_start_addr; | 105 | if (queuedsize) |
106 | pcm_play_get_more_callback(&dma_start_addr, &nextsize); | ||
107 | |||
108 | if (nextsize != 0) | ||
109 | { | 106 | { |
110 | if (nextsize >= 4096) | 107 | memcpy(dblbuf, queuedbuf, queuedsize); |
111 | { | 108 | DMABASE0 = (unsigned int)dblbuf; |
112 | dblbufsize = (nextsize >> 4) & ~3; | 109 | DMATCNT0 = queuedsize / 2 - 1; |
113 | nextsize = nextsize - dblbufsize; | 110 | queuedsize = 0; |
114 | dblbuf = dma_start_addr + nextsize; | ||
115 | dmamode = 0; | ||
116 | } | ||
117 | nextsize = (nextsize >> 1) - 1; | ||
118 | clean_dcache(); | ||
119 | return dma_start_addr; | ||
120 | } | 111 | } |
121 | else | 112 | else |
122 | { | 113 | { |
123 | nextsize = -1; | 114 | if (!nextsize) pcm_play_get_more_callback((void**)&nextbuf, &nextsize); |
124 | return 0; | 115 | if (!nextsize) break; |
116 | queuedsize = MIN(sizeof(dblbuf), nextsize / 2); | ||
117 | nextsize -= queuedsize; | ||
118 | queuedbuf = nextbuf + nextsize; | ||
119 | DMABASE0 = (unsigned int)nextbuf; | ||
120 | DMATCNT0 = nextsize / 2 - 1; | ||
121 | nextsize = 0; | ||
125 | } | 122 | } |
123 | clean_dcache(); | ||
124 | DMACOM0 = 4; | ||
125 | DMACOM0 = 7; | ||
126 | } | 126 | } |
127 | else | ||
128 | { | ||
129 | dmamode = 1; | ||
130 | nextsize = (dblbufsize >> 1) - 1; | ||
131 | return dblbuf; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked)) ICODE_ATTR; | ||
136 | void fiq_handler(void) | ||
137 | { | ||
138 | asm volatile ( | ||
139 | "cmn r11, #1 \n" | ||
140 | "strne r10, [r8] \n" /* DMABASE0 */ | ||
141 | "strne r11, [r8,#0x08] \n" /* DMATCNT0 */ | ||
142 | "strne r9, [r8,#0x14] \n" /* DMACOM0 */ | ||
143 | "moveq r10, #5 \n" /* STOP DMA */ | ||
144 | "streq r10, [r8,#0x14] \n" /* DMACOM0 */ | ||
145 | "mov r10, #7 \n" /* CLEAR IRQ */ | ||
146 | "str r10, [r8,#0x14] \n" /* DMACOM0 */ | ||
147 | "mov r11, #0x39C00000 \n" /* SRCPND */ | ||
148 | "mov r10, #0x00000400 \n" /* INT_DMA */ | ||
149 | "str r10, [r11] \n" /* ACK FIQ */ | ||
150 | "stmfd sp!, {r0-r3,lr} \n" | ||
151 | "bl dma_callback \n" | ||
152 | "mov r10, r0 \n" | ||
153 | "ldmfd sp!, {r0-r3,lr} \n" | ||
154 | "ldr r11, =nextsize \n" | ||
155 | "ldr r11, [r11] \n" | ||
156 | "subs pc, lr, #4 \n" | ||
157 | ); | ||
158 | } | ||
159 | |||
160 | void bootstrap_fiq(const void* addr, size_t tcnt) __attribute__((naked,noinline)); | ||
161 | void bootstrap_fiq(const void* addr, size_t tcnt) | ||
162 | { | ||
163 | (void)addr; | ||
164 | (void)tcnt; | ||
165 | asm volatile ( | ||
166 | "add r2, lr, #4 \n" | ||
167 | "mrs r3, cpsr \n" | ||
168 | "msr cpsr_c, #0xD1 \n" /* FIQ mode, IRQ/FIQ disabled */ | ||
169 | "mov r8, #0x38400000 \n" /* DMA BASE */ | ||
170 | "mov r9, #4 \n" /* START DMA */ | ||
171 | "mov r10, r0 \n" | ||
172 | "mov r11, r1 \n" | ||
173 | "mov r0, #0 \n" | ||
174 | "ldr r12, =fiq_handler \n" | ||
175 | "ldr sp, =_fiqstackend \n" | ||
176 | "mov lr, r2 \n" | ||
177 | "msr spsr_all, r3 \n" | ||
178 | "bx r12 \n" | ||
179 | ); | ||
180 | } | 127 | } |
181 | 128 | ||
182 | void pcm_play_dma_start(const void *addr_in, size_t size) | 129 | void pcm_play_dma_start(const void* addr, size_t size) |
183 | { | 130 | { |
184 | unsigned char* addr = (unsigned char*)addr_in; | 131 | /* DMA channel on */ |
185 | 132 | nextbuf = (const unsigned char*)addr; | |
186 | /* S1: DMA channel 0 set */ | 133 | nextsize = size; |
187 | DMACON0 = (0 << 30) | /* DEVSEL */ | 134 | queuedsize = 0; |
188 | (1 << 29) | /* DIR */ | 135 | DMABASE0 = (unsigned int)(&zerosample); |
189 | (0 << 24) | /* SCHCNT */ | 136 | DMATCNT0 = 0; |
190 | (1 << 22) | /* DSIZE */ | 137 | DMACOM0 = 4; |
191 | (0 << 19) | /* BLEN */ | 138 | |
192 | (0 << 18) | /* RELOAD */ | 139 | /* IIS Tx clock on */ |
193 | (0 << 17) | /* HCOMINT */ | ||
194 | (1 << 16) | /* WCOMINT */ | ||
195 | (0 << 0); /* OFFSET */ | ||
196 | |||
197 | #ifdef IPOD_NANO2G | ||
198 | PCON5 = (PCON5 & ~(0xFFFF0000)) | 0x77720000; | ||
199 | PCON6 = (PCON6 & ~(0x0F000000)) | 0x02000000; | ||
200 | |||
201 | I2STXCON = (1 << 20) | /* undocumented */ | ||
202 | (0 << 16) | /* burst length */ | ||
203 | (0 << 15) | /* 0 = falling edge */ | ||
204 | (0 << 13) | /* 0 = basic I2S format */ | ||
205 | (0 << 12) | /* 0 = MSB first */ | ||
206 | (0 << 11) | /* 0 = left channel for low polarity */ | ||
207 | (5 << 8) | /* MCLK divider */ | ||
208 | (0 << 5) | /* 0 = 16-bit */ | ||
209 | (2 << 3) | /* bit clock per frame */ | ||
210 | (1 << 0); /* channel index */ | ||
211 | #else | ||
212 | /* S2: IIS Tx mode set */ | ||
213 | I2STXCON = (DMA_IISOUT_BLEN << 16) | /* burst length */ | ||
214 | (0 << 15) | /* 0 = falling edge */ | ||
215 | (0 << 13) | /* 0 = basic I2S format */ | ||
216 | (0 << 12) | /* 0 = MSB first */ | ||
217 | (0 << 11) | /* 0 = left channel for low polarity */ | ||
218 | (3 << 8) | /* MCLK divider */ | ||
219 | (0 << 5) | /* 0 = 16-bit */ | ||
220 | (0 << 3) | /* bit clock per frame */ | ||
221 | (1 << 0); /* channel index */ | ||
222 | #endif | ||
223 | |||
224 | /* S3: DMA channel 0 on */ | ||
225 | clean_dcache(); | ||
226 | if (size >= 4096) | ||
227 | { | ||
228 | dblbufsize = (size >> 4) & ~3; | ||
229 | size = size - dblbufsize; | ||
230 | dblbuf = addr + size; | ||
231 | dmamode = 0; | ||
232 | } | ||
233 | else dmamode = 1; | ||
234 | bootstrap_fiq(addr, (size >> 1) - 1); | ||
235 | |||
236 | /* S4: IIS Tx clock on */ | ||
237 | I2SCLKCON = (1 << 0); /* 1 = power on */ | 140 | I2SCLKCON = (1 << 0); /* 1 = power on */ |
238 | 141 | ||
239 | /* S5: IIS Tx on */ | 142 | /* IIS Tx on */ |
240 | I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ | 143 | I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ |
241 | (1 << 2) | /* 1 = I2S interface enable */ | 144 | (1 << 2) | /* 1 = I2S interface enable */ |
242 | (1 << 1) | /* 1 = DMA request enable */ | 145 | (1 << 1) | /* 1 = DMA request enable */ |
@@ -248,9 +151,6 @@ void pcm_play_dma_stop(void) | |||
248 | /* DMA channel off */ | 151 | /* DMA channel off */ |
249 | DMACOM0 = 5; | 152 | DMACOM0 = 5; |
250 | 153 | ||
251 | /* TODO Some time wait */ | ||
252 | /* LRCK half cycle wait */ | ||
253 | |||
254 | /* IIS Tx off */ | 154 | /* IIS Tx off */ |
255 | I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ | 155 | I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ |
256 | (0 << 2) | /* 1 = I2S interface enable */ | 156 | (0 << 2) | /* 1 = I2S interface enable */ |
@@ -269,37 +169,9 @@ void pcm_play_dma_pause(bool pause) | |||
269 | } | 169 | } |
270 | } | 170 | } |
271 | 171 | ||
272 | void pcm_play_dma_init(void) | 172 | static void pcm_dma_set_freq(enum hw_freq_indexes idx) |
273 | { | ||
274 | /* configure IIS pins */ | ||
275 | #ifdef IPOD_NANO2G | ||
276 | PCON5 = (PCON5 & ~(0xFFFF0000)) | 0x22220000; | ||
277 | PCON6 = (PCON6 & ~(0x0F000000)) | 0x02000000; | ||
278 | #else | ||
279 | PCON7 = (PCON7 & ~(0x0FFFFF00)) | 0x02222200; | ||
280 | #endif | ||
281 | |||
282 | /* enable clock to the IIS module */ | ||
283 | PWRCON &= ~(1 << 6); | ||
284 | |||
285 | /* Enable the DMA FIQ */ | ||
286 | INTMOD |= (1 << 10); | ||
287 | INTMSK |= (1 << 10); | ||
288 | |||
289 | audiohw_preinit(); | ||
290 | } | ||
291 | |||
292 | void pcm_postinit(void) | ||
293 | { | 173 | { |
294 | audiohw_postinit(); | 174 | struct div_entry div = div_table[idx]; |
295 | } | ||
296 | |||
297 | /* set the configured PCM frequency */ | ||
298 | void pcm_dma_apply_settings(void) | ||
299 | { | ||
300 | // audiohw_set_frequency(pcm_sampr); | ||
301 | |||
302 | struct div_entry div = div_table[pcm_fsel]; | ||
303 | 175 | ||
304 | PLLCON &= ~4; | 176 | PLLCON &= ~4; |
305 | PLLCON &= ~0x10; | 177 | PLLCON &= ~0x10; |
@@ -324,6 +196,74 @@ void pcm_dma_apply_settings(void) | |||
324 | (div.cdiv - 1); /* MCLK_DIV_VAL */ | 196 | (div.cdiv - 1); /* MCLK_DIV_VAL */ |
325 | } | 197 | } |
326 | 198 | ||
199 | void pcm_play_dma_init(void) | ||
200 | { | ||
201 | /* configure IIS pins */ | ||
202 | #ifdef IPOD_NANO2G | ||
203 | PCON5 = (PCON5 & ~(0xFFFF0000)) | 0x77720000; | ||
204 | PCON6 = (PCON6 & ~(0x0F000000)) | 0x02000000; | ||
205 | #else | ||
206 | PCON7 = (PCON7 & ~(0x0FFFFF00)) | 0x02222200; | ||
207 | #endif | ||
208 | |||
209 | /* configure DMA channel */ | ||
210 | DMACON0 = (0 << 30) | /* DEVSEL */ | ||
211 | (1 << 29) | /* DIR */ | ||
212 | (0 << 24) | /* SCHCNT */ | ||
213 | (1 << 22) | /* DSIZE */ | ||
214 | (0 << 19) | /* BLEN */ | ||
215 | (0 << 18) | /* RELOAD */ | ||
216 | (0 << 17) | /* HCOMINT */ | ||
217 | (1 << 16) | /* WCOMINT */ | ||
218 | (0 << 0); /* OFFSET */ | ||
219 | |||
220 | /* Enable the DMA IRQ */ | ||
221 | INTMSK |= (1 << 10); | ||
222 | |||
223 | /* setup PLL */ | ||
224 | pcm_dma_set_freq(HW_FREQ_44); | ||
225 | |||
226 | /* enable clock to the IIS module */ | ||
227 | PWRCON &= ~(1 << 6); | ||
228 | |||
229 | /* configure IIS core */ | ||
230 | #ifdef IPOD_NANO2G | ||
231 | I2STXCON = (1 << 20) | /* undocumented */ | ||
232 | (0 << 16) | /* burst length */ | ||
233 | (0 << 15) | /* 0 = falling edge */ | ||
234 | (0 << 13) | /* 0 = basic I2S format */ | ||
235 | (0 << 12) | /* 0 = MSB first */ | ||
236 | (0 << 11) | /* 0 = left channel for low polarity */ | ||
237 | (5 << 8) | /* MCLK divider */ | ||
238 | (0 << 5) | /* 0 = 16-bit */ | ||
239 | (2 << 3) | /* bit clock per frame */ | ||
240 | (1 << 0); /* channel index */ | ||
241 | #else | ||
242 | I2STXCON = (DMA_IISOUT_BLEN << 16) | /* burst length */ | ||
243 | (0 << 15) | /* 0 = falling edge */ | ||
244 | (0 << 13) | /* 0 = basic I2S format */ | ||
245 | (0 << 12) | /* 0 = MSB first */ | ||
246 | (0 << 11) | /* 0 = left channel for low polarity */ | ||
247 | (3 << 8) | /* MCLK divider */ | ||
248 | (0 << 5) | /* 0 = 16-bit */ | ||
249 | (0 << 3) | /* bit clock per frame */ | ||
250 | (1 << 0); /* channel index */ | ||
251 | #endif | ||
252 | |||
253 | audiohw_preinit(); | ||
254 | } | ||
255 | |||
256 | void pcm_postinit(void) | ||
257 | { | ||
258 | audiohw_postinit(); | ||
259 | } | ||
260 | |||
261 | /* set the configured PCM frequency */ | ||
262 | void pcm_dma_apply_settings(void) | ||
263 | { | ||
264 | pcm_dma_set_freq(pcm_fsel); | ||
265 | } | ||
266 | |||
327 | size_t pcm_get_bytes_waiting(void) | 267 | size_t pcm_get_bytes_waiting(void) |
328 | { | 268 | { |
329 | return (nextsize + DMACTCNT0 + 2) << 1; | 269 | return (nextsize + DMACTCNT0 + 2) << 1; |