diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2022-01-11 13:56:46 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2022-01-16 19:17:25 -0500 |
commit | 15e3d37110f1674ec7d52c2f055ebfad1d77b5da (patch) | |
tree | e46b290421ece555aabdd3e807db94ab4b21fcdc | |
parent | 0fbaeed250fde8ad11cb9dbe8cc63667d269b5d2 (diff) | |
download | rockbox-15e3d37110f1674ec7d52c2f055ebfad1d77b5da.tar.gz rockbox-15e3d37110f1674ec7d52c2f055ebfad1d77b5da.zip |
x1000: core PCM recording support
Change-Id: I71883272cc3bffadc1235b0931c3f42bb38e4c1e
-rw-r--r-- | firmware/export/x1000.h | 5 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/debug-x1000.c | 6 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/pcm-x1000.c | 179 |
3 files changed, 135 insertions, 55 deletions
diff --git a/firmware/export/x1000.h b/firmware/export/x1000.h index 102d4ec978..de3d394c02 100644 --- a/firmware/export/x1000.h +++ b/firmware/export/x1000.h | |||
@@ -57,6 +57,11 @@ | |||
57 | #define X1000_STACKSIZE 0x1e00 | 57 | #define X1000_STACKSIZE 0x1e00 |
58 | #define X1000_IRQSTACKSIZE 0x300 | 58 | #define X1000_IRQSTACKSIZE 0x300 |
59 | 59 | ||
60 | /* Required for pcm_rec_dma_get_peak_buffer(), doesn't do anything | ||
61 | * except on targets with recording. */ | ||
62 | #define HAVE_PCM_DMA_ADDRESS | ||
63 | #define HAVE_PCM_REC_DMA_ADDRESS | ||
64 | |||
60 | /* Convert kseg0 address to physical address or uncached address */ | 65 | /* Convert kseg0 address to physical address or uncached address */ |
61 | #define PHYSADDR(x) ((unsigned long)(x) & 0x1fffffff) | 66 | #define PHYSADDR(x) ((unsigned long)(x) & 0x1fffffff) |
62 | #define UNCACHEDADDR(x) (PHYSADDR(x) | 0xa0000000) | 67 | #define UNCACHEDADDR(x) (PHYSADDR(x) | 0xa0000000) |
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c index 98b8f95fb5..236442a880 100644 --- a/firmware/target/mips/ingenic_x1000/debug-x1000.c +++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c | |||
@@ -118,12 +118,18 @@ static bool dbg_gpios(void) | |||
118 | } | 118 | } |
119 | 119 | ||
120 | extern volatile unsigned aic_tx_underruns; | 120 | extern volatile unsigned aic_tx_underruns; |
121 | #ifdef HAVE_RECORDING | ||
122 | extern volatile unsigned aic_rx_overruns; | ||
123 | #endif | ||
121 | 124 | ||
122 | static bool dbg_audio(void) | 125 | static bool dbg_audio(void) |
123 | { | 126 | { |
124 | do { | 127 | do { |
125 | lcd_clear_display(); | 128 | lcd_clear_display(); |
126 | lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns); | 129 | lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns); |
130 | #ifdef HAVE_RECORDING | ||
131 | lcd_putsf(0, 1, "RX overruns: %u", aic_rx_overruns); | ||
132 | #endif | ||
127 | lcd_update(); | 133 | lcd_update(); |
128 | } while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL); | 134 | } while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL); |
129 | 135 | ||
diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c index ef54d45e62..7d1c83a35a 100644 --- a/firmware/target/mips/ingenic_x1000/pcm-x1000.c +++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2021 Aidan MacDonald | 10 | * Copyright (C) 2021-2022 Aidan MacDonald |
11 | * | 11 | * |
12 | * This program is free software; you can redistribute it and/or | 12 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 13 | * modify it under the terms of the GNU General Public License |
@@ -31,28 +31,31 @@ | |||
31 | #include "x1000/aic.h" | 31 | #include "x1000/aic.h" |
32 | #include "x1000/cpm.h" | 32 | #include "x1000/cpm.h" |
33 | 33 | ||
34 | #define AIC_STATE_STOPPED 0 | 34 | #define AIC_STATE_STOPPED 0x00 |
35 | #define AIC_STATE_PLAYING 1 | 35 | #define AIC_STATE_PLAYING 0x01 |
36 | #define AIC_STATE_RECORDING 0x02 | ||
36 | 37 | ||
37 | volatile unsigned aic_tx_underruns = 0; | 38 | volatile unsigned aic_tx_underruns = 0; |
38 | 39 | ||
39 | static int aic_state = AIC_STATE_STOPPED; | 40 | static int aic_state = AIC_STATE_STOPPED; |
40 | 41 | ||
41 | static int aic_lock = 0; | 42 | static int play_lock = 0; |
42 | static volatile int aic_dma_pending_event = DMA_EVENT_NONE; | 43 | static volatile int play_dma_pending_event = DMA_EVENT_NONE; |
43 | 44 | static dma_desc play_dma_desc; | |
44 | static dma_desc aic_dma_desc; | ||
45 | |||
46 | static void pcm_play_dma_int_cb(int event); | 45 | static void pcm_play_dma_int_cb(int event); |
46 | |||
47 | #ifdef HAVE_RECORDING | 47 | #ifdef HAVE_RECORDING |
48 | volatile unsigned aic_rx_overruns = 0; | ||
49 | static int rec_lock = 0; | ||
50 | static volatile int rec_dma_pending_event = DMA_EVENT_NONE; | ||
51 | static dma_desc rec_dma_desc; | ||
52 | |||
48 | static void pcm_rec_dma_int_cb(int event); | 53 | static void pcm_rec_dma_int_cb(int event); |
49 | #endif | 54 | #endif |
50 | 55 | ||
51 | void pcm_play_dma_init(void) | 56 | void pcm_play_dma_init(void) |
52 | { | 57 | { |
53 | /* Ungate clock, assign pins. NB this overlaps with pins labeled "sa0-sa4" | 58 | /* Ungate clock */ |
54 | * on Ingenic's datasheets but I'm not sure what they are. Probably safe to | ||
55 | * assume they are not useful to Rockbox... */ | ||
56 | jz_writef(CPM_CLKGR, AIC(0)); | 59 | jz_writef(CPM_CLKGR, AIC(0)); |
57 | 60 | ||
58 | /* Configure AIC with some sane defaults */ | 61 | /* Configure AIC with some sane defaults */ |
@@ -79,7 +82,7 @@ void pcm_play_dma_init(void) | |||
79 | #endif | 82 | #endif |
80 | 83 | ||
81 | /* Set DMA settings */ | 84 | /* Set DMA settings */ |
82 | jz_writef(AIC_CFG, TFTH(16), RFTH(16)); | 85 | jz_writef(AIC_CFG, TFTH(16), RFTH(15)); |
83 | dma_set_callback(DMA_CHANNEL_AUDIO, pcm_play_dma_int_cb); | 86 | dma_set_callback(DMA_CHANNEL_AUDIO, pcm_play_dma_int_cb); |
84 | #ifdef HAVE_RECORDING | 87 | #ifdef HAVE_RECORDING |
85 | dma_set_callback(DMA_CHANNEL_RECORD, pcm_rec_dma_int_cb); | 88 | dma_set_callback(DMA_CHANNEL_RECORD, pcm_rec_dma_int_cb); |
@@ -106,23 +109,23 @@ void pcm_dma_apply_settings(void) | |||
106 | audiohw_set_frequency(pcm_fsel); | 109 | audiohw_set_frequency(pcm_fsel); |
107 | } | 110 | } |
108 | 111 | ||
109 | static void pcm_dma_start(const void* addr, size_t size) | 112 | static void play_dma_start(const void* addr, size_t size) |
110 | { | 113 | { |
111 | aic_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9), | 114 | play_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9), |
112 | SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO), | 115 | SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO), |
113 | STDE(0), TIE(1), LINK(0)); | 116 | STDE(0), TIE(1), LINK(0)); |
114 | aic_dma_desc.sa = PHYSADDR(addr); | 117 | play_dma_desc.sa = PHYSADDR(addr); |
115 | aic_dma_desc.ta = PHYSADDR(JA_AIC_DR); | 118 | play_dma_desc.ta = PHYSADDR(JA_AIC_DR); |
116 | aic_dma_desc.tc = size; | 119 | play_dma_desc.tc = size; |
117 | aic_dma_desc.sd = 0; | 120 | play_dma_desc.sd = 0; |
118 | aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX)); | 121 | play_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX)); |
119 | aic_dma_desc.pad0 = 0; | 122 | play_dma_desc.pad0 = 0; |
120 | aic_dma_desc.pad1 = 0; | 123 | play_dma_desc.pad1 = 0; |
121 | 124 | ||
122 | commit_dcache_range(&aic_dma_desc, sizeof(dma_desc)); | 125 | commit_dcache_range(&play_dma_desc, sizeof(dma_desc)); |
123 | commit_dcache_range(addr, size); | 126 | commit_dcache_range(addr, size); |
124 | 127 | ||
125 | REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_dma_desc); | 128 | REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&play_dma_desc); |
126 | jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0)); | 129 | jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0)); |
127 | jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO); | 130 | jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO); |
128 | jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1)); | 131 | jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1)); |
@@ -130,13 +133,13 @@ static void pcm_dma_start(const void* addr, size_t size) | |||
130 | pcm_play_dma_status_callback(PCM_DMAST_STARTED); | 133 | pcm_play_dma_status_callback(PCM_DMAST_STARTED); |
131 | } | 134 | } |
132 | 135 | ||
133 | static void pcm_dma_handle_event(int event) | 136 | static void play_dma_handle_event(int event) |
134 | { | 137 | { |
135 | if(event == DMA_EVENT_COMPLETE) { | 138 | if(event == DMA_EVENT_COMPLETE) { |
136 | const void* addr; | 139 | const void* addr; |
137 | size_t size; | 140 | size_t size; |
138 | if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size)) | 141 | if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size)) |
139 | pcm_dma_start(addr, size); | 142 | play_dma_start(addr, size); |
140 | } else if(event == DMA_EVENT_NONE) { | 143 | } else if(event == DMA_EVENT_NONE) { |
141 | /* ignored, so callers don't need to check for this */ | 144 | /* ignored, so callers don't need to check for this */ |
142 | } else { | 145 | } else { |
@@ -146,20 +149,20 @@ static void pcm_dma_handle_event(int event) | |||
146 | 149 | ||
147 | static void pcm_play_dma_int_cb(int event) | 150 | static void pcm_play_dma_int_cb(int event) |
148 | { | 151 | { |
149 | if(aic_lock) { | 152 | if(play_lock) { |
150 | aic_dma_pending_event = event; | 153 | play_dma_pending_event = event; |
151 | return; | 154 | return; |
152 | } else { | 155 | } else { |
153 | pcm_dma_handle_event(event); | 156 | play_dma_handle_event(event); |
154 | } | 157 | } |
155 | } | 158 | } |
156 | 159 | ||
157 | void pcm_play_dma_start(const void* addr, size_t size) | 160 | void pcm_play_dma_start(const void* addr, size_t size) |
158 | { | 161 | { |
159 | aic_dma_pending_event = DMA_EVENT_NONE; | 162 | play_dma_pending_event = DMA_EVENT_NONE; |
160 | aic_state = AIC_STATE_PLAYING; | 163 | aic_state |= AIC_STATE_PLAYING; |
161 | 164 | ||
162 | pcm_dma_start(addr, size); | 165 | play_dma_start(addr, size); |
163 | jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1)); | 166 | jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1)); |
164 | } | 167 | } |
165 | 168 | ||
@@ -168,21 +171,23 @@ void pcm_play_dma_stop(void) | |||
168 | jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0)); | 171 | jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0)); |
169 | jz_writef(AIC_CCR, TFLUSH(1)); | 172 | jz_writef(AIC_CCR, TFLUSH(1)); |
170 | 173 | ||
171 | aic_dma_pending_event = DMA_EVENT_NONE; | 174 | play_dma_pending_event = DMA_EVENT_NONE; |
172 | aic_state = AIC_STATE_STOPPED; | 175 | aic_state &= ~AIC_STATE_PLAYING; |
173 | } | 176 | } |
174 | 177 | ||
175 | void pcm_play_lock(void) | 178 | void pcm_play_lock(void) |
176 | { | 179 | { |
177 | ++aic_lock; | 180 | int irq = disable_irq_save(); |
181 | ++play_lock; | ||
182 | restore_irq(irq); | ||
178 | } | 183 | } |
179 | 184 | ||
180 | void pcm_play_unlock(void) | 185 | void pcm_play_unlock(void) |
181 | { | 186 | { |
182 | int irq = disable_irq_save(); | 187 | int irq = disable_irq_save(); |
183 | if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) { | 188 | if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) { |
184 | pcm_dma_handle_event(aic_dma_pending_event); | 189 | play_dma_handle_event(play_dma_pending_event); |
185 | aic_dma_pending_event = DMA_EVENT_NONE; | 190 | play_dma_pending_event = DMA_EVENT_NONE; |
186 | } | 191 | } |
187 | 192 | ||
188 | restore_irq(irq); | 193 | restore_irq(irq); |
@@ -193,11 +198,56 @@ void pcm_play_unlock(void) | |||
193 | * Recording | 198 | * Recording |
194 | */ | 199 | */ |
195 | 200 | ||
196 | /* FIXME need to implement this!! */ | 201 | static void rec_dma_start(void* addr, size_t size) |
202 | { | ||
203 | /* NOTE: Rockbox always records in stereo and the AIC pushes in the | ||
204 | * sample for each channel separately. One frame therefore requires | ||
205 | * two 16-bit transfers from the AIC. */ | ||
206 | rec_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(0), DAI(1), RDIL(6), | ||
207 | SP_V(16BIT), DP_V(16BIT), TSZ_V(16BIT), | ||
208 | STDE(0), TIE(1), LINK(0)); | ||
209 | rec_dma_desc.sa = PHYSADDR(JA_AIC_DR); | ||
210 | rec_dma_desc.ta = PHYSADDR(addr); | ||
211 | rec_dma_desc.tc = size / 2; | ||
212 | rec_dma_desc.sd = 0; | ||
213 | rec_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_RX)); | ||
214 | rec_dma_desc.pad0 = 0; | ||
215 | rec_dma_desc.pad1 = 0; | ||
216 | |||
217 | commit_dcache_range(&rec_dma_desc, sizeof(dma_desc)); | ||
218 | if((unsigned long)addr < 0xa0000000ul) | ||
219 | discard_dcache_range(addr, size); | ||
220 | |||
221 | REG_DMA_CHN_DA(DMA_CHANNEL_RECORD) = PHYSADDR(&rec_dma_desc); | ||
222 | jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), DES8(1), NDES(0)); | ||
223 | jz_set(DMA_DB, 1 << DMA_CHANNEL_RECORD); | ||
224 | jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), CTE(1)); | ||
225 | |||
226 | pcm_rec_dma_status_callback(PCM_DMAST_STARTED); | ||
227 | } | ||
228 | |||
229 | static void rec_dma_handle_event(int event) | ||
230 | { | ||
231 | if(event == DMA_EVENT_COMPLETE) { | ||
232 | void* addr; | ||
233 | size_t size; | ||
234 | if(pcm_rec_dma_complete_callback(PCM_DMAST_OK, &addr, &size)) | ||
235 | rec_dma_start(addr, size); | ||
236 | } else if(event == DMA_EVENT_NONE) { | ||
237 | /* ignored, so callers don't need to check for this */ | ||
238 | } else { | ||
239 | pcm_rec_dma_status_callback(PCM_DMAST_ERR_DMA); | ||
240 | } | ||
241 | } | ||
197 | 242 | ||
198 | static void pcm_rec_dma_int_cb(int event) | 243 | static void pcm_rec_dma_int_cb(int event) |
199 | { | 244 | { |
200 | (void)event; | 245 | if(rec_lock) { |
246 | rec_dma_pending_event = event; | ||
247 | return; | ||
248 | } else { | ||
249 | rec_dma_handle_event(event); | ||
250 | } | ||
201 | } | 251 | } |
202 | 252 | ||
203 | void pcm_rec_dma_init(void) | 253 | void pcm_rec_dma_init(void) |
@@ -210,40 +260,52 @@ void pcm_rec_dma_close(void) | |||
210 | 260 | ||
211 | void pcm_rec_dma_start(void* addr, size_t size) | 261 | void pcm_rec_dma_start(void* addr, size_t size) |
212 | { | 262 | { |
213 | (void)addr; | 263 | rec_dma_pending_event = DMA_EVENT_NONE; |
214 | (void)size; | 264 | aic_state |= AIC_STATE_RECORDING; |
265 | |||
266 | rec_dma_start(addr, size); | ||
267 | jz_writef(AIC_CCR, RDMS(1), EROR(1), EREC(1)); | ||
215 | } | 268 | } |
216 | 269 | ||
217 | void pcm_rec_dma_stop(void) | 270 | void pcm_rec_dma_stop(void) |
218 | { | 271 | { |
272 | jz_writef(AIC_CCR, RDMS(0), EROR(0), EREC(0)); | ||
273 | jz_writef(AIC_CCR, RFLUSH(1)); | ||
274 | |||
275 | rec_dma_pending_event = DMA_EVENT_NONE; | ||
276 | aic_state &= ~AIC_STATE_RECORDING; | ||
219 | } | 277 | } |
220 | 278 | ||
221 | void pcm_rec_lock(void) | 279 | void pcm_rec_lock(void) |
222 | { | 280 | { |
223 | 281 | int irq = disable_irq_save(); | |
282 | ++rec_lock; | ||
283 | restore_irq(irq); | ||
224 | } | 284 | } |
225 | 285 | ||
226 | void pcm_rec_unlock(void) | 286 | void pcm_rec_unlock(void) |
227 | { | 287 | { |
288 | int irq = disable_irq_save(); | ||
289 | if(--rec_lock == 0 && (aic_state & AIC_STATE_RECORDING)) { | ||
290 | rec_dma_handle_event(rec_dma_pending_event); | ||
291 | rec_dma_pending_event = DMA_EVENT_NONE; | ||
292 | } | ||
228 | 293 | ||
294 | restore_irq(irq); | ||
229 | } | 295 | } |
230 | 296 | ||
231 | const void* pcm_rec_dma_get_peak_buffer(void) | 297 | const void* pcm_rec_dma_get_peak_buffer(void) |
232 | { | 298 | { |
233 | return NULL; | 299 | return (const void*)UNCACHEDADDR(REG_DMA_CHN_TA(DMA_CHANNEL_RECORD)); |
234 | } | ||
235 | |||
236 | void audio_set_output_source(int source) | ||
237 | { | ||
238 | (void)source; | ||
239 | } | 300 | } |
301 | #endif /* HAVE_RECORDING */ | ||
240 | 302 | ||
241 | void audio_input_mux(int source, unsigned flags) | 303 | #ifdef HAVE_PCM_DMA_ADDRESS |
304 | void* pcm_dma_addr(void* addr) | ||
242 | { | 305 | { |
243 | (void)source; | 306 | return (void*)UNCACHEDADDR(addr); |
244 | (void)flags; | ||
245 | } | 307 | } |
246 | #endif /* HAVE_RECORDING */ | 308 | #endif |
247 | 309 | ||
248 | void AIC(void) | 310 | void AIC(void) |
249 | { | 311 | { |
@@ -251,4 +313,11 @@ void AIC(void) | |||
251 | aic_tx_underruns += 1; | 313 | aic_tx_underruns += 1; |
252 | jz_writef(AIC_SR, TUR(0)); | 314 | jz_writef(AIC_SR, TUR(0)); |
253 | } | 315 | } |
316 | |||
317 | #ifdef HAVE_RECORDING | ||
318 | if(jz_readf(AIC_SR, ROR)) { | ||
319 | aic_rx_overruns += 1; | ||
320 | jz_writef(AIC_SR, ROR(0)); | ||
321 | } | ||
322 | #endif /* HAVE_RECORDING */ | ||
254 | } | 323 | } |