summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-01-11 13:56:46 +0000
committerAidan MacDonald <amachronic@protonmail.com>2022-01-16 19:17:25 -0500
commit15e3d37110f1674ec7d52c2f055ebfad1d77b5da (patch)
treee46b290421ece555aabdd3e807db94ab4b21fcdc
parent0fbaeed250fde8ad11cb9dbe8cc63667d269b5d2 (diff)
downloadrockbox-15e3d37110f1674ec7d52c2f055ebfad1d77b5da.tar.gz
rockbox-15e3d37110f1674ec7d52c2f055ebfad1d77b5da.zip
x1000: core PCM recording support
Change-Id: I71883272cc3bffadc1235b0931c3f42bb38e4c1e
-rw-r--r--firmware/export/x1000.h5
-rw-r--r--firmware/target/mips/ingenic_x1000/debug-x1000.c6
-rw-r--r--firmware/target/mips/ingenic_x1000/pcm-x1000.c179
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
120extern volatile unsigned aic_tx_underruns; 120extern volatile unsigned aic_tx_underruns;
121#ifdef HAVE_RECORDING
122extern volatile unsigned aic_rx_overruns;
123#endif
121 124
122static bool dbg_audio(void) 125static 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
37volatile unsigned aic_tx_underruns = 0; 38volatile unsigned aic_tx_underruns = 0;
38 39
39static int aic_state = AIC_STATE_STOPPED; 40static int aic_state = AIC_STATE_STOPPED;
40 41
41static int aic_lock = 0; 42static int play_lock = 0;
42static volatile int aic_dma_pending_event = DMA_EVENT_NONE; 43static volatile int play_dma_pending_event = DMA_EVENT_NONE;
43 44static dma_desc play_dma_desc;
44static dma_desc aic_dma_desc;
45
46static void pcm_play_dma_int_cb(int event); 45static void pcm_play_dma_int_cb(int event);
46
47#ifdef HAVE_RECORDING 47#ifdef HAVE_RECORDING
48volatile unsigned aic_rx_overruns = 0;
49static int rec_lock = 0;
50static volatile int rec_dma_pending_event = DMA_EVENT_NONE;
51static dma_desc rec_dma_desc;
52
48static void pcm_rec_dma_int_cb(int event); 53static void pcm_rec_dma_int_cb(int event);
49#endif 54#endif
50 55
51void pcm_play_dma_init(void) 56void 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
109static void pcm_dma_start(const void* addr, size_t size) 112static 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
133static void pcm_dma_handle_event(int event) 136static 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
147static void pcm_play_dma_int_cb(int event) 150static 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
157void pcm_play_dma_start(const void* addr, size_t size) 160void 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
175void pcm_play_lock(void) 178void 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
180void pcm_play_unlock(void) 185void 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!! */ 201static 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
229static 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
198static void pcm_rec_dma_int_cb(int event) 243static 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
203void pcm_rec_dma_init(void) 253void pcm_rec_dma_init(void)
@@ -210,40 +260,52 @@ void pcm_rec_dma_close(void)
210 260
211void pcm_rec_dma_start(void* addr, size_t size) 261void 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
217void pcm_rec_dma_stop(void) 270void 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
221void pcm_rec_lock(void) 279void pcm_rec_lock(void)
222{ 280{
223 281 int irq = disable_irq_save();
282 ++rec_lock;
283 restore_irq(irq);
224} 284}
225 285
226void pcm_rec_unlock(void) 286void 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
231const void* pcm_rec_dma_get_peak_buffer(void) 297const 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
236void audio_set_output_source(int source)
237{
238 (void)source;
239} 300}
301#endif /* HAVE_RECORDING */
240 302
241void audio_input_mux(int source, unsigned flags) 303#ifdef HAVE_PCM_DMA_ADDRESS
304void* 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
248void AIC(void) 310void 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}