From 3ec66893e377b088c1284d2d23adb2aeea6d7965 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sat, 27 Feb 2021 22:08:58 +0000 Subject: New port: FiiO M3K on bare metal Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe --- firmware/target/mips/ingenic_x1000/pcm-x1000.c | 165 +++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 firmware/target/mips/ingenic_x1000/pcm-x1000.c (limited to 'firmware/target/mips/ingenic_x1000/pcm-x1000.c') diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c new file mode 100644 index 0000000000..9ae6f5a956 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c @@ -0,0 +1,165 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "system.h" +#include "kernel.h" +#include "audiohw.h" +#include "pcm.h" +#include "pcm-internal.h" +#include "panic.h" +#include "dma-x1000.h" +#include "irq-x1000.h" +#include "x1000/aic.h" + +#define AIC_STATE_STOPPED 0 +#define AIC_STATE_PLAYING 1 + +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 void pcm_dma_int_cb(int event); + +void pcm_play_dma_init(void) +{ + /* Let the target initialize its hardware and setup the AIC */ + audiohw_init(); + + /* Set DMA callback */ + dma_set_callback(DMA_CHANNEL_AUDIO, pcm_dma_int_cb); + + /* Program FIFO threshold -- DMA settings must match */ + jz_writef(AIC_CFG, TFTH(16)); + + /* Ensure all playback is disabled */ + jz_writef(AIC_CCR, ERPL(0)); + + /* Enable the controller */ + jz_writef(AIC_CFG, ENABLE(1)); + + /* Enable interrupts */ + system_enable_irq(IRQ_AIC); +} + +void pcm_play_dma_postinit(void) +{ + audiohw_postinit(); +} + +void pcm_dma_apply_settings(void) +{ + audiohw_set_frequency(pcm_fsel); +} + +static void pcm_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)); + commit_dcache_range(addr, size); + + REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_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)); + + pcm_play_dma_status_callback(PCM_DMAST_STARTED); +} + +static void pcm_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); + } else if(event == DMA_EVENT_NONE) { + /* ignored, so callers don't need to check for this */ + } else { + pcm_play_dma_status_callback(PCM_DMAST_ERR_DMA); + } +} + +static void pcm_dma_int_cb(int event) +{ + if(aic_lock) { + aic_dma_pending_event = event; + return; + } else { + pcm_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; + + pcm_dma_start(addr, size); + jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1)); +} + +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; +} + +void pcm_play_lock(void) +{ + ++aic_lock; +} + +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; + } + + restore_irq(irq); +} + +void AIC(void) +{ + if(jz_readf(AIC_SR, TUR)) { + aic_tx_underruns += 1; + jz_writef(AIC_SR, TUR(0)); + } +} -- cgit v1.2.3