summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c')
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c171
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
289static 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
298static 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
321void 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
330void 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
339void 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
346void 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
357void 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
379void pcm_rec_dma_close(void)
380{
381 pcm_rec_dma_stop();
382}
383
384void pcm_rec_dma_init(void)
385{
386 pcm_rec_dma_stop();
387}
388
389const 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 */