diff options
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 171 |
1 files changed, 140 insertions, 31 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c index 4710e2d82b..76369a0c6a 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | |||
@@ -111,17 +111,12 @@ void pcm_play_dma_init(void) | |||
111 | SSI_SCR2 &= ~SSI_SCR_SSIEN; | 111 | SSI_SCR2 &= ~SSI_SCR_SSIEN; |
112 | SSI_SCR1 &= ~SSI_SCR_SSIEN; | 112 | SSI_SCR1 &= ~SSI_SCR_SSIEN; |
113 | 113 | ||
114 | SSI_SIER1 = SSI_SIER_TFE0; | ||
115 | SSI_SIER2 = 0; | ||
116 | |||
117 | /* Set up audio mux */ | 114 | /* Set up audio mux */ |
118 | 115 | ||
119 | /* Port 1 (internally connected to SSI1) | 116 | /* Port 1 (internally connected to SSI1) |
120 | * All clocking is output sourced from port 4 */ | 117 | * All clocking is output sourced from port 4 */ |
121 | AUDMUX_PTCR1 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | | 118 | AUDMUX_PTCR1 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | |
122 | AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | | 119 | AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | |
123 | AUDMUX_PTCR_RFSDIR | AUDMUX_PTCR_RFSSEL_PORT4 | | ||
124 | AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4 | | ||
125 | AUDMUX_PTCR_SYN; | 120 | AUDMUX_PTCR_SYN; |
126 | 121 | ||
127 | /* Receive data from port 4 */ | 122 | /* Receive data from port 4 */ |
@@ -133,18 +128,22 @@ void pcm_play_dma_init(void) | |||
133 | /* Receive data from port 1 */ | 128 | /* Receive data from port 1 */ |
134 | AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1; | 129 | AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1; |
135 | 130 | ||
136 | /* Port 2 (internally connected to SSI2) routes clocking to port 5 to | 131 | /* PORT2 (internally connected to SSI2) routes clocking to PORT5 to |
137 | * provide MCLK to the codec */ | 132 | * provide MCLK to the codec */ |
138 | /* All port 2 clocks are inputs taken from SSI2 */ | 133 | /* TX clocks are inputs taken from SSI2 */ |
139 | AUDMUX_PTCR2 = 0; | 134 | /* RX clocks are outputs taken from PORT4 */ |
140 | AUDMUX_PDCR2 = 0; | 135 | AUDMUX_PTCR2 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 | |
141 | /* Port 5 outputs TCLK sourced from port 2 */ | 136 | AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4; |
137 | /* RX data taken from PORT4 */ | ||
138 | AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4; | ||
139 | |||
140 | /* PORT5 outputs TCLK sourced from PORT2 (SSI2) */ | ||
142 | AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT2; | 141 | AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT2; |
143 | AUDMUX_PDCR5 = 0; | 142 | AUDMUX_PDCR5 = 0; |
144 | 143 | ||
145 | /* Setup SSIs */ | 144 | /* Setup SSIs */ |
146 | 145 | ||
147 | /* SSI1 - interface for all I2S data */ | 146 | /* SSI1 - SoC software interface for all I2S data out */ |
148 | SSI_SCR1 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; | 147 | SSI_SCR1 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; |
149 | SSI_STCR1 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | | 148 | SSI_STCR1 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | |
150 | SSI_STCR_TEFS | SSI_STCR_TFEN0; | 149 | SSI_STCR_TEFS | SSI_STCR_TFEN0; |
@@ -153,26 +152,11 @@ void pcm_play_dma_init(void) | |||
153 | SSI_STCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | | 152 | SSI_STCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | |
154 | SSI_STRCCR_PMw(4-1); | 153 | SSI_STRCCR_PMw(4-1); |
155 | 154 | ||
155 | /* Transmit low watermark - 2 samples in FIFO */ | ||
156 | SSI_SFCSR1 = SSI_SFCSR_TFWM1w(1) | SSI_SFCSR_TFWM0w(2); | ||
156 | SSI_STMSK1 = 0; | 157 | SSI_STMSK1 = 0; |
157 | 158 | ||
158 | /* Receive */ | 159 | /* SSI2 - provides MCLK to codec. Receives data from codec. */ |
159 | SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | | ||
160 | SSI_SRCR_REFS | SSI_SRCR_RFEN0; | ||
161 | |||
162 | /* 16 bits per word, 2 words per frame */ | ||
163 | SSI_SRCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | | ||
164 | SSI_STRCCR_PMw(4-1); | ||
165 | |||
166 | /* Receive high watermark - 6 samples in FIFO | ||
167 | * Transmit low watermark - 2 samples in FIFO */ | ||
168 | SSI_SFCSR1 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_TFWM1w(1) | | ||
169 | SSI_SFCSR_RFWM0w(6) | SSI_SFCSR_TFWM0w(2); | ||
170 | |||
171 | SSI_SRMSK1 = 0; | ||
172 | |||
173 | /* SSI2 - provides MCLK only */ | ||
174 | SSI_SCR2 = 0; | ||
175 | SSI_SRCR2 = 0; | ||
176 | SSI_STCR2 = SSI_STCR_TXDIR; | 160 | SSI_STCR2 = SSI_STCR_TXDIR; |
177 | 161 | ||
178 | /* f(INT_BIT_CLK) = | 162 | /* f(INT_BIT_CLK) = |
@@ -189,6 +173,20 @@ void pcm_play_dma_init(void) | |||
189 | */ | 173 | */ |
190 | SSI_STCCR2 = SSI_STRCCR_DIV2 | SSI_STRCCR_PMw(1-1); | 174 | SSI_STCCR2 = SSI_STRCCR_DIV2 | SSI_STRCCR_PMw(1-1); |
191 | 175 | ||
176 | /* SSI2 - receive - asynchronous clocks */ | ||
177 | SSI_SCR2 = SSI_SCR_I2S_MODE_SLAVE; | ||
178 | |||
179 | SSI_SRCR2 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | | ||
180 | SSI_SRCR_REFS; | ||
181 | |||
182 | /* 16 bits per word, 2 words per frame */ | ||
183 | SSI_SRCCR2 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | | ||
184 | SSI_STRCCR_PMw(4-1); | ||
185 | |||
186 | /* Receive high watermark - 6 samples in FIFO */ | ||
187 | SSI_SFCSR2 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_RFWM0w(6); | ||
188 | SSI_SRMSK2 = 0; | ||
189 | |||
192 | /* Enable SSI2 (codec clock) */ | 190 | /* Enable SSI2 (codec clock) */ |
193 | SSI_SCR2 |= SSI_SCR_SSIEN; | 191 | SSI_SCR2 |= SSI_SCR_SSIEN; |
194 | 192 | ||
@@ -210,7 +208,8 @@ static void play_start_pcm(void) | |||
210 | dma_play_data.state = 1; | 208 | dma_play_data.state = 1; |
211 | 209 | ||
212 | /* Fill the FIFO or start when data is used up */ | 210 | /* Fill the FIFO or start when data is used up */ |
213 | SSI_SCR1 |= SSI_SCR_SSIEN; /* Enable SSI */ | 211 | SSI_SCR1 |= SSI_SCR_SSIEN; /* Enable SSI */ |
212 | SSI_STCR1 |= SSI_STCR_TFEN0; /* Enable TX FIFO */ | ||
214 | 213 | ||
215 | while (1) | 214 | while (1) |
216 | { | 215 | { |
@@ -235,6 +234,7 @@ static void play_stop_pcm(void) | |||
235 | while (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 0); | 234 | while (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 0); |
236 | 235 | ||
237 | /* Disable transmission */ | 236 | /* Disable transmission */ |
237 | SSI_STCR1 &= ~SSI_STCR_TFEN0; | ||
238 | SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); | 238 | SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); |
239 | 239 | ||
240 | /* Do not enable interrupt on unlock */ | 240 | /* Do not enable interrupt on unlock */ |
@@ -285,4 +285,113 @@ const void * pcm_play_dma_get_peak_buffer(int *count) | |||
285 | return (void *)((addr + 2) & ~3); | 285 | return (void *)((addr + 2) & ~3); |
286 | } | 286 | } |
287 | 287 | ||
288 | /* Any recording functionality should be implemented similarly */ | 288 | #ifdef HAVE_RECORDING |
289 | static struct dma_data dma_rec_data = | ||
290 | { | ||
291 | /* Initialize to a locked, stopped state */ | ||
292 | .p = NULL, | ||
293 | .size = 0, | ||
294 | .locked = 0, | ||
295 | .state = 0 | ||
296 | }; | ||
297 | |||
298 | static void __attribute__((interrupt("IRQ"))) SSI2_HANDLER(void) | ||
299 | { | ||
300 | register pcm_more_callback_type2 more_ready; | ||
301 | |||
302 | while (dma_rec_data.size > 0) | ||
303 | { | ||
304 | if (SSI_SFCSR_RFCNT0r(SSI_SFCSR2) < 2) | ||
305 | return; | ||
306 | |||
307 | *dma_rec_data.p++ = SSI_SRX0_2; | ||
308 | *dma_rec_data.p++ = SSI_SRX0_2; | ||
309 | dma_rec_data.size -= 4; | ||
310 | } | ||
311 | |||
312 | more_ready = pcm_callback_more_ready; | ||
313 | |||
314 | if (more_ready == NULL || more_ready(0) < 0) { | ||
315 | /* Finished recording */ | ||
316 | pcm_rec_dma_stop(); | ||
317 | pcm_rec_dma_stopped_callback(); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | void pcm_rec_lock(void) | ||
322 | { | ||
323 | if (++dma_rec_data.locked == 1) | ||
324 | { | ||
325 | /* Atomically disable receive interrupt */ | ||
326 | imx31_regclr32(&SSI_SIER2, SSI_SIER_RIE); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | void pcm_rec_unlock(void) | ||
331 | { | ||
332 | if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) | ||
333 | { | ||
334 | /* Atomically enable receive interrupt */ | ||
335 | imx31_regset32(&SSI_SIER2, SSI_SIER_RIE); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | void pcm_record_more(void *start, size_t size) | ||
340 | { | ||
341 | pcm_rec_peak_addr = start; /* Start peaking at dest */ | ||
342 | dma_rec_data.p = start; /* Start of RX buffer */ | ||
343 | dma_rec_data.size = size; /* Bytes to transfer */ | ||
344 | } | ||
345 | |||
346 | void pcm_rec_dma_stop(void) | ||
347 | { | ||
348 | /* Stop receiving data */ | ||
349 | SSI_SCR2 &= ~SSI_SCR_RE; /* Disable RX */ | ||
350 | SSI_SRCR2 &= ~SSI_SRCR_RFEN0; /* Disable RX FIFO */ | ||
351 | |||
352 | dma_rec_data.state = 0; | ||
353 | |||
354 | avic_disable_int(SSI2); | ||
355 | } | ||
356 | |||
357 | void pcm_rec_dma_start(void *addr, size_t size) | ||
358 | { | ||
359 | pcm_rec_dma_stop(); | ||
360 | |||
361 | pcm_rec_peak_addr = addr; | ||
362 | dma_rec_data.p = addr; | ||
363 | dma_rec_data.size = size; | ||
364 | |||
365 | dma_rec_data.state = 1; | ||
366 | |||
367 | avic_enable_int(SSI2, IRQ, 9, SSI2_HANDLER); | ||
368 | |||
369 | SSI_SRCR2 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ | ||
370 | |||
371 | /* Ensure clear FIFO */ | ||
372 | while (SSI_SFCSR2 & SSI_SFCSR_RFCNT0) | ||
373 | SSI_SRX0_2; | ||
374 | |||
375 | /* Enable receive */ | ||
376 | SSI_SCR2 |= SSI_SCR_RE; | ||
377 | } | ||
378 | |||
379 | void pcm_rec_dma_close(void) | ||
380 | { | ||
381 | pcm_rec_dma_stop(); | ||
382 | } | ||
383 | |||
384 | void pcm_rec_dma_init(void) | ||
385 | { | ||
386 | pcm_rec_dma_stop(); | ||
387 | } | ||
388 | |||
389 | const void * pcm_rec_dma_get_peak_buffer(int *count) | ||
390 | { | ||
391 | unsigned long addr = (uint32_t)pcm_rec_peak_addr; | ||
392 | unsigned long end = (uint32_t)dma_rec_data.p; | ||
393 | *count = (end >> 2) - (addr >> 2); | ||
394 | return (void *)(addr & ~3); | ||
395 | } | ||
396 | |||
397 | #endif /* HAVE_RECORDING */ | ||