Gigabeat S: Implement an SDMA API and use it in the PCM driver. Some other miscellaneous adjustments to recording and PCM buffer to accomodate use of physical addresses and cache coherency.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19949 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2009-02-08 22:32:41 +00:00
parent 0222d0a5eb
commit 94537f954e
21 changed files with 3002 additions and 141 deletions

View file

@ -1001,6 +1001,11 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
/* Give 5ms clearance. */
bufstart += NATIVE_FREQUENCY * 4 / 200;
#ifdef HAVE_PCM_DMA_ADDRESS
/* Returned peak addresses are DMA addresses */
bufend = pcm_dma_addr(bufend);
#endif
/* Wrapped above? */
if (bufstart >= bufend)
bufstart -= pcmbuf_size;

View file

@ -1148,8 +1148,8 @@ static void pcmrec_init(void)
buffer = audio_get_recording_buffer(&rec_buffer_size);
/* Line align pcm_buffer 2^4=16 bytes */
pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
/* Line align pcm_buffer 2^5=32 bytes */
pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
PCM_MAX_FEED_SIZE, 2);
/* Adjust available buffer for possible align advancement */

View file

@ -756,7 +756,6 @@ target/arm/imx31/gigabeat-s/avic-imx31.c
target/arm/imx31/gigabeat-s/backlight-imx31.c
target/arm/imx31/gigabeat-s/button-imx31.c
target/arm/imx31/gigabeat-s/clkctl-imx31.c
target/arm/imx31/gigabeat-s/dma_start.c
target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c
target/arm/imx31/gigabeat-s/gpio-imx31.c
target/arm/imx31/gigabeat-s/kernel-imx31.c
@ -773,6 +772,7 @@ target/arm/imx31/gigabeat-s/system-imx31.c
target/arm/imx31/gigabeat-s/usb-imx31.c
target/arm/imx31/gigabeat-s/wmcodec-imx31.c
#ifndef BOOTLOADER
target/arm/imx31/sdma-imx31.c
target/arm/imx31/gigabeat-s/audio-gigabeat-s.c
target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c

View file

@ -214,6 +214,8 @@
/* Define this if you have adjustable CPU frequency */
/* #define HAVE_ADJUSTABLE_CPU_FREQ */
#define HAVE_PCM_DMA_ADDRESS
#define BOOTFILE_EXT "gigabeat"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"

View file

@ -118,6 +118,22 @@
#define WDOG1_BASE_ADDR WDOG_BASE_ADDR
#define CRM_MCU_BASE_ADDR CCM_BASE_ADDR
/* IIM */
#define IIM_PREV (*(REG32_PTR_T)(IIM_BASE_ADDR + 0x20))
#define IIM_PREV_SIG (0x1f << 3)
#define IIM_PREV_SIG_IMX31 (0x01 << 3) /* i.MX31 */
#define IIM_SREV (*(REG32_PTR_T)(IIM_BASE_ADDR + 0x24))
#define IIM_SREV_SREV (0xff << 0)
#define IIM_SREV_1_0 0x00 /* i.MX31/L 1.0, L38W */
#define IIM_SREV_1_1 0x10 /* i.MX31 1.1, 2L38W */
#define IIM_SREV_1_1L 0x11 /* i.MX31L 1.1, 2L38W */
#define IIM_SREV_1_15 0x12 /* i.MX31 1.15, 2L38W/3L38W */
#define IIM_SREV_1_15L 0x13 /* i.MX31L 1.15, 2L38W/3L38W */
#define IIM_SREV_1_2 0x14 /* i.MX31 1.2, 3L38W, M45G */
#define IIM_SREV_1_2L 0x15 /* i.MX31L 1.2, 3L38W, M45G */
#define IIM_SREV_2_0_1 0x28 /* i.MX31 2.0/2.0.1, M91E */
#define IIM_SREV_2_0_1L 0x29 /* i.MX31L 2.0/2.0.1, M91E */
/* IOMUXC */
#define IOMUXC_(o) (*(REG32_PTR_T)(IOMUXC_BASE_ADDR+(o)))
@ -1618,6 +1634,141 @@
#define EUARTUTS_RXFULL (1 << 3) // RxFIFO full
#define EUARTUTS_SOFTRST (1 << 0) // Software reset
/* SDMA */
#define SDMA_MC0PTR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x000))
#define SDMA_INTR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x004))
#define SDMA_STOP_STAT (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x008))
#define SDMA_HSTART (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x00C))
#define SDMA_EVTOVR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x010))
#define SDMA_DSPOVR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x014))
#define SDMA_HOSTOVR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x018))
#define SDMA_EVTPEND (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x01C))
#define SDMA_DSPENBL (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x020))
#define SDMA_RESET (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x024))
#define SDMA_EVTERR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x028))
#define SDMA_INTRMSK (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x02C))
#define SDMA_PSW (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x030))
#define SDMA_EVTERRDBG (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x034))
#define SDMA_CONFIG (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x038))
#define SDMA_ONCE_ENB (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x040))
#define SDMA_ONCE_DATA (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x044))
#define SDMA_ONCE_INSTR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x048))
#define SDMA_ONCE_STAT (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x04C))
#define SDMA_ONCE_CMD (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x050))
#define SDMA_EVT_MIRROR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x054))
#define SDMA_ILLINSTADDR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x058))
#define SDMA_CHN0ADDR (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x05C))
#define SDMA_ONCE_RTB (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x060))
#define SDMA_XTRIG_CONF1 (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x070))
#define SDMA_XTRIG_CONF2 (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x074))
/* SDMA_CHNENBL: 0x080 - 0x0FC */
#define SDMA_CHNENBL(n) (((REG32_PTR_T)(SDMA_BASE_ADDR + 0x080))[n]) /* 0..31 */
/* SDMA_CHNPRI: 0x100 - 0x17C */
#define SDMA_CHNPRI(n) (((REG32_PTR_T)(SDMA_BASE_ADDR + 0x100))[n]) /* 0..31 */
#define SDMA_ONCE_COUNT (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x200))
#define SDMA_ONCE_ECTL (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x204))
#define SDMA_ONCE_EAA (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x208))
#define SDMA_ONCE_EAB (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x20C))
#define SDMA_ONCE_EAM (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x210))
#define SDMA_ONCE_ED (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x214))
#define SDMA_ONCE_EDM (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x218))
#define SDMA_ONCE_PCMATCH (*(REG32_PTR_T)(SDMA_BASE_ADDR + 0x21C))
/* SDMA_RESET */
#define SDMA_RESET_RESCHED (0x1 << 1)
#define SDMA_RESET_RESET (0x1 << 0)
/* SDMA_PSW */
#define SDMA_PSW_NCP (0x7 << 13)
#define SDMA_PSW_NCR (0x1f << 8)
#define SDMA_PSW_CCP (0x7 << 5)
#define SDMA_PSW_CCR (0x1f << 0)
/* SDMA_CONFIG */
#define SDMA_CONFIG_DSPDMA (0x1 << 12)
#define SDMA_CONFIG_RTDOBS (0x1 << 11)
#define SDMA_CONFIG_ACR (0x1 << 4)
#define SDMA_CONFIG_CSM (0x3 << 0)
#define SDMA_CONFIG_CSM_STATIC (0x0 << 0)
#define SDMA_CONFIG_CSM_DYNAMIC_LOW_POWER (0x1 << 0)
#define SDMA_CONFIG_CSM_DYNAMIC_NO_LOOP (0x2 << 0)
#define SDMA_CONFIG_CSM_DYNAMIC (0x3 << 0)
/* SDMA_ONCE_ENB */
#define SDMA_ONCE_ENB_ENB (0x1 << 0)
/* SDMA_ONCE_STAT */
#define SDMA_ONCE_STAT_PST (0xf << 12)
#define SDMA_ONCE_STAT_PST_PROGRAM (0x0 << 12)
#define SDMA_ONCE_STAT_PST_DATA (0x1 << 12)
#define SDMA_ONCE_STAT_PST_CHANGE_OF_FLOW (0x2 << 12)
#define SDMA_ONCE_STAT_PST_CHANGE_OF_FLOW_IN_LOOP (0x3 << 12)
#define SDMA_ONCE_STAT_PST_DEBUG (0x4 << 12)
#define SDMA_ONCE_STAT_PST_FUNCTIONAL_UNIT (0x5 << 12)
#define SDMA_ONCE_STAT_PST_SLEEP (0x6 << 12)
#define SDMA_ONCE_STAT_PST_SAVE (0x7 << 12)
#define SDMA_ONCE_STAT_PST_PROGRAM_IN_SLEEP (0x8 << 12)
#define SDMA_ONCE_STAT_PST_DATA_IN_SLEEP (0x9 << 12)
#define SDMA_ONCE_STAT_PST_CHANGE_OF_FLOW_IN_SLEEP (0xa << 12)
#define SDMA_ONCE_STAT_PST_CHANGE_OF_FLOW_IN_LOOP_IN_SLEEP (0xb << 12)
#define SDMA_ONCE_STAT_PST_DEBUG_IN_SLEEP (0xc << 12)
#define SDMA_ONCE_STAT_PST_FUNCTIONAL_UNIT_IN_SLEEP (0xd << 12)
#define SDMA_ONCE_STAT_PST_SLEEP_AFTER_RESET (0xe << 12)
#define SDMA_ONCE_STAT_PST_RESTORE (0xf << 12)
#define SDMA_ONCE_STAT_RCV (0x1 << 11)
#define SDMA_ONCE_STAT_EDR (0x1 << 10)
#define SDMA_ONCE_STAT_ODR (0x1 << 9)
#define SDMA_ONCE_STAT_SWB (0x1 << 8)
#define SDMA_ONCE_STAT_MST (0x1 << 7)
#define SDMA_ONCE_STAT_ECDR (0x7 << 0)
#define SDMA_ONCE_STAT_ECDR_MATCHED_ADDRA_COND (0x1 << 0)
#define SDMA_ONCE_STAT_ECDR_MATCHED_ADDRB_COND (0x1 << 1)
#define SDMA_ONCE_STAT_ECDR_MATCHED_DATA_COND (0x1 << 2)
/* SDMA_ONCE_CMD */
#define SDMA_ONCE_CMD_RSTATUS 0x0
#define SDMA_ONCE_CMD_DMOV 0x1
#define SDMA_ONCE_CMD_EXEC_ONCE 0x2
#define SDMA_ONCE_CMD_RUN_CORE 0x3
#define SDMA_ONCE_CMD_EXEC_CORE 0x4
#define SDMA_ONCE_CMD_DEBUG_RQST 0x5
#define SDMA_ONCE_CMD_RBUFFER 0x6
/* 7-15 reserved */
/* SDMA_CHN0ADDR */
#define SDMA_CHN0ADDR_SMSZ (0x1 << 14)
/* 13:0 = 0x0050 by default (std. boot code) */
/* SDMA_EVT_MIRROR */
#define SDMA_EVT_MIRROR_EVENTS(n) (0x1 << (n))
/* SDMA_XTRIG_CONF1 */
#define SDMA_XTRIG_CONF1_CNF3 (0x1 << 30)
#define SDMA_XTRIG_CONF1_NUM3 (0x1f << 24)
#define SDMA_XTRIG_CONF1_CNF2 (0x1 << 22)
#define SDMA_XTRIG_CONF1_NUM2 (0x1f << 16)
#define SDMA_XTRIG_CONF1_CNF1 (0x1 << 14)
#define SDMA_XTRIG_CONF1_NUM1 (0x1f << 8)
#define SDMA_XTRIG_CONF1_CNF0 (0x1 << 6)
#define SDMA_XTRIG_CONF1_NUM0 (0x1f << 0)
/* SDMA_XTRIG_CONF2 */
#define SDMA_XTRIG_CONF2_CNF7 (0x1 << 30)
#define SDMA_XTRIG_CONF2_NUM7 (0x1f << 24)
#define SDMA_XTRIG_CONF2_CNF6 (0x1 << 22)
#define SDMA_XTRIG_CONF2_NUM6 (0x1f << 16)
#define SDMA_XTRIG_CONF2_CNF5 (0x1 << 14)
#define SDMA_XTRIG_CONF2_NUM5 (0x1f << 8)
#define SDMA_XTRIG_CONF2_CNF4 (0x1 << 6)
#define SDMA_XTRIG_CONF2_NUM4 (0x1f << 0)
/* SDMA_CHNENBL(n) */
#define SDMA_CHNENBL_ENBL(n) (0x1 << (n))
#define L2CC_ENABLED
/* Assuming 26MHz input clock */

View file

@ -90,6 +90,10 @@ extern unsigned long pcm_curr_sampr;
extern unsigned long pcm_sampr;
extern int pcm_fsel;
#ifdef HAVE_PCM_DMA_ADDRESS
void * pcm_dma_addr(void *addr);
#endif
/* the registered callback function to ask for more mp3 data */
extern volatile pcm_more_callback_type pcm_callback_for_more;
extern volatile bool pcm_playing;

View file

@ -53,6 +53,7 @@
* ==Playback/Recording==
* Semi-private -
* pcm_dma_apply_settings
* pcm_dma_addr
*
* ==Recording==
* Public -
@ -65,7 +66,7 @@
* pcm_rec_dma_stop
* pcm_rec_dma_get_peak_buffer
* Data Read/Written within TSP -
* pcm_rec_peak_addr (R)
* pcm_rec_peak_addr (R/W)
* pcm_callback_more_ready (R)
* pcm_recording (R)
*

View file

@ -46,6 +46,10 @@ bool __dbg_hw_info(void)
while (1)
{
line = 0;
snprintf(buf, sizeof (buf), "Sys Rev Code: 0x%02X",
iim_system_rev());
lcd_puts(0, line++, buf); line++;
mpctl = CLKCTL_MPCTL;
spctl = CLKCTL_SPCTL;
upctl = CLKCTL_UPCTL;

View file

@ -24,6 +24,11 @@
#include "cpu.h"
#include "clkctl-imx31.h"
unsigned int imx31_get_src_pll(void)
{
return (CLKCTL_PMCR0 & 0xC0000000) == 0 ? PLL_SERIAL : PLL_MCU;
}
void imx31_clkctl_module_clock_gating(enum IMX31_CG_LIST cg,
enum IMX31_CG_MODES mode)
{
@ -72,8 +77,8 @@ unsigned int imx31_clkctl_get_pll(enum IMX31_PLLS pll)
unsigned int imx31_clkctl_get_ipg_clk(void)
{
unsigned int pll = imx31_clkctl_get_pll((CLKCTL_PMCR0 & 0xC0000000) == 0 ?
PLL_SERIAL : PLL_MCU);
unsigned int pllnum = imx31_get_src_pll();
unsigned int pll = imx31_clkctl_get_pll(pllnum);
uint32_t reg = CLKCTL_PDR0;
unsigned int max_pdf = ((reg >> 3) & 0x7) + 1;
unsigned int ipg_pdf = ((reg >> 6) & 0x3) + 1;
@ -81,6 +86,15 @@ unsigned int imx31_clkctl_get_ipg_clk(void)
return pll / (max_pdf * ipg_pdf);
}
unsigned int imx31_clkctl_get_ahb_clk(void)
{
unsigned int pllnum = imx31_get_src_pll();
unsigned int pll = imx31_clkctl_get_pll(pllnum);
unsigned int max_pdf = ((CLKCTL_PDR0 >> 3) & 0x7) + 1;
return pll / max_pdf;
}
unsigned int imx31_clkctl_get_ata_clk(void)
{
return imx31_clkctl_get_ipg_clk();

View file

@ -105,6 +105,9 @@ unsigned int imx31_clkctl_get_pll(enum IMX31_PLLS pll);
/* Return ipg_clk in HZ */
unsigned int imx31_clkctl_get_ipg_clk(void);
/* Return ahb_clk in HZ */
unsigned int imx31_clkctl_get_ahb_clk(void);
/* Return the ATA frequency in HZ */
unsigned int imx31_clkctl_get_ata_clk(void);

View file

@ -1,8 +0,0 @@
#include <sys/types.h>
void dma_start(const void* addr, size_t size) {
(void) addr;
(void) size;
//TODO:
}

View file

@ -24,6 +24,7 @@
#include "spi-imx31.h"
#include "mc13783.h"
#include "clkctl-imx31.h"
#include "sdma-imx31.h"
#include "kernel.h"
#include "thread.h"
@ -64,6 +65,9 @@ void tick_start(unsigned int interval_in_ms)
void kernel_device_init(void)
{
#ifndef BOOTLOADER
sdma_init();
#endif
spi_init();
mc13783_init();
}

View file

@ -22,21 +22,12 @@
#include "mmu-imx31.h"
#include "mmu-arm.h"
void memory_init(void) {
#if 0
ttb_init();
set_page_tables();
enable_mmu();
#endif
unsigned long addr_virt_to_phys(unsigned long addr)
{
return addr | CSD0_BASE_ADDR;
}
void set_page_tables() {
#if 0
map_section(0, 0, 0x1000, CACHE_NONE); /* map every memory region to itself */
/*This pa *might* change*/
map_section(0x80000000, 0, 64, CACHE_ALL); /* map RAM to 0 and enable caching for it */
map_section((int)FRAME1, (int)FRAME1, 1, BUFFERED); /* enable buffered writing for the framebuffer */
map_section((int)FRAME2, (int)FRAME2, 1, BUFFERED);
#endif
unsigned long addr_phys_to_virt(unsigned long addr)
{
return addr & ~CSD0_BASE_ADDR;
}

View file

@ -23,5 +23,7 @@
void memory_init(void);
void set_page_tables(void);
unsigned long addr_virt_to_phys(unsigned long addr);
unsigned long addr_phys_to_virt(unsigned long addr);
#endif /* MMU_IMX31_H */

View file

@ -23,78 +23,93 @@
#include "kernel.h"
#include "audio.h"
#include "sound.h"
#include "avic-imx31.h"
#include "clkctl-imx31.h"
#include "sdma-imx31.h"
#include "mmu-imx31.h"
/* This isn't DMA-based at the moment and is handled like Portal Player but
* will suffice for starters. */
#define DMA_PLAY_CH_NUM 2
#define DMA_REC_CH_NUM 1
static struct buffer_descriptor dma_play_bd DEVBSS_ATTR;
static struct channel_descriptor dma_play_cd DEVBSS_ATTR;
struct dma_data
{
uint16_t *p;
size_t size;
int locked;
int callback_pending; /* DMA interrupt happened while locked */
int state;
};
static struct dma_data dma_play_data =
{
/* Initialize to a locked, stopped state */
.p = NULL,
.size = 0,
.locked = 0,
.callback_pending = 0,
.state = 0
};
static void play_dma_callback(void)
{
unsigned char *start;
size_t size;
pcm_more_callback_type get_more = pcm_callback_for_more;
if (dma_play_data.locked)
{
/* Callback is locked out */
dma_play_data.callback_pending = 1;
return;
}
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
{
start = (void*)(((unsigned long)start + 3) & ~3);
size &= ~3;
/* Flush any pending cache writes */
clean_dcache_range(start, size);
dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)start);
dma_play_bd.mode.count = size;
dma_play_bd.mode.command = TRANSFER_16BIT;
dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
sdma_channel_run(DMA_PLAY_CH_NUM);
}
}
void pcm_play_lock(void)
{
if (++dma_play_data.locked == 1)
{
/* Atomically disable transmit interrupt */
imx31_regclr32(&SSI_SIER1, SSI_SIER_TIE);
}
imx31_regclr32(&SSI_SIER1, SSI_SIER_TDMAE);
}
void pcm_play_unlock(void)
{
if (--dma_play_data.locked == 0 && dma_play_data.state != 0)
{
/* Atomically enable transmit interrupt */
imx31_regset32(&SSI_SIER1, SSI_SIER_TIE);
}
}
bool pending = false;
int oldstatus = disable_irq_save();
static void __attribute__((interrupt("IRQ"))) SSI1_HANDLER(void)
{
register pcm_more_callback_type get_more;
do
{
while (dma_play_data.size > 0)
if (dma_play_data.callback_pending)
{
if (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 6)
{
return;
}
SSI_STX0_1 = *dma_play_data.p++;
SSI_STX0_1 = *dma_play_data.p++;
dma_play_data.size -= 4;
pending = true;
dma_play_data.callback_pending = 0;
}
/* p is empty, get some more data */
get_more = pcm_callback_for_more;
SSI_SIER1 |= SSI_SIER_TDMAE;
restore_irq(oldstatus);
if (get_more)
{
get_more((unsigned char **)&dma_play_data.p,
&dma_play_data.size);
}
/* Should an interrupt be forced instead? The upper pcm layer can
* call producer's callback in thread context so technically this is
* acceptable. */
if (pending)
play_dma_callback();
}
while (dma_play_data.size > 0);
/* No more data, so disable the FIFO/interrupt */
pcm_play_dma_stop();
pcm_play_dma_stopped_callback();
}
void pcm_dma_apply_settings(void)
@ -104,6 +119,17 @@ void pcm_dma_apply_settings(void)
void pcm_play_dma_init(void)
{
/* Init channel information */
dma_play_cd.bd_count = 1;
dma_play_cd.callback = play_dma_callback;
dma_play_cd.shp_addr = SDMA_PER_ADDR_SSI1_TX1;
dma_play_cd.wml = SDMA_SSI_TXFIFO_WML*2;
dma_play_cd.per_type = SDMA_PER_SSI;
dma_play_cd.tran_type = SDMA_TRAN_EMI_2_PER;
dma_play_cd.event_id1 = SDMA_REQ_SSI1_TX1;
sdma_channel_init(DMA_PLAY_CH_NUM, &dma_play_cd, &dma_play_bd);
imx31_clkctl_module_clock_gating(CG_SSI1, CGM_ON_ALL);
imx31_clkctl_module_clock_gating(CG_SSI2, CGM_ON_ALL);
@ -111,8 +137,8 @@ void pcm_play_dma_init(void)
SSI_SCR2 &= ~SSI_SCR_SSIEN;
SSI_SCR1 &= ~SSI_SCR_SSIEN;
SSI_SIER1 = SSI_SIER_TFE0; /* TX0 can issue an interrupt */
SSI_SIER2 = SSI_SIER_RFF0; /* RX0 can issue an interrupt */
SSI_SIER1 = 0;
SSI_SIER2 = 0;
/* Set up audio mux */
@ -155,8 +181,9 @@ void pcm_play_dma_init(void)
SSI_STCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) |
SSI_STRCCR_PMw(4-1);
/* Transmit low watermark - 2 samples in FIFO */
SSI_SFCSR1 = SSI_SFCSR_TFWM1w(1) | SSI_SFCSR_TFWM0w(2);
/* Transmit low watermark */
SSI_SFCSR1 = (SSI_SFCSR1 & ~SSI_SFCSR_TFWM0) |
SSI_SFCSR_TFWM0w(8-SDMA_SSI_TXFIFO_WML);
SSI_STMSK1 = 0;
/* SSI2 - provides MCLK to codec. Receives data from codec. */
@ -186,8 +213,9 @@ void pcm_play_dma_init(void)
SSI_SRCCR2 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) |
SSI_STRCCR_PMw(4-1);
/* Receive high watermark - 6 samples in FIFO */
SSI_SFCSR2 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_RFWM0w(6);
/* Receive high watermark */
SSI_SFCSR2 = (SSI_SFCSR2 & ~SSI_SFCSR_RFWM0) |
SSI_SFCSR_RFWM0w(SDMA_SSI_RXFIFO_WML);
SSI_SRMSK2 = 0;
/* Enable SSI2 (codec clock) */
@ -199,7 +227,6 @@ void pcm_play_dma_init(void)
void pcm_postinit(void)
{
audiohw_postinit();
avic_enable_int(SSI1, IRQ, 8, SSI1_HANDLER);
}
static void play_start_pcm(void)
@ -207,32 +234,23 @@ static void play_start_pcm(void)
/* Stop transmission (if in progress) */
SSI_SCR1 &= ~SSI_SCR_TE;
/* Enable interrupt on unlock */
dma_play_data.state = 1;
/* Fill the FIFO or start when data is used up */
SSI_SCR1 |= SSI_SCR_SSIEN; /* Enable SSI */
SSI_STCR1 |= SSI_STCR_TFEN0; /* Enable TX FIFO */
while (1)
{
if (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 6 || dma_play_data.size == 0)
{
SSI_SCR1 |= SSI_SCR_TE; /* Start transmitting */
return;
}
dma_play_data.state = 1; /* Enable DMA requests on unlock */
SSI_STX0_1 = *dma_play_data.p++;
SSI_STX0_1 = *dma_play_data.p++;
dma_play_data.size -= 4;
}
/* Do prefill to prevent swapped channels (see TLSbo61214 in MCIMX31CE).
* No actual solution was offered but this appears to work. */
SSI_STX0_1 = 0;
SSI_STX0_1 = 0;
SSI_STX0_1 = 0;
SSI_STX0_1 = 0;
SSI_SCR1 |= SSI_SCR_TE; /* Start transmitting */
}
static void play_stop_pcm(void)
{
/* Disable interrupt */
SSI_SIER1 &= ~SSI_SIER_TIE;
/* Wait for FIFO to empty */
while (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 0);
@ -240,135 +258,227 @@ static void play_stop_pcm(void)
SSI_STCR1 &= ~SSI_STCR_TFEN0;
SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN);
/* Do not enable interrupt on unlock */
/* Do not enable DMA requests on unlock */
dma_play_data.state = 0;
dma_play_data.callback_pending = 0;
}
void pcm_play_dma_start(const void *addr, size_t size)
{
dma_play_data.p = (void *)(((uintptr_t)addr + 3) & ~3);
dma_play_data.size = (size & ~3);
sdma_channel_stop(DMA_PLAY_CH_NUM);
/* Disable transmission */
SSI_STCR1 &= ~SSI_STCR_TFEN0;
SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN);
addr = (void *)(((unsigned long)addr + 3) & ~3);
size &= ~3;
clean_dcache_range(addr, size);
dma_play_bd.buf_addr =
(void *)addr_virt_to_phys((unsigned long)(void *)addr);
dma_play_bd.mode.count = size;
dma_play_bd.mode.command = TRANSFER_16BIT;
dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
play_start_pcm();
sdma_channel_start(DMA_PLAY_CH_NUM);
}
void pcm_play_dma_stop(void)
{
sdma_channel_stop(DMA_PLAY_CH_NUM);
play_stop_pcm();
dma_play_data.size = 0;
}
void pcm_play_dma_pause(bool pause)
{
if (pause)
{
sdma_channel_pause(DMA_PLAY_CH_NUM);
play_stop_pcm();
}
else
{
uint32_t addr = (uint32_t)dma_play_data.p;
dma_play_data.p = (void *)((addr + 2) & ~3);
dma_play_data.size &= ~3;
play_start_pcm();
sdma_channel_run(DMA_PLAY_CH_NUM);
}
}
/* Return the number of bytes waiting - full L-R sample pairs only */
size_t pcm_get_bytes_waiting(void)
{
return dma_play_data.size & ~3;
static unsigned long dsa DEVBSS_ATTR;
long offs, size;
int oldstatus;
/* read burst dma source address register in channel context */
sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1);
oldstatus = disable_irq_save();
offs = dsa - (unsigned long)dma_play_bd.buf_addr;
size = dma_play_bd.mode.count;
restore_irq(oldstatus);
/* Be addresses are coherent (no buffer change during read) */
if (offs >= 0 && offs < size)
{
return (size - offs) & ~3;
}
return 0;
}
/* Return a pointer to the samples and the number of them in *count */
const void * pcm_play_dma_get_peak_buffer(int *count)
{
uint32_t addr = (uint32_t)dma_play_data.p;
size_t cnt = dma_play_data.size;
*count = cnt >> 2;
return (void *)((addr + 2) & ~3);
static unsigned long dsa DEVBSS_ATTR;
unsigned long addr;
long offs, size;
int oldstatus;
/* read burst dma source address register in channel context */
sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1);
oldstatus = disable_irq_save();
addr = dsa;
offs = addr - (unsigned long)dma_play_bd.buf_addr;
size = dma_play_bd.mode.count;
restore_irq(oldstatus);
/* Be addresses are coherent (no buffer change during read) */
if (offs >= 0 && offs < size)
{
*count = (size - offs) >> 2;
return (void *)((addr + 2) & ~3);
}
*count = 0;
return NULL;
}
void * pcm_dma_addr(void *addr)
{
return (void *)addr_virt_to_phys((unsigned long)addr);
}
#ifdef HAVE_RECORDING
static struct buffer_descriptor dma_rec_bd DEVBSS_ATTR;
static struct channel_descriptor dma_rec_cd DEVBSS_ATTR;
static struct dma_data dma_rec_data =
{
/* Initialize to a locked, stopped state */
.p = NULL,
.size = 0,
.locked = 0,
.state = 0
};
static void __attribute__((interrupt("IRQ"))) SSI2_HANDLER(void)
static void rec_dma_callback(void)
{
register pcm_more_callback_type2 more_ready;
pcm_more_callback_type2 more_ready;
int status = 0;
while (dma_rec_data.size > 0)
if (dma_rec_data.locked)
{
if (SSI_SFCSR_RFCNT0r(SSI_SFCSR2) < 2)
return;
*dma_rec_data.p++ = SSI_SRX0_2;
*dma_rec_data.p++ = SSI_SRX0_2;
dma_rec_data.size -= 4;
dma_rec_data.callback_pending = 1;
return; /* Callback is locked out */
}
if (dma_rec_bd.mode.status & BD_RROR)
status = DMA_REC_ERROR_DMA;
more_ready = pcm_callback_more_ready;
if (more_ready == NULL || more_ready(0) < 0) {
/* Finished recording */
pcm_rec_dma_stop();
pcm_rec_dma_stopped_callback();
if (more_ready != NULL && more_ready(status) >= 0)
{
sdma_channel_run(DMA_REC_CH_NUM);
return;
}
/* Finished recording */
pcm_rec_dma_stop();
pcm_rec_dma_stopped_callback();
}
void pcm_rec_lock(void)
{
if (++dma_rec_data.locked == 1)
{
/* Atomically disable receive interrupt */
imx31_regclr32(&SSI_SIER2, SSI_SIER_RIE);
}
imx31_regclr32(&SSI_SIER2, SSI_SIER_RDMAE);
}
void pcm_rec_unlock(void)
{
if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0)
{
/* Atomically enable receive interrupt */
imx31_regset32(&SSI_SIER2, SSI_SIER_RIE);
bool pending = false;
int oldstatus = disable_irq_save();
if (dma_rec_data.callback_pending)
{
pending = true;
dma_rec_data.callback_pending = 0;
}
SSI_SIER2 |= SSI_SIER_RDMAE;
restore_irq(oldstatus);
/* Should an interrupt be forced instead? The upper pcm layer can
* call consumer's callback in thread context so technically this is
* acceptable. */
if (pending)
rec_dma_callback();
}
}
void pcm_record_more(void *start, size_t size)
{
pcm_rec_peak_addr = start; /* Start peaking at dest */
dma_rec_data.p = start; /* Start of RX buffer */
dma_rec_data.size = size; /* Bytes to transfer */
start = (void *)(((unsigned long)start + 3) & ~3);
size &= ~3;
/* Write back and invalidate - buffer must be coherent */
invalidate_dcache_range(start, size);
start = (void *)addr_virt_to_phys((unsigned long)start);
pcm_rec_peak_addr = start;
dma_rec_bd.buf_addr = start;
dma_rec_bd.mode.count = size;
dma_rec_bd.mode.command = TRANSFER_16BIT;
dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
}
void pcm_rec_dma_stop(void)
{
/* Stop receiving data */
sdma_channel_stop(DMA_REC_CH_NUM);
imx31_regclr32(&SSI_SIER2, SSI_SIER_RDMAE);
SSI_SCR2 &= ~SSI_SCR_RE; /* Disable RX */
SSI_SRCR2 &= ~SSI_SRCR_RFEN0; /* Disable RX FIFO */
dma_rec_data.state = 0;
avic_disable_int(SSI2);
dma_rec_data.callback_pending = 0;
}
void pcm_rec_dma_start(void *addr, size_t size)
{
pcm_rec_dma_stop();
addr = (void *)(((unsigned long)addr + 3) & ~3);
size &= ~3;
invalidate_dcache_range(addr, size);
addr = (void *)addr_virt_to_phys((unsigned long)addr);
pcm_rec_peak_addr = addr;
dma_rec_data.p = addr;
dma_rec_data.size = size;
dma_rec_bd.buf_addr = addr;
dma_rec_bd.mode.count = size;
dma_rec_bd.mode.command = TRANSFER_16BIT;
dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
dma_rec_data.state = 1;
avic_enable_int(SSI2, IRQ, 9, SSI2_HANDLER);
SSI_SRCR2 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */
/* Ensure clear FIFO */
@ -377,24 +487,58 @@ void pcm_rec_dma_start(void *addr, size_t size)
/* Enable receive */
SSI_SCR2 |= SSI_SCR_RE;
sdma_channel_start(DMA_REC_CH_NUM);
}
void pcm_rec_dma_close(void)
{
pcm_rec_dma_stop();
sdma_channel_close(DMA_REC_CH_NUM);
}
void pcm_rec_dma_init(void)
{
pcm_rec_dma_stop();
/* Init channel information */
dma_rec_cd.bd_count = 1;
dma_rec_cd.callback = rec_dma_callback;
dma_rec_cd.shp_addr = SDMA_PER_ADDR_SSI2_RX1;
dma_rec_cd.wml = SDMA_SSI_RXFIFO_WML*2;
dma_rec_cd.per_type = SDMA_PER_SSI;
dma_rec_cd.tran_type = SDMA_TRAN_PER_2_EMI;
dma_rec_cd.event_id1 = SDMA_REQ_SSI2_RX1;
sdma_channel_init(DMA_REC_CH_NUM, &dma_rec_cd, &dma_rec_bd);
}
const void * pcm_rec_dma_get_peak_buffer(int *count)
{
unsigned long addr = (uint32_t)pcm_rec_peak_addr;
unsigned long end = (uint32_t)dma_rec_data.p;
*count = (end >> 2) - (addr >> 2);
return (void *)(addr & ~3);
static unsigned long pda DEVBSS_ATTR;
unsigned long buf, addr, end, bufend;
int oldstatus;
/* read burst dma destination address register in channel context */
sdma_read_words(&pda, CHANNEL_CONTEXT_ADDR(DMA_REC_CH_NUM)+0x0a, 1);
oldstatus = disable_irq_save();
end = pda;
buf = (unsigned long)dma_rec_bd.buf_addr;
addr = (unsigned long)pcm_rec_peak_addr;
bufend = buf + dma_rec_bd.mode.count;
restore_irq(oldstatus);
/* Be addresses are coherent (no buffer change during read) */
if (addr >= buf && addr < bufend &&
end >= buf && end < bufend)
{
*count = (end >> 2) - (addr >> 2);
return (void *)(addr & ~3);
}
*count = 0;
return NULL;
}
#endif /* HAVE_RECORDING */

View file

@ -32,6 +32,28 @@
#include "clkctl-imx31.h"
#include "mc13783.h"
static unsigned long product_rev;
static unsigned long system_rev;
/** IC revision info routines **/
unsigned int iim_system_rev(void)
{
return system_rev & IIM_SREV_SREV;
}
unsigned int iim_prod_rev(void)
{
return product_rev;
}
static void iim_init(void)
{
/* Initialize the IC revision info (required by SDMA) */
imx31_clkctl_module_clock_gating(CG_IIM, CGM_ON_ALL);
product_rev = IIM_PREV;
system_rev = IIM_SREV;
}
/** Watchdog timer routines **/
/* Initialize the watchdog timer */
@ -155,6 +177,8 @@ void system_init(void)
/* MCR WFI enables wait mode */
CLKCTL_CCMR &= ~(3 << 14);
iim_init();
imx31_regset32(&SDHC1_CLOCK_CONTROL, STOP_CLK);
imx31_regset32(&SDHC2_CLOCK_CONTROL, STOP_CLK);
imx31_regset32(&RNGA_CONTROL, RNGA_CONTROL_SLEEP);

View file

@ -43,6 +43,8 @@ void watchdog_service(void);
void gpt_start(void);
void gpt_stop(void);
unsigned int iim_system_rev(void);
/* Prepare for transition to firmware */
void system_prepare_fw_start(void);
void tick_stop(void);

View file

@ -0,0 +1,807 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 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 "config.h"
#include "system.h"
#include <string.h>
#include "logf.h"
#include "panic.h"
#include "clkctl-imx31.h"
#include "avic-imx31.h"
#include "sdma_struct.h"
#include "sdma-imx31.h"
#include "sdma_script_code.h"
#include "mmu-imx31.h"
/* Most of the code in here is based upon the Linux BSP provided by Freescale
* Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. */
/* Cut down to bare bones essentials */
/* Script information that depends on system revision */
static struct sdma_script_start_addrs script_info;
/* Mask of channels with callback enabled */
static unsigned long sdma_enabled_ints = 0;
/* One channel control block per channel in physically mapped device RAM */
static struct channel_control_block ccb_array[CH_NUM] DEVBSS_ATTR;
/* Channel 0 (command channel) data */
static struct buffer_descriptor_extd c0_buffer_desc DEVBSS_ATTR;
/* All SDMA channel interrupts are handled here.
* Dispatches lower channel numbers first (prioritized by SDMA API callers
* who specify the desired channel number).
*/
static void __attribute__((interrupt("IRQ"))) SDMA_HANDLER(void)
{
unsigned long pending = SDMA_INTR;
SDMA_INTR = pending; /* Ack all ints */
pending &= sdma_enabled_ints; /* Only dispatch ints with callback */
while (1)
{
unsigned int channel;
if (pending == 0)
break; /* No bits set */
channel = find_first_set_bit(pending);
pending &= ~(1ul << channel);
/* Call callback (required if using an interrupt) */
ccb_array[channel].channel_desc->callback();
}
}
/* Initialize script information based upon the system revision */
static void init_script_info(void)
{
if (iim_system_rev() == IIM_SREV_1_0)
{
/* Channel script info */
script_info.app_2_mcu_addr = app_2_mcu_ADDR_1;
script_info.ap_2_ap_addr = ap_2_ap_ADDR_1;
script_info.ap_2_bp_addr = -1;
script_info.bp_2_ap_addr = -1;
script_info.loopback_on_dsp_side_addr = -1;
script_info.mcu_2_app_addr = mcu_2_app_ADDR_1;
script_info.mcu_2_shp_addr = mcu_2_shp_ADDR_1;
script_info.mcu_interrupt_only_addr = -1;
script_info.shp_2_mcu_addr = shp_2_mcu_ADDR_1;
script_info.uartsh_2_mcu_addr = uartsh_2_mcu_ADDR_1;
script_info.uart_2_mcu_addr = uart_2_mcu_ADDR_1;
script_info.dptc_dvfs_addr = dptc_dvfs_ADDR_1;
script_info.firi_2_mcu_addr = firi_2_mcu_ADDR_1;
script_info.firi_2_per_addr = -1;
script_info.mshc_2_mcu_addr = mshc_2_mcu_ADDR_1;
script_info.per_2_app_addr = -1;
script_info.per_2_firi_addr = -1;
script_info.per_2_shp_addr = -1;
script_info.mcu_2_ata_addr = mcu_2_ata_ADDR_1;
script_info.mcu_2_firi_addr = mcu_2_firi_ADDR_1;
script_info.mcu_2_mshc_addr = mcu_2_mshc_ADDR_1;
script_info.ata_2_mcu_addr = ata_2_mcu_ADDR_1;
script_info.uartsh_2_per_addr = -1;
script_info.shp_2_per_addr = -1;
script_info.uart_2_per_addr = -1;
script_info.app_2_per_addr = -1;
/* Main code block info */
script_info.ram_code_size = RAM_CODE_SIZE_1;
script_info.ram_code_start_addr = RAM_CODE_START_ADDR_1;
script_info.mcu_start_addr = (unsigned long)sdma_code_1;
}
else
{
/* Channel script info */
script_info.app_2_mcu_addr = app_2_mcu_patched_ADDR_2;
script_info.ap_2_ap_addr = ap_2_ap_ADDR_2;
script_info.ap_2_bp_addr = ap_2_bp_ADDR_2;
script_info.bp_2_ap_addr = bp_2_ap_ADDR_2;
script_info.loopback_on_dsp_side_addr = -1;
script_info.mcu_2_app_addr = mcu_2_app_ADDR_2;
script_info.mcu_2_shp_addr = mcu_2_shp_patched_ADDR_2;
script_info.mcu_interrupt_only_addr = -1;
script_info.shp_2_mcu_addr = shp_2_mcu_patched_ADDR_2;
script_info.uartsh_2_mcu_addr = uartsh_2_mcu_patched_ADDR_2;
script_info.uart_2_mcu_addr = uart_2_mcu_patched_ADDR_2;
script_info.dptc_dvfs_addr = -1;
script_info.firi_2_mcu_addr = firi_2_mcu_ADDR_2;
script_info.firi_2_per_addr = -1;
script_info.mshc_2_mcu_addr = -1;
script_info.per_2_app_addr = -1;
script_info.per_2_firi_addr = -1;
script_info.per_2_shp_addr = per_2_shp_ADDR_2;
script_info.mcu_2_ata_addr = mcu_2_ata_ADDR_2;
script_info.mcu_2_firi_addr = mcu_2_firi_ADDR_2;
script_info.mcu_2_mshc_addr = -1;
script_info.ata_2_mcu_addr = ata_2_mcu_ADDR_2;
script_info.uartsh_2_per_addr = -1;
script_info.shp_2_per_addr = shp_2_per_ADDR_2;
script_info.uart_2_per_addr = -1;
script_info.app_2_per_addr = -1;
/* Main code block info */
script_info.ram_code_size = RAM_CODE_SIZE_2;
script_info.ram_code_start_addr = RAM_CODE_START_ADDR_2;
script_info.mcu_start_addr = (unsigned long)sdma_code_2;
}
}
/* Return pc of SDMA script in SDMA halfword space according to peripheral
* and transfer type */
static unsigned long get_script_pc(unsigned int peripheral_type,
unsigned int transfer_type)
{
unsigned long res = (unsigned short)-1;
switch (peripheral_type)
{
case SDMA_PER_MEMORY:
switch (transfer_type)
{
case SDMA_TRAN_EMI_2_INT:
case SDMA_TRAN_EMI_2_EMI:
case SDMA_TRAN_INT_2_EMI:
res = script_info.ap_2_ap_addr;
break;
default:
break;
}
break;
#if 0 /* Not using this */
case SDMA_PER_DSP:
switch (transfer_type)
{
case SDMA_TRAN_EMI_2_DSP:
res = script_info.ap_2_bp_addr;
break;
case SDMA_TRAN_DSP_2_EMI:
res = script_info.bp_2_ap_addr;
break;
case SDMA_TRAN_DSP_2_EMI_LOOP:
res = script_info.loopback_on_dsp_side_addr;
break;
case SDMA_TRAN_EMI_2_DSP_LOOP:
res = script_info.mcu_interrupt_only_addr;
break;
default:
break;
}
break;
#endif
#if 0 /* Not using this */
case SDMA_PER_FIRI:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_INT:
res = script_info.firi_2_per_addr;
break;
case SDMA_TRAN_PER_2_EMI:
res = script_info.firi_2_mcu_addr;
break;
case SDMA_TRAN_INT_2_PER:
res = script_info.per_2_firi_addr;
break;
case SDMA_TRAN_EMI_2_PER:
res = script_info.mcu_2_firi_addr;
break;
default:
break;
}
break;
#endif
#if 0 /* Not using this */
case SDMA_PER_UART:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_INT:
res = script_info.uart_2_per_addr;
break;
case SDMA_TRAN_PER_2_EMI:
res = script_info.uart_2_mcu_addr;
break;
case SDMA_TRAN_INT_2_PER:
res = script_info.per_2_app_addr;
break;
case SDMA_TRAN_EMI_2_PER:
res = script_info.mcu_2_app_addr;
break;
default:
break;
}
break;
#endif
#if 0 /* Not using this */
case SDMA_PER_UART_SP:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_INT:
res = script_info.uartsh_2_per_addr;
break;
case SDMA_TRAN_PER_2_EMI:
res = script_info.uartsh_2_mcu_addr;
break;
case SDMA_TRAN_INT_2_PER:
res = script_info.per_2_shp_addr;
break;
case SDMA_TRAN_EMI_2_PER:
res = script_info.mcu_2_shp_addr;
break;
default:
break;
}
break;
#endif
case SDMA_PER_ATA:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_EMI:
res = script_info.ata_2_mcu_addr;
break;
case SDMA_TRAN_EMI_2_PER:
res = script_info.mcu_2_ata_addr;
break;
default:
break;
}
break;
case SDMA_PER_CSPI:
case SDMA_PER_EXT:
case SDMA_PER_SSI:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_INT:
res = script_info.app_2_per_addr;
break;
case SDMA_TRAN_PER_2_EMI:
res = script_info.app_2_mcu_addr;
break;
case SDMA_TRAN_INT_2_PER:
res = script_info.per_2_app_addr;
break;
case SDMA_TRAN_EMI_2_PER:
res = script_info.mcu_2_app_addr;
break;
default:
break;
}
break;
#if 0 /* Not using this */
case SDMA_PER_MMC:
case SDMA_PER_SDHC:
#endif
case SDMA_PER_SSI_SP:
case SDMA_PER_CSPI_SP:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_INT:
res = script_info.shp_2_per_addr;
break;
case SDMA_TRAN_PER_2_EMI:
res = script_info.shp_2_mcu_addr;
break;
case SDMA_TRAN_INT_2_PER:
res = script_info.per_2_shp_addr;
break;
case SDMA_TRAN_EMI_2_PER:
res = script_info.mcu_2_shp_addr;
break;
default:
break;
}
case SDMA_PER_MSHC:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_EMI:
res = script_info.mshc_2_mcu_addr;
break;
case SDMA_TRAN_EMI_2_PER:
res = script_info.mcu_2_mshc_addr;
break;
default:
break;
}
case SDMA_PER_CCM:
switch (transfer_type)
{
case SDMA_TRAN_PER_2_EMI:
res = script_info.dptc_dvfs_addr;
break;
default:
break;
}
}
if (res == (unsigned short)-1)
{
logf("SDMA script not found\n");
}
return res;
}
static unsigned int get_config(unsigned int transfer_type)
{
unsigned int res = -1;
switch (transfer_type)
{
case SDMA_TRAN_PER_2_INT:
case SDMA_TRAN_PER_2_EMI:
case SDMA_TRAN_INT_2_PER:
case SDMA_TRAN_EMI_2_PER:
/*
* Peripheral <------> Memory
* evtOvr = 0 mcuOvr = 0 dspOvr = 1
*/
res = CH_OWNSHP_MCU | CH_OWNSHP_EVT;
break;
#if 0 /* Not using this */
case SDMA_TRAN_DSP_2_PER:
res = 0;
break;
case SDMA_TRAN_EMI_2_DSP:
case SDMA_TRAN_INT_2_DSP:
case SDMA_TRAN_DSP_2_INT:
case SDMA_TRAN_DSP_2_EMI:
case SDMA_TRAN_DSP_2_DSP:
/*
* DSP <-----------> Memory
* evtOvr = 1 mcuOvr = 0 dspOvr = 0
*/
res = CH_OWNSHP_MCU | CH_OWNSHP_DSP;
break;
#endif
case SDMA_TRAN_EMI_2_INT:
case SDMA_TRAN_EMI_2_EMI:
case SDMA_TRAN_INT_2_INT:
case SDMA_TRAN_INT_2_EMI:
#if 0 /* Not using this */
case SDMA_TRAN_DSP_2_EMI_LOOP:
case SDMA_TRAN_EMI_2_DSP_LOOP:
#endif
/* evtOvr = 1 mcuOvr = 0 dspOvr = 1 */
res = CH_OWNSHP_MCU;
break;
#if 0 /* Not using this */
case SDMA_TRAN_PER_2_DSP:
/* evtOvr = 0 mcuOvr = 1 dspOvr = 0 */
res = CH_OWNSHP_DSP | CH_OWNSHP_EVT;
break;
#endif
default:
break;
}
return res;
}
/* Fill the buffer descriptor with the values given in parameter.
* Expects physical addresses. */
static inline void set_buffer_descriptor(
struct buffer_descriptor *bd_p,
unsigned int command, /* C0_* command or transfer size */
unsigned int status, /* BD_* flags */
unsigned int count, /* Size of buffer to transfer */
void *buf_addr, /* Buffer to transfer */
void *buf_addr_ext)
{
bd_p->mode.command = command;
bd_p->mode.status = status;
bd_p->mode.count = count;
bd_p->buf_addr = buf_addr;
if (status & BD_EXTD)
((struct buffer_descriptor_extd *)bd_p)->buf_addr_ext = buf_addr_ext;
}
/* Configure channel ownership */
static void set_channel_ownership(unsigned int channel, unsigned int config)
{
unsigned long bit = 1ul << channel;
/* DSP side */
#if 0 /* Not using this */
imx31_regmod32(&SDMA_DSPOVR, (config & CH_OWNSHP_DSP) ? 0 : bit, bit);
#endif
/* Event */
imx31_regmod32(&SDMA_EVTOVR, (config & CH_OWNSHP_EVT) ? 0 : bit, bit);
/* MCU side */
imx31_regmod32(&SDMA_HOSTOVR, (config & CH_OWNSHP_MCU) ? 0 : bit, bit);
}
static bool setup_channel(struct channel_control_block *ccb_p)
{
static struct context_data context_buffer DEVBSS_ATTR;
struct channel_descriptor *cd_p;
unsigned int channel_cfg;
unsigned int channel;
unsigned long pc;
memset(&context_buffer, 0x00, sizeof (context_buffer));
channel = ccb_p - ccb_array;
cd_p = ccb_p->channel_desc;
/* Obtain script start address for perihperal and transfer type */
pc = get_script_pc(cd_p->per_type, cd_p->tran_type);
if (pc == (unsigned short)-1)
return false; /* Failed to find a script */
context_buffer.channel_state.pc = pc;
if (cd_p->per_type != SDMA_PER_MEMORY && cd_p->per_type != SDMA_PER_DSP)
{
/* Set peripheral DMA request mask for this channel */
context_buffer.event_mask1 = 1ul << cd_p->event_id1;
if (cd_p->per_type == SDMA_PER_ATA)
{
/* ATA has two */
context_buffer.event_mask2 = 1ul << cd_p->event_id2;
}
context_buffer.shp_addr = cd_p->shp_addr;
context_buffer.wml = cd_p->wml;
}
else
{
context_buffer.wml = SDMA_PER_ADDR_SDRAM;
}
/* Send channel context to SDMA core */
clean_dcache_range(&context_buffer, sizeof (context_buffer));
sdma_write_words((unsigned long *)&context_buffer,
CHANNEL_CONTEXT_ADDR(channel),
sizeof (context_buffer)/4);
if (cd_p->is_setup != 0)
return true; /* No more to do */
/* Obtain channel ownership configuration */
channel_cfg = get_config(cd_p->tran_type);
if (channel_cfg == (unsigned int)-1)
return false;
/* Set who owns it and thus can activate it */
set_channel_ownership(channel, channel_cfg);
if (channel_cfg & CH_OWNSHP_EVT)
{
/* Set event ID to channel activation bitmapping */
imx31_regset32(&SDMA_CHNENBL(cd_p->event_id1), 1ul << channel);
if (cd_p->per_type == SDMA_PER_ATA)
{
/* ATA has two */
imx31_regset32(&SDMA_CHNENBL(cd_p->event_id2), 1ul << channel);
}
}
cd_p->is_setup = 1;
return true;
}
/** Public routines **/
void sdma_init(void)
{
imx31_clkctl_module_clock_gating(CG_SDMA, CGM_ON_RUN_WAIT);
int i;
unsigned long acr;
/* Reset the controller */
SDMA_RESET |= SDMA_RESET_RESET;
while (SDMA_RESET & SDMA_RESET_RESET);
init_script_info();
/* No channel enabled, all priorities 0 */
for (i = 0; i < CH_NUM; i++)
{
SDMA_CHNENBL(i) = 0;
SDMA_CHNPRI(i) = 0;
}
/* Ensure no ints pending */
SDMA_INTR = 0xffffffff;
/* Nobody owns any channel (yet) */
SDMA_HOSTOVR = 0xffffffff;
SDMA_DSPOVR = 0xffffffff;
SDMA_EVTOVR = 0xffffffff;
SDMA_MC0PTR = 0x00000000;
/* 32-word channel contexts, use default bootscript address */
SDMA_CHN0ADDR = SDMA_CHN0ADDR_SMSZ | 0x0050;
avic_enable_int(SDMA, IRQ, 8, SDMA_HANDLER);
/* SDMA core must run at the proper frequency based upon the AHB/IPG ratio */
acr = (imx31_clkctl_get_ahb_clk() / imx31_clkctl_get_ipg_clk()) < 2 ?
SDMA_CONFIG_ACR : 0;
/* No dsp, no debug
* Static context switching - TLSbo86520L SW Workaround for SDMA Chnl0
* access issue */
SDMA_CONFIG = acr;
/* Tell SDMA where the host channel table is */
SDMA_MC0PTR = (unsigned long)ccb_array;
ccb_array[0].status.opened_init = 1;
ccb_array[0].curr_bd_ptr = &c0_buffer_desc.bd;
ccb_array[0].base_bd_ptr = &c0_buffer_desc.bd;
ccb_array[0].channel_desc = NULL; /* No channel descriptor */
/* Command channel owned by AP */
set_channel_ownership(0, CH_OWNSHP_MCU);
sdma_channel_set_priority(0, 1);
/* Load SDMA script code */
set_buffer_descriptor(&c0_buffer_desc.bd,
C0_SETPM,
BD_DONE | BD_WRAP | BD_EXTD,
script_info.ram_code_size,
(void *)addr_virt_to_phys(script_info.mcu_start_addr),
(void *)(unsigned long)script_info.ram_code_start_addr);
SDMA_HSTART = 1ul;
sdma_channel_wait_nonblocking(0);
/* No dsp, no debug, dynamic context switching */
SDMA_CONFIG = acr | SDMA_CONFIG_CSM_DYNAMIC;
}
/* Busy wait for a channel to complete */
void sdma_channel_wait_nonblocking(unsigned int channel)
{
unsigned long mask;
if (channel >= CH_NUM)
return;
if (ccb_array[channel].status.opened_init == 0)
return;
mask = 1ul << channel;
while (SDMA_STOP_STAT & mask);
}
/* Set a new channel priority */
void sdma_channel_set_priority(unsigned int channel, unsigned int priority)
{
if (channel >= CH_NUM || priority > MAX_CH_PRIORITY)
return;
if (ccb_array[channel].status.opened_init == 0)
return;
SDMA_CHNPRI(channel) = priority;
}
/* Start a channel cold - resets execution to start of script */
void sdma_channel_start(unsigned int channel)
{
struct channel_control_block *ccb_p;
if (channel == 0 || channel >= CH_NUM)
return;
ccb_p = &ccb_array[channel];
if (ccb_p->status.opened_init == 0)
return;
if (!setup_channel(ccb_p))
return;
SDMA_HSTART = 1ul << channel;
}
/* Resume or start execution on a channel */
void sdma_channel_run(unsigned int channel)
{
if (channel == 0 || channel >= CH_NUM)
return;
if (ccb_array[channel].status.opened_init == 0)
return;
SDMA_HSTART = 1ul << channel;
}
/* Pause a running channel - can be resumed */
void sdma_channel_pause(unsigned int channel)
{
if (channel == 0 || channel >= CH_NUM)
return;
if (ccb_array[channel].status.opened_init == 0)
return;
SDMA_STOP_STAT = 1ul << channel;
}
/* Stop a channel from executing - cannot be resumed */
void sdma_channel_stop(unsigned int channel)
{
struct channel_control_block *ccb_p;
unsigned long chmsk;
unsigned long intmsk;
int oldstatus;
int i;
if (channel == 0 || channel >= CH_NUM)
return;
ccb_p = &ccb_array[channel];
if (ccb_p->status.opened_init == 0)
return;
chmsk = 1ul << channel;
/* Lock callback */
oldstatus = disable_irq_save();
intmsk = sdma_enabled_ints;
sdma_enabled_ints &= ~chmsk;
restore_irq(oldstatus);
/* Stop execution */
for (i = ccb_p->channel_desc->bd_count - 1; i >= 0; i--)
ccb_p->base_bd_ptr[i].mode.status &= ~BD_DONE;
SDMA_STOP_STAT = chmsk;
while (SDMA_STOP_STAT & chmsk);
/* Unlock callback if it was set */
if (intmsk & chmsk)
imx31_regset32(&sdma_enabled_ints, chmsk);
logf("SDMA ch closed: %d", channel);
}
bool sdma_channel_init(unsigned int channel,
struct channel_descriptor *cd_p,
struct buffer_descriptor *base_bd_p)
{
struct channel_control_block *ccb_p;
if (channel == 0 || channel >= CH_NUM ||
cd_p == NULL || base_bd_p == NULL)
return false;
ccb_p = &ccb_array[channel];
/* If initialized already, should close first then init. */
if (ccb_p->status.opened_init != 0)
return false;
/* Initialize channel control block. */
ccb_p->curr_bd_ptr = base_bd_p;
ccb_p->base_bd_ptr = base_bd_p;
ccb_p->channel_desc = cd_p;
ccb_p->status.error = 0;
ccb_p->status.opened_init = 1;
ccb_p->status.state_direction = 0;
ccb_p->status.execute = 0;
/* Finish any channel descriptor inits. */
cd_p->ccb_ptr = ccb_p;
cd_p->is_setup = 0;
/* Do an initial setup now. */
if (!setup_channel(ccb_p))
{
logf("SDMA ch init failed: %d", channel);
cd_p->ccb_ptr = NULL;
memset(ccb_p, 0x00, sizeof (struct channel_control_block));
return false;
}
/* Enable interrupt if a callback is specified. */
if (cd_p->callback != NULL)
imx31_regset32(&sdma_enabled_ints, 1ul << channel);
/* Minimum schedulable = 1 */
sdma_channel_set_priority(channel, 1);
logf("SDMA ch initialized: %d", channel);
return true;
}
void sdma_channel_close(unsigned int channel)
{
struct channel_control_block *ccb_p;
int i;
if (channel == 0 || channel >= CH_NUM)
return;
ccb_p = &ccb_array[channel];
/* Block callbacks (if not initialized, it won't be set). */
imx31_regclr32(&sdma_enabled_ints, 1ul << channel);
if (ccb_p->status.opened_init == 0)
return;
/* Stop the channel if running */
for (i = ccb_p->channel_desc->bd_count - 1; i >= 0; i--)
ccb_p->base_bd_ptr[i].mode.status &= ~BD_DONE;
sdma_channel_stop(channel);
/* No ownership */
set_channel_ownership(channel, 0);
/* Cannot schedule it again */
sdma_channel_set_priority(channel, 0);
/* Reset channel control block entry */
memset(ccb_p, 0x00, sizeof (struct channel_control_block));
}
/* Write 32-bit words to SDMA core memory. Host endian->SDMA endian. */
void sdma_write_words(const unsigned long *buf, unsigned long start, int count)
{
/* Setup buffer descriptor with channel 0 command */
set_buffer_descriptor(&c0_buffer_desc.bd,
C0_SETDM,
BD_DONE | BD_WRAP | BD_EXTD,
count,
(void *)addr_virt_to_phys((unsigned long)buf),
(void *)start);
SDMA_HSTART = 1ul;
sdma_channel_wait_nonblocking(0);
}
/* Read 32-bit words from SDMA core memory. SDMA endian->host endian. */
void sdma_read_words(unsigned long *buf, unsigned long start, int count)
{
/* Setup buffer descriptor with channel 0 command */
set_buffer_descriptor(&c0_buffer_desc.bd,
C0_GETDM,
BD_DONE | BD_WRAP | BD_EXTD,
count,
(void *)addr_virt_to_phys((unsigned long)buf),
(void *)start);
SDMA_HSTART = 1ul;
sdma_channel_wait_nonblocking(0);
}

View file

@ -0,0 +1,225 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 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.
*
****************************************************************************/
#ifndef SDMA_IMX31_H
#define SDMA_IMX31_H
/* Much of the code in here is based upon the Linux BSP provided by Freescale
* Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. */
/* Peripheral and transfer type - used to select the proper SDMA channel
* script to execute. */
enum SDMA_PERIPHERAL_TYPE
{
__SDMA_PER_FIRST = -1,
SDMA_PER_MEMORY,
SDMA_PER_DSP,
SDMA_PER_FIRI,
SDMA_PER_UART,
SDMA_PER_UART_SP, /* Shared */
SDMA_PER_ATA,
SDMA_PER_CSPI,
SDMA_PER_EXT,
SDMA_PER_SSI,
SDMA_PER_SSI_SP, /* Shared */
SDMA_PER_MMC,
SDMA_PER_SDHC,
SDMA_PER_CSPI_SP, /* Shared */
SDMA_PER_MSHC,
SDMA_PER_MSHC_SP, /* Shared */
SDMA_PER_CCM,
SDMA_PER_ASRC,
SDMA_PER_ESAI,
SDMA_PER_SIM,
SDMA_PER_SPDIF,
SDMA_PER_IPU_MEMORY,
};
enum SDMA_TRANSFER_TYPE
{
__SDMA_TRAN_FIRST = -1,
SDMA_TRAN_INT_2_INT,
SDMA_TRAN_EMI_2_INT,
SDMA_TRAN_EMI_2_EMI,
SDMA_TRAN_INT_2_EMI,
SDMA_TRAN_INT_2_DSP,
SDMA_TRAN_DSP_2_INT,
SDMA_TRAN_DSP_2_DSP,
SDMA_TRAN_DSP_2_PER,
SDMA_TRAN_PER_2_DSP,
SDMA_TRAN_EMI_2_DSP,
SDMA_TRAN_DSP_2_EMI,
SDMA_TRAN_DSP_2_EMI_LOOP,
SDMA_TRAN_EMI_2_DSP_LOOP,
SDMA_TRAN_PER_2_INT,
SDMA_TRAN_PER_2_EMI,
SDMA_TRAN_INT_2_PER,
SDMA_TRAN_EMI_2_PER,
};
/* 2.3 - Smart Direct Memory Access (SDMA) Events, Table 2-5 */
/* These are indexes into the SDMA_CHNENBL register array (each a bitmask
* determining which channels are triggered by requests). */
enum SDMA_REQUEST_TYPE
{
SDMA_REQ_EXT0 = 0, /* Extern DMA request from MCU1_0 */
SDMA_REQ_CCM = 1, /* DVFS/DPTC event (ccm_dvfs_sdma_int) */
SDMA_REQ_ATA_TX_END = 2, /* ata_txfer_end_alarm (event_id) */
SDMA_REQ_ATA_TX = 3, /* ata_tx_fifo_alarm (event_id2) */
SDMA_REQ_ATA_RX = 4, /* ata_rcv_fifo_alarm (event_id2) */
SDMA_REQ_SIM = 5, /* */
SDMA_REQ_CSPI2_RX = 6, /* DMA Rx request */
SDMA_REQ_CSPI2_TX = 7, /* DMA Tx request */
SDMA_REQ_CSPI1_RX = 8, /* DMA Rx request of CSPI */
SDMA_REQ_UART3_RX = 8, /* DMA Rx request RxFIFO of UART3 */
SDMA_REQ_CSPI1_TX = 9, /* DMA Tx request of CSPI */
SDMA_REQ_UART3_TX = 9, /* DMA Tx request TxFIFO of UART3 */
SDMA_REQ_CSPI3_RX = 10, /* RxFIFO or CSPI3 Rx request */
SDMA_REQ_UART5_RX = 10, /* RxFIFO or CSPI3 Rx request */
SDMA_REQ_CSPI3_TX = 11, /* TxFIFO or CSPI3 Tx request */
SDMA_REQ_UART5_TX = 11, /* TxFIFO or CSPI3 Tx request */
SDMA_REQ_UART4_RX = 12, /* RxFIFO */
SDMA_REQ_UART4_TX = 13, /* TxFIFO */
SDMA_REQ_EXT2 = 14, /* External DMA request from MCU1_2 or from
MBX (Graphic accelerator) */
SDMA_REQ_EXT1 = 15, /* External request from MCU1_1 */
SDMA_REQ_FIRI_RX = 16, /* DMA request of FIR's receiver FIFO
controlled by the pgp_firi signal
from the IOMUXC PGP register */
SDMA_REQ_UART2_RX = 16, /* RxFIFO of UART2 */
SDMA_REQ_FIRI_TX = 17, /* DMA request of FIR's transmitter
FIFO controled by the pgp_firi signal
the IOMUXC PGP register */
SDMA_REQ_UART2_TX = 17, /* TxFIFO of UART2 */
SDMA_REQ_UART1_RX = 18, /* RxFIFO */
SDMA_REQ_UART1_TX = 19, /* TxFIFO */
SDMA_REQ_MMC1 = 20, /* MMC DMA request */
SDMA_REQ_SDHC1 = 20, /* SDHC1 DMA request */
SDMA_REQ_MSHC1 = 20, /* MSHC1 DMA request */
SDMA_REQ_MMC2 = 21, /* MMC DMA request */
SDMA_REQ_SDHC2 = 21, /* SDHC2 DMA request */
SDMA_REQ_MSHC2 = 21, /* MSHC2 DMA request */
SDMA_REQ_SSI2_RX2 = 22, /* SSI #2 receive 2 DMA request (SRX1_2) */
SDMA_REQ_SSI2_TX2 = 23, /* SSI #2 transmit 2 DMA request (STX1_2) */
SDMA_REQ_SSI2_RX1 = 24, /* SSI #2 receive 1 DMA request (SRX0_2) */
SDMA_REQ_SSI2_TX1 = 25, /* SSI #2 transmit 1 DMA request (STX0_2) */
SDMA_REQ_SSI1_RX2 = 26, /* SSI #1 receive 2 DMA request (SRX1_1) */
SDMA_REQ_SSI1_TX2 = 27, /* SSI #1 transmit 2 DMA request (STX1_1) */
SDMA_REQ_SSI1_RX1 = 28, /* SSI #1 receive 1 DMA request (SRX1_0) */
SDMA_REQ_SSI1_TX1 = 29, /* SSI #1 transmit 1 DMA request (STX1_0) */
SDMA_REQ_NFC = 30, /* NAND-flash controller */
SDMA_REQ_IPU = 31, /* IPU source (defaults to IPU at reset) */
SDMA_REQ_ECT = 31, /* ECT source */
};
/* Addresses for peripheral DMA transfers */
enum SDMA_PER_ADDR
{
SDMA_PER_ADDR_SDRAM = SDRAM_BASE_ADDR, /* memory */
SDMA_PER_ADDR_CCM = CCM_BASE_ADDR+0x00, /* CCMR */
/* ATA */
SDMA_PER_ADDR_ATA_TX = ATA_DMA_BASE_ADDR+0x18,
SDMA_PER_ADDR_ATA_RX = ATA_DMA_BASE_ADDR,
#if 0
SDMA_PER_ADDR_ATA_TX16 =
SDMA_PER_ADDR_ATA_RX16 =
#endif
#if 0
SDMA_PER_ADDR_SIM =
#endif
/* CSPI2 */
SDMA_PER_ADDR_CSPI2_RX = CSPI2_BASE_ADDR+0x00, /* RXDATA2 */
SDMA_PER_ADDR_CSPI2_TX = CSPI2_BASE_ADDR+0x04, /* TXDATA2 */
/* CSPI1 */
SDMA_PER_ADDR_CSPI1_RX = CSPI1_BASE_ADDR+0x00, /* RXDATA1 */
SDMA_PER_ADDR_CSPI1_TX = CSPI1_BASE_ADDR+0x04, /* TXDATA1 */
/* UART3 */
SDMA_PER_ADDR_UART3_RX = UART3_BASE_ADDR+0x00, /* URXD3 */
SDMA_PER_ADDR_UART3_TX = UART3_BASE_ADDR+0x40, /* UTXD3 */
/* CSPI3 */
SDMA_PER_ADDR_CSPI3_RX = CSPI3_BASE_ADDR+0x00, /* RXDATA3 */
SDMA_PER_ADDR_CSPI3_TX = CSPI3_BASE_ADDR+0x04, /* TXDATA3 */
/* UART5 */
SDMA_PER_ADDR_UART5_RX = UART5_BASE_ADDR+0x00, /* URXD5 */
SDMA_PER_ADDR_UART5_TX = UART5_BASE_ADDR+0x40, /* UTXD5 */
/* UART4 */
SDMA_PER_ADDR_UART4_RX = UART4_BASE_ADDR+0x00, /* URXD4 */
SDMA_PER_ADDR_UART4_TX = UART4_BASE_ADDR+0x40, /* UTXD4 */
/* FIRI */
SDMA_PER_ADDR_FIRI_RX = FIRI_BASE_ADDR+0x18, /* Receiver FIFO */
SDMA_PER_ADDR_FIRI_TX = FIRI_BASE_ADDR+0x14, /* Transmitter FIFO */
/* UART2 */
SDMA_PER_ADDR_UART2_RX = UART2_BASE_ADDR+0x00, /* URXD2 */
SDMA_PER_ADDR_UART2_TX = UART2_BASE_ADDR+0x40, /* UTXD2 */
/* UART1 */
SDMA_PER_ADDR_UART1_RX = UART1_BASE_ADDR+0x00, /* URXD1 */
SDMA_PER_ADDR_UART1_TX = UART1_BASE_ADDR+0x40, /* UTXD1 */
SDMA_PER_ADDR_MMC_SDHC1 = MMC_SDHC1_BASE_ADDR+0x38, /* BUFFER_ACCESS */
SDMA_PER_ADDR_MMC_SDHC2 = MMC_SDHC2_BASE_ADDR+0x38, /* BUFFER_ACCESS */
#if 0
SDMA_PER_ADDR_MSHC1 =
SDMA_PER_ADDR_MSHC2 =
#endif
/* SSI2 */
SDMA_PER_ADDR_SSI2_RX2 = SSI2_BASE_ADDR+0x0C, /* SRX1_2 */
SDMA_PER_ADDR_SSI2_TX2 = SSI2_BASE_ADDR+0x04, /* STX1_2 */
SDMA_PER_ADDR_SSI2_RX1 = SSI2_BASE_ADDR+0x08, /* SRX0_2 */
SDMA_PER_ADDR_SSI2_TX1 = SSI2_BASE_ADDR+0x00, /* STX0_2 */
/* SSI1 */
SDMA_PER_ADDR_SSI1_RX2 = SSI1_BASE_ADDR+0x0C, /* SRX1_1 */
SDMA_PER_ADDR_SSI1_TX2 = SSI1_BASE_ADDR+0x04, /* STX1_1 */
SDMA_PER_ADDR_SSI1_RX1 = SSI1_BASE_ADDR+0x08, /* SRX0_1 */
SDMA_PER_ADDR_SSI1_TX1 = SSI1_BASE_ADDR+0x00, /* STX0_1 */
#if 0
SDMA_PER_ADDR_NFC =
SDMA_PER_ADDR_IPU =
SDMA_PER_ADDR_ECT =
#endif
};
/* DMA driver defines */
#define SDMA_SDHC_MMC_WML 16
#define SDMA_SDHC_SD_WML 64
#define SDMA_SSI_TXFIFO_WML 4 /* Four samples written per channel activation */
#define SDMA_SSI_RXFIFO_WML 6 /* Six samples read per channel activation */
#define SDMA_FIRI_WML 16
#define SDMA_ATA_WML 32 /* DMA watermark level in bytes */
#define SDMA_ATA_BD_NR (512/3/4) /* Number of BDs per channel */
#include "sdma_struct.h"
void sdma_init(void);
void sdma_read_words(unsigned long *buf, unsigned long start, int count);
void sdma_write_words(const unsigned long *buf, unsigned long start, int count);
void sdma_channel_set_priority(unsigned int channel, unsigned int priority);
void sdma_channel_start(unsigned int channel);
void sdma_channel_run(unsigned int channel);
void sdma_channel_pause(unsigned int channel);
void sdma_channel_stop(unsigned int channel);
void sdma_channel_wait_nonblocking(unsigned int channel);
bool sdma_channel_init(unsigned int channel,
struct channel_descriptor *cd_p,
struct buffer_descriptor *base_bd_p);
void sdma_channel_close(unsigned int channel);
#endif /* SDMA_IMX31_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,426 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 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.
*
****************************************************************************/
/* Largely taken from sdmaStruct.h from the Linux BSP provided by Freescale.
* Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/* Other information gleaned from RE-ing the BSP and SDMA code */
#ifndef SDMA_STRUCT_H
#define SDMA_STRUCT_H
/**
* Number of channels
*/
#define CH_NUM 32
/**
* Ownership
*/
#define CH_OWNSHP_EVT (1 << 0)
#define CH_OWNSHP_MCU (1 << 1)
#if 0
#define CH_OWNSHP_DSP (1 << 2)
#endif
/**
* Channel contexts management
*/
/* Contexts for each channel begin here within SDMA core */
#define CHANNEL_CONTEXT_BASE_ADDR 0x800
/* Compute SDMA address where context for a channel is stored */
#define CHANNEL_CONTEXT_ADDR(channel) \
(CHANNEL_CONTEXT_BASE_ADDR+sizeof(struct context_data)/4*(channel))
/**
* Error bit set in the CCB status field by the SDMA,
* in setbd routine, in case of a transfer error
*/
#define DATA_ERROR (1 << 28) /* BD_RROR set on last buffer descriptor */
#define DATA_FAULT (1 << 29) /* A source or destination fault occured */
/**
* Buffer descriptor status values.
*/
#define BD_DONE 0x01 /* Set by host, cleared when SDMA has finished with
this BD */
#define BD_WRAP 0x02 /* If set in last descriptor, allows circular buffer
* structure. curr_bd_ptr will be reset to base_bd_ptr
*/
#define BD_CONT 0x04 /* If set, more descriptors follow (multi-buffer) */
#define BD_INTR 0x08 /* Interrupt when transfer complete */
#define BD_RROR 0x10 /* Error during BD processing (set by SDMA) */
#define BD_LAST 0x20 /* Set by SDMA ap_2_bp and bp_2_ap scripts.
TODO: determine function */
#define BD_EXTD 0x80 /* Use extended buffer address (indicates BD is 12
bytes instead of 8) */
/**
* Buffer descriptor channel 0 commands.
*/
#define C0_SETCTX 0x07 /* Write context for a channel (ch# = BD command [7:3]) */
#define C0_GETCTX 0x03 /* Read context for a channel (ch# = BD command [7:3]) */
#define C0_SETDM 0x01 /* Write 32-bit words to SDMA memory */
#define C0_GETDM 0x02 /* Read 32-bit words from SDMA memory */
#define C0_SETPM 0x04 /* Write 16-bit halfwords to SDMA memory */
#define C0_GETPM 0x08 /* Read 16-bit halfwords from SDMA memory */
/* Treated the same as those above */
#define C0_ADDR 0x01
#define C0_LOAD 0x02
#define C0_DUMP 0x03
/**
* Transfer sizes, encoded in the BD command field (when not a C0_ command).
*/
#define TRANSFER_32BIT 0x00
#define TRANSFER_8BIT 0x01
#define TRANSFER_16BIT 0x02
#define TRANSFER_24BIT 0x03
/**
* Change endianness indicator in the BD command field
*/
#define CHANGE_ENDIANNESS 0x80
/**
* Size in bytes of struct buffer_descriptor
*/
#define SDMA_BD_SIZE 8
#define SDMA_EXTENDED_BD_SIZE 12
#define BD_NUMBER 4
/**
* Channel interrupt policy
*/
#define DEFAULT_POLL 0
#define CALLBACK_ISR 1
/**
* Channel status
*/
#define UNINITIALIZED 0
#define INITIALIZED 1
/**
* IoCtl particular values
*/
#define SET_BIT_ALL 0xFFFFFFFF
#define BD_NUM_OFFSET 16
#define BD_NUM_MASK 0xFFFF0000
/**
* Maximum values for IoCtl calls, used in high or middle level calls
*/
#define MAX_BD_NUM 256
#define MAX_BD_SIZE 65536
#define MAX_BLOCKING 2
#define MAX_SYNCH 2
#define MAX_OWNERSHIP 8
#define MAX_CH_PRIORITY 8
#define MAX_TRUST 2
#define MAX_WML 256
/**
* Default values for channel descriptor - nobody owns the channel
*/
#define CD_DEFAULT_OWNERSHIP 7
#if 0 /* IPC not used */
/**
* Data Node descriptor status values.
*/
#define DND_END_OF_FRAME 0x80
#define DND_END_OF_XFER 0x40
#define DND_DONE 0x20
#define DND_UNUSED 0x01
/**
* IPCV2 descriptor status values.
*/
#define BD_IPCV2_END_OF_FRAME 0x40
#define IPCV2_MAX_NODES 50
/**
* User Type Section
*/
/**
* Command/Mode/Count of buffer descriptors
*/
struct mode_count_ipcv2
{
unsigned long count : 16; /* size of the buffer pointed by this BD */
unsigned long reserved : 8; /* reserved */
unsigned long status : 8; /* L, E, D bits stored here */
};
/**
* Data Node descriptor - IPCv2
* (differentiated between evolutions of SDMA)
*/
struct data_node_descriptor
{
struct mode_count_ipcv2 mode; /* command, status and count */
void* buffer_addr; /* address of the buffer described */
};
struct mode_count_ipcv1_v2
{
unsigned long count : 16; /* size of the buffer pointed by this BD */
unsigned long status : 8; /* E,R,I,C,W,D status bits stored here */
unsigned long reserved : 7;
unsigned long endianness : 1;
};
/**
* Buffer descriptor
* (differentiated between evolutions of SDMA)
*/
struct buffer_descriptor_ipcv1_v2
{
struct mode_count_ipcv1_v2 mode; /* command, status and count */
void *buffer_addr; /* address of the buffer described */
void *ext_buffer_addr; /* extended buffer address */
};
#endif /* No IPC */
/**
* Mode/Count of data node descriptors - IPCv2
*/
struct mode_count
{
unsigned long count : 16; /* size of the buffer pointed by this BD */
unsigned long status : 8; /* E,R,I,C,W,D status bits stored here:
* SDMA r/w */
unsigned long command : 8; /* channel 0 command or transfer size */
};
/**
* Buffer descriptor - describes each buffer in a DMA transfer.
* (differentiated between evolutions of SDMA)
*/
/* (mode.status & BD_EXTD) == 0 (8 bytes) */
struct buffer_descriptor
{
volatile struct mode_count mode; /* command, status and count: SDMA r/w */
void *buf_addr; /* address of the buffer described: SDMA r */
};
/* (mode.status & BD_EXTD) != 0 (12 bytes) */
struct buffer_descriptor_extd
{
struct buffer_descriptor bd;
void *buf_addr_ext; /* extended buffer address described (r6): SDMA r */
};
#if 0 /* A different one is defined for Rockbox use - this has too much.
* See below. */
struct channel_control_block;
struct channel_descriptor;
/**
* Channel Descriptor
*/
struct channel_descriptor
{
unsigned char channel_number; /* stores the channel number */
unsigned char buffer_desc_count; /* number of BD's allocated
for this channel */
unsigned short buffer_per_desc_size; /* size (in bytes) of buffer
descriptors' data buffers */
unsigned long blocking : 3; /* polling/ callback method
selection */
unsigned long callback_synch : 1; /* (iapi) blocking / non blocking
feature selection */
unsigned long ownership : 3; /* ownership of the channel
(host+dedicated+event) */
unsigned long priority : 3; /* reflects the SDMA channel
priority register */
unsigned long trust : 1; /* trusted buffers or kernel
allocated */
unsigned long use_data_size : 1; /* (iapi) indicates if the dataSize
field is meaningfull */
unsigned long data_size : 2; /* (iapi->BD) data transfer
size - 8,16,24 or 32 bits*/
unsigned long force_close : 1; /* If TRUE, close channel even
with BD owned by SDMA*/
unsigned long script_id : 7; /* number of the script */
unsigned long watermark_level : 10; /* (greg) Watermark level for the
peripheral access */
unsigned long event_mask1; /* (greg) First Event mask */
unsigned long event_mask2; /* (greg) Second Event mask */
unsigned long shp_addr; /* (greg) Address of the peripheral
or its fifo when needed */
void (* callback)(struct channel_descriptor *); /* pointer to the
callback function (or NULL) */
struct channel_control_block *ccb_ptr; /* pointer to the channel control
block associated to this
channel */
};
#endif
/* Only what we need, members sorted by size, no code-bloating bitfields */
struct channel_descriptor
{
unsigned int bd_count; /* number of BD's allocated
for this channel */
struct channel_control_block *ccb_ptr; /* pointer to the channel control
block associated to this
channel */
void (* callback)(void); /* pointer to the
callback function (or NULL) */
unsigned long shp_addr; /* (greg) Address of the peripheral
or its fifo when needed */
unsigned short wml; /* (greg) Watermark level for the
peripheral access */
unsigned char per_type; /* Peripheral type */
unsigned char tran_type; /* Transfer type */
unsigned char event_id1; /* DMA request ID */
unsigned char event_id2; /* DMA request ID 2 */
unsigned char is_setup; /* Channel setup has been done */
};
/**
* Channel Status
*/
struct channel_status
{
unsigned long unused : 28;
unsigned long error : 1; /* Last BD triggered an error:
SDMA r/w */
unsigned long opened_init : 1; /* Channel is initialized:
SDMA r/w */
unsigned long state_direction : 1; /* SDMA is reading/writing (as seen
from channel owner core) */
unsigned long execute : 1; /* Channel is being processed
(started) or not */
};
/**
* Channel control Block
* SDMA ROM code expects these are 16-bytes each in an array
* (MC0PTR + 16*CCR)
*/
struct channel_control_block
{
/* current buffer descriptor processed: SDMA r/w */
struct buffer_descriptor * volatile curr_bd_ptr;
/* first element of buffer descriptor array: SDMA r */
struct buffer_descriptor *base_bd_ptr;
/* pointer to the channel descriptor: SDMA ignored */
struct channel_descriptor *channel_desc;
/* open/close ; started/stopped ; read/write: SDMA r/w */
volatile struct channel_status status;
};
/**
* Channel context structure.
*/
/* Channel state bits on SDMA core side */
struct state_registers
{
/* Offset 0 */
unsigned long pc : 14; /* program counter */
unsigned long unused0 : 1;
unsigned long t : 1; /* test bit: status of arithmetic & test
instruction */
unsigned long rpc : 14; /* return program counter */
unsigned long unused1 : 1;
unsigned long sf : 1; /* source fault while loading data */
/* Offset 1 */
unsigned long spc : 14; /* loop start program counter */
unsigned long unused2 : 1;
unsigned long df : 1; /* destination falut while storing data */
unsigned long epc : 14; /* loop end program counter */
unsigned long lm : 2; /* loop mode */
};
/* Context data saved for every channel on the SDMA core. This is 32 words
* long which is specified in the SDMA initialization on the AP side. The
* SDMA scripts utilize the scratch space. */
struct context_data
{
struct state_registers channel_state; /* channel state bits */
union
{
unsigned long r[8]; /* general registers (r0-r7) */
struct /* script usage of said when
setting contexts */
{
unsigned long event_mask2; /* 08h */
unsigned long event_mask1; /* 0Ch */
unsigned long r2; /* 10h */
unsigned long r3; /* 14h */
unsigned long r4; /* 18h */
unsigned long r5; /* 1Ch */
unsigned long shp_addr; /* 20h */
unsigned long wml; /* 24h */
};
};
unsigned long mda; /* burst dma destination address register */
unsigned long msa; /* burst dma source address register */
unsigned long ms; /* burst dma status register */
unsigned long md; /* burst dma data register */
unsigned long pda; /* peripheral dma destination address register */
unsigned long psa; /* peripheral dma source address register */
unsigned long ps; /* peripheral dma status register */
unsigned long pd; /* peripheral dma data register */
unsigned long ca; /* CRC polynomial register */
unsigned long cs; /* CRC accumulator register */
unsigned long dda; /* dedicated core destination address register */
unsigned long dsa; /* dedicated core source address register */
unsigned long ds; /* dedicated core status register */
unsigned long dd; /* dedicated core data register */
unsigned long scratch[8]; /* channel context scratch RAM */
};
/**
* This structure holds the necessary data for the assignment in the
* dynamic channel-script association
*/
struct script_data
{
unsigned long load_address; /* start address of the script */
unsigned long wml; /* parameters for the channel descriptor */
unsigned long shp_addr; /* shared peripheral base address */
unsigned long event_mask1; /* first event mask */
unsigned long event_mask2; /* second event mask */
};
/**
* This structure holds the the useful bits of the CONFIG register
*/
struct configs_data
{
unsigned long dspdma : 1; /* indicates if the DSPDMA is used */
unsigned long rtdobs : 1; /* indicates if Real-Time Debug pins are
enabled */
unsigned long acr : 1; /* indicates if AHB freq /core freq = 2 or 1 */
unsigned long csm : 2; /* indicates which context switch mode is
selected */
};
#endif /* SDMA_STRUCT_H */