@ -19,6 +19,7 @@
* SSICR
*/
# define FORCE (1 << 31) /* Fixed */
# define DMEN (1 << 28) /* DMA Enable */
# define UIEN (1 << 27) /* Underflow Interrupt Enable */
# define OIEN (1 << 26) /* Overflow Interrupt Enable */
# define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
@ -51,6 +52,11 @@
# define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
# define DIRQ (1 << 24) /* Data Interrupt Status Flag */
/*
* SSIWSR
*/
# define CONT (1 << 8) /* WS Continue Function */
struct rsnd_ssi {
struct clk * clk ;
struct rsnd_ssi_platform_info * info ; /* rcar_snd.h */
@ -63,6 +69,7 @@ struct rsnd_ssi {
u32 cr_clk ;
u32 cr_etc ;
int err ;
int dma_offset ;
unsigned int usrcnt ;
unsigned int rate ;
} ;
@ -83,7 +90,10 @@ struct rsnd_ssiu {
# define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr)
# define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
# define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0)
# define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
# define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
# define rsnd_ssi_dma_available(ssi) \
rsnd_dma_available ( rsnd_mod_to_dma ( & ( ssi ) - > mod ) )
# define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
# define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
# define rsnd_ssi_mode_flags(p) ((p)->info->flags)
@ -477,6 +487,79 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
. stop = rsnd_ssi_pio_stop ,
} ;
static int rsnd_ssi_dma_inquiry ( struct rsnd_dma * dma , dma_addr_t * buf , int * len )
{
struct rsnd_ssi * ssi = rsnd_dma_to_ssi ( dma ) ;
struct rsnd_dai_stream * io = ssi - > io ;
struct snd_pcm_runtime * runtime = rsnd_io_to_runtime ( io ) ;
* len = io - > byte_per_period ;
* buf = runtime - > dma_addr +
rsnd_dai_pointer_offset ( io , ssi - > dma_offset + * len ) ;
ssi - > dma_offset = * len ; /* it cares A/B plane */
return 0 ;
}
static int rsnd_ssi_dma_complete ( struct rsnd_dma * dma )
{
struct rsnd_ssi * ssi = rsnd_dma_to_ssi ( dma ) ;
struct rsnd_dai_stream * io = ssi - > io ;
u32 status = rsnd_mod_read ( & ssi - > mod , SSISR ) ;
rsnd_ssi_record_error ( ssi , status ) ;
rsnd_dai_pointer_update ( ssi - > io , io - > byte_per_period ) ;
return 0 ;
}
static int rsnd_ssi_dma_start ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai ,
struct rsnd_dai_stream * io )
{
struct rsnd_ssi * ssi = rsnd_mod_to_ssi ( mod ) ;
struct rsnd_dma * dma = rsnd_mod_to_dma ( & ssi - > mod ) ;
/* enable DMA transfer */
ssi - > cr_etc = DMEN ;
ssi - > dma_offset = 0 ;
rsnd_dma_start ( dma ) ;
rsnd_ssi_hw_start ( ssi , ssi - > rdai , io ) ;
/* enable WS continue */
if ( rsnd_rdai_is_clk_master ( rdai ) )
rsnd_mod_write ( & ssi - > mod , SSIWSR , CONT ) ;
return 0 ;
}
static int rsnd_ssi_dma_stop ( struct rsnd_mod * mod ,
struct rsnd_dai * rdai ,
struct rsnd_dai_stream * io )
{
struct rsnd_ssi * ssi = rsnd_mod_to_ssi ( mod ) ;
struct rsnd_dma * dma = rsnd_mod_to_dma ( & ssi - > mod ) ;
ssi - > cr_etc = 0 ;
rsnd_ssi_hw_stop ( ssi , rdai ) ;
rsnd_dma_stop ( dma ) ;
return 0 ;
}
static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
. name = " ssi (dma) " ,
. init = rsnd_ssi_init ,
. quit = rsnd_ssi_quit ,
. start = rsnd_ssi_dma_start ,
. stop = rsnd_ssi_dma_stop ,
} ;
/*
* Non SSI
*/
@ -573,10 +656,27 @@ int rsnd_ssi_probe(struct platform_device *pdev,
ops = & rsnd_ssi_non_ops ;
/*
* SSI DMA case
*/
if ( pinfo - > dma_id > 0 ) {
ret = rsnd_dma_init (
priv , rsnd_mod_to_dma ( & ssi - > mod ) ,
( rsnd_ssi_mode_flags ( ssi ) & RSND_SSI_PLAY ) ,
pinfo - > dma_id ,
rsnd_ssi_dma_inquiry ,
rsnd_ssi_dma_complete ) ;
if ( ret < 0 )
dev_info ( dev , " SSI DMA failed. try PIO transter \n " ) ;
else
ops = & rsnd_ssi_dma_ops ;
}
/*
* SSI PIO case
*/
if ( rsnd_ssi_is_pio ( ssi ) ) {
if ( ! rsnd_ssi_dma_available ( ssi ) & &
rsnd_ssi_pio_available ( ssi ) ) {
ret = devm_request_irq ( dev , pinfo - > pio_irq ,
& rsnd_ssi_pio_interrupt ,
IRQF_SHARED ,
@ -605,6 +705,10 @@ void rsnd_ssi_remove(struct platform_device *pdev,
struct rsnd_ssi * ssi ;
int i ;
for_each_rsnd_ssi ( ssi , priv , i )
for_each_rsnd_ssi ( ssi , priv , i ) {
clk_put ( ssi - > clk ) ;
if ( rsnd_ssi_dma_available ( ssi ) )
rsnd_dma_quit ( priv , rsnd_mod_to_dma ( & ssi - > mod ) ) ;
}
}