summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-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 @@
#define X1000_STACKSIZE 0x1e00
#define X1000_IRQSTACKSIZE 0x300
+/* Required for pcm_rec_dma_get_peak_buffer(), doesn't do anything
+ * except on targets with recording. */
+#define HAVE_PCM_DMA_ADDRESS
+#define HAVE_PCM_REC_DMA_ADDRESS
+
/* Convert kseg0 address to physical address or uncached address */
#define PHYSADDR(x) ((unsigned long)(x) & 0x1fffffff)
#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)
}
extern volatile unsigned aic_tx_underruns;
+#ifdef HAVE_RECORDING
+extern volatile unsigned aic_rx_overruns;
+#endif
static bool dbg_audio(void)
{
do {
lcd_clear_display();
lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns);
+#ifdef HAVE_RECORDING
+ lcd_putsf(0, 1, "RX overruns: %u", aic_rx_overruns);
+#endif
lcd_update();
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
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 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,28 +31,31 @@
#include "x1000/aic.h"
#include "x1000/cpm.h"
-#define AIC_STATE_STOPPED 0
-#define AIC_STATE_PLAYING 1
+#define AIC_STATE_STOPPED 0x00
+#define AIC_STATE_PLAYING 0x01
+#define AIC_STATE_RECORDING 0x02
volatile unsigned aic_tx_underruns = 0;
static int aic_state = AIC_STATE_STOPPED;
-static int aic_lock = 0;
-static volatile int aic_dma_pending_event = DMA_EVENT_NONE;
-
-static dma_desc aic_dma_desc;
-
+static int play_lock = 0;
+static volatile int play_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc play_dma_desc;
static void pcm_play_dma_int_cb(int event);
+
#ifdef HAVE_RECORDING
+volatile unsigned aic_rx_overruns = 0;
+static int rec_lock = 0;
+static volatile int rec_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc rec_dma_desc;
+
static void pcm_rec_dma_int_cb(int event);
#endif
void pcm_play_dma_init(void)
{
- /* Ungate clock, assign pins. NB this overlaps with pins labeled "sa0-sa4"
- * on Ingenic's datasheets but I'm not sure what they are. Probably safe to
- * assume they are not useful to Rockbox... */
+ /* Ungate clock */
jz_writef(CPM_CLKGR, AIC(0));
/* Configure AIC with some sane defaults */
@@ -79,7 +82,7 @@ void pcm_play_dma_init(void)
#endif
/* Set DMA settings */
- jz_writef(AIC_CFG, TFTH(16), RFTH(16));
+ jz_writef(AIC_CFG, TFTH(16), RFTH(15));
dma_set_callback(DMA_CHANNEL_AUDIO, pcm_play_dma_int_cb);
#ifdef HAVE_RECORDING
dma_set_callback(DMA_CHANNEL_RECORD, pcm_rec_dma_int_cb);
@@ -106,23 +109,23 @@ void pcm_dma_apply_settings(void)
audiohw_set_frequency(pcm_fsel);
}
-static void pcm_dma_start(const void* addr, size_t size)
+static void play_dma_start(const void* addr, size_t size)
{
- aic_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
- SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
- STDE(0), TIE(1), LINK(0));
- aic_dma_desc.sa = PHYSADDR(addr);
- aic_dma_desc.ta = PHYSADDR(JA_AIC_DR);
- aic_dma_desc.tc = size;
- aic_dma_desc.sd = 0;
- aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
- aic_dma_desc.pad0 = 0;
- aic_dma_desc.pad1 = 0;
-
- commit_dcache_range(&aic_dma_desc, sizeof(dma_desc));
+ play_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
+ SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
+ STDE(0), TIE(1), LINK(0));
+ play_dma_desc.sa = PHYSADDR(addr);
+ play_dma_desc.ta = PHYSADDR(JA_AIC_DR);
+ play_dma_desc.tc = size;
+ play_dma_desc.sd = 0;
+ play_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
+ play_dma_desc.pad0 = 0;
+ play_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&play_dma_desc, sizeof(dma_desc));
commit_dcache_range(addr, size);
- REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_dma_desc);
+ REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&play_dma_desc);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0));
jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
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)
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
}
-static void pcm_dma_handle_event(int event)
+static void play_dma_handle_event(int event)
{
if(event == DMA_EVENT_COMPLETE) {
const void* addr;
size_t size;
if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
- pcm_dma_start(addr, size);
+ play_dma_start(addr, size);
} else if(event == DMA_EVENT_NONE) {
/* ignored, so callers don't need to check for this */
} else {
@@ -146,20 +149,20 @@ static void pcm_dma_handle_event(int event)
static void pcm_play_dma_int_cb(int event)
{
- if(aic_lock) {
- aic_dma_pending_event = event;
+ if(play_lock) {
+ play_dma_pending_event = event;
return;
} else {
- pcm_dma_handle_event(event);
+ play_dma_handle_event(event);
}
}
void pcm_play_dma_start(const void* addr, size_t size)
{
- aic_dma_pending_event = DMA_EVENT_NONE;
- aic_state = AIC_STATE_PLAYING;
+ play_dma_pending_event = DMA_EVENT_NONE;
+ aic_state |= AIC_STATE_PLAYING;
- pcm_dma_start(addr, size);
+ play_dma_start(addr, size);
jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1));
}
@@ -168,21 +171,23 @@ void pcm_play_dma_stop(void)
jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0));
jz_writef(AIC_CCR, TFLUSH(1));
- aic_dma_pending_event = DMA_EVENT_NONE;
- aic_state = AIC_STATE_STOPPED;
+ play_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_PLAYING;
}
void pcm_play_lock(void)
{
- ++aic_lock;
+ int irq = disable_irq_save();
+ ++play_lock;
+ restore_irq(irq);
}
void pcm_play_unlock(void)
{
int irq = disable_irq_save();
- if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) {
- pcm_dma_handle_event(aic_dma_pending_event);
- aic_dma_pending_event = DMA_EVENT_NONE;
+ if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) {
+ play_dma_handle_event(play_dma_pending_event);
+ play_dma_pending_event = DMA_EVENT_NONE;
}
restore_irq(irq);
@@ -193,11 +198,56 @@ void pcm_play_unlock(void)
* Recording
*/
-/* FIXME need to implement this!! */
+static void rec_dma_start(void* addr, size_t size)
+{
+ /* NOTE: Rockbox always records in stereo and the AIC pushes in the
+ * sample for each channel separately. One frame therefore requires
+ * two 16-bit transfers from the AIC. */
+ rec_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(0), DAI(1), RDIL(6),
+ SP_V(16BIT), DP_V(16BIT), TSZ_V(16BIT),
+ STDE(0), TIE(1), LINK(0));
+ rec_dma_desc.sa = PHYSADDR(JA_AIC_DR);
+ rec_dma_desc.ta = PHYSADDR(addr);
+ rec_dma_desc.tc = size / 2;
+ rec_dma_desc.sd = 0;
+ rec_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_RX));
+ rec_dma_desc.pad0 = 0;
+ rec_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&rec_dma_desc, sizeof(dma_desc));
+ if((unsigned long)addr < 0xa0000000ul)
+ discard_dcache_range(addr, size);
+
+ REG_DMA_CHN_DA(DMA_CHANNEL_RECORD) = PHYSADDR(&rec_dma_desc);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), DES8(1), NDES(0));
+ jz_set(DMA_DB, 1 << DMA_CHANNEL_RECORD);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), CTE(1));
+
+ pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
+}
+
+static void rec_dma_handle_event(int event)
+{
+ if(event == DMA_EVENT_COMPLETE) {
+ void* addr;
+ size_t size;
+ if(pcm_rec_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
+ rec_dma_start(addr, size);
+ } else if(event == DMA_EVENT_NONE) {
+ /* ignored, so callers don't need to check for this */
+ } else {
+ pcm_rec_dma_status_callback(PCM_DMAST_ERR_DMA);
+ }
+}
static void pcm_rec_dma_int_cb(int event)
{
- (void)event;
+ if(rec_lock) {
+ rec_dma_pending_event = event;
+ return;
+ } else {
+ rec_dma_handle_event(event);
+ }
}
void pcm_rec_dma_init(void)
@@ -210,40 +260,52 @@ void pcm_rec_dma_close(void)
void pcm_rec_dma_start(void* addr, size_t size)
{
- (void)addr;
- (void)size;
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state |= AIC_STATE_RECORDING;
+
+ rec_dma_start(addr, size);
+ jz_writef(AIC_CCR, RDMS(1), EROR(1), EREC(1));
}
void pcm_rec_dma_stop(void)
{
+ jz_writef(AIC_CCR, RDMS(0), EROR(0), EREC(0));
+ jz_writef(AIC_CCR, RFLUSH(1));
+
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_RECORDING;
}
void pcm_rec_lock(void)
{
-
+ int irq = disable_irq_save();
+ ++rec_lock;
+ restore_irq(irq);
}
void pcm_rec_unlock(void)
{
+ int irq = disable_irq_save();
+ if(--rec_lock == 0 && (aic_state & AIC_STATE_RECORDING)) {
+ rec_dma_handle_event(rec_dma_pending_event);
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ }
+ restore_irq(irq);
}
const void* pcm_rec_dma_get_peak_buffer(void)
{
- return NULL;
-}
-
-void audio_set_output_source(int source)
-{
- (void)source;
+ return (const void*)UNCACHEDADDR(REG_DMA_CHN_TA(DMA_CHANNEL_RECORD));
}
+#endif /* HAVE_RECORDING */
-void audio_input_mux(int source, unsigned flags)
+#ifdef HAVE_PCM_DMA_ADDRESS
+void* pcm_dma_addr(void* addr)
{
- (void)source;
- (void)flags;
+ return (void*)UNCACHEDADDR(addr);
}
-#endif /* HAVE_RECORDING */
+#endif
void AIC(void)
{
@@ -251,4 +313,11 @@ void AIC(void)
aic_tx_underruns += 1;
jz_writef(AIC_SR, TUR(0));
}
+
+#ifdef HAVE_RECORDING
+ if(jz_readf(AIC_SR, ROR)) {
+ aic_rx_overruns += 1;
+ jz_writef(AIC_SR, ROR(0));
+ }
+#endif /* HAVE_RECORDING */
}