@ -16,6 +16,7 @@
# include <linux/init.h>
# include <linux/device.h>
# include <linux/slab.h>
# include <linux/dmaengine.h>
# include <linux/dma-mapping.h>
# include <sound/core.h>
@ -53,43 +54,34 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
struct ep93xx_runtime_data
{
struct ep93xx_dma_m2p_client cl ;
struct ep93xx_pcm_dma_params * params ;
int pointer_bytes ;
struct tasklet_struct period_tasklet ;
int periods ;
struct ep93xx_dma_buffer buf [ 32 ] ;
int period_bytes ;
struct dma_chan * dma_chan ;
struct ep93xx_dma_data dma_data ;
} ;
static void ep93xx_pcm_period_elapsed ( unsigned long data )
static void ep93xx_pcm_dma_callback ( void * data )
{
struct snd_pcm_substream * substream = ( struct snd_pcm_substream * ) data ;
snd_pcm_period_elapsed ( substream ) ;
}
struct snd_pcm_substream * substream = data ;
struct ep93xx_runtime_data * rtd = substream - > runtime - > private_data ;
static void ep93xx_pcm_buffer_started ( void * cookie ,
struct ep93xx_dma_buffer * buf )
{
rtd - > pointer_bytes + = rtd - > period_bytes ;
rtd - > pointer_bytes % = rtd - > period_bytes * rtd - > periods ;
snd_pcm_period_elapsed ( substream ) ;
}
static void ep93xx_pcm_buffer_finished ( void * cookie ,
struct ep93xx_dma_buffer * buf ,
int bytes , int error )
static bool ep93xx_pcm_dma_filter ( struct dma_chan * chan , void * filter_param )
{
struct snd_pcm_substream * substream = cookie ;
struct ep93xx_runtime_data * rtd = substream - > runtime - > private_data ;
if ( buf = = rtd - > buf + rtd - > periods - 1 )
rtd - > pointer_bytes = 0 ;
else
rtd - > pointer_bytes + = buf - > size ;
struct ep93xx_dma_data * data = filter_param ;
if ( ! error ) {
ep93xx_dma_m2p_submit_recursive ( & rtd - > cl , buf ) ;
tasklet_schedule ( & rtd - > period_tasklet ) ;
} else {
snd_pcm_stop ( substream , SNDRV_PCM_STATE_XRUN ) ;
if ( data - > direction = = ep93xx_dma_chan_direction ( chan ) ) {
chan - > private = data ;
return true ;
}
return false ;
}
static int ep93xx_pcm_open ( struct snd_pcm_substream * substream )
@ -98,30 +90,38 @@ static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
struct snd_soc_dai * cpu_dai = soc_rtd - > cpu_dai ;
struct ep93xx_pcm_dma_params * dma_params ;
struct ep93xx_runtime_data * rtd ;
dma_cap_mask_t mask ;
int ret ;
dma_params = snd_soc_dai_get_dma_data ( cpu_dai , substream ) ;
ret = snd_pcm_hw_constraint_integer ( substream - > runtime ,
SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( ret < 0 )
return ret ;
snd_soc_set_runtime_hwparams ( substream , & ep93xx_pcm_hardware ) ;
rtd = kmalloc ( sizeof ( * rtd ) , GFP_KERNEL ) ;
if ( ! rtd )
return - ENOMEM ;
memset ( & rtd - > period_tasklet , 0 , sizeof ( rtd - > period_tasklet ) ) ;
rtd - > period_tasklet . func = ep93xx_pcm_period_elapsed ;
rtd - > period_tasklet . data = ( unsigned long ) substream ;
rtd - > cl . name = dma_params - > name ;
rtd - > cl . flags = dma_params - > dma_port | EP93XX_DMA_M2P_IGNORE_ERROR |
( ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ?
EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX ) ;
rtd - > cl . cookie = substream ;
rtd - > cl . buffer_started = ep93xx_pcm_buffer_started ;
rtd - > cl . buffer_finished = ep93xx_pcm_buffer_finished ;
ret = ep93xx_dma_m2p_client_register ( & rtd - > cl ) ;
if ( ret < 0 ) {
dma_cap_zero ( mask ) ;
dma_cap_set ( DMA_SLAVE , mask ) ;
dma_cap_set ( DMA_CYCLIC , mask ) ;
dma_params = snd_soc_dai_get_dma_data ( cpu_dai , substream ) ;
rtd - > dma_data . port = dma_params - > dma_port ;
rtd - > dma_data . name = dma_params - > name ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
rtd - > dma_data . direction = DMA_TO_DEVICE ;
else
rtd - > dma_data . direction = DMA_FROM_DEVICE ;
rtd - > dma_chan = dma_request_channel ( mask , ep93xx_pcm_dma_filter ,
& rtd - > dma_data ) ;
if ( ! rtd - > dma_chan ) {
kfree ( rtd ) ;
return ret ;
return - EINVAL ;
}
substream - > runtime - > private_data = rtd ;
@ -132,31 +132,52 @@ static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
{
struct ep93xx_runtime_data * rtd = substream - > runtime - > private_data ;
ep93xx_dma_m2p_client_unregister ( & rtd - > cl ) ;
dma_release_channel ( rtd - > dma_chan ) ;
kfree ( rtd ) ;
return 0 ;
}
static int ep93xx_pcm_dma_submit ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct ep93xx_runtime_data * rtd = runtime - > private_data ;
struct dma_chan * chan = rtd - > dma_chan ;
struct dma_device * dma_dev = chan - > device ;
struct dma_async_tx_descriptor * desc ;
rtd - > pointer_bytes = 0 ;
desc = dma_dev - > device_prep_dma_cyclic ( chan , runtime - > dma_addr ,
rtd - > period_bytes * rtd - > periods ,
rtd - > period_bytes ,
rtd - > dma_data . direction ) ;
if ( ! desc )
return - EINVAL ;
desc - > callback = ep93xx_pcm_dma_callback ;
desc - > callback_param = substream ;
dmaengine_submit ( desc ) ;
return 0 ;
}
static void ep93xx_pcm_dma_flush ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct ep93xx_runtime_data * rtd = runtime - > private_data ;
dmaengine_terminate_all ( rtd - > dma_chan ) ;
}
static int ep93xx_pcm_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct ep93xx_runtime_data * rtd = runtime - > private_data ;
size_t totsize = params_buffer_bytes ( params ) ;
size_t period = params_period_bytes ( params ) ;
int i ;
snd_pcm_set_runtime_buffer ( substream , & substream - > dma_buffer ) ;
runtime - > dma_bytes = totsize ;
rtd - > periods = ( totsize + period - 1 ) / period ;
for ( i = 0 ; i < rtd - > periods ; i + + ) {
rtd - > buf [ i ] . bus_addr = runtime - > dma_addr + ( i * period ) ;
rtd - > buf [ i ] . size = period ;
if ( ( i + 1 ) * period > totsize )
rtd - > buf [ i ] . size = totsize - ( i * period ) ;
}
rtd - > periods = params_periods ( params ) ;
rtd - > period_bytes = params_period_bytes ( params ) ;
return 0 ;
}
@ -168,24 +189,20 @@ static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
static int ep93xx_pcm_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct ep93xx_runtime_data * rtd = substream - > runtime - > private_data ;
int ret ;
int i ;
ret = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
rtd - > pointer_bytes = 0 ;
for ( i = 0 ; i < rtd - > periods ; i + + )
ep93xx_dma_m2p_submit ( & rtd - > cl , rtd - > buf + i ) ;
ret = ep93xx_pcm_dma_submit ( substream ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
ep93xx_dma_m2p_flush ( & rtd - > cl ) ;
ep93xx_pcm_dma_flush ( substream ) ;
break ;
default :