diff options
author | Bertrik Sikken <bertrik@sikken.nl> | 2009-07-05 13:41:16 +0000 |
---|---|---|
committer | Bertrik Sikken <bertrik@sikken.nl> | 2009-07-05 13:41:16 +0000 |
commit | 9b1c774218e59fbcb7160709687686275ee04c13 (patch) | |
tree | a003f64e70e7b54ca31d8a9e6f508123d755d687 | |
parent | eec5842e285ab885a7ad5b1f94eb9078894528bf (diff) |
S5L8700: initial framework for PCM (using DMA transfers)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21656 a1c6a512-1295-4272-9138-f99709370657
-rwxr-xr-x | firmware/target/arm/s5l8700/dma-s5l8700.c | 119 | ||||
-rwxr-xr-x | firmware/target/arm/s5l8700/dma-target.h | 41 | ||||
-rw-r--r-- | firmware/target/arm/s5l8700/pcm-s5l8700.c | 276 |
3 files changed, 436 insertions, 0 deletions
diff --git a/firmware/target/arm/s5l8700/dma-s5l8700.c b/firmware/target/arm/s5l8700/dma-s5l8700.c new file mode 100755 index 0000000000..f766cef98a --- /dev/null +++ b/firmware/target/arm/s5l8700/dma-s5l8700.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright © 2009 Bertrik Sikken + * + * 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 "config.h" + +#include "s5l8700.h" +#include "dma-target.h" +#include "panic.h" +#include "system.h" + +/* Driver for the IODMA part of the s5l8700 + + When requesting a DMA transfer the supplied callback is stored and called + upon completion of the DMA transfer (callback runs in interrupt context). + */ + + +#define DMAC_BASE 0x38400000 + +#define DMABASE(c) (*(volatile unsigned int*)(DMAC_BASE+0x00+(0x20*c))) +#define DMACON(c) (*(volatile unsigned int*)(DMAC_BASE+0x04+(0x20*c))) +#define DMATCNT(c) (*(volatile unsigned int*)(DMAC_BASE+0x08+(0x20*c))) +#define DMACADDR(c) (*(volatile unsigned int*)(DMAC_BASE+0x0C+(0x20*c))) +#define DMACTCNT(c) (*(volatile unsigned int*)(DMAC_BASE+0x10+(0x20*c))) +#define DMACOM(c) (*(volatile unsigned int*)(DMAC_BASE+0x14+(0x20*c))) +#define DMANOFF(c) (*(volatile unsigned int*)(DMAC_BASE+0x18+(0x20*c))) + +#define DMACOM_HOLD 2 /* only allowed on channel 0 */ +#define DMACOM_SKIP 3 /* only allowed on channel 0 */ +#define DMACOM_CHAN_ON 4 +#define DMACOM_CHAN_OFF 5 +#define DMACOM_CLEAR_HCOM 6 +#define DMACOM_CLEAR_HCOM_WCOM 7 + + +/* one completion callback for each channel */ +static void (*dma_callback[4])(void); + + +void dma_init(void) +{ + int i; + + for (i = 0; i < 4; i++) { + dma_callback[i] = NULL; + dma_disable_channel(i); + } + + INTMSK |= (1 << 10); +} + +/* setup a DMA transfer, but do not start it yet */ +void dma_setup_channel(int channel, int sel, int dir, int dsize, int blen, + void *addr, size_t size, void (*callback)(void)) +{ + dma_callback[channel] = callback; + + DMACON(channel) = (sel << 30) | /* DEVSEL */ + (dir << 29) | /* DIR */ + (0 << 24) | /* SCHCNT */ + (dsize << 22) | /* DSIZE */ + (blen << 19) | /* BLEN */ + (0 << 18) | /* RELOAD */ + (0 << 17) | /* HCOMINT */ + (1 << 16) | /* WCOMINT */ + (0 << 0); /* OFFSET */ + DMABASE(channel) = (unsigned int)addr; + DMATCNT(channel) = size; +} + +void dma_enable_channel(int channel) +{ + DMACOM(channel) = DMACOM_CHAN_ON; +} + +void dma_disable_channel(int channel) +{ + DMACOM(channel) = DMACOM_CHAN_OFF; +} + +/* interrupt handler for all DMA channels */ +void INT_DMA(void) +{ + unsigned int mask; + int channel; + + mask = (1 << 0) | /* WCOMx interrupt bit */ + (1 << 1); /* HCOMx interrupt bit */ + for (channel = 0; channel < 4; channel++) { + if (DMAALLST & mask) { + /* clear half and whole completion bits */ + DMACOM(channel) = DMACOM_CLEAR_HCOM_WCOM; + + if (dma_callback[channel]) { + dma_callback[channel](); + } + } + mask <<= 4; + } +} + diff --git a/firmware/target/arm/s5l8700/dma-target.h b/firmware/target/arm/s5l8700/dma-target.h new file mode 100755 index 0000000000..6bb3a92bbd --- /dev/null +++ b/firmware/target/arm/s5l8700/dma-target.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright © 2009 Bertrik Sikken + * + * 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> + +/* generic DMA definitions */ +#define DMA_IO_TO_MEM 0 +#define DMA_MEM_TO_IO 1 + +/* IISOUT DMA settings */ +#define DMA_IISOUT_CHANNEL 0 +#define DMA_IISOUT_SELECT 0 +#define DMA_IISOUT_DSIZE 1 /* 1 = 16-bit/transfer */ +#define DMA_IISOUT_BLEN 0 /* 0 = 1 transfer/burst */ + +void dma_init(void); + +void dma_setup_channel(int channel, int sel, int dir, int dsize, int blen, + void *addr, size_t size, void (*callback)(void)); + +void dma_enable_channel(int channel); +void dma_disable_channel(int channel); + diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c new file mode 100644 index 0000000000..38b581b80c --- /dev/null +++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c @@ -0,0 +1,276 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright © 2009 Bertrik Sikken + * + * 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 <string.h> + +#include "config.h" +#include "system.h" +#include "audio.h" +#include "s5l8700.h" +#include "panic.h" +#include "audiohw.h" +#include "pcm.h" +#include "pcm_sampr.h" +#include "dma-target.h" + +/* Driver for the IIS/PCM part of the s5l8700 using DMA + + Notes: + - not all possible PCM sample rates are enabled (no support in codec driver) + - pcm_play_dma_pause is untested, not sure if implemented the right way + - pcm_play_dma_stop is untested, not sure if implemented the right way + - pcm_play_dma_get_peak_buffer is not implemented + - recording is not implemented +*/ + +static void dma_callback(void); +static volatile int locked = 0; + +/* table of recommended PLL/MCLK dividers for mode 256Fs from the datasheet */ +static const struct div_entry { + int pdiv, mdiv, sdiv, cdiv; +} div_table[HW_NUM_FREQ] = { + [HW_FREQ_11] = { 26, 189, 3, 8}, + [HW_FREQ_22] = { 50, 98, 2, 8}, + [HW_FREQ_44] = { 37, 151, 1, 9}, + [HW_FREQ_88] = { 50, 98, 1, 4}, +#if 0 /* disabled because the codec driver does not support it (yet) */ + [HW_FREQ_8 ] = { 28, 192, 3, 12} + [HW_FREQ_16] = { 28, 192, 3, 6}, + [HW_FREQ_32] = { 28, 192, 2, 6}, + + [HW_FREQ_12] = { 28, 192, 3, 8}, + [HW_FREQ_24] = { 28, 192, 2, 8}, + [HW_FREQ_48] = { 28, 192, 2, 4}, + [HW_FREQ_96] = { 28, 192, 1, 4}, +#endif +}; + +/* Mask the DMA interrupt */ +void pcm_play_lock(void) +{ + if (locked++ == 0) { + INTMSK &= ~(1 << 10); + } +} + +/* Unmask the DMA interrupt if enabled */ +void pcm_play_unlock(void) +{ + if (--locked == 0) { + INTMSK |= ~(1 << 10); + } +} + +static void play_next(void *addr, size_t size) +{ + /* setup DMA */ + dma_setup_channel(DMA_IISOUT_CHANNEL, DMA_IISOUT_SELECT, DMA_MEM_TO_IO, + DMA_IISOUT_DSIZE, DMA_IISOUT_BLEN, addr, size / 2, + dma_callback); + + /* DMA channel on */ + dma_enable_channel(DMA_IISOUT_CHANNEL); +} + +static void dma_callback(void) +{ + unsigned char *dma_start_addr; + size_t dma_size; + + register pcm_more_callback_type get_more = pcm_callback_for_more; + if (get_more) { + get_more(&dma_start_addr, &dma_size); + + if (dma_size == 0) { + pcm_play_dma_stop(); + pcm_play_dma_stopped_callback(); + } + else { + play_next(dma_start_addr, dma_size); + } + } +} + +void pcm_play_dma_start(const void *addr, size_t size) +{ + /* S1: DMA channel 0 set */ + dma_setup_channel(DMA_IISOUT_CHANNEL, DMA_IISOUT_SELECT, DMA_MEM_TO_IO, + DMA_IISOUT_DSIZE, DMA_IISOUT_BLEN, (void *)addr, size / 2, + dma_callback); + + /* S2: IIS Tx mode set */ + I2STXCON = (DMA_IISOUT_BLEN << 16) | /* burst length */ + (0 << 15) | /* 0 = falling edge */ + (0 << 13) | /* 0 = basic I2S format */ + (0 << 12) | /* 0 = MSB first */ + (0 << 11) | /* 0 = left channel for low polarity */ + (3 << 8) | /* MCLK divider */ + (0 << 5) | /* 0 = 16-bit */ + (0 << 3) | /* bit clock per frame */ + (1 << 0); /* channel index */ + + /* S3: DMA channel 0 on */ + dma_enable_channel(DMA_IISOUT_CHANNEL); + + /* S4: IIS Tx clock on */ + I2SCLKCON = (1 << 0); /* 1 = power on */ + + /* S5: IIS Tx on */ + I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ + (1 << 2) | /* 1 = I2S interface enable */ + (1 << 1) | /* 1 = DMA request enable */ + (0 << 0); /* 0 = LRCK on */ +} + +void pcm_play_dma_stop(void) +{ + /* DMA channel off */ + dma_disable_channel(DMA_IISOUT_CHANNEL); + + /* TODO Some time wait */ + /* LRCK half cycle wait */ + + /* IIS Tx off */ + I2STXCOM = (1 << 3) | /* 1 = transmit mode on */ + (0 << 2) | /* 1 = I2S interface enable */ + (1 << 1) | /* 1 = DMA request enable */ + (0 << 0); /* 0 = LRCK on */ +} + +/* pause playback by disabling further DMA requests */ +void pcm_play_dma_pause(bool pause) +{ + if (pause) { + I2STXCOM &= ~(1 << 1); /* DMA request enable */ + } + else { + I2STXCOM |= (1 << 1); /* DMA request enable */ + } +} + +void pcm_play_dma_init(void) +{ + /* configure IIS pins */ + PCON7 = (PCON7 & ~(0x0FFFFF00)) | 0x02222200; + + /* enable clock to the IIS module */ + PWRCON &= ~(1 << 6); + + audiohw_preinit(); +} + +void pcm_postinit(void) +{ + audiohw_postinit(); +} + +/* set the configured PCM frequency */ +void pcm_dma_apply_settings(void) +{ + audiohw_set_frequency(pcm_sampr); + + struct div_entry div = div_table[pcm_fsel]; + + /* configure PLL1 and MCLK for the desired sample rate */ + PLL1PMS = (div.pdiv << 16) | + (div.mdiv << 8) | + (div.sdiv << 0); + PLL1LCNT = 7500; /* no idea what to put here */ + + /* enable PLL1 and wait for lock */ + PLLCON |= (1 << 1); + while ((PLLLOCK & (1 << 1)) == 0); + + /* configure MCLK */ + CLKCON = (CLKCON & ~(0xFF)) | + (0 << 7) | /* MCLK_MASK */ + (2 << 5) | /* MCLK_SEL = PLL1 */ + (1 << 4) | /* MCLK_DIV_ON */ + (div.cdiv - 1); /* MCLK_DIV_VAL */ +} + +size_t pcm_get_bytes_waiting(void) +{ + return DMATCNT0 * 2; +} + +const void * pcm_play_dma_get_peak_buffer(int *count) +{ + /* currently not supported */ + *count = 0; + return 0; +} + +#ifdef HAVE_PCM_DMA_ADDRESS +void * pcm_dma_addr(void *addr) +{ + if (addr != NULL) + addr = UNCACHED_ADDR(addr); + return addr; +} +#endif + + +/**************************************************************************** + ** Recording DMA transfer + **/ +#ifdef HAVE_RECORDING +void pcm_rec_lock(void) +{ +} + +void pcm_rec_unlock(void) +{ +} + +void pcm_record_more(void *start, size_t size) +{ + (void)start; + (void)size; +} + +void pcm_rec_dma_stop(void) +{ +} + +void pcm_rec_dma_start(void *addr, size_t size) +{ + (void)addr; + (void)size; +} + +void pcm_rec_dma_close(void) +{ +} + + +void pcm_rec_dma_init(void) +{ +} + + +const void * pcm_rec_dma_get_peak_buffer(int *count) +{ + (void)count; +} + +#endif /* HAVE_RECORDING */ + |