summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c')
-rw-r--r--firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
new file mode 100644
index 0000000000..237bf264f5
--- /dev/null
+++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
@@ -0,0 +1,292 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 by Michael Sevakis
+ *
+ * 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 <stdlib.h>
+#include "system.h"
+#include "kernel.h"
+#include "logf.h"
+#include "audio.h"
+#include "sound.h"
+#include "file.h"
+
+/* PCM interrupt routine lockout */
+static struct
+{
+ int locked;
+ unsigned long state;
+} dma_play_lock =
+{
+ .locked = 0,
+ .state = 0,
+};
+
+#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
+
+/* Setup for the DMA controller */
+#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
+
+#ifdef HAVE_UDA1341
+/* for PCLK = 50 MHz, frame size = 32 */
+/* [prescaler, master clock rate] */
+static const unsigned char pcm_freq_parms[HW_NUM_FREQ][2] =
+{
+ [HW_FREQ_64] = { 2, IISMOD_MASTER_CLOCK_256FS },
+ [HW_FREQ_44] = { 3, IISMOD_MASTER_CLOCK_384FS },
+ [HW_FREQ_22] = { 8, IISMOD_MASTER_CLOCK_256FS },
+ [HW_FREQ_11] = { 17, IISMOD_MASTER_CLOCK_256FS },
+};
+#endif
+
+/* DMA count has hit zero - no more data */
+/* Get more data from the callback and top off the FIFO */
+void fiq_handler(void) __attribute__((interrupt ("FIQ")));
+
+/* Mask the DMA interrupt */
+void pcm_play_lock(void)
+{
+ if (++dma_play_lock.locked == 1)
+ s3c_regset32(&INTMSK, DMA2_MASK);
+}
+
+/* Unmask the DMA interrupt if enabled */
+void pcm_play_unlock(void)
+{
+ if (--dma_play_lock.locked == 0)
+ s3c_regclr32(&INTMSK, dma_play_lock.state);
+}
+
+void pcm_play_dma_init(void)
+{
+ /* There seem to be problems when changing the IIS interface configuration
+ * when a clock is not present.
+ */
+ s3c_regset32(&CLKCON, 1<<17);
+
+#ifdef HAVE_UDA1341
+ /* master, transmit mode, 16 bit samples, BCLK 32fs, PCLK */
+ IISMOD = IISMOD_MASTER_CLOCK_PCLK | IISMOD_MASTER_MODE | IISMOD_TRANSMIT_MODE
+ | IISMOD_16_BIT | IISMOD_MASTER_CLOCK_256FS | IISMOD_BIT_CLOCK_32FS;
+
+ /* TX idle, enable prescaler */
+ IISCON |= IISCON_TX_IDLE | IISCON_IIS_PRESCALER_ENABLE;
+#else
+ /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
+ BCLK 32fs */
+ IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0);
+
+ /* RX,TX off,on */
+ IISCON |= (1<<3) | (1<<2);
+#endif
+
+ s3c_regclr32(&CLKCON, 1<<17);
+
+ audiohw_init();
+
+ /* init GPIO */
+#ifdef GIGABEAT_F
+/* GPCCON = (GPCCON & ~(3<<14)) | (1<<14); */
+ S3C244_GPIO_CONFIG (GPCCON, 7, GPIO_OUTPUT);
+ GPCDAT |= (1<<7);
+#endif
+
+ /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
+ GPECON = (GPECON & ~0x3ff) | 0x2aa;
+
+ /* Do not service DMA requests, yet */
+
+ /* clear any pending int and mask it */
+ s3c_regset32(&INTMSK, DMA2_MASK);
+ SRCPND = DMA2_MASK;
+
+ /* connect to FIQ */
+ s3c_regset32(&INTMOD, DMA2_MASK);
+}
+
+void pcm_postinit(void)
+{
+ audiohw_postinit();
+}
+
+void pcm_dma_apply_settings(void)
+{
+#ifdef HAVE_UDA1341
+ /* set prescaler and master clock rate according to freq */
+ IISPSR = (pcm_freq_parms [pcm_fsel][0] * IISPSR_PRESCALER_A) | pcm_freq_parms [pcm_fsel][0];
+ IISMOD |= ~IISMOD_MASTER_CLOCK_384FS | pcm_freq_parms [pcm_fsel][1] ;
+#endif
+
+ audiohw_set_frequency(pcm_fsel);
+}
+
+/* Connect the DMA and start filling the FIFO */
+static void play_start_pcm(void)
+{
+ /* clear pending DMA interrupt */
+ SRCPND = DMA2_MASK;
+
+ /* Flush any pending writes */
+ clean_dcache_range((char*)DISRC2-0x30000000, (DCON2 & 0xFFFFF) * 2);
+
+ /* unmask DMA interrupt when unlocking */
+ dma_play_lock.state = DMA2_MASK;
+
+ /* turn on the request */
+ IISCON |= (1<<5);
+
+ /* Activate the channel */
+ DMASKTRIG2 = 0x2;
+
+ /* turn off the idle */
+ IISCON &= ~(1<<3);
+
+ /* start the IIS */
+ IISCON |= (1<<0);
+}
+
+/* Disconnect the DMA and wait for the FIFO to clear */
+static void play_stop_pcm(void)
+{
+ /* Mask DMA interrupt */
+ s3c_regset32(&INTMSK, DMA2_MASK);
+
+ /* De-Activate the DMA channel */
+ DMASKTRIG2 = 0x4;
+
+ /* are we playing? wait for the chunk to finish */
+ if (dma_play_lock.state != 0)
+ {
+ /* wait for the FIFO to empty and DMA to stop */
+ while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
+ }
+
+ /* Keep interrupt masked when unlocking */
+ dma_play_lock.state = 0;
+
+ /* turn off the request */
+ IISCON &= ~(1<<5);
+
+ /* turn on the idle */
+ IISCON |= (1<<3);
+
+ /* stop the IIS */
+ IISCON &= ~(1<<0);
+}
+
+void pcm_play_dma_start(const void *addr, size_t size)
+{
+ /* Enable the IIS clock */
+ s3c_regset32(&CLKCON, 1<<17);
+
+ /* stop any DMA in progress - idle IIS */
+ play_stop_pcm();
+
+ /* connect DMA to the FIFO and enable the FIFO */
+ IISFCON = (1<<15) | (1<<13);
+
+ /* set DMA dest */
+ DIDST2 = (unsigned int)&IISFIFO;
+
+ /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
+ DIDSTC2 = 0x03;
+
+ /* set DMA source and options */
+ DISRC2 = (unsigned int)addr + 0x30000000;
+ /* How many transfers to make - we transfer half-word at a time = 2 bytes */
+ /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
+ /* no auto-reload, half-word (16bit) */
+ DCON2 = DMA_CONTROL_SETUP | (size / 2);
+ DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
+
+ play_start_pcm();
+}
+
+/* Promptly stop DMA transfers and stop IIS */
+void pcm_play_dma_stop(void)
+{
+ play_stop_pcm();
+
+ /* Disconnect the IIS clock */
+ s3c_regclr32(&CLKCON, 1<<17);
+}
+
+void pcm_play_dma_pause(bool pause)
+{
+ if (pause)
+ {
+ /* pause playback on current buffer */
+ play_stop_pcm();
+ }
+ else
+ {
+ /* restart playback on current buffer */
+ /* make sure we're aligned on left channel - skip any right
+ channel sample left waiting */
+ DISRC2 = (DCSRC2 + 2) & ~0x3;
+ DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
+ play_start_pcm();
+ }
+}
+
+void fiq_handler(void)
+{
+ static unsigned char *start;
+ static size_t size;
+ register pcm_more_callback_type get_more; /* No stack for this */
+
+ /* clear any pending interrupt */
+ SRCPND = DMA2_MASK;
+
+ /* Buffer empty. Try to get more. */
+ get_more = pcm_callback_for_more;
+ size = 0;
+
+ if (get_more == NULL || (get_more(&start, &size), size == 0))
+ {
+ /* Callback missing or no more DMA to do */
+ pcm_play_dma_stop();
+ pcm_play_dma_stopped_callback();
+ }
+ else
+ {
+ /* Flush any pending cache writes */
+ clean_dcache_range(start, size);
+
+ /* set the new DMA values */
+ DCON2 = DMA_CONTROL_SETUP | (size >> 1);
+ DISRC2 = (unsigned int)start + 0x30000000;
+
+ /* Re-Activate the channel */
+ DMASKTRIG2 = 0x2;
+ }
+}
+
+size_t pcm_get_bytes_waiting(void)
+{
+ /* lie a little and only return full pairs */
+ return (DSTAT2 & 0xFFFFE) * 2;
+}
+
+const void * pcm_play_dma_get_peak_buffer(int *count)
+{
+ unsigned long addr = DCSRC2;
+ int cnt = DSTAT2;
+ *count = (cnt & 0xFFFFF) >> 1;
+ return (void *)((addr + 2) & ~3);
+}