summaryrefslogtreecommitdiff
path: root/firmware/target/arm/rk27xx/pcm-rk27xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/rk27xx/pcm-rk27xx.c')
-rw-r--r--firmware/target/arm/rk27xx/pcm-rk27xx.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/firmware/target/arm/rk27xx/pcm-rk27xx.c b/firmware/target/arm/rk27xx/pcm-rk27xx.c
new file mode 100644
index 0000000000..87830f47b4
--- /dev/null
+++ b/firmware/target/arm/rk27xx/pcm-rk27xx.c
@@ -0,0 +1,238 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 Marcin Bukat
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 "system.h"
22#include "audio.h"
23#include "string.h"
24#include "panic.h"
25#include "audiohw.h"
26#include "sound.h"
27#include "pcm-internal.h"
28
29static int locked = 0;
30
31/* Mask the DMA interrupt */
32void pcm_play_lock(void)
33{
34 if (++locked == 1)
35 {
36 int old = disable_irq_save();
37 INTC_IMR &= ~(1<<12); /* mask HDMA interrupt */
38 restore_irq(old);
39 }
40}
41
42/* Unmask the DMA interrupt if enabled */
43void pcm_play_unlock(void)
44{
45 if(--locked == 0)
46 {
47 int old = disable_irq_save();
48 INTC_IMR |= (1<<12); /* unmask HDMA interrupt */
49 restore_irq(old);
50 }
51}
52
53void pcm_play_dma_stop(void)
54{
55 HDMA_CON0 = 0x00;
56 HDMA_ISR = 0x00;
57
58 locked = 1;
59}
60
61static void hdma_i2s_transfer(const void *addr, size_t size)
62{
63 SCU_CLKCFG &= ~(1<<3); /* enable HDMA clock */
64
65 commit_discard_dcache_range(addr, size);
66
67 HDMA_ISRC0 = (uint32_t)addr; /* source address */
68 HDMA_IDST0 = (uint32_t)&I2S_TXR; /* i2s tx fifo */
69 HDMA_ICNT0 = (uint16_t)((size>>2) - 1); /* number of dma transactions
70 * of transfer size bytes
71 * (zero based)
72 */
73
74 HDMA_ISR = ((1<<13) | /* mask ch1 accumulation overflow irq */
75 (1<<12) | /* mask ch0 accumulation overflow irq */
76 (1<<11) | /* mask ch1 page count down irq */
77 (0<<10) | /* UNMASK ch0 page count down irq */
78 (1<<9) | /* mask ch0 transfer irq */
79 (1<<8) | /* mask ch1 transfer irq */
80 (0<<5) | /* clear ch1 accumulation overflow flag */
81 (0<<4) | /* clear ch0 accumulation overflow flag */
82 (0<<3) | /* clear ch1 count down to zero flag */
83 (0<<2) | /* clear ch0 count down to zero flag */
84 (0<<1) | /* clear ch1 active flag */
85 (0<<0)); /* clear ch0 active flag */
86
87 HDMA_ISCNT0 = 0x07; /* slice size in transfer size units (zero base) */
88
89 HDMA_IPNCNTD0 = 0x01; /* page count */
90
91 HDMA_CON0 = ((0<<23) | /* page mode */
92 (1<<22) | /* slice mode */
93 (1<<21) | /* DMA enable */
94 (1<<18) | /* generate interrupt */
95 (0<<16) | /* on-the-fly is not supported by rk27xx */
96 (5<<13) | /* transfer mode inc8 */
97 (6<<9) | /* external hdreq from i2s tx */
98 (0<<7) | /* increment source address */
99 (1<<5) | /* fixed destination address */
100 (2<<3) | /* transfer size = 32bits word */
101 (0<<1) | /* command of software DMA (not relevant) */
102 (1<<0)); /* hardware trigger DMA mode */
103}
104
105void pcm_play_dma_start(const void *addr, size_t size)
106{
107 /* Stop any DMA in progress */
108 pcm_play_dma_stop();
109
110 /* kick in DMA transfer */
111 hdma_i2s_transfer(addr, size);
112}
113
114/* pause DMA transfer by disabling clock to DMA module */
115void pcm_play_dma_pause(bool pause)
116{
117 if(pause)
118 {
119 SCU_CLKCFG |= (1<<3);
120 locked = 1;
121 }
122 else
123 {
124 SCU_CLKCFG &= ~(1<<3);
125 locked = 0;
126 }
127}
128
129static void i2s_init(void)
130{
131#if defined(HAVE_RK27XX_CODEC)
132 /* iomux I2S internal */
133 SCU_IOMUXA_CON &= ~(1<<18); /* i2s external bit */
134 SCU_IOMUXB_CON &= ~((1<<4) | /* i2s_mclk */
135 (1<<3) | /* i2s_sdo */
136 (1<<2) | /* i2s_sdi */
137 (1<<1) | /* i2s_lrck */
138 (1<<0)); /* i2s_bck */
139#else
140 /* iomux I2S external */
141 SCU_IOMUXA_CON |= (1<<18); /* i2s external bit */
142 SCU_IOMUXB_CON |= ((1<<4) | /* i2s_mclk */
143 (1<<3) | /* i2s_sdo */
144 (1<<2) | /* i2s_sdi */
145 (1<<1) | /* i2s_lrck */
146 (1<<0)); /* i2s_bck */
147#endif
148
149 /* enable i2s clocks */
150 SCU_CLKCFG &= ~((1<<17) | /* i2s_pclk */
151 (1<<16)); /* i2s_clk */
152
153 /* configure I2S module */
154 I2S_IER = 0; /* disable all i2s interrupts */
155
156 I2S_TXCTL = (1<<16) | /* LRCK/SCLK = 64 */
157 (4<<8) | /* MCLK/SCLK = 4 */
158 (1<<4) | /* 16bit samples */
159 (0<<3) | /* stereo */
160 (0<<1) | /* I2S IF */
161 (0<<0); /* slave mode */
162
163 /* the fifo is 16x32bits according to my tests
164 * while the docs state 32x32bits
165 */
166 I2S_FIFOSTS = (1<<18) | /* Tx trigger level half full */
167 (1<<16); /* Rx trigger level half full */
168
169 I2S_OPR = (1<<17) | /* reset Tx */
170 (1<<16) | /* reset Rx */
171 (0<<6) | /* HDMA Req1 enable */
172 (1<<5) | /* HDMA Req2 disable */
173 (0<<4) | /* Req1 for Tx fifo */
174 (1<<3) | /* Req2 for Rx fifo */
175 (0<<2) | /* normal operation */
176 (0<<1) | /* start Tx (in master mode) */
177 (0<<0); /* start Rx (in master mode) */
178}
179
180void pcm_play_dma_init(void)
181{
182 /* unmask HDMA interrupt in INTC */
183 INTC_IMR |= (1<<12);
184 INTC_IECR |= (1<<12);
185
186 audiohw_preinit();
187
188 i2s_init();
189}
190
191void pcm_play_dma_postinit(void)
192{
193 audiohw_postinit();
194}
195
196void pcm_dma_apply_settings(void)
197{
198 /* I2S module runs in slave mode */
199 return;
200}
201
202size_t pcm_get_bytes_waiting(void)
203{
204 /* current terminate count is in transfer size units (4bytes here) */
205 return (HDMA_CCNT0 & 0xffff)<<2;
206}
207
208/* audio DMA ISR called when chunk from callers buffer has been transfered */
209void INT_HDMA(void)
210{
211 void *start;
212 size_t size;
213
214 pcm_play_get_more_callback(&start, &size);
215
216 if (size != 0)
217 {
218 hdma_i2s_transfer(start, size);
219 pcm_play_dma_started_callback();
220 }
221}
222
223const void * pcm_play_dma_get_peak_buffer(int *count)
224{
225 uint32_t addr;
226
227 int old = disable_irq_save();
228 addr = HDMA_CSRC0;
229 *count = ((HDMA_CCNT0 & 0xffff)<<2);
230 restore_interrupt(old);
231
232 return (void*)addr;
233}
234
235/****************************************************************************
236 ** Recording DMA transfer
237 **/
238/* TODO */