forked from len0rd/rockbox
x1000: core PCM recording support
Change-Id: I71883272cc3bffadc1235b0931c3f42bb38e4c1e
This commit is contained in:
parent
0fbaeed250
commit
15e3d37110
3 changed files with 136 additions and 56 deletions
|
|
@ -57,6 +57,11 @@
|
||||||
#define X1000_STACKSIZE 0x1e00
|
#define X1000_STACKSIZE 0x1e00
|
||||||
#define X1000_IRQSTACKSIZE 0x300
|
#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 */
|
/* Convert kseg0 address to physical address or uncached address */
|
||||||
#define PHYSADDR(x) ((unsigned long)(x) & 0x1fffffff)
|
#define PHYSADDR(x) ((unsigned long)(x) & 0x1fffffff)
|
||||||
#define UNCACHEDADDR(x) (PHYSADDR(x) | 0xa0000000)
|
#define UNCACHEDADDR(x) (PHYSADDR(x) | 0xa0000000)
|
||||||
|
|
|
||||||
|
|
@ -118,12 +118,18 @@ static bool dbg_gpios(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern volatile unsigned aic_tx_underruns;
|
extern volatile unsigned aic_tx_underruns;
|
||||||
|
#ifdef HAVE_RECORDING
|
||||||
|
extern volatile unsigned aic_rx_overruns;
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool dbg_audio(void)
|
static bool dbg_audio(void)
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
lcd_clear_display();
|
lcd_clear_display();
|
||||||
lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns);
|
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();
|
lcd_update();
|
||||||
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
|
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
* \/ \/ \/ \/ \/
|
* \/ \/ \/ \/ \/
|
||||||
* $Id$
|
* $Id$
|
||||||
*
|
*
|
||||||
* Copyright (C) 2021 Aidan MacDonald
|
* Copyright (C) 2021-2022 Aidan MacDonald
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -31,28 +31,31 @@
|
||||||
#include "x1000/aic.h"
|
#include "x1000/aic.h"
|
||||||
#include "x1000/cpm.h"
|
#include "x1000/cpm.h"
|
||||||
|
|
||||||
#define AIC_STATE_STOPPED 0
|
#define AIC_STATE_STOPPED 0x00
|
||||||
#define AIC_STATE_PLAYING 1
|
#define AIC_STATE_PLAYING 0x01
|
||||||
|
#define AIC_STATE_RECORDING 0x02
|
||||||
|
|
||||||
volatile unsigned aic_tx_underruns = 0;
|
volatile unsigned aic_tx_underruns = 0;
|
||||||
|
|
||||||
static int aic_state = AIC_STATE_STOPPED;
|
static int aic_state = AIC_STATE_STOPPED;
|
||||||
|
|
||||||
static int aic_lock = 0;
|
static int play_lock = 0;
|
||||||
static volatile int aic_dma_pending_event = DMA_EVENT_NONE;
|
static volatile int play_dma_pending_event = DMA_EVENT_NONE;
|
||||||
|
static dma_desc play_dma_desc;
|
||||||
static dma_desc aic_dma_desc;
|
|
||||||
|
|
||||||
static void pcm_play_dma_int_cb(int event);
|
static void pcm_play_dma_int_cb(int event);
|
||||||
|
|
||||||
#ifdef HAVE_RECORDING
|
#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);
|
static void pcm_rec_dma_int_cb(int event);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void pcm_play_dma_init(void)
|
void pcm_play_dma_init(void)
|
||||||
{
|
{
|
||||||
/* Ungate clock, assign pins. NB this overlaps with pins labeled "sa0-sa4"
|
/* Ungate clock */
|
||||||
* on Ingenic's datasheets but I'm not sure what they are. Probably safe to
|
|
||||||
* assume they are not useful to Rockbox... */
|
|
||||||
jz_writef(CPM_CLKGR, AIC(0));
|
jz_writef(CPM_CLKGR, AIC(0));
|
||||||
|
|
||||||
/* Configure AIC with some sane defaults */
|
/* Configure AIC with some sane defaults */
|
||||||
|
|
@ -79,7 +82,7 @@ void pcm_play_dma_init(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Set DMA settings */
|
/* 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);
|
dma_set_callback(DMA_CHANNEL_AUDIO, pcm_play_dma_int_cb);
|
||||||
#ifdef HAVE_RECORDING
|
#ifdef HAVE_RECORDING
|
||||||
dma_set_callback(DMA_CHANNEL_RECORD, pcm_rec_dma_int_cb);
|
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);
|
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),
|
play_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
|
||||||
SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
|
SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
|
||||||
STDE(0), TIE(1), LINK(0));
|
STDE(0), TIE(1), LINK(0));
|
||||||
aic_dma_desc.sa = PHYSADDR(addr);
|
play_dma_desc.sa = PHYSADDR(addr);
|
||||||
aic_dma_desc.ta = PHYSADDR(JA_AIC_DR);
|
play_dma_desc.ta = PHYSADDR(JA_AIC_DR);
|
||||||
aic_dma_desc.tc = size;
|
play_dma_desc.tc = size;
|
||||||
aic_dma_desc.sd = 0;
|
play_dma_desc.sd = 0;
|
||||||
aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
|
play_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
|
||||||
aic_dma_desc.pad0 = 0;
|
play_dma_desc.pad0 = 0;
|
||||||
aic_dma_desc.pad1 = 0;
|
play_dma_desc.pad1 = 0;
|
||||||
|
|
||||||
commit_dcache_range(&aic_dma_desc, sizeof(dma_desc));
|
commit_dcache_range(&play_dma_desc, sizeof(dma_desc));
|
||||||
commit_dcache_range(addr, size);
|
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_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0));
|
||||||
jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
|
jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
|
||||||
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1));
|
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);
|
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) {
|
if(event == DMA_EVENT_COMPLETE) {
|
||||||
const void* addr;
|
const void* addr;
|
||||||
size_t size;
|
size_t size;
|
||||||
if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &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) {
|
} else if(event == DMA_EVENT_NONE) {
|
||||||
/* ignored, so callers don't need to check for this */
|
/* ignored, so callers don't need to check for this */
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -146,20 +149,20 @@ static void pcm_dma_handle_event(int event)
|
||||||
|
|
||||||
static void pcm_play_dma_int_cb(int event)
|
static void pcm_play_dma_int_cb(int event)
|
||||||
{
|
{
|
||||||
if(aic_lock) {
|
if(play_lock) {
|
||||||
aic_dma_pending_event = event;
|
play_dma_pending_event = event;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
pcm_dma_handle_event(event);
|
play_dma_handle_event(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_play_dma_start(const void* addr, size_t size)
|
void pcm_play_dma_start(const void* addr, size_t size)
|
||||||
{
|
{
|
||||||
aic_dma_pending_event = DMA_EVENT_NONE;
|
play_dma_pending_event = DMA_EVENT_NONE;
|
||||||
aic_state = AIC_STATE_PLAYING;
|
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));
|
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, TDMS(0), ETUR(0), ERPL(0));
|
||||||
jz_writef(AIC_CCR, TFLUSH(1));
|
jz_writef(AIC_CCR, TFLUSH(1));
|
||||||
|
|
||||||
aic_dma_pending_event = DMA_EVENT_NONE;
|
play_dma_pending_event = DMA_EVENT_NONE;
|
||||||
aic_state = AIC_STATE_STOPPED;
|
aic_state &= ~AIC_STATE_PLAYING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_play_lock(void)
|
void pcm_play_lock(void)
|
||||||
{
|
{
|
||||||
++aic_lock;
|
int irq = disable_irq_save();
|
||||||
|
++play_lock;
|
||||||
|
restore_irq(irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_play_unlock(void)
|
void pcm_play_unlock(void)
|
||||||
{
|
{
|
||||||
int irq = disable_irq_save();
|
int irq = disable_irq_save();
|
||||||
if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) {
|
if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) {
|
||||||
pcm_dma_handle_event(aic_dma_pending_event);
|
play_dma_handle_event(play_dma_pending_event);
|
||||||
aic_dma_pending_event = DMA_EVENT_NONE;
|
play_dma_pending_event = DMA_EVENT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_irq(irq);
|
restore_irq(irq);
|
||||||
|
|
@ -193,11 +198,56 @@ void pcm_play_unlock(void)
|
||||||
* Recording
|
* 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)
|
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)
|
void pcm_rec_dma_init(void)
|
||||||
|
|
@ -210,45 +260,64 @@ void pcm_rec_dma_close(void)
|
||||||
|
|
||||||
void pcm_rec_dma_start(void* addr, size_t size)
|
void pcm_rec_dma_start(void* addr, size_t size)
|
||||||
{
|
{
|
||||||
(void)addr;
|
rec_dma_pending_event = DMA_EVENT_NONE;
|
||||||
(void)size;
|
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)
|
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)
|
void pcm_rec_lock(void)
|
||||||
{
|
{
|
||||||
|
int irq = disable_irq_save();
|
||||||
|
++rec_lock;
|
||||||
|
restore_irq(irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_rec_unlock(void)
|
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)
|
const void* pcm_rec_dma_get_peak_buffer(void)
|
||||||
{
|
{
|
||||||
return NULL;
|
return (const void*)UNCACHEDADDR(REG_DMA_CHN_TA(DMA_CHANNEL_RECORD));
|
||||||
}
|
|
||||||
|
|
||||||
void audio_set_output_source(int source)
|
|
||||||
{
|
|
||||||
(void)source;
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_input_mux(int source, unsigned flags)
|
|
||||||
{
|
|
||||||
(void)source;
|
|
||||||
(void)flags;
|
|
||||||
}
|
}
|
||||||
#endif /* HAVE_RECORDING */
|
#endif /* HAVE_RECORDING */
|
||||||
|
|
||||||
|
#ifdef HAVE_PCM_DMA_ADDRESS
|
||||||
|
void* pcm_dma_addr(void* addr)
|
||||||
|
{
|
||||||
|
return (void*)UNCACHEDADDR(addr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void AIC(void)
|
void AIC(void)
|
||||||
{
|
{
|
||||||
if(jz_readf(AIC_SR, TUR)) {
|
if(jz_readf(AIC_SR, TUR)) {
|
||||||
aic_tx_underruns += 1;
|
aic_tx_underruns += 1;
|
||||||
jz_writef(AIC_SR, TUR(0));
|
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 */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue