2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-05-31 15:49:14 +04:00
/*
* kirkwood - dma . c
*
* ( c ) 2010 Arnaud Patard < apatard @ mandriva . com >
2010-09-09 16:10:33 +04:00
* ( c ) 2010 Arnaud Patard < arnaud . patard @ rtp - net . org >
2010-05-31 15:49:14 +04:00
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/dma-mapping.h>
# include <linux/mbus.h>
# include <sound/soc.h>
# include "kirkwood.h"
2013-08-04 23:22:03 +04:00
static struct kirkwood_dma_data * kirkwood_priv ( struct snd_pcm_substream * subs )
{
struct snd_soc_pcm_runtime * soc_runtime = subs - > private_data ;
return snd_soc_dai_get_drvdata ( soc_runtime - > cpu_dai ) ;
}
2010-05-31 15:49:14 +04:00
2017-08-17 13:16:10 +03:00
static const struct snd_pcm_hardware kirkwood_dma_snd_hw = {
2014-06-26 18:23:20 +04:00
. info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP ,
2013-08-04 23:28:04 +04:00
. buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES ,
2010-05-31 15:49:14 +04:00
. period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES ,
. period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES ,
. periods_min = KIRKWOOD_SND_MIN_PERIODS ,
. periods_max = KIRKWOOD_SND_MAX_PERIODS ,
. fifo_size = 0 ,
} ;
static irqreturn_t kirkwood_dma_irq ( int irq , void * dev_id )
{
2013-08-04 23:22:03 +04:00
struct kirkwood_dma_data * priv = dev_id ;
2010-05-31 15:49:14 +04:00
unsigned long mask , status , cause ;
mask = readl ( priv - > io + KIRKWOOD_INT_MASK ) ;
status = readl ( priv - > io + KIRKWOOD_INT_CAUSE ) & mask ;
cause = readl ( priv - > io + KIRKWOOD_ERR_CAUSE ) ;
if ( unlikely ( cause ) ) {
printk ( KERN_WARNING " %s: got err interrupt 0x%lx \n " ,
__func__ , cause ) ;
writel ( cause , priv - > io + KIRKWOOD_ERR_CAUSE ) ;
}
/* we've enabled only bytes interrupts ... */
if ( status & ~ ( KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
KIRKWOOD_INT_CAUSE_REC_BYTES ) ) {
printk ( KERN_WARNING " %s: unexpected interrupt %lx \n " ,
__func__ , status ) ;
return IRQ_NONE ;
}
/* ack int */
writel ( status , priv - > io + KIRKWOOD_INT_CAUSE ) ;
if ( status & KIRKWOOD_INT_CAUSE_PLAY_BYTES )
2013-08-04 23:22:03 +04:00
snd_pcm_period_elapsed ( priv - > substream_play ) ;
2010-05-31 15:49:14 +04:00
if ( status & KIRKWOOD_INT_CAUSE_REC_BYTES )
2013-08-04 23:22:03 +04:00
snd_pcm_period_elapsed ( priv - > substream_rec ) ;
2010-05-31 15:49:14 +04:00
return IRQ_HANDLED ;
}
2011-12-08 00:48:07 +04:00
static void
kirkwood_dma_conf_mbus_windows ( void __iomem * base , int win ,
unsigned long dma ,
const struct mbus_dram_target_info * dram )
2010-05-31 15:49:14 +04:00
{
int i ;
/* First disable and clear windows */
writel ( 0 , base + KIRKWOOD_AUDIO_WIN_CTRL_REG ( win ) ) ;
writel ( 0 , base + KIRKWOOD_AUDIO_WIN_BASE_REG ( win ) ) ;
/* try to find matching cs for current dma address */
for ( i = 0 ; i < dram - > num_cs ; i + + ) {
2011-12-08 00:48:07 +04:00
const struct mbus_dram_window * cs = dram - > cs + i ;
2010-05-31 15:49:14 +04:00
if ( ( cs - > base & 0xffff0000 ) < ( dma & 0xffff0000 ) ) {
writel ( cs - > base & 0xffff0000 ,
base + KIRKWOOD_AUDIO_WIN_BASE_REG ( win ) ) ;
writel ( ( ( cs - > size - 1 ) & 0xffff0000 ) |
( cs - > mbus_attr < < 8 ) |
( dram - > mbus_dram_target_id < < 4 ) | 1 ,
base + KIRKWOOD_AUDIO_WIN_CTRL_REG ( win ) ) ;
}
}
}
static int kirkwood_dma_open ( struct snd_pcm_substream * substream )
{
int err ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2013-08-04 23:22:03 +04:00
struct kirkwood_dma_data * priv = kirkwood_priv ( substream ) ;
2011-12-08 00:48:07 +04:00
const struct mbus_dram_target_info * dram ;
2010-05-31 15:49:14 +04:00
unsigned long addr ;
snd_soc_set_runtime_hwparams ( substream , & kirkwood_dma_snd_hw ) ;
2011-03-31 05:57:33 +04:00
/* Ensure that all constraints linked to dma burst are fulfilled */
2010-05-31 15:49:14 +04:00
err = snd_pcm_hw_constraint_minmax ( runtime ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES ,
priv - > burst * 2 ,
KIRKWOOD_AUDIO_BUF_MAX - 1 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES ,
priv - > burst ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_constraint_step ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES ,
priv - > burst ) ;
if ( err < 0 )
return err ;
2013-08-04 23:22:03 +04:00
if ( ! priv - > substream_play & & ! priv - > substream_rec ) {
2010-05-31 15:49:14 +04:00
err = request_irq ( priv - > irq , kirkwood_dma_irq , IRQF_SHARED ,
2013-08-04 23:22:03 +04:00
" kirkwood-i2s " , priv ) ;
if ( err )
2010-05-31 15:49:14 +04:00
return - EBUSY ;
/*
* Enable Error interrupts . We ' re only ack ' ing them but
2011-03-31 05:57:33 +04:00
* it ' s useful for diagnostics
2010-05-31 15:49:14 +04:00
*/
2013-09-29 18:00:13 +04:00
writel ( ( unsigned int ) - 1 , priv - > io + KIRKWOOD_ERR_MASK ) ;
2010-05-31 15:49:14 +04:00
}
2011-12-08 00:48:07 +04:00
dram = mv_mbus_dram_info ( ) ;
2012-11-20 16:17:51 +04:00
addr = substream - > dma_buffer . addr ;
2010-05-31 15:49:14 +04:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
2015-07-21 19:32:58 +03:00
if ( priv - > substream_play )
return - EBUSY ;
2013-08-04 23:22:03 +04:00
priv - > substream_play = substream ;
2010-05-31 15:49:14 +04:00
kirkwood_dma_conf_mbus_windows ( priv - > io ,
2011-12-08 00:48:07 +04:00
KIRKWOOD_PLAYBACK_WIN , addr , dram ) ;
2010-05-31 15:49:14 +04:00
} else {
2015-07-21 19:32:58 +03:00
if ( priv - > substream_rec )
return - EBUSY ;
2013-08-04 23:22:03 +04:00
priv - > substream_rec = substream ;
2010-05-31 15:49:14 +04:00
kirkwood_dma_conf_mbus_windows ( priv - > io ,
2011-12-08 00:48:07 +04:00
KIRKWOOD_RECORD_WIN , addr , dram ) ;
2010-05-31 15:49:14 +04:00
}
return 0 ;
}
static int kirkwood_dma_close ( struct snd_pcm_substream * substream )
{
2013-08-04 23:22:03 +04:00
struct kirkwood_dma_data * priv = kirkwood_priv ( substream ) ;
2010-05-31 15:49:14 +04:00
2013-08-04 23:22:03 +04:00
if ( ! priv )
2010-05-31 15:49:14 +04:00
return 0 ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2013-08-04 23:22:03 +04:00
priv - > substream_play = NULL ;
2010-05-31 15:49:14 +04:00
else
2013-08-04 23:22:03 +04:00
priv - > substream_rec = NULL ;
2010-05-31 15:49:14 +04:00
2013-08-04 23:22:03 +04:00
if ( ! priv - > substream_play & & ! priv - > substream_rec ) {
2010-05-31 15:49:14 +04:00
writel ( 0 , priv - > io + KIRKWOOD_ERR_MASK ) ;
2013-08-04 23:22:03 +04:00
free_irq ( priv - > irq , priv ) ;
2010-05-31 15:49:14 +04:00
}
return 0 ;
}
static int kirkwood_dma_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
snd_pcm_set_runtime_buffer ( substream , & substream - > dma_buffer ) ;
runtime - > dma_bytes = params_buffer_bytes ( params ) ;
return 0 ;
}
static int kirkwood_dma_hw_free ( struct snd_pcm_substream * substream )
{
snd_pcm_set_runtime_buffer ( substream , NULL ) ;
return 0 ;
}
static int kirkwood_dma_prepare ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2013-08-04 23:22:03 +04:00
struct kirkwood_dma_data * priv = kirkwood_priv ( substream ) ;
2010-05-31 15:49:14 +04:00
unsigned long size , count ;
/* compute buffer size in term of "words" as requested in specs */
size = frames_to_bytes ( runtime , runtime - > buffer_size ) ;
size = ( size > > 2 ) - 1 ;
count = snd_pcm_lib_period_bytes ( substream ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
writel ( count , priv - > io + KIRKWOOD_PLAY_BYTE_INT_COUNT ) ;
writel ( runtime - > dma_addr , priv - > io + KIRKWOOD_PLAY_BUF_ADDR ) ;
writel ( size , priv - > io + KIRKWOOD_PLAY_BUF_SIZE ) ;
} else {
writel ( count , priv - > io + KIRKWOOD_REC_BYTE_INT_COUNT ) ;
writel ( runtime - > dma_addr , priv - > io + KIRKWOOD_REC_BUF_ADDR ) ;
writel ( size , priv - > io + KIRKWOOD_REC_BUF_SIZE ) ;
}
return 0 ;
}
static snd_pcm_uframes_t kirkwood_dma_pointer ( struct snd_pcm_substream
* substream )
{
2013-08-04 23:22:03 +04:00
struct kirkwood_dma_data * priv = kirkwood_priv ( substream ) ;
2010-05-31 15:49:14 +04:00
snd_pcm_uframes_t count ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
count = bytes_to_frames ( substream - > runtime ,
readl ( priv - > io + KIRKWOOD_PLAY_BYTE_COUNT ) ) ;
else
count = bytes_to_frames ( substream - > runtime ,
readl ( priv - > io + KIRKWOOD_REC_BYTE_COUNT ) ) ;
return count ;
}
2016-09-08 03:35:23 +03:00
static const struct snd_pcm_ops kirkwood_dma_ops = {
2010-05-31 15:49:14 +04:00
. open = kirkwood_dma_open ,
. close = kirkwood_dma_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = kirkwood_dma_hw_params ,
. hw_free = kirkwood_dma_hw_free ,
. prepare = kirkwood_dma_prepare ,
. pointer = kirkwood_dma_pointer ,
} ;
static int kirkwood_dma_preallocate_dma_buffer ( struct snd_pcm * pcm ,
int stream )
{
struct snd_pcm_substream * substream = pcm - > streams [ stream ] . substream ;
struct snd_dma_buffer * buf = & substream - > dma_buffer ;
size_t size = kirkwood_dma_snd_hw . buffer_bytes_max ;
buf - > dev . type = SNDRV_DMA_TYPE_DEV ;
buf - > dev . dev = pcm - > card - > dev ;
buf - > area = dma_alloc_coherent ( pcm - > card - > dev , size ,
& buf - > addr , GFP_KERNEL ) ;
if ( ! buf - > area )
return - ENOMEM ;
buf - > bytes = size ;
buf - > private_data = NULL ;
return 0 ;
}
2011-06-07 19:08:33 +04:00
static int kirkwood_dma_new ( struct snd_soc_pcm_runtime * rtd )
2010-05-31 15:49:14 +04:00
{
2011-06-07 19:08:33 +04:00
struct snd_card * card = rtd - > card - > snd_card ;
struct snd_pcm * pcm = rtd - > pcm ;
2010-05-31 15:49:14 +04:00
int ret ;
2013-06-27 15:53:37 +04:00
ret = dma_coerce_mask_and_coherent ( card - > dev , DMA_BIT_MASK ( 32 ) ) ;
if ( ret )
return ret ;
2010-05-31 15:49:14 +04:00
2012-01-01 04:58:44 +04:00
if ( pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream ) {
2010-05-31 15:49:14 +04:00
ret = kirkwood_dma_preallocate_dma_buffer ( pcm ,
SNDRV_PCM_STREAM_PLAYBACK ) ;
if ( ret )
return ret ;
}
2012-01-01 04:58:44 +04:00
if ( pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream ) {
2010-05-31 15:49:14 +04:00
ret = kirkwood_dma_preallocate_dma_buffer ( pcm ,
SNDRV_PCM_STREAM_CAPTURE ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static void kirkwood_dma_free_dma_buffers ( struct snd_pcm * pcm )
{
struct snd_pcm_substream * substream ;
struct snd_dma_buffer * buf ;
int stream ;
for ( stream = 0 ; stream < 2 ; stream + + ) {
substream = pcm - > streams [ stream ] . substream ;
if ( ! substream )
continue ;
buf = & substream - > dma_buffer ;
if ( ! buf - > area )
continue ;
dma_free_coherent ( pcm - > card - > dev , buf - > bytes ,
buf - > area , buf - > addr ) ;
buf - > area = NULL ;
}
}
2018-01-29 05:48:03 +03:00
const struct snd_soc_component_driver kirkwood_soc_component = {
. name = DRV_NAME ,
2010-03-17 23:15:21 +03:00
. ops = & kirkwood_dma_ops ,
2010-05-31 15:49:14 +04:00
. pcm_new = kirkwood_dma_new ,
. pcm_free = kirkwood_dma_free_dma_buffers ,
} ;