2019-05-29 07:17:58 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-03-03 16:21:55 -08:00
/*
* Copyright ( c ) 2010 - 2011 , 2013 - 2015 The Linux Foundation . All rights reserved .
*
* lpass - platform . c - - ALSA SoC platform driver for QTi LPASS
*/
# include <linux/dma-mapping.h>
# include <linux/export.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <sound/pcm_params.h>
# include <linux/regmap.h>
# include <sound/soc.h>
2015-05-16 13:32:17 +01:00
# include "lpass-lpaif-reg.h"
2015-03-03 16:21:55 -08:00
# include "lpass.h"
2018-01-29 02:48:52 +00:00
# define DRV_NAME "lpass-platform"
2015-05-16 13:32:34 +01:00
struct lpass_pcm_data {
2016-10-31 11:25:44 +00:00
int dma_ch ;
2015-05-16 13:32:34 +01:00
int i2s_port ;
} ;
2015-03-03 16:21:55 -08:00
# define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
# define LPASS_PLATFORM_PERIODS 2
2017-08-17 15:46:12 +05:30
static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
2015-03-03 16:21:55 -08:00
. info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME ,
. formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32 ,
. rates = SNDRV_PCM_RATE_8000_192000 ,
. rate_min = 8000 ,
. rate_max = 192000 ,
. channels_min = 1 ,
. channels_max = 8 ,
. buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE ,
. period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE /
LPASS_PLATFORM_PERIODS ,
. period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE /
LPASS_PLATFORM_PERIODS ,
. periods_min = LPASS_PLATFORM_PERIODS ,
. periods_max = LPASS_PLATFORM_PERIODS ,
. fifo_size = 0 ,
} ;
2020-08-14 16:23:01 +05:30
static int lpass_platform_alloc_dmactl_fields ( struct device * dev ,
struct regmap * map )
{
struct lpass_data * drvdata = dev_get_drvdata ( dev ) ;
struct lpass_variant * v = drvdata - > variant ;
struct lpaif_dmactl * rd_dmactl , * wr_dmactl ;
drvdata - > rd_dmactl = devm_kzalloc ( dev , sizeof ( struct lpaif_dmactl ) ,
GFP_KERNEL ) ;
if ( drvdata - > rd_dmactl = = NULL )
return - ENOMEM ;
drvdata - > wr_dmactl = devm_kzalloc ( dev , sizeof ( struct lpaif_dmactl ) ,
GFP_KERNEL ) ;
if ( drvdata - > wr_dmactl = = NULL )
return - ENOMEM ;
rd_dmactl = drvdata - > rd_dmactl ;
wr_dmactl = drvdata - > wr_dmactl ;
rd_dmactl - > bursten = devm_regmap_field_alloc ( dev , map , v - > rdma_bursten ) ;
rd_dmactl - > wpscnt = devm_regmap_field_alloc ( dev , map , v - > rdma_wpscnt ) ;
rd_dmactl - > fifowm = devm_regmap_field_alloc ( dev , map , v - > rdma_fifowm ) ;
rd_dmactl - > intf = devm_regmap_field_alloc ( dev , map , v - > rdma_intf ) ;
rd_dmactl - > enable = devm_regmap_field_alloc ( dev , map , v - > rdma_enable ) ;
rd_dmactl - > dyncclk = devm_regmap_field_alloc ( dev , map , v - > rdma_dyncclk ) ;
if ( IS_ERR ( rd_dmactl - > bursten ) | | IS_ERR ( rd_dmactl - > wpscnt ) | |
IS_ERR ( rd_dmactl - > fifowm ) | | IS_ERR ( rd_dmactl - > intf ) | |
IS_ERR ( rd_dmactl - > enable ) | | IS_ERR ( rd_dmactl - > dyncclk ) )
return - EINVAL ;
wr_dmactl - > bursten = devm_regmap_field_alloc ( dev , map , v - > wrdma_bursten ) ;
wr_dmactl - > wpscnt = devm_regmap_field_alloc ( dev , map , v - > wrdma_wpscnt ) ;
wr_dmactl - > fifowm = devm_regmap_field_alloc ( dev , map , v - > wrdma_fifowm ) ;
wr_dmactl - > intf = devm_regmap_field_alloc ( dev , map , v - > wrdma_intf ) ;
wr_dmactl - > enable = devm_regmap_field_alloc ( dev , map , v - > wrdma_enable ) ;
wr_dmactl - > dyncclk = devm_regmap_field_alloc ( dev , map , v - > wrdma_dyncclk ) ;
if ( IS_ERR ( wr_dmactl - > bursten ) | | IS_ERR ( wr_dmactl - > wpscnt ) | |
IS_ERR ( wr_dmactl - > fifowm ) | | IS_ERR ( wr_dmactl - > intf ) | |
IS_ERR ( wr_dmactl - > enable ) | | IS_ERR ( wr_dmactl - > dyncclk ) )
return - EINVAL ;
return 0 ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcmops_open ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2015-03-03 16:21:55 -08:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_runtime = asoc_substream_to_rtd ( substream ) ;
2020-03-23 14:20:01 +09:00
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( soc_runtime , 0 ) ;
2018-01-29 02:48:52 +00:00
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 11:25:43 +00:00
struct lpass_variant * v = drvdata - > variant ;
int ret , dma_ch , dir = substream - > stream ;
struct lpass_pcm_data * data ;
2020-08-14 16:23:00 +05:30
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2016-10-31 11:25:43 +00:00
if ( ! data )
return - ENOMEM ;
data - > i2s_port = cpu_dai - > driver - > id ;
runtime - > private_data = data ;
if ( v - > alloc_dma_channel )
dma_ch = v - > alloc_dma_channel ( drvdata , dir ) ;
2016-11-08 14:38:52 +01:00
else
dma_ch = 0 ;
2016-10-31 11:25:43 +00:00
if ( dma_ch < 0 )
return dma_ch ;
drvdata - > substream [ dma_ch ] = substream ;
ret = regmap_write ( drvdata - > lpaif_map ,
LPAIF_DMACTL_REG ( v , dma_ch , dir ) , 0 ) ;
if ( ret ) {
dev_err ( soc_runtime - > dev ,
2017-01-30 13:03:37 -08:00
" error writing to rdmactl reg: %d \n " , ret ) ;
2018-11-16 15:06:36 +00:00
return ret ;
2016-10-31 11:25:43 +00:00
}
2016-10-31 11:25:44 +00:00
data - > dma_ch = dma_ch ;
2015-03-03 16:21:55 -08:00
snd_soc_set_runtime_hwparams ( substream , & lpass_platform_pcm_hardware ) ;
runtime - > dma_bytes = lpass_platform_pcm_hardware . buffer_bytes_max ;
ret = snd_pcm_hw_constraint_integer ( runtime ,
SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( ret < 0 ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " setting constraints failed: %d \n " ,
ret ) ;
2015-03-03 16:21:55 -08:00
return - EINVAL ;
}
snd_pcm_set_runtime_buffer ( substream , & substream - > dma_buffer ) ;
return 0 ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcmops_close ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2016-10-31 11:25:43 +00:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2018-01-29 02:48:52 +00:00
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 11:25:43 +00:00
struct lpass_variant * v = drvdata - > variant ;
struct lpass_pcm_data * data ;
data = runtime - > private_data ;
2016-10-31 11:25:44 +00:00
drvdata - > substream [ data - > dma_ch ] = NULL ;
2016-10-31 11:25:43 +00:00
if ( v - > free_dma_channel )
2016-10-31 11:25:44 +00:00
v - > free_dma_channel ( drvdata , data - > dma_ch ) ;
2016-10-31 11:25:43 +00:00
2020-08-14 16:23:00 +05:30
kfree ( data ) ;
2016-10-31 11:25:43 +00:00
return 0 ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcmops_hw_params ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
2015-03-03 16:21:55 -08:00
{
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_runtime = asoc_substream_to_rtd ( substream ) ;
2018-01-29 02:48:52 +00:00
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 11:25:43 +00:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 13:32:17 +01:00
struct lpass_variant * v = drvdata - > variant ;
2015-03-03 16:21:55 -08:00
snd_pcm_format_t format = params_format ( params ) ;
unsigned int channels = params_channels ( params ) ;
unsigned int regval ;
2020-08-14 16:23:01 +05:30
struct lpaif_dmactl * dmactl ;
int id , dir = substream - > stream ;
2015-03-03 16:21:55 -08:00
int bitwidth ;
2016-02-11 12:17:30 +00:00
int ret , dma_port = pcm_data - > i2s_port + v - > dmactl_audif_start ;
2015-03-03 16:21:55 -08:00
2020-08-14 16:23:01 +05:30
if ( dir = = SNDRV_PCM_STREAM_PLAYBACK ) {
dmactl = drvdata - > rd_dmactl ;
id = pcm_data - > dma_ch ;
} else {
dmactl = drvdata - > wr_dmactl ;
id = pcm_data - > dma_ch - v - > wrdma_channel_start ;
}
2016-02-11 12:18:33 +00:00
2015-03-03 16:21:55 -08:00
bitwidth = snd_pcm_format_width ( format ) ;
if ( bitwidth < 0 ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " invalid bit width given: %d \n " ,
bitwidth ) ;
2015-03-03 16:21:55 -08:00
return bitwidth ;
}
2020-08-14 16:23:01 +05:30
ret = regmap_fields_write ( dmactl - > bursten , id , LPAIF_DMACTL_BURSTEN_INCR4 ) ;
if ( ret ) {
dev_err ( soc_runtime - > dev , " error updating bursten field: %d \n " , ret ) ;
return ret ;
}
regmap_fields_write ( dmactl - > fifowm , id , LPAIF_DMACTL_FIFOWM_8 ) ;
if ( ret ) {
dev_err ( soc_runtime - > dev , " error updating fifowm field: %d \n " , ret ) ;
return ret ;
}
regmap_fields_write ( dmactl - > intf , id , LPAIF_DMACTL_AUDINTF ( dma_port ) ) ;
if ( ret ) {
dev_err ( soc_runtime - > dev , " error updating audintf field: %d \n " , ret ) ;
return ret ;
}
2015-03-03 16:21:55 -08:00
switch ( bitwidth ) {
case 16 :
switch ( channels ) {
case 1 :
case 2 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_ONE ;
2015-03-03 16:21:55 -08:00
break ;
case 4 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_TWO ;
2015-03-03 16:21:55 -08:00
break ;
case 6 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_THREE ;
2015-03-03 16:21:55 -08:00
break ;
case 8 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_FOUR ;
2015-03-03 16:21:55 -08:00
break ;
default :
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" invalid PCM config given: bw=%d, ch=%u \n " ,
bitwidth , channels ) ;
2015-03-03 16:21:55 -08:00
return - EINVAL ;
}
break ;
case 24 :
case 32 :
switch ( channels ) {
case 1 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_ONE ;
2015-03-03 16:21:55 -08:00
break ;
case 2 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_TWO ;
2015-03-03 16:21:55 -08:00
break ;
case 4 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_FOUR ;
2015-03-03 16:21:55 -08:00
break ;
case 6 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_SIX ;
2015-03-03 16:21:55 -08:00
break ;
case 8 :
2020-08-14 16:23:01 +05:30
regval = LPAIF_DMACTL_WPSCNT_EIGHT ;
2015-03-03 16:21:55 -08:00
break ;
default :
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" invalid PCM config given: bw=%d, ch=%u \n " ,
bitwidth , channels ) ;
2015-03-03 16:21:55 -08:00
return - EINVAL ;
}
break ;
default :
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " invalid PCM config given: bw=%d, ch=%u \n " ,
bitwidth , channels ) ;
2015-03-03 16:21:55 -08:00
return - EINVAL ;
}
2020-08-14 16:23:01 +05:30
ret = regmap_fields_write ( dmactl - > wpscnt , id , regval ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2020-08-14 16:23:01 +05:30
dev_err ( soc_runtime - > dev , " error writing to dmactl reg: %d \n " ,
2017-01-30 13:03:37 -08:00
ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
return 0 ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcmops_hw_free ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2015-03-03 16:21:55 -08:00
{
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_runtime = asoc_substream_to_rtd ( substream ) ;
2018-01-29 02:48:52 +00:00
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 11:25:43 +00:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 13:32:17 +01:00
struct lpass_variant * v = drvdata - > variant ;
2016-02-11 12:18:33 +00:00
unsigned int reg ;
2015-03-03 16:21:55 -08:00
int ret ;
2016-10-31 11:25:44 +00:00
reg = LPAIF_DMACTL_REG ( v , pcm_data - > dma_ch , substream - > stream ) ;
2016-02-11 12:18:33 +00:00
ret = regmap_write ( drvdata - > lpaif_map , reg , 0 ) ;
2015-03-03 16:21:55 -08:00
if ( ret )
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " error writing to rdmactl reg: %d \n " ,
ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcmops_prepare ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2015-03-03 16:21:55 -08:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_runtime = asoc_substream_to_rtd ( substream ) ;
2018-01-29 02:48:52 +00:00
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 11:25:43 +00:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 13:32:17 +01:00
struct lpass_variant * v = drvdata - > variant ;
2020-08-14 16:23:01 +05:30
struct lpaif_dmactl * dmactl ;
int ret , id , ch , dir = substream - > stream ;
2016-02-11 12:18:33 +00:00
2016-10-31 11:25:44 +00:00
ch = pcm_data - > dma_ch ;
2020-08-14 16:23:01 +05:30
if ( dir = = SNDRV_PCM_STREAM_PLAYBACK ) {
dmactl = drvdata - > rd_dmactl ;
id = pcm_data - > dma_ch ;
} else {
dmactl = drvdata - > wr_dmactl ;
id = pcm_data - > dma_ch - v - > wrdma_channel_start ;
}
2015-03-03 16:21:55 -08:00
ret = regmap_write ( drvdata - > lpaif_map ,
2016-02-11 12:18:33 +00:00
LPAIF_DMABASE_REG ( v , ch , dir ) ,
2015-03-03 16:21:55 -08:00
runtime - > dma_addr ) ;
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " error writing to rdmabase reg: %d \n " ,
ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
ret = regmap_write ( drvdata - > lpaif_map ,
2016-02-11 12:18:20 +00:00
LPAIF_DMABUFF_REG ( v , ch , dir ) ,
2015-03-03 16:21:55 -08:00
( snd_pcm_lib_buffer_bytes ( substream ) > > 2 ) - 1 ) ;
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " error writing to rdmabuff reg: %d \n " ,
ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
ret = regmap_write ( drvdata - > lpaif_map ,
2016-02-11 12:18:20 +00:00
LPAIF_DMAPER_REG ( v , ch , dir ) ,
2015-03-03 16:21:55 -08:00
( snd_pcm_lib_period_bytes ( substream ) > > 2 ) - 1 ) ;
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " error writing to rdmaper reg: %d \n " ,
ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
2020-08-14 16:23:01 +05:30
ret = regmap_fields_write ( dmactl - > enable , id , LPAIF_DMACTL_ENABLE_ON ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " error writing to rdmactl reg: %d \n " ,
ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
return 0 ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcmops_trigger ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream ,
int cmd )
2015-03-03 16:21:55 -08:00
{
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_runtime = asoc_substream_to_rtd ( substream ) ;
2018-01-29 02:48:52 +00:00
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 11:25:43 +00:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 13:32:17 +01:00
struct lpass_variant * v = drvdata - > variant ;
2020-08-14 16:23:01 +05:30
struct lpaif_dmactl * dmactl ;
int ret , ch , id ;
int dir = substream - > stream ;
2016-02-11 12:18:33 +00:00
2016-10-31 11:25:44 +00:00
ch = pcm_data - > dma_ch ;
2020-08-14 16:23:01 +05:30
if ( dir = = SNDRV_PCM_STREAM_PLAYBACK ) {
dmactl = drvdata - > rd_dmactl ;
id = pcm_data - > dma_ch ;
} else {
dmactl = drvdata - > wr_dmactl ;
id = pcm_data - > dma_ch - v - > wrdma_channel_start ;
}
2015-03-03 16:21:55 -08:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
/* clear status before enabling interrupts */
ret = regmap_write ( drvdata - > lpaif_map ,
2015-05-16 13:32:17 +01:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 13:32:34 +01:00
LPAIF_IRQ_ALL ( ch ) ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
ret = regmap_update_bits ( drvdata - > lpaif_map ,
2015-05-16 13:32:17 +01:00
LPAIF_IRQEN_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 13:32:34 +01:00
LPAIF_IRQ_ALL ( ch ) ,
LPAIF_IRQ_ALL ( ch ) ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to irqen reg: %d \n " , ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
2020-08-14 16:23:01 +05:30
ret = regmap_fields_write ( dmactl - > enable , id ,
LPAIF_DMACTL_ENABLE_ON ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to rdmactl reg: %d \n " , ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2020-08-14 16:23:01 +05:30
ret = regmap_fields_write ( dmactl - > enable , id ,
LPAIF_DMACTL_ENABLE_OFF ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to rdmactl reg: %d \n " , ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
ret = regmap_update_bits ( drvdata - > lpaif_map ,
2015-05-16 13:32:17 +01:00
LPAIF_IRQEN_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 13:32:34 +01:00
LPAIF_IRQ_ALL ( ch ) , 0 ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to irqen reg: %d \n " , ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
break ;
}
return 0 ;
}
static snd_pcm_uframes_t lpass_platform_pcmops_pointer (
2019-10-02 14:33:29 +09:00
struct snd_soc_component * component ,
2015-03-03 16:21:55 -08:00
struct snd_pcm_substream * substream )
{
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_runtime = asoc_substream_to_rtd ( substream ) ;
2018-01-29 02:48:52 +00:00
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 11:25:43 +00:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 13:32:17 +01:00
struct lpass_variant * v = drvdata - > variant ;
2015-03-03 16:21:55 -08:00
unsigned int base_addr , curr_addr ;
2016-02-11 12:18:33 +00:00
int ret , ch , dir = substream - > stream ;
2016-10-31 11:25:44 +00:00
ch = pcm_data - > dma_ch ;
2015-03-03 16:21:55 -08:00
ret = regmap_read ( drvdata - > lpaif_map ,
2016-02-11 12:18:20 +00:00
LPAIF_DMABASE_REG ( v , ch , dir ) , & base_addr ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error reading from rdmabase reg: %d \n " , ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
ret = regmap_read ( drvdata - > lpaif_map ,
2016-02-11 12:18:20 +00:00
LPAIF_DMACURR_REG ( v , ch , dir ) , & curr_addr ) ;
2015-03-03 16:21:55 -08:00
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error reading from rdmacurr reg: %d \n " , ret ) ;
2015-03-03 16:21:55 -08:00
return ret ;
}
return bytes_to_frames ( substream - > runtime , curr_addr - base_addr ) ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcmops_mmap ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream ,
struct vm_area_struct * vma )
2015-03-03 16:21:55 -08:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2020-08-14 16:22:59 +05:30
return dma_mmap_coherent ( component - > dev , vma , runtime - > dma_area ,
runtime - > dma_addr , runtime - > dma_bytes ) ;
2015-03-03 16:21:55 -08:00
}
2015-05-21 22:53:14 +01:00
static irqreturn_t lpass_dma_interrupt_handler (
struct snd_pcm_substream * substream ,
struct lpass_data * drvdata ,
int chan , u32 interrupts )
2015-03-03 16:21:55 -08:00
{
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_runtime = asoc_substream_to_rtd ( substream ) ;
2015-05-16 13:32:17 +01:00
struct lpass_variant * v = drvdata - > variant ;
2015-03-03 16:21:55 -08:00
irqreturn_t ret = IRQ_NONE ;
2015-05-21 22:53:14 +01:00
int rv ;
2015-05-16 13:32:34 +01:00
if ( interrupts & LPAIF_IRQ_PER ( chan ) ) {
2015-03-03 16:21:55 -08:00
rv = regmap_write ( drvdata - > lpaif_map ,
2015-05-16 13:32:17 +01:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 13:32:34 +01:00
LPAIF_IRQ_PER ( chan ) ) ;
2015-03-03 16:21:55 -08:00
if ( rv ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , rv ) ;
2015-03-03 16:21:55 -08:00
return IRQ_NONE ;
}
snd_pcm_period_elapsed ( substream ) ;
ret = IRQ_HANDLED ;
}
2015-05-16 13:32:34 +01:00
if ( interrupts & LPAIF_IRQ_XRUN ( chan ) ) {
2015-03-03 16:21:55 -08:00
rv = regmap_write ( drvdata - > lpaif_map ,
2015-05-16 13:32:17 +01:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 13:32:34 +01:00
LPAIF_IRQ_XRUN ( chan ) ) ;
2015-03-03 16:21:55 -08:00
if ( rv ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , rv ) ;
2015-03-03 16:21:55 -08:00
return IRQ_NONE ;
}
2017-01-30 13:03:37 -08:00
dev_warn ( soc_runtime - > dev , " xrun warning \n " ) ;
2018-07-04 16:01:44 +02:00
snd_pcm_stop_xrun ( substream ) ;
2015-03-03 16:21:55 -08:00
ret = IRQ_HANDLED ;
}
2015-05-16 13:32:34 +01:00
if ( interrupts & LPAIF_IRQ_ERR ( chan ) ) {
2015-03-03 16:21:55 -08:00
rv = regmap_write ( drvdata - > lpaif_map ,
2015-05-16 13:32:17 +01:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 13:32:34 +01:00
LPAIF_IRQ_ERR ( chan ) ) ;
2015-03-03 16:21:55 -08:00
if ( rv ) {
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , rv ) ;
2015-03-03 16:21:55 -08:00
return IRQ_NONE ;
}
2017-01-30 13:03:37 -08:00
dev_err ( soc_runtime - > dev , " bus access error \n " ) ;
2015-03-03 16:21:55 -08:00
snd_pcm_stop ( substream , SNDRV_PCM_STATE_DISCONNECTED ) ;
ret = IRQ_HANDLED ;
}
return ret ;
}
2015-05-21 22:53:14 +01:00
static irqreturn_t lpass_platform_lpaif_irq ( int irq , void * data )
{
struct lpass_data * drvdata = data ;
struct lpass_variant * v = drvdata - > variant ;
unsigned int irqs ;
int rv , chan ;
rv = regmap_read ( drvdata - > lpaif_map ,
LPAIF_IRQSTAT_REG ( v , LPAIF_IRQ_PORT_HOST ) , & irqs ) ;
if ( rv ) {
2017-01-30 13:03:37 -08:00
pr_err ( " error reading from irqstat reg: %d \n " , rv ) ;
2015-05-21 22:53:14 +01:00
return IRQ_NONE ;
}
/* Handle per channel interrupts */
for ( chan = 0 ; chan < LPASS_MAX_DMA_CHANNELS ; chan + + ) {
if ( irqs & LPAIF_IRQ_ALL ( chan ) & & drvdata - > substream [ chan ] ) {
rv = lpass_dma_interrupt_handler (
drvdata - > substream [ chan ] ,
drvdata , chan , irqs ) ;
if ( rv ! = IRQ_HANDLED )
return rv ;
}
}
return IRQ_HANDLED ;
}
2019-10-02 14:33:29 +09:00
static int lpass_platform_pcm_new ( struct snd_soc_component * component ,
struct snd_soc_pcm_runtime * soc_runtime )
2015-03-03 16:21:55 -08:00
{
struct snd_pcm * pcm = soc_runtime - > pcm ;
2016-02-11 12:18:33 +00:00
struct snd_pcm_substream * psubstream , * csubstream ;
2016-03-31 19:23:14 +01:00
int ret = - EINVAL ;
2016-02-11 12:17:17 +00:00
size_t size = lpass_platform_pcm_hardware . buffer_bytes_max ;
2015-05-16 13:32:34 +01:00
2016-02-11 12:18:33 +00:00
psubstream = pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream ;
if ( psubstream ) {
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV ,
2018-01-29 02:48:52 +00:00
component - > dev ,
2016-02-11 12:18:33 +00:00
size , & psubstream - > dma_buffer ) ;
if ( ret ) {
2016-10-31 11:25:43 +00:00
dev_err ( soc_runtime - > dev , " Cannot allocate buffer(s) \n " ) ;
return ret ;
2016-02-11 12:18:33 +00:00
}
}
csubstream = pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream ;
if ( csubstream ) {
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV ,
2018-01-29 02:48:52 +00:00
component - > dev ,
2016-02-11 12:18:33 +00:00
size , & csubstream - > dma_buffer ) ;
if ( ret ) {
2016-10-31 11:25:43 +00:00
dev_err ( soc_runtime - > dev , " Cannot allocate buffer(s) \n " ) ;
if ( psubstream )
snd_dma_free_pages ( & psubstream - > dma_buffer ) ;
return ret ;
2016-02-11 12:18:33 +00:00
}
2016-10-31 11:25:43 +00:00
2015-03-03 16:21:55 -08:00
}
return 0 ;
}
2019-10-02 14:33:29 +09:00
static void lpass_platform_pcm_free ( struct snd_soc_component * component ,
struct snd_pcm * pcm )
2015-03-03 16:21:55 -08:00
{
2016-02-11 12:18:33 +00:00
struct snd_pcm_substream * substream ;
2016-10-31 11:25:43 +00:00
int i ;
2016-02-11 12:18:33 +00:00
2020-02-17 17:28:44 +09:00
for_each_pcm_streams ( i ) {
2016-02-11 12:18:33 +00:00
substream = pcm - > streams [ i ] . substream ;
if ( substream ) {
snd_dma_free_pages ( & substream - > dma_buffer ) ;
substream - > dma_buffer . area = NULL ;
substream - > dma_buffer . addr = 0 ;
}
}
2015-03-03 16:21:55 -08:00
}
2018-01-29 02:48:52 +00:00
static const struct snd_soc_component_driver lpass_component_driver = {
. name = DRV_NAME ,
2019-10-02 14:33:29 +09:00
. open = lpass_platform_pcmops_open ,
. close = lpass_platform_pcmops_close ,
. hw_params = lpass_platform_pcmops_hw_params ,
. hw_free = lpass_platform_pcmops_hw_free ,
. prepare = lpass_platform_pcmops_prepare ,
. trigger = lpass_platform_pcmops_trigger ,
. pointer = lpass_platform_pcmops_pointer ,
. mmap = lpass_platform_pcmops_mmap ,
. pcm_construct = lpass_platform_pcm_new ,
. pcm_destruct = lpass_platform_pcm_free ,
2015-03-03 16:21:55 -08:00
} ;
int asoc_qcom_lpass_platform_register ( struct platform_device * pdev )
{
struct lpass_data * drvdata = platform_get_drvdata ( pdev ) ;
2015-05-21 22:53:14 +01:00
struct lpass_variant * v = drvdata - > variant ;
int ret ;
2015-03-03 16:21:55 -08:00
2020-08-14 16:23:07 +05:30
drvdata - > lpaif_irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 11:15:49 -07:00
if ( drvdata - > lpaif_irq < 0 )
2015-03-03 16:21:55 -08:00
return - ENODEV ;
2015-05-21 22:53:14 +01:00
/* ensure audio hardware is disabled */
ret = regmap_write ( drvdata - > lpaif_map ,
LPAIF_IRQEN_REG ( v , LPAIF_IRQ_PORT_HOST ) , 0 ) ;
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( & pdev - > dev , " error writing to irqen reg: %d \n " , ret ) ;
2015-05-21 22:53:14 +01:00
return ret ;
}
ret = devm_request_irq ( & pdev - > dev , drvdata - > lpaif_irq ,
lpass_platform_lpaif_irq , IRQF_TRIGGER_RISING ,
" lpass-irq-lpaif " , drvdata ) ;
if ( ret ) {
2017-01-30 13:03:37 -08:00
dev_err ( & pdev - > dev , " irq request failed: %d \n " , ret ) ;
2015-05-21 22:53:14 +01:00
return ret ;
}
2020-08-14 16:23:01 +05:30
ret = lpass_platform_alloc_dmactl_fields ( & pdev - > dev ,
drvdata - > lpaif_map ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" error initializing dmactl fields: %d \n " , ret ) ;
return ret ;
}
2015-05-21 22:53:14 +01:00
2018-01-29 02:48:52 +00:00
return devm_snd_soc_register_component ( & pdev - > dev ,
& lpass_component_driver , NULL , 0 ) ;
2015-03-03 16:21:55 -08:00
}
EXPORT_SYMBOL_GPL ( asoc_qcom_lpass_platform_register ) ;
MODULE_DESCRIPTION ( " QTi LPASS Platform Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;