2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-03-11 18:32:42 +03:00
/*
* ALSA SoC Voice Codec Interface for TI DAVINCI processor
*
* Copyright ( C ) 2010 Texas Instruments .
*
* Author : Miguel Aguilar < miguel . aguilar @ ridgerun . com >
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/delay.h>
2010-03-29 21:52:29 +04:00
# include <linux/slab.h>
2010-03-11 18:32:42 +03:00
# include <linux/io.h>
# include <linux/mfd/davinci_voicecodec.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/initval.h>
# include <sound/soc.h>
2015-03-03 17:45:19 +03:00
# include <sound/dmaengine_pcm.h>
2010-03-11 18:32:42 +03:00
2015-03-03 17:45:19 +03:00
# include "edma-pcm.h"
2010-03-11 18:32:42 +03:00
# include "davinci-i2s.h"
# define MOD_REG_BIT(val, mask, set) do { \
if ( set ) { \
val | = mask ; \
} else { \
val & = ~ mask ; \
} \
} while ( 0 )
struct davinci_vcif_dev {
struct davinci_vc * davinci_vc ;
2015-03-03 17:45:19 +03:00
struct snd_dmaengine_dai_dma_data dma_data [ 2 ] ;
int dma_request [ 2 ] ;
2010-03-11 18:32:42 +03:00
} ;
static void davinci_vcif_start ( struct snd_pcm_substream * substream )
{
2020-07-20 04:17:48 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2010-03-11 18:32:42 +03:00
struct davinci_vcif_dev * davinci_vcif_dev =
2020-03-23 08:21:14 +03:00
snd_soc_dai_get_drvdata ( asoc_rtd_to_cpu ( rtd , 0 ) ) ;
2010-03-11 18:32:42 +03:00
struct davinci_vc * davinci_vc = davinci_vcif_dev - > davinci_vc ;
u32 w ;
/* Start the sample generator and enable transmitter/receiver */
w = readl ( davinci_vc - > base + DAVINCI_VC_CTRL ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2011-07-20 16:06:04 +04:00
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RSTDAC , 0 ) ;
2010-03-11 18:32:42 +03:00
else
2011-07-20 16:06:04 +04:00
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RSTADC , 0 ) ;
2010-03-11 18:32:42 +03:00
writel ( w , davinci_vc - > base + DAVINCI_VC_CTRL ) ;
}
static void davinci_vcif_stop ( struct snd_pcm_substream * substream )
{
2020-07-20 04:17:48 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2010-03-11 18:32:42 +03:00
struct davinci_vcif_dev * davinci_vcif_dev =
2020-03-23 08:21:14 +03:00
snd_soc_dai_get_drvdata ( asoc_rtd_to_cpu ( rtd , 0 ) ) ;
2010-03-11 18:32:42 +03:00
struct davinci_vc * davinci_vc = davinci_vcif_dev - > davinci_vc ;
u32 w ;
/* Reset transmitter/receiver and sample rate/frame sync generators */
w = readl ( davinci_vc - > base + DAVINCI_VC_CTRL ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2011-07-20 16:06:04 +04:00
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RSTDAC , 1 ) ;
2010-03-11 18:32:42 +03:00
else
2011-07-20 16:06:04 +04:00
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RSTADC , 1 ) ;
2010-03-11 18:32:42 +03:00
writel ( w , davinci_vc - > base + DAVINCI_VC_CTRL ) ;
}
static int davinci_vcif_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2010-03-17 23:15:21 +03:00
struct davinci_vcif_dev * davinci_vcif_dev = snd_soc_dai_get_drvdata ( dai ) ;
2010-03-11 18:32:42 +03:00
struct davinci_vc * davinci_vc = davinci_vcif_dev - > davinci_vc ;
u32 w ;
/* Restart the codec before setup */
davinci_vcif_stop ( substream ) ;
davinci_vcif_start ( substream ) ;
/* General line settings */
writel ( DAVINCI_VC_CTRL_MASK , davinci_vc - > base + DAVINCI_VC_CTRL ) ;
writel ( DAVINCI_VC_INT_MASK , davinci_vc - > base + DAVINCI_VC_INTCLR ) ;
writel ( DAVINCI_VC_INT_MASK , davinci_vc - > base + DAVINCI_VC_INTEN ) ;
w = readl ( davinci_vc - > base + DAVINCI_VC_CTRL ) ;
/* Determine xfer data type */
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_U8 :
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RD_BITS_8 |
DAVINCI_VC_CTRL_RD_UNSIGNED |
DAVINCI_VC_CTRL_WD_BITS_8 |
DAVINCI_VC_CTRL_WD_UNSIGNED , 1 ) ;
break ;
case SNDRV_PCM_FORMAT_S8 :
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RD_BITS_8 |
DAVINCI_VC_CTRL_WD_BITS_8 , 1 ) ;
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RD_UNSIGNED |
DAVINCI_VC_CTRL_WD_UNSIGNED , 0 ) ;
break ;
case SNDRV_PCM_FORMAT_S16_LE :
MOD_REG_BIT ( w , DAVINCI_VC_CTRL_RD_BITS_8 |
DAVINCI_VC_CTRL_RD_UNSIGNED |
DAVINCI_VC_CTRL_WD_BITS_8 |
DAVINCI_VC_CTRL_WD_UNSIGNED , 0 ) ;
break ;
default :
printk ( KERN_WARNING " davinci-vcif: unsupported PCM format " ) ;
return - EINVAL ;
}
writel ( w , davinci_vc - > base + DAVINCI_VC_CTRL ) ;
return 0 ;
}
static int davinci_vcif_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
int ret = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
davinci_vcif_start ( substream ) ;
2011-07-20 16:07:18 +04:00
break ;
2010-03-11 18:32:42 +03:00
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
davinci_vcif_stop ( substream ) ;
break ;
default :
ret = - EINVAL ;
}
return ret ;
}
# define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
2010-03-11 18:32:42 +03:00
. trigger = davinci_vcif_trigger ,
. hw_params = davinci_vcif_hw_params ,
} ;
2015-03-03 17:45:19 +03:00
static int davinci_vcif_dai_probe ( struct snd_soc_dai * dai )
{
struct davinci_vcif_dev * dev = snd_soc_dai_get_drvdata ( dai ) ;
dai - > playback_dma_data = & dev - > dma_data [ SNDRV_PCM_STREAM_PLAYBACK ] ;
dai - > capture_dma_data = & dev - > dma_data [ SNDRV_PCM_STREAM_CAPTURE ] ;
return 0 ;
}
2010-03-17 23:15:21 +03:00
static struct snd_soc_dai_driver davinci_vcif_dai = {
2015-03-03 17:45:19 +03:00
. probe = davinci_vcif_dai_probe ,
2010-03-11 18:32:42 +03:00
. playback = {
. channels_min = 1 ,
. channels_max = 2 ,
. rates = DAVINCI_VCIF_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
. capture = {
. channels_min = 1 ,
. channels_max = 2 ,
. rates = DAVINCI_VCIF_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
. ops = & davinci_vcif_dai_ops ,
} ;
2013-03-21 14:31:07 +04:00
static const struct snd_soc_component_driver davinci_vcif_component = {
. name = " davinci-vcif " ,
} ;
2010-03-11 18:32:42 +03:00
static int davinci_vcif_probe ( struct platform_device * pdev )
{
2011-04-06 18:39:45 +04:00
struct davinci_vc * davinci_vc = pdev - > dev . platform_data ;
2010-03-11 18:32:42 +03:00
struct davinci_vcif_dev * davinci_vcif_dev ;
int ret ;
2011-12-29 20:51:20 +04:00
davinci_vcif_dev = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct davinci_vcif_dev ) ,
GFP_KERNEL ) ;
2017-08-11 17:51:46 +03:00
if ( ! davinci_vcif_dev )
2010-03-11 18:32:42 +03:00
return - ENOMEM ;
/* DMA tx params */
davinci_vcif_dev - > davinci_vc = davinci_vc ;
2015-03-03 17:45:19 +03:00
davinci_vcif_dev - > dma_data [ SNDRV_PCM_STREAM_PLAYBACK ] . filter_data =
& davinci_vc - > davinci_vcif . dma_tx_channel ;
davinci_vcif_dev - > dma_data [ SNDRV_PCM_STREAM_PLAYBACK ] . addr =
davinci_vc - > davinci_vcif . dma_tx_addr ;
2010-03-11 18:32:42 +03:00
/* DMA rx params */
2015-03-03 17:45:19 +03:00
davinci_vcif_dev - > dma_data [ SNDRV_PCM_STREAM_CAPTURE ] . filter_data =
& davinci_vc - > davinci_vcif . dma_rx_channel ;
davinci_vcif_dev - > dma_data [ SNDRV_PCM_STREAM_CAPTURE ] . addr =
davinci_vc - > davinci_vcif . dma_rx_addr ;
2010-03-11 18:32:42 +03:00
2010-03-17 23:15:21 +03:00
dev_set_drvdata ( & pdev - > dev , davinci_vcif_dev ) ;
2010-03-11 18:32:42 +03:00
2015-08-15 05:31:55 +03:00
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& davinci_vcif_component ,
& davinci_vcif_dai , 1 ) ;
2010-03-11 18:32:42 +03:00
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " could not register dai \n " ) ;
2011-12-29 20:51:20 +04:00
return ret ;
2010-03-11 18:32:42 +03:00
}
2015-03-03 17:45:19 +03:00
ret = edma_pcm_platform_register ( & pdev - > dev ) ;
2012-08-27 17:26:39 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " register PCM failed: %d \n " , ret ) ;
return ret ;
}
2010-03-11 18:32:42 +03:00
return 0 ;
}
static struct platform_driver davinci_vcif_driver = {
. probe = davinci_vcif_probe ,
. driver = {
2010-03-17 23:15:21 +03:00
. name = " davinci-vcif " ,
2010-03-11 18:32:42 +03:00
} ,
} ;
2011-11-25 06:09:27 +04:00
module_platform_driver ( davinci_vcif_driver ) ;
2010-03-11 18:32:42 +03:00
MODULE_AUTHOR ( " Miguel Aguilar " ) ;
MODULE_DESCRIPTION ( " Texas Instruments DaVinci ASoC Voice Codec Interface " ) ;
MODULE_LICENSE ( " GPL " ) ;