2009-05-24 03:12:59 +04:00
/*
* Freescale MPC5200 PSC DMA
* ALSA SoC Platform driver
*
* Copyright ( C ) 2008 Secret Lab Technologies Ltd .
2009-05-26 16:34:08 +04:00
* Copyright ( C ) 2009 Jon Smirl , Digispeaker
2009-05-24 03:12:59 +04:00
*/
# include <linux/module.h>
# include <linux/of_device.h>
# include <sound/soc.h>
# include <sysdev/bestcomm/bestcomm.h>
# include <sysdev/bestcomm/gen_bd.h>
# include <asm/mpc52xx_psc.h>
# include "mpc5200_dma.h"
/*
* Interrupt handlers
*/
2009-05-24 03:13:01 +04:00
static irqreturn_t psc_dma_status_irq ( int irq , void * _psc_dma )
2009-05-24 03:12:59 +04:00
{
2009-05-24 03:13:01 +04:00
struct psc_dma * psc_dma = _psc_dma ;
struct mpc52xx_psc __iomem * regs = psc_dma - > psc_regs ;
2009-05-24 03:12:59 +04:00
u16 isr ;
isr = in_be16 ( & regs - > mpc52xx_psc_isr ) ;
/* Playback underrun error */
2009-05-24 03:13:01 +04:00
if ( psc_dma - > playback . active & & ( isr & MPC52xx_PSC_IMR_TXEMP ) )
psc_dma - > stats . underrun_count + + ;
2009-05-24 03:12:59 +04:00
/* Capture overrun error */
2009-05-24 03:13:01 +04:00
if ( psc_dma - > capture . active & & ( isr & MPC52xx_PSC_IMR_ORERR ) )
psc_dma - > stats . overrun_count + + ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
out_8 ( & regs - > command , MPC52xx_PSC_RST_ERR_STAT ) ;
2009-05-24 03:12:59 +04:00
return IRQ_HANDLED ;
}
/**
2009-05-24 03:13:01 +04:00
* psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer
2009-05-24 03:12:59 +04:00
* @ s : pointer to stream private data structure
*
* Enqueues another audio period buffer into the bestcomm queue .
*
* Note : The routine must only be called when there is space available in
* the queue . Otherwise the enqueue will fail and the audio ring buffer
* will get out of sync
*/
2009-05-24 03:13:01 +04:00
static void psc_dma_bcom_enqueue_next_buffer ( struct psc_dma_stream * s )
2009-05-24 03:12:59 +04:00
{
struct bcom_bd * bd ;
/* Prepare and enqueue the next buffer descriptor */
bd = bcom_prepare_next_buffer ( s - > bcom_task ) ;
bd - > status = s - > period_bytes ;
2009-11-07 11:33:53 +03:00
bd - > data [ 0 ] = s - > runtime - > dma_addr + ( s - > period_next * s - > period_bytes ) ;
2009-05-24 03:12:59 +04:00
bcom_submit_next_buffer ( s - > bcom_task , NULL ) ;
/* Update for next period */
2009-11-07 11:33:53 +03:00
s - > period_next = ( s - > period_next + 1 ) % s - > runtime - > periods ;
2009-05-24 03:12:59 +04:00
}
2009-05-26 16:34:08 +04:00
static void psc_dma_bcom_enqueue_tx ( struct psc_dma_stream * s )
{
2009-07-29 19:38:55 +04:00
if ( s - > appl_ptr > s - > runtime - > control - > appl_ptr ) {
/*
* In this case s - > runtime - > control - > appl_ptr has wrapped around .
* Play the data to the end of the boundary , then wrap our own
* appl_ptr back around .
*/
while ( s - > appl_ptr < s - > runtime - > boundary ) {
if ( bcom_queue_full ( s - > bcom_task ) )
return ;
2009-11-07 11:33:53 +03:00
s - > appl_ptr + = s - > runtime - > period_size ;
2009-07-29 19:38:55 +04:00
psc_dma_bcom_enqueue_next_buffer ( s ) ;
}
s - > appl_ptr - = s - > runtime - > boundary ;
}
2009-05-26 16:34:08 +04:00
while ( s - > appl_ptr < s - > runtime - > control - > appl_ptr ) {
if ( bcom_queue_full ( s - > bcom_task ) )
return ;
2009-11-07 11:33:53 +03:00
s - > appl_ptr + = s - > runtime - > period_size ;
2009-05-26 16:34:08 +04:00
psc_dma_bcom_enqueue_next_buffer ( s ) ;
}
}
2009-05-24 03:12:59 +04:00
/* Bestcomm DMA irq handler */
2009-05-26 16:34:08 +04:00
static irqreturn_t psc_dma_bcom_irq_tx ( int irq , void * _psc_dma_stream )
2009-05-24 03:12:59 +04:00
{
2009-05-24 03:13:01 +04:00
struct psc_dma_stream * s = _psc_dma_stream ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
spin_lock ( & s - > psc_dma - > lock ) ;
2009-05-24 03:12:59 +04:00
/* For each finished period, dequeue the completed period buffer
* and enqueue a new one in it ' s place . */
while ( bcom_buffer_done ( s - > bcom_task ) ) {
bcom_retrieve_buffer ( s - > bcom_task , NULL , NULL ) ;
2009-05-26 16:34:08 +04:00
2009-11-07 11:33:53 +03:00
s - > period_current = ( s - > period_current + 1 ) % s - > runtime - > periods ;
2009-05-24 03:12:59 +04:00
}
2009-05-26 16:34:08 +04:00
psc_dma_bcom_enqueue_tx ( s ) ;
spin_unlock ( & s - > psc_dma - > lock ) ;
2009-05-24 03:12:59 +04:00
/* If the stream is active, then also inform the PCM middle layer
* of the period finished event . */
if ( s - > active )
snd_pcm_period_elapsed ( s - > stream ) ;
return IRQ_HANDLED ;
}
2009-05-26 16:34:08 +04:00
static irqreturn_t psc_dma_bcom_irq_rx ( int irq , void * _psc_dma_stream )
2009-05-24 03:12:59 +04:00
{
2009-05-26 16:34:08 +04:00
struct psc_dma_stream * s = _psc_dma_stream ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
spin_lock ( & s - > psc_dma - > lock ) ;
/* For each finished period, dequeue the completed period buffer
* and enqueue a new one in it ' s place . */
while ( bcom_buffer_done ( s - > bcom_task ) ) {
bcom_retrieve_buffer ( s - > bcom_task , NULL , NULL ) ;
2009-05-24 03:12:59 +04:00
2009-11-07 11:33:53 +03:00
s - > period_current = ( s - > period_current + 1 ) % s - > runtime - > periods ;
2009-05-26 16:34:08 +04:00
psc_dma_bcom_enqueue_next_buffer ( s ) ;
2009-05-24 03:12:59 +04:00
}
2009-05-26 16:34:08 +04:00
spin_unlock ( & s - > psc_dma - > lock ) ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
/* If the stream is active, then also inform the PCM middle layer
* of the period finished event . */
if ( s - > active )
snd_pcm_period_elapsed ( s - > stream ) ;
return IRQ_HANDLED ;
2009-05-24 03:12:59 +04:00
}
2009-05-26 16:34:08 +04:00
static int psc_dma_hw_free ( struct snd_pcm_substream * substream )
2009-05-24 03:12:59 +04:00
{
snd_pcm_set_runtime_buffer ( substream , NULL ) ;
return 0 ;
}
/**
2009-05-24 03:13:01 +04:00
* psc_dma_trigger : start and stop the DMA transfer .
2009-05-24 03:12:59 +04:00
*
* This function is called by ALSA to start , stop , pause , and resume the DMA
* transfer of data .
*/
2009-05-26 16:34:08 +04:00
static int psc_dma_trigger ( struct snd_pcm_substream * substream , int cmd )
2009-05-24 03:12:59 +04:00
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2009-05-24 03:13:01 +04:00
struct psc_dma * psc_dma = rtd - > dai - > cpu_dai - > private_data ;
2009-05-24 03:12:59 +04:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2009-05-24 03:13:01 +04:00
struct psc_dma_stream * s ;
struct mpc52xx_psc __iomem * regs = psc_dma - > psc_regs ;
2009-05-24 03:12:59 +04:00
u16 imr ;
unsigned long flags ;
2009-05-26 16:34:08 +04:00
int i ;
2009-05-24 03:12:59 +04:00
if ( substream - > pstr - > stream = = SNDRV_PCM_STREAM_CAPTURE )
2009-05-24 03:13:01 +04:00
s = & psc_dma - > capture ;
2009-05-24 03:12:59 +04:00
else
2009-05-24 03:13:01 +04:00
s = & psc_dma - > playback ;
2009-05-24 03:12:59 +04:00
2009-05-24 03:13:01 +04:00
dev_dbg ( psc_dma - > dev , " psc_dma_trigger(substream=%p, cmd=%i) "
2009-05-24 03:12:59 +04:00
" stream_id=%i \n " ,
substream , cmd , substream - > pstr - > stream ) ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
s - > period_bytes = frames_to_bytes ( runtime ,
runtime - > period_size ) ;
2009-11-07 11:33:53 +03:00
s - > period_next = 0 ;
s - > period_current = 0 ;
2009-05-24 03:12:59 +04:00
s - > active = 1 ;
2009-05-26 16:34:08 +04:00
/* track appl_ptr so that we have a better chance of detecting
* end of stream and not over running it .
*/
s - > runtime = runtime ;
s - > appl_ptr = s - > runtime - > control - > appl_ptr -
( runtime - > period_size * runtime - > periods ) ;
/* Fill up the bestcomm bd queue and enable DMA.
* This will begin filling the PSC ' s fifo .
*/
spin_lock_irqsave ( & psc_dma - > lock , flags ) ;
2009-05-24 03:12:59 +04:00
if ( substream - > pstr - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
2009-05-26 16:34:08 +04:00
bcom_gen_bd_rx_reset ( s - > bcom_task ) ;
for ( i = 0 ; i < runtime - > periods ; i + + )
if ( ! bcom_queue_full ( s - > bcom_task ) )
psc_dma_bcom_enqueue_next_buffer ( s ) ;
2009-05-24 03:12:59 +04:00
} else {
2009-05-26 16:34:08 +04:00
bcom_gen_bd_tx_reset ( s - > bcom_task ) ;
psc_dma_bcom_enqueue_tx ( s ) ;
2009-05-24 03:12:59 +04:00
}
bcom_enable ( s - > bcom_task ) ;
2009-05-24 03:13:01 +04:00
spin_unlock_irqrestore ( & psc_dma - > lock , flags ) ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
out_8 ( & regs - > command , MPC52xx_PSC_RST_ERR_STAT ) ;
2009-05-24 03:12:59 +04:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
s - > active = 0 ;
2009-05-26 16:34:08 +04:00
spin_lock_irqsave ( & psc_dma - > lock , flags ) ;
2009-05-24 03:12:59 +04:00
bcom_disable ( s - > bcom_task ) ;
2009-05-26 16:34:08 +04:00
if ( substream - > pstr - > stream = = SNDRV_PCM_STREAM_CAPTURE )
bcom_gen_bd_rx_reset ( s - > bcom_task ) ;
else
bcom_gen_bd_tx_reset ( s - > bcom_task ) ;
spin_unlock_irqrestore ( & psc_dma - > lock , flags ) ;
2009-05-24 03:12:59 +04:00
break ;
default :
2009-05-24 03:13:01 +04:00
dev_dbg ( psc_dma - > dev , " invalid command \n " ) ;
2009-05-24 03:12:59 +04:00
return - EINVAL ;
}
/* Update interrupt enable settings */
imr = 0 ;
2009-05-24 03:13:01 +04:00
if ( psc_dma - > playback . active )
2009-05-24 03:12:59 +04:00
imr | = MPC52xx_PSC_IMR_TXEMP ;
2009-05-24 03:13:01 +04:00
if ( psc_dma - > capture . active )
2009-05-24 03:12:59 +04:00
imr | = MPC52xx_PSC_IMR_ORERR ;
2009-05-26 16:34:08 +04:00
out_be16 ( & regs - > isr_imr . imr , psc_dma - > imr | imr ) ;
2009-05-24 03:12:59 +04:00
return 0 ;
}
/* ---------------------------------------------------------------------
* The PSC DMA ' ASoC platform ' driver
*
* Can be referenced by an ' ASoC machine ' driver
* This driver only deals with the audio bus ; it doesn ' t have any
* interaction with the attached codec
*/
2009-05-26 16:34:08 +04:00
static const struct snd_pcm_hardware psc_dma_hardware = {
2009-05-24 03:12:59 +04:00
. info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_BATCH ,
. formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
2009-05-26 16:34:08 +04:00
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE ,
2009-05-24 03:12:59 +04:00
. rate_min = 8000 ,
. rate_max = 48000 ,
2009-05-26 16:34:08 +04:00
. channels_min = 1 ,
2009-05-24 03:12:59 +04:00
. channels_max = 2 ,
. period_bytes_max = 1024 * 1024 ,
. period_bytes_min = 32 ,
. periods_min = 2 ,
. periods_max = 256 ,
. buffer_bytes_max = 2 * 1024 * 1024 ,
2009-05-26 16:34:08 +04:00
. fifo_size = 512 ,
2009-05-24 03:12:59 +04:00
} ;
2009-05-26 16:34:08 +04:00
static int psc_dma_open ( struct snd_pcm_substream * substream )
2009-05-24 03:12:59 +04:00
{
2009-05-26 16:34:08 +04:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2009-05-24 03:12:59 +04:00
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2009-05-24 03:13:01 +04:00
struct psc_dma * psc_dma = rtd - > dai - > cpu_dai - > private_data ;
struct psc_dma_stream * s ;
2009-05-26 16:34:08 +04:00
int rc ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
dev_dbg ( psc_dma - > dev , " psc_dma_open(substream=%p) \n " , substream ) ;
2009-05-24 03:12:59 +04:00
if ( substream - > pstr - > stream = = SNDRV_PCM_STREAM_CAPTURE )
2009-05-24 03:13:01 +04:00
s = & psc_dma - > capture ;
2009-05-24 03:12:59 +04:00
else
2009-05-24 03:13:01 +04:00
s = & psc_dma - > playback ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
snd_soc_set_runtime_hwparams ( substream , & psc_dma_hardware ) ;
rc = snd_pcm_hw_constraint_integer ( runtime ,
SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( rc < 0 ) {
dev_err ( substream - > pcm - > card - > dev , " invalid buffer size \n " ) ;
return rc ;
}
2009-05-24 03:12:59 +04:00
s - > stream = substream ;
return 0 ;
}
2009-05-26 16:34:08 +04:00
static int psc_dma_close ( struct snd_pcm_substream * substream )
2009-05-24 03:12:59 +04:00
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2009-05-24 03:13:01 +04:00
struct psc_dma * psc_dma = rtd - > dai - > cpu_dai - > private_data ;
struct psc_dma_stream * s ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
dev_dbg ( psc_dma - > dev , " psc_dma_close(substream=%p) \n " , substream ) ;
2009-05-24 03:12:59 +04:00
if ( substream - > pstr - > stream = = SNDRV_PCM_STREAM_CAPTURE )
2009-05-24 03:13:01 +04:00
s = & psc_dma - > capture ;
2009-05-24 03:12:59 +04:00
else
2009-05-24 03:13:01 +04:00
s = & psc_dma - > playback ;
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
if ( ! psc_dma - > playback . active & &
! psc_dma - > capture . active ) {
/* Disable all interrupts and reset the PSC */
out_be16 ( & psc_dma - > psc_regs - > isr_imr . imr , psc_dma - > imr ) ;
out_8 ( & psc_dma - > psc_regs - > command , 4 < < 4 ) ; /* reset error */
}
2009-05-24 03:12:59 +04:00
s - > stream = NULL ;
return 0 ;
}
static snd_pcm_uframes_t
2009-05-26 16:34:08 +04:00
psc_dma_pointer ( struct snd_pcm_substream * substream )
2009-05-24 03:12:59 +04:00
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2009-05-24 03:13:01 +04:00
struct psc_dma * psc_dma = rtd - > dai - > cpu_dai - > private_data ;
struct psc_dma_stream * s ;
2009-05-24 03:12:59 +04:00
dma_addr_t count ;
if ( substream - > pstr - > stream = = SNDRV_PCM_STREAM_CAPTURE )
2009-05-24 03:13:01 +04:00
s = & psc_dma - > capture ;
2009-05-24 03:12:59 +04:00
else
2009-05-24 03:13:01 +04:00
s = & psc_dma - > playback ;
2009-05-24 03:12:59 +04:00
2009-11-07 11:33:53 +03:00
count = s - > period_current * s - > period_bytes ;
2009-05-24 03:12:59 +04:00
return bytes_to_frames ( substream - > runtime , count ) ;
}
2009-05-26 16:34:08 +04:00
static int
psc_dma_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
snd_pcm_set_runtime_buffer ( substream , & substream - > dma_buffer ) ;
return 0 ;
}
static struct snd_pcm_ops psc_dma_ops = {
. open = psc_dma_open ,
. close = psc_dma_close ,
. hw_free = psc_dma_hw_free ,
2009-05-24 03:12:59 +04:00
. ioctl = snd_pcm_lib_ioctl ,
2009-05-26 16:34:08 +04:00
. pointer = psc_dma_pointer ,
. trigger = psc_dma_trigger ,
. hw_params = psc_dma_hw_params ,
2009-05-24 03:12:59 +04:00
} ;
2009-05-26 16:34:08 +04:00
static u64 psc_dma_dmamask = 0xffffffff ;
static int psc_dma_new ( struct snd_card * card , struct snd_soc_dai * dai ,
2009-05-24 03:12:59 +04:00
struct snd_pcm * pcm )
{
struct snd_soc_pcm_runtime * rtd = pcm - > private_data ;
2009-05-26 16:34:08 +04:00
struct psc_dma * psc_dma = rtd - > dai - > cpu_dai - > private_data ;
size_t size = psc_dma_hardware . buffer_bytes_max ;
2009-05-24 03:12:59 +04:00
int rc = 0 ;
2009-05-26 16:34:08 +04:00
dev_dbg ( rtd - > socdev - > dev , " psc_dma_new(card=%p, dai=%p, pcm=%p) \n " ,
2009-05-24 03:12:59 +04:00
card , dai , pcm ) ;
if ( ! card - > dev - > dma_mask )
2009-05-26 16:34:08 +04:00
card - > dev - > dma_mask = & psc_dma_dmamask ;
2009-05-24 03:12:59 +04:00
if ( ! card - > dev - > coherent_dma_mask )
card - > dev - > coherent_dma_mask = 0xffffffff ;
if ( pcm - > streams [ 0 ] . substream ) {
2009-05-26 16:34:08 +04:00
rc = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , pcm - > card - > dev ,
size , & pcm - > streams [ 0 ] . substream - > dma_buffer ) ;
2009-05-24 03:12:59 +04:00
if ( rc )
goto playback_alloc_err ;
}
if ( pcm - > streams [ 1 ] . substream ) {
2009-05-26 16:34:08 +04:00
rc = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , pcm - > card - > dev ,
size , & pcm - > streams [ 1 ] . substream - > dma_buffer ) ;
2009-05-24 03:12:59 +04:00
if ( rc )
goto capture_alloc_err ;
}
2009-05-26 16:34:08 +04:00
if ( rtd - > socdev - > card - > codec - > ac97 )
rtd - > socdev - > card - > codec - > ac97 - > private_data = psc_dma ;
2009-05-24 03:12:59 +04:00
return 0 ;
capture_alloc_err :
if ( pcm - > streams [ 0 ] . substream )
snd_dma_free_pages ( & pcm - > streams [ 0 ] . substream - > dma_buffer ) ;
2009-05-26 16:34:08 +04:00
2009-05-24 03:12:59 +04:00
playback_alloc_err :
dev_err ( card - > dev , " Cannot allocate buffer(s) \n " ) ;
2009-05-26 16:34:08 +04:00
2009-05-24 03:12:59 +04:00
return - ENOMEM ;
}
2009-05-26 16:34:08 +04:00
static void psc_dma_free ( struct snd_pcm * pcm )
2009-05-24 03:12:59 +04:00
{
struct snd_soc_pcm_runtime * rtd = pcm - > private_data ;
struct snd_pcm_substream * substream ;
int stream ;
2009-05-26 16:34:08 +04:00
dev_dbg ( rtd - > socdev - > dev , " psc_dma_free(pcm=%p) \n " , pcm ) ;
2009-05-24 03:12:59 +04:00
for ( stream = 0 ; stream < 2 ; stream + + ) {
substream = pcm - > streams [ stream ] . substream ;
if ( substream ) {
snd_dma_free_pages ( & substream - > dma_buffer ) ;
substream - > dma_buffer . area = NULL ;
substream - > dma_buffer . addr = 0 ;
}
}
}
2009-05-26 16:34:08 +04:00
struct snd_soc_platform mpc5200_audio_dma_platform = {
2009-05-24 03:12:59 +04:00
. name = " mpc5200-psc-audio " ,
2009-05-26 16:34:08 +04:00
. pcm_ops = & psc_dma_ops ,
. pcm_new = & psc_dma_new ,
. pcm_free = & psc_dma_free ,
2009-05-24 03:12:59 +04:00
} ;
2009-05-26 16:34:08 +04:00
EXPORT_SYMBOL_GPL ( mpc5200_audio_dma_platform ) ;
int mpc5200_audio_dma_create ( struct of_device * op )
{
phys_addr_t fifo ;
struct psc_dma * psc_dma ;
struct resource res ;
int size , irq , rc ;
const __be32 * prop ;
void __iomem * regs ;
2009-09-12 16:25:35 +04:00
int ret ;
2009-05-26 16:34:08 +04:00
/* Fetch the registers and IRQ of the PSC */
irq = irq_of_parse_and_map ( op - > node , 0 ) ;
if ( of_address_to_resource ( op - > node , 0 , & res ) ) {
dev_err ( & op - > dev , " Missing reg property \n " ) ;
return - ENODEV ;
}
regs = ioremap ( res . start , 1 + res . end - res . start ) ;
if ( ! regs ) {
dev_err ( & op - > dev , " Could not map registers \n " ) ;
return - ENODEV ;
}
/* Allocate and initialize the driver private data */
psc_dma = kzalloc ( sizeof * psc_dma , GFP_KERNEL ) ;
if ( ! psc_dma ) {
2009-09-12 16:25:35 +04:00
ret = - ENOMEM ;
goto out_unmap ;
2009-05-26 16:34:08 +04:00
}
/* Get the PSC ID */
prop = of_get_property ( op - > node , " cell-index " , & size ) ;
2009-09-12 16:25:35 +04:00
if ( ! prop | | size < sizeof * prop ) {
ret = - ENODEV ;
goto out_free ;
}
2009-05-26 16:34:08 +04:00
spin_lock_init ( & psc_dma - > lock ) ;
2009-07-02 21:57:25 +04:00
mutex_init ( & psc_dma - > mutex ) ;
2009-05-26 16:34:08 +04:00
psc_dma - > id = be32_to_cpu ( * prop ) ;
psc_dma - > irq = irq ;
psc_dma - > psc_regs = regs ;
psc_dma - > fifo_regs = regs + sizeof * psc_dma - > psc_regs ;
psc_dma - > dev = & op - > dev ;
psc_dma - > playback . psc_dma = psc_dma ;
psc_dma - > capture . psc_dma = psc_dma ;
snprintf ( psc_dma - > name , sizeof psc_dma - > name , " PSC%u " , psc_dma - > id ) ;
/* Find the address of the fifo data registers and setup the
* DMA tasks */
fifo = res . start + offsetof ( struct mpc52xx_psc , buffer . buffer_32 ) ;
psc_dma - > capture . bcom_task =
bcom_psc_gen_bd_rx_init ( psc_dma - > id , 10 , fifo , 512 ) ;
psc_dma - > playback . bcom_task =
bcom_psc_gen_bd_tx_init ( psc_dma - > id , 10 , fifo ) ;
if ( ! psc_dma - > capture . bcom_task | |
! psc_dma - > playback . bcom_task ) {
dev_err ( & op - > dev , " Could not allocate bestcomm tasks \n " ) ;
2009-09-12 16:25:35 +04:00
ret = - ENODEV ;
goto out_free ;
2009-05-26 16:34:08 +04:00
}
/* Disable all interrupts and reset the PSC */
out_be16 ( & psc_dma - > psc_regs - > isr_imr . imr , psc_dma - > imr ) ;
/* reset receiver */
out_8 ( & psc_dma - > psc_regs - > command , MPC52xx_PSC_RST_RX ) ;
/* reset transmitter */
out_8 ( & psc_dma - > psc_regs - > command , MPC52xx_PSC_RST_TX ) ;
/* reset error */
out_8 ( & psc_dma - > psc_regs - > command , MPC52xx_PSC_RST_ERR_STAT ) ;
/* reset mode */
out_8 ( & psc_dma - > psc_regs - > command , MPC52xx_PSC_SEL_MODE_REG_1 ) ;
/* Set up mode register;
* First write : RxRdy ( FIFO Alarm ) generates rx FIFO irq
* Second write : register Normal mode for non loopback
*/
out_8 ( & psc_dma - > psc_regs - > mode , 0 ) ;
out_8 ( & psc_dma - > psc_regs - > mode , 0 ) ;
/* Set the TX and RX fifo alarm thresholds */
out_be16 ( & psc_dma - > fifo_regs - > rfalarm , 0x100 ) ;
out_8 ( & psc_dma - > fifo_regs - > rfcntl , 0x4 ) ;
out_be16 ( & psc_dma - > fifo_regs - > tfalarm , 0x100 ) ;
out_8 ( & psc_dma - > fifo_regs - > tfcntl , 0x7 ) ;
/* Lookup the IRQ numbers */
psc_dma - > playback . irq =
bcom_get_task_irq ( psc_dma - > playback . bcom_task ) ;
psc_dma - > capture . irq =
bcom_get_task_irq ( psc_dma - > capture . bcom_task ) ;
rc = request_irq ( psc_dma - > irq , & psc_dma_status_irq , IRQF_SHARED ,
" psc-dma-status " , psc_dma ) ;
rc | = request_irq ( psc_dma - > capture . irq ,
& psc_dma_bcom_irq_rx , IRQF_SHARED ,
" psc-dma-capture " , & psc_dma - > capture ) ;
rc | = request_irq ( psc_dma - > playback . irq ,
& psc_dma_bcom_irq_tx , IRQF_SHARED ,
" psc-dma-playback " , & psc_dma - > playback ) ;
if ( rc ) {
2009-09-12 16:25:35 +04:00
ret = - ENODEV ;
goto out_irq ;
2009-05-26 16:34:08 +04:00
}
2009-05-24 03:12:59 +04:00
2009-05-26 16:34:08 +04:00
/* Save what we've done so it can be found again later */
dev_set_drvdata ( & op - > dev , psc_dma ) ;
/* Tell the ASoC OF helpers about it */
return snd_soc_register_platform ( & mpc5200_audio_dma_platform ) ;
2009-09-12 16:25:35 +04:00
out_irq :
free_irq ( psc_dma - > irq , psc_dma ) ;
free_irq ( psc_dma - > capture . irq , & psc_dma - > capture ) ;
free_irq ( psc_dma - > playback . irq , & psc_dma - > playback ) ;
out_free :
kfree ( psc_dma ) ;
out_unmap :
iounmap ( regs ) ;
return ret ;
2009-05-26 16:34:08 +04:00
}
EXPORT_SYMBOL_GPL ( mpc5200_audio_dma_create ) ;
int mpc5200_audio_dma_destroy ( struct of_device * op )
{
struct psc_dma * psc_dma = dev_get_drvdata ( & op - > dev ) ;
dev_dbg ( & op - > dev , " mpc5200_audio_dma_destroy() \n " ) ;
snd_soc_unregister_platform ( & mpc5200_audio_dma_platform ) ;
bcom_gen_bd_rx_release ( psc_dma - > capture . bcom_task ) ;
bcom_gen_bd_tx_release ( psc_dma - > playback . bcom_task ) ;
/* Release irqs */
free_irq ( psc_dma - > irq , psc_dma ) ;
free_irq ( psc_dma - > capture . irq , & psc_dma - > capture ) ;
free_irq ( psc_dma - > playback . irq , & psc_dma - > playback ) ;
iounmap ( psc_dma - > psc_regs ) ;
kfree ( psc_dma ) ;
dev_set_drvdata ( & op - > dev , NULL ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( mpc5200_audio_dma_destroy ) ;
MODULE_AUTHOR ( " Grant Likely <grant.likely@secretlab.ca> " ) ;
MODULE_DESCRIPTION ( " Freescale MPC5200 PSC in DMA mode ASoC Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;