summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s5l8700/pcm-s5l8700.c
diff options
context:
space:
mode:
authorMichael Sparmann <theseven@rockbox.org>2010-11-13 21:08:15 +0000
committerMichael Sparmann <theseven@rockbox.org>2010-11-13 21:08:15 +0000
commit23fd886c4fb69ddc0f7ccf41621b36fd92033226 (patch)
tree4c7856ca8ab83c2da7e9a4f8453892cb848fddfc /firmware/target/arm/s5l8700/pcm-s5l8700.c
parent9156dbaef5b97c673b95de1745ac33daf6518145 (diff)
downloadrockbox-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/pcm-s5l8700.c')
-rw-r--r--firmware/target/arm/s5l8700/pcm-s5l8700.c274
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
43static volatile int locked = 0; 42static volatile int locked = 0;
43static const int zerosample = 0;
44static unsigned char dblbuf[1024];
45static const unsigned char* queuedbuf;
46static size_t queuedsize;
47static const unsigned char* nextbuf;
44static size_t nextsize; 48static size_t nextsize;
45static size_t dblbufsize;
46static int dmamode;
47static const unsigned char* dblbuf;
48 49
49/* table of recommended PLL/MCLK dividers for mode 256Fs from the datasheet */
50static const struct div_entry { 50static 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
100static const void* dma_callback(void) ICODE_ATTR __attribute__((used)); 99void INT_DMA(void) ICODE_ATTR;
101static const void* dma_callback(void) 100void 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
135void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked)) ICODE_ATTR;
136void 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
160void bootstrap_fiq(const void* addr, size_t tcnt) __attribute__((naked,noinline));
161void 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
182void pcm_play_dma_start(const void *addr_in, size_t size) 129void 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
272void pcm_play_dma_init(void) 172static 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
292void pcm_postinit(void)
293{ 173{
294 audiohw_postinit(); 174 struct div_entry div = div_table[idx];
295}
296
297/* set the configured PCM frequency */
298void 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
199void 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
256void pcm_postinit(void)
257{
258 audiohw_postinit();
259}
260
261/* set the configured PCM frequency */
262void pcm_dma_apply_settings(void)
263{
264 pcm_dma_set_freq(pcm_fsel);
265}
266
327size_t pcm_get_bytes_waiting(void) 267size_t pcm_get_bytes_waiting(void)
328{ 268{
329 return (nextsize + DMACTCNT0 + 2) << 1; 269 return (nextsize + DMACTCNT0 + 2) << 1;