rockbox/firmware/target/arm/rk27xx/pcm-rk27xx.c
Marcin Bukat 472314ba73 rk27xx - implement pcm driver.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30444 a1c6a512-1295-4272-9138-f99709370657
2011-09-06 12:38:22 +00:00

238 lines
7.1 KiB
C

/***************************************************************************
* __________ __ ___.
* 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 */