summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2011-09-06 12:38:22 +0000
committerMarcin Bukat <marcin.bukat@gmail.com>2011-09-06 12:38:22 +0000
commit472314ba734ce2d42e14cd44ba55d7600c98d222 (patch)
treee722f41da3d9a395a9996fc7dad03aa0edbab124
parent727b98e7004519148b6514419364c96f545e3478 (diff)
downloadrockbox-472314ba734ce2d42e14cd44ba55d7600c98d222.zip
rockbox-472314ba734ce2d42e14cd44ba55d7600c98d222.tar.gz
rockbox-472314ba734ce2d42e14cd44ba55d7600c98d222.tar.bz2
rockbox-472314ba734ce2d42e14cd44ba55d7600c98d222.tar.xz
rk27xx - implement pcm driver.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30444 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/target/arm/rk27xx/pcm-rk27xx.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/firmware/target/arm/rk27xx/pcm-rk27xx.c b/firmware/target/arm/rk27xx/pcm-rk27xx.c
new file mode 100644
index 0000000..87830f4
--- /dev/null
+++ b/firmware/target/arm/rk27xx/pcm-rk27xx.c
@@ -0,0 +1,238 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * 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 "audio.h"
+#include "string.h"
+#include "panic.h"
+#include "audiohw.h"
+#include "sound.h"
+#include "pcm-internal.h"
+
+static int locked = 0;
+
+/* Mask the DMA interrupt */
+void pcm_play_lock(void)
+{
+ if (++locked == 1)
+ {
+ int old = disable_irq_save();
+ INTC_IMR &= ~(1<<12); /* mask HDMA interrupt */
+ restore_irq(old);
+ }
+}
+
+/* Unmask the DMA interrupt if enabled */
+void pcm_play_unlock(void)
+{
+ if(--locked == 0)
+ {
+ int old = disable_irq_save();
+ INTC_IMR |= (1<<12); /* unmask HDMA interrupt */
+ restore_irq(old);
+ }
+}
+
+void pcm_play_dma_stop(void)
+{
+ HDMA_CON0 = 0x00;
+ HDMA_ISR = 0x00;
+
+ locked = 1;
+}
+
+static void hdma_i2s_transfer(const void *addr, size_t size)
+{
+ SCU_CLKCFG &= ~(1<<3); /* enable HDMA clock */
+
+ commit_discard_dcache_range(addr, size);
+
+ HDMA_ISRC0 = (uint32_t)addr; /* source address */
+ HDMA_IDST0 = (uint32_t)&I2S_TXR; /* i2s tx fifo */
+ HDMA_ICNT0 = (uint16_t)((size>>2) - 1); /* number of dma transactions
+ * of transfer size bytes
+ * (zero based)
+ */
+
+ HDMA_ISR = ((1<<13) | /* mask ch1 accumulation overflow irq */
+ (1<<12) | /* mask ch0 accumulation overflow irq */
+ (1<<11) | /* mask ch1 page count down irq */
+ (0<<10) | /* UNMASK ch0 page count down irq */
+ (1<<9) | /* mask ch0 transfer irq */
+ (1<<8) | /* mask ch1 transfer irq */
+ (0<<5) | /* clear ch1 accumulation overflow flag */
+ (0<<4) | /* clear ch0 accumulation overflow flag */
+ (0<<3) | /* clear ch1 count down to zero flag */
+ (0<<2) | /* clear ch0 count down to zero flag */
+ (0<<1) | /* clear ch1 active flag */
+ (0<<0)); /* clear ch0 active flag */
+
+ HDMA_ISCNT0 = 0x07; /* slice size in transfer size units (zero base) */
+
+ HDMA_IPNCNTD0 = 0x01; /* page count */
+
+ HDMA_CON0 = ((0<<23) | /* page mode */
+ (1<<22) | /* slice mode */
+ (1<<21) | /* DMA enable */
+ (1<<18) | /* generate interrupt */
+ (0<<16) | /* on-the-fly is not supported by rk27xx */
+ (5<<13) | /* transfer mode inc8 */
+ (6<<9) | /* external hdreq from i2s tx */
+ (0<<7) | /* increment source address */
+ (1<<5) | /* fixed destination address */
+ (2<<3) | /* transfer size = 32bits word */
+ (0<<1) | /* command of software DMA (not relevant) */
+ (1<<0)); /* hardware trigger DMA mode */
+}
+
+void pcm_play_dma_start(const void *addr, size_t size)
+{
+ /* Stop any DMA in progress */
+ pcm_play_dma_stop();
+
+ /* kick in DMA transfer */
+ hdma_i2s_transfer(addr, size);
+}
+
+/* pause DMA transfer by disabling clock to DMA module */
+void pcm_play_dma_pause(bool pause)
+{
+ if(pause)
+ {
+ SCU_CLKCFG |= (1<<3);
+ locked = 1;
+ }
+ else
+ {
+ SCU_CLKCFG &= ~(1<<3);
+ locked = 0;
+ }
+}
+
+static void i2s_init(void)
+{
+#if defined(HAVE_RK27XX_CODEC)
+ /* iomux I2S internal */
+ SCU_IOMUXA_CON &= ~(1<<18); /* i2s external bit */
+ SCU_IOMUXB_CON &= ~((1<<4) | /* i2s_mclk */
+ (1<<3) | /* i2s_sdo */
+ (1<<2) | /* i2s_sdi */
+ (1<<1) | /* i2s_lrck */
+ (1<<0)); /* i2s_bck */
+#else
+ /* iomux I2S external */
+ SCU_IOMUXA_CON |= (1<<18); /* i2s external bit */
+ SCU_IOMUXB_CON |= ((1<<4) | /* i2s_mclk */
+ (1<<3) | /* i2s_sdo */
+ (1<<2) | /* i2s_sdi */
+ (1<<1) | /* i2s_lrck */
+ (1<<0)); /* i2s_bck */
+#endif
+
+ /* enable i2s clocks */
+ SCU_CLKCFG &= ~((1<<17) | /* i2s_pclk */
+ (1<<16)); /* i2s_clk */
+
+ /* configure I2S module */
+ I2S_IER = 0; /* disable all i2s interrupts */
+
+ I2S_TXCTL = (1<<16) | /* LRCK/SCLK = 64 */
+ (4<<8) | /* MCLK/SCLK = 4 */
+ (1<<4) | /* 16bit samples */
+ (0<<3) | /* stereo */
+ (0<<1) | /* I2S IF */
+ (0<<0); /* slave mode */
+
+ /* the fifo is 16x32bits according to my tests
+ * while the docs state 32x32bits
+ */
+ I2S_FIFOSTS = (1<<18) | /* Tx trigger level half full */
+ (1<<16); /* Rx trigger level half full */
+
+ I2S_OPR = (1<<17) | /* reset Tx */
+ (1<<16) | /* reset Rx */
+ (0<<6) | /* HDMA Req1 enable */
+ (1<<5) | /* HDMA Req2 disable */
+ (0<<4) | /* Req1 for Tx fifo */
+ (1<<3) | /* Req2 for Rx fifo */
+ (0<<2) | /* normal operation */
+ (0<<1) | /* start Tx (in master mode) */
+ (0<<0); /* start Rx (in master mode) */
+}
+
+void pcm_play_dma_init(void)
+{
+ /* unmask HDMA interrupt in INTC */
+ INTC_IMR |= (1<<12);
+ INTC_IECR |= (1<<12);
+
+ audiohw_preinit();
+
+ i2s_init();
+}
+
+void pcm_play_dma_postinit(void)
+{
+ audiohw_postinit();
+}
+
+void pcm_dma_apply_settings(void)
+{
+ /* I2S module runs in slave mode */
+ return;
+}
+
+size_t pcm_get_bytes_waiting(void)
+{
+ /* current terminate count is in transfer size units (4bytes here) */
+ return (HDMA_CCNT0 & 0xffff)<<2;
+}
+
+/* audio DMA ISR called when chunk from callers buffer has been transfered */
+void INT_HDMA(void)
+{
+ void *start;
+ size_t size;
+
+ pcm_play_get_more_callback(&start, &size);
+
+ if (size != 0)
+ {
+ hdma_i2s_transfer(start, size);
+ pcm_play_dma_started_callback();
+ }
+}
+
+const void * pcm_play_dma_get_peak_buffer(int *count)
+{
+ uint32_t addr;
+
+ int old = disable_irq_save();
+ addr = HDMA_CSRC0;
+ *count = ((HDMA_CCNT0 & 0xffff)<<2);
+ restore_interrupt(old);
+
+ return (void*)addr;
+}
+
+/****************************************************************************
+ ** Recording DMA transfer
+ **/
+/* TODO */