diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2011-09-06 12:38:22 +0000 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2011-09-06 12:38:22 +0000 |
commit | 472314ba734ce2d42e14cd44ba55d7600c98d222 (patch) | |
tree | e722f41da3d9a395a9996fc7dad03aa0edbab124 /firmware/target/arm/rk27xx/pcm-rk27xx.c | |
parent | 727b98e7004519148b6514419364c96f545e3478 (diff) | |
download | rockbox-472314ba734ce2d42e14cd44ba55d7600c98d222.tar.gz rockbox-472314ba734ce2d42e14cd44ba55d7600c98d222.zip |
rk27xx - implement pcm driver.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30444 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/rk27xx/pcm-rk27xx.c')
-rw-r--r-- | firmware/target/arm/rk27xx/pcm-rk27xx.c | 238 |
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 | |||
29 | static int locked = 0; | ||
30 | |||
31 | /* Mask the DMA interrupt */ | ||
32 | void 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 */ | ||
43 | void 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 | |||
53 | void pcm_play_dma_stop(void) | ||
54 | { | ||
55 | HDMA_CON0 = 0x00; | ||
56 | HDMA_ISR = 0x00; | ||
57 | |||
58 | locked = 1; | ||
59 | } | ||
60 | |||
61 | static 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 | |||
105 | void 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 */ | ||
115 | void 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 | |||
129 | static 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 | |||
180 | void 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 | |||
191 | void pcm_play_dma_postinit(void) | ||
192 | { | ||
193 | audiohw_postinit(); | ||
194 | } | ||
195 | |||
196 | void pcm_dma_apply_settings(void) | ||
197 | { | ||
198 | /* I2S module runs in slave mode */ | ||
199 | return; | ||
200 | } | ||
201 | |||
202 | size_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 */ | ||
209 | void 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 | |||
223 | const 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 */ | ||