2011-09-02 09:44:35 +09:00
/* linux/arch/arm/plat-samsung/dma-ops.c
*
* Copyright ( c ) 2011 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Samsung DMA Operations
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/amba/pl330.h>
# include <linux/scatterlist.h>
2011-10-08 23:24:48 -04:00
# include <linux/export.h>
2011-09-02 09:44:35 +09:00
# include <mach/dma.h>
static unsigned samsung_dmadev_request ( enum dma_ch dma_ch ,
2013-01-18 17:17:02 +05:30
struct samsung_dma_req * param ,
struct device * dev , char * ch_name )
2011-09-02 09:44:35 +09:00
{
dma_cap_mask_t mask ;
2011-10-24 11:43:38 +02:00
void * filter_param ;
2011-09-02 09:44:35 +09:00
dma_cap_zero ( mask ) ;
2012-06-19 13:26:53 +09:00
dma_cap_set ( param - > cap , mask ) ;
2011-09-02 09:44:35 +09:00
2011-10-24 11:43:38 +02:00
/*
* If a dma channel property of a device node from device tree is
* specified , use that as the fliter parameter .
*/
2012-06-19 13:26:53 +09:00
filter_param = ( dma_ch = = DMACH_DT_PROP ) ?
( void * ) param - > dt_dmach_prop : ( void * ) dma_ch ;
2013-01-18 17:17:02 +05:30
if ( dev - > of_node )
return ( unsigned ) dma_request_slave_channel ( dev , ch_name ) ;
else
return ( unsigned ) dma_request_channel ( mask , pl330_filter ,
filter_param ) ;
2012-06-19 13:26:53 +09:00
}
2011-09-02 09:44:35 +09:00
2012-06-27 11:29:54 +09:00
static int samsung_dmadev_release ( unsigned ch , void * param )
2012-06-19 13:26:53 +09:00
{
dma_release_channel ( ( struct dma_chan * ) ch ) ;
return 0 ;
}
static int samsung_dmadev_config ( unsigned ch ,
struct samsung_dma_config * param )
{
struct dma_chan * chan = ( struct dma_chan * ) ch ;
struct dma_slave_config slave_config ;
if ( param - > direction = = DMA_DEV_TO_MEM ) {
2011-09-02 09:44:35 +09:00
memset ( & slave_config , 0 , sizeof ( struct dma_slave_config ) ) ;
2012-06-19 13:26:53 +09:00
slave_config . direction = param - > direction ;
slave_config . src_addr = param - > fifo ;
slave_config . src_addr_width = param - > width ;
2011-09-02 09:44:35 +09:00
slave_config . src_maxburst = 1 ;
dmaengine_slave_config ( chan , & slave_config ) ;
2012-06-19 13:26:53 +09:00
} else if ( param - > direction = = DMA_MEM_TO_DEV ) {
2011-09-02 09:44:35 +09:00
memset ( & slave_config , 0 , sizeof ( struct dma_slave_config ) ) ;
2012-06-19 13:26:53 +09:00
slave_config . direction = param - > direction ;
slave_config . dst_addr = param - > fifo ;
slave_config . dst_addr_width = param - > width ;
2011-09-02 09:44:35 +09:00
slave_config . dst_maxburst = 1 ;
dmaengine_slave_config ( chan , & slave_config ) ;
2012-06-19 13:26:53 +09:00
} else {
pr_warn ( " unsupported direction \n " ) ;
return - EINVAL ;
2011-09-02 09:44:35 +09:00
}
return 0 ;
}
static int samsung_dmadev_prepare ( unsigned ch ,
2012-06-19 13:26:53 +09:00
struct samsung_dma_prep * param )
2011-09-02 09:44:35 +09:00
{
struct scatterlist sg ;
struct dma_chan * chan = ( struct dma_chan * ) ch ;
struct dma_async_tx_descriptor * desc ;
2012-06-19 13:26:53 +09:00
switch ( param - > cap ) {
2011-09-02 09:44:35 +09:00
case DMA_SLAVE :
sg_init_table ( & sg , 1 ) ;
2012-06-19 13:26:53 +09:00
sg_dma_len ( & sg ) = param - > len ;
sg_set_page ( & sg , pfn_to_page ( PFN_DOWN ( param - > buf ) ) ,
param - > len , offset_in_page ( param - > buf ) ) ;
sg_dma_address ( & sg ) = param - > buf ;
2011-09-02 09:44:35 +09:00
2012-03-08 16:11:18 -05:00
desc = dmaengine_prep_slave_sg ( chan ,
2012-06-19 13:26:53 +09:00
& sg , 1 , param - > direction , DMA_PREP_INTERRUPT ) ;
2011-09-02 09:44:35 +09:00
break ;
case DMA_CYCLIC :
2012-06-19 13:26:53 +09:00
desc = dmaengine_prep_dma_cyclic ( chan , param - > buf ,
2012-09-24 10:58:05 +03:00
param - > len , param - > period , param - > direction ,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK ) ;
2011-09-02 09:44:35 +09:00
break ;
default :
dev_err ( & chan - > dev - > device , " unsupported format \n " ) ;
return - EFAULT ;
}
if ( ! desc ) {
dev_err ( & chan - > dev - > device , " cannot prepare cyclic dma \n " ) ;
return - EFAULT ;
}
2012-06-19 13:26:53 +09:00
desc - > callback = param - > fp ;
desc - > callback_param = param - > fp_param ;
2011-09-02 09:44:35 +09:00
dmaengine_submit ( ( struct dma_async_tx_descriptor * ) desc ) ;
return 0 ;
}
static inline int samsung_dmadev_trigger ( unsigned ch )
{
dma_async_issue_pending ( ( struct dma_chan * ) ch ) ;
return 0 ;
}
static inline int samsung_dmadev_flush ( unsigned ch )
{
return dmaengine_terminate_all ( ( struct dma_chan * ) ch ) ;
}
2012-01-21 12:00:13 +09:00
static struct samsung_dma_ops dmadev_ops = {
2011-09-02 09:44:35 +09:00
. request = samsung_dmadev_request ,
. release = samsung_dmadev_release ,
2012-06-19 13:26:53 +09:00
. config = samsung_dmadev_config ,
2011-09-02 09:44:35 +09:00
. prepare = samsung_dmadev_prepare ,
. trigger = samsung_dmadev_trigger ,
. started = NULL ,
. flush = samsung_dmadev_flush ,
. stop = samsung_dmadev_flush ,
} ;
void * samsung_dmadev_get_ops ( void )
{
return & dmadev_ops ;
}
EXPORT_SYMBOL ( samsung_dmadev_get_ops ) ;