2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-03-04 03:21:55 +03: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 15:32:17 +03:00
# include "lpass-lpaif-reg.h"
2015-03-04 03:21:55 +03:00
# include "lpass.h"
2018-01-29 05:48:52 +03:00
# define DRV_NAME "lpass-platform"
2015-05-16 15:32:34 +03:00
struct lpass_pcm_data {
2016-10-31 14:25:44 +03:00
int dma_ch ;
2015-05-16 15:32:34 +03:00
int i2s_port ;
} ;
2015-03-04 03:21:55 +03:00
# define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
# define LPASS_PLATFORM_PERIODS 2
2017-08-17 13:16:12 +03:00
static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
2015-03-04 03:21:55 +03: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 ,
} ;
static int lpass_platform_pcmops_open ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2016-10-31 14:25:43 +03:00
struct snd_soc_dai * cpu_dai = soc_runtime - > cpu_dai ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 14:25:43 +03:00
struct lpass_variant * v = drvdata - > variant ;
int ret , dma_ch , dir = substream - > stream ;
struct lpass_pcm_data * data ;
data = devm_kzalloc ( soc_runtime - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
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 16:38:52 +03:00
else
dma_ch = 0 ;
2016-10-31 14:25:43 +03: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-31 00:03:37 +03:00
" error writing to rdmactl reg: %d \n " , ret ) ;
2018-11-16 18:06:36 +03:00
return ret ;
2016-10-31 14:25:43 +03:00
}
2016-10-31 14:25:44 +03:00
data - > dma_ch = dma_ch ;
2015-03-04 03:21:55 +03: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-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " setting constraints failed: %d \n " ,
ret ) ;
2015-03-04 03:21:55 +03:00
return - EINVAL ;
}
snd_pcm_set_runtime_buffer ( substream , & substream - > dma_buffer ) ;
return 0 ;
}
2016-10-31 14:25:43 +03:00
static int lpass_platform_pcmops_close ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 14:25:43 +03:00
struct lpass_variant * v = drvdata - > variant ;
struct lpass_pcm_data * data ;
data = runtime - > private_data ;
2016-10-31 14:25:44 +03:00
drvdata - > substream [ data - > dma_ch ] = NULL ;
2016-10-31 14:25:43 +03:00
if ( v - > free_dma_channel )
2016-10-31 14:25:44 +03:00
v - > free_dma_channel ( drvdata , data - > dma_ch ) ;
2016-10-31 14:25:43 +03:00
return 0 ;
}
2015-03-04 03:21:55 +03:00
static int lpass_platform_pcmops_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 14:25:43 +03:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 15:32:17 +03:00
struct lpass_variant * v = drvdata - > variant ;
2015-03-04 03:21:55 +03:00
snd_pcm_format_t format = params_format ( params ) ;
unsigned int channels = params_channels ( params ) ;
unsigned int regval ;
2016-02-11 15:18:33 +03:00
int ch , dir = substream - > stream ;
2015-03-04 03:21:55 +03:00
int bitwidth ;
2016-02-11 15:17:30 +03:00
int ret , dma_port = pcm_data - > i2s_port + v - > dmactl_audif_start ;
2015-03-04 03:21:55 +03:00
2016-10-31 14:25:44 +03:00
ch = pcm_data - > dma_ch ;
2016-02-11 15:18:33 +03:00
2015-03-04 03:21:55 +03:00
bitwidth = snd_pcm_format_width ( format ) ;
if ( bitwidth < 0 ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " invalid bit width given: %d \n " ,
bitwidth ) ;
2015-03-04 03:21:55 +03:00
return bitwidth ;
}
2016-02-11 15:18:20 +03:00
regval = LPAIF_DMACTL_BURSTEN_INCR4 |
LPAIF_DMACTL_AUDINTF ( dma_port ) |
LPAIF_DMACTL_FIFOWM_8 ;
2015-03-04 03:21:55 +03:00
switch ( bitwidth ) {
case 16 :
switch ( channels ) {
case 1 :
case 2 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_ONE ;
2015-03-04 03:21:55 +03:00
break ;
case 4 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_TWO ;
2015-03-04 03:21:55 +03:00
break ;
case 6 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_THREE ;
2015-03-04 03:21:55 +03:00
break ;
case 8 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_FOUR ;
2015-03-04 03:21:55 +03:00
break ;
default :
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" invalid PCM config given: bw=%d, ch=%u \n " ,
bitwidth , channels ) ;
2015-03-04 03:21:55 +03:00
return - EINVAL ;
}
break ;
case 24 :
case 32 :
switch ( channels ) {
case 1 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_ONE ;
2015-03-04 03:21:55 +03:00
break ;
case 2 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_TWO ;
2015-03-04 03:21:55 +03:00
break ;
case 4 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_FOUR ;
2015-03-04 03:21:55 +03:00
break ;
case 6 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_SIX ;
2015-03-04 03:21:55 +03:00
break ;
case 8 :
2016-02-11 15:18:20 +03:00
regval | = LPAIF_DMACTL_WPSCNT_EIGHT ;
2015-03-04 03:21:55 +03:00
break ;
default :
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" invalid PCM config given: bw=%d, ch=%u \n " ,
bitwidth , channels ) ;
2015-03-04 03:21:55 +03:00
return - EINVAL ;
}
break ;
default :
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " invalid PCM config given: bw=%d, ch=%u \n " ,
bitwidth , channels ) ;
2015-03-04 03:21:55 +03:00
return - EINVAL ;
}
ret = regmap_write ( drvdata - > lpaif_map ,
2016-02-11 15:18:33 +03:00
LPAIF_DMACTL_REG ( v , ch , dir ) , regval ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " error writing to rdmactl reg: %d \n " ,
ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
return 0 ;
}
static int lpass_platform_pcmops_hw_free ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 14:25:43 +03:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 15:32:17 +03:00
struct lpass_variant * v = drvdata - > variant ;
2016-02-11 15:18:33 +03:00
unsigned int reg ;
2015-03-04 03:21:55 +03:00
int ret ;
2016-10-31 14:25:44 +03:00
reg = LPAIF_DMACTL_REG ( v , pcm_data - > dma_ch , substream - > stream ) ;
2016-02-11 15:18:33 +03:00
ret = regmap_write ( drvdata - > lpaif_map , reg , 0 ) ;
2015-03-04 03:21:55 +03:00
if ( ret )
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " error writing to rdmactl reg: %d \n " ,
ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
static int lpass_platform_pcmops_prepare ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 14:25:43 +03:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 15:32:17 +03:00
struct lpass_variant * v = drvdata - > variant ;
2016-02-11 15:18:33 +03:00
int ret , ch , dir = substream - > stream ;
2016-10-31 14:25:44 +03:00
ch = pcm_data - > dma_ch ;
2015-03-04 03:21:55 +03:00
ret = regmap_write ( drvdata - > lpaif_map ,
2016-02-11 15:18:33 +03:00
LPAIF_DMABASE_REG ( v , ch , dir ) ,
2015-03-04 03:21:55 +03:00
runtime - > dma_addr ) ;
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " error writing to rdmabase reg: %d \n " ,
ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
ret = regmap_write ( drvdata - > lpaif_map ,
2016-02-11 15:18:20 +03:00
LPAIF_DMABUFF_REG ( v , ch , dir ) ,
2015-03-04 03:21:55 +03:00
( snd_pcm_lib_buffer_bytes ( substream ) > > 2 ) - 1 ) ;
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " error writing to rdmabuff reg: %d \n " ,
ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
ret = regmap_write ( drvdata - > lpaif_map ,
2016-02-11 15:18:20 +03:00
LPAIF_DMAPER_REG ( v , ch , dir ) ,
2015-03-04 03:21:55 +03:00
( snd_pcm_lib_period_bytes ( substream ) > > 2 ) - 1 ) ;
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " error writing to rdmaper reg: %d \n " ,
ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
ret = regmap_update_bits ( drvdata - > lpaif_map ,
2016-02-11 15:18:20 +03:00
LPAIF_DMACTL_REG ( v , ch , dir ) ,
LPAIF_DMACTL_ENABLE_MASK , LPAIF_DMACTL_ENABLE_ON ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " error writing to rdmactl reg: %d \n " ,
ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
return 0 ;
}
static int lpass_platform_pcmops_trigger ( struct snd_pcm_substream * substream ,
int cmd )
{
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 14:25:43 +03:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 15:32:17 +03:00
struct lpass_variant * v = drvdata - > variant ;
2016-02-11 15:18:33 +03:00
int ret , ch , dir = substream - > stream ;
2016-10-31 14:25:44 +03:00
ch = pcm_data - > dma_ch ;
2015-03-04 03:21:55 +03: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 15:32:17 +03:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 15:32:34 +03:00
LPAIF_IRQ_ALL ( ch ) ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
ret = regmap_update_bits ( drvdata - > lpaif_map ,
2015-05-16 15:32:17 +03:00
LPAIF_IRQEN_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 15:32:34 +03:00
LPAIF_IRQ_ALL ( ch ) ,
LPAIF_IRQ_ALL ( ch ) ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to irqen reg: %d \n " , ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
ret = regmap_update_bits ( drvdata - > lpaif_map ,
2016-02-11 15:18:20 +03:00
LPAIF_DMACTL_REG ( v , ch , dir ) ,
LPAIF_DMACTL_ENABLE_MASK ,
LPAIF_DMACTL_ENABLE_ON ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to rdmactl reg: %d \n " , ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
ret = regmap_update_bits ( drvdata - > lpaif_map ,
2016-02-11 15:18:20 +03:00
LPAIF_DMACTL_REG ( v , ch , dir ) ,
LPAIF_DMACTL_ENABLE_MASK ,
LPAIF_DMACTL_ENABLE_OFF ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to rdmactl reg: %d \n " , ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
ret = regmap_update_bits ( drvdata - > lpaif_map ,
2015-05-16 15:32:17 +03:00
LPAIF_IRQEN_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 15:32:34 +03:00
LPAIF_IRQ_ALL ( ch ) , 0 ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to irqen reg: %d \n " , ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
break ;
}
return 0 ;
}
static snd_pcm_uframes_t lpass_platform_pcmops_pointer (
struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
struct lpass_data * drvdata = snd_soc_component_get_drvdata ( component ) ;
2016-10-31 14:25:43 +03:00
struct snd_pcm_runtime * rt = substream - > runtime ;
struct lpass_pcm_data * pcm_data = rt - > private_data ;
2015-05-16 15:32:17 +03:00
struct lpass_variant * v = drvdata - > variant ;
2015-03-04 03:21:55 +03:00
unsigned int base_addr , curr_addr ;
2016-02-11 15:18:33 +03:00
int ret , ch , dir = substream - > stream ;
2016-10-31 14:25:44 +03:00
ch = pcm_data - > dma_ch ;
2015-03-04 03:21:55 +03:00
ret = regmap_read ( drvdata - > lpaif_map ,
2016-02-11 15:18:20 +03:00
LPAIF_DMABASE_REG ( v , ch , dir ) , & base_addr ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error reading from rdmabase reg: %d \n " , ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
ret = regmap_read ( drvdata - > lpaif_map ,
2016-02-11 15:18:20 +03:00
LPAIF_DMACURR_REG ( v , ch , dir ) , & curr_addr ) ;
2015-03-04 03:21:55 +03:00
if ( ret ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error reading from rdmacurr reg: %d \n " , ret ) ;
2015-03-04 03:21:55 +03:00
return ret ;
}
return bytes_to_frames ( substream - > runtime , curr_addr - base_addr ) ;
}
static int lpass_platform_pcmops_mmap ( struct snd_pcm_substream * substream ,
struct vm_area_struct * vma )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
return dma_mmap_coherent ( substream - > pcm - > card - > dev , vma ,
runtime - > dma_area , runtime - > dma_addr ,
runtime - > dma_bytes ) ;
}
2016-09-08 03:35:23 +03:00
static const struct snd_pcm_ops lpass_platform_pcm_ops = {
2015-03-04 03:21:55 +03:00
. open = lpass_platform_pcmops_open ,
2016-10-31 14:25:43 +03:00
. close = lpass_platform_pcmops_close ,
2015-03-04 03:21:55 +03:00
. ioctl = snd_pcm_lib_ioctl ,
. 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 ,
} ;
2015-05-22 00:53:14 +03:00
static irqreturn_t lpass_dma_interrupt_handler (
struct snd_pcm_substream * substream ,
struct lpass_data * drvdata ,
int chan , u32 interrupts )
2015-03-04 03:21:55 +03:00
{
struct snd_soc_pcm_runtime * soc_runtime = substream - > private_data ;
2015-05-16 15:32:17 +03:00
struct lpass_variant * v = drvdata - > variant ;
2015-03-04 03:21:55 +03:00
irqreturn_t ret = IRQ_NONE ;
2015-05-22 00:53:14 +03:00
int rv ;
2015-05-16 15:32:34 +03:00
if ( interrupts & LPAIF_IRQ_PER ( chan ) ) {
2015-03-04 03:21:55 +03:00
rv = regmap_write ( drvdata - > lpaif_map ,
2015-05-16 15:32:17 +03:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 15:32:34 +03:00
LPAIF_IRQ_PER ( chan ) ) ;
2015-03-04 03:21:55 +03:00
if ( rv ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , rv ) ;
2015-03-04 03:21:55 +03:00
return IRQ_NONE ;
}
snd_pcm_period_elapsed ( substream ) ;
ret = IRQ_HANDLED ;
}
2015-05-16 15:32:34 +03:00
if ( interrupts & LPAIF_IRQ_XRUN ( chan ) ) {
2015-03-04 03:21:55 +03:00
rv = regmap_write ( drvdata - > lpaif_map ,
2015-05-16 15:32:17 +03:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 15:32:34 +03:00
LPAIF_IRQ_XRUN ( chan ) ) ;
2015-03-04 03:21:55 +03:00
if ( rv ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , rv ) ;
2015-03-04 03:21:55 +03:00
return IRQ_NONE ;
}
2017-01-31 00:03:37 +03:00
dev_warn ( soc_runtime - > dev , " xrun warning \n " ) ;
2018-07-04 17:01:44 +03:00
snd_pcm_stop_xrun ( substream ) ;
2015-03-04 03:21:55 +03:00
ret = IRQ_HANDLED ;
}
2015-05-16 15:32:34 +03:00
if ( interrupts & LPAIF_IRQ_ERR ( chan ) ) {
2015-03-04 03:21:55 +03:00
rv = regmap_write ( drvdata - > lpaif_map ,
2015-05-16 15:32:17 +03:00
LPAIF_IRQCLEAR_REG ( v , LPAIF_IRQ_PORT_HOST ) ,
2015-05-16 15:32:34 +03:00
LPAIF_IRQ_ERR ( chan ) ) ;
2015-03-04 03:21:55 +03:00
if ( rv ) {
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev ,
" error writing to irqclear reg: %d \n " , rv ) ;
2015-03-04 03:21:55 +03:00
return IRQ_NONE ;
}
2017-01-31 00:03:37 +03:00
dev_err ( soc_runtime - > dev , " bus access error \n " ) ;
2015-03-04 03:21:55 +03:00
snd_pcm_stop ( substream , SNDRV_PCM_STATE_DISCONNECTED ) ;
ret = IRQ_HANDLED ;
}
return ret ;
}
2015-05-22 00:53:14 +03: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-31 00:03:37 +03:00
pr_err ( " error reading from irqstat reg: %d \n " , rv ) ;
2015-05-22 00:53:14 +03: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 ;
}
2015-03-04 03:21:55 +03:00
static int lpass_platform_pcm_new ( struct snd_soc_pcm_runtime * soc_runtime )
{
struct snd_pcm * pcm = soc_runtime - > pcm ;
2016-02-11 15:18:33 +03:00
struct snd_pcm_substream * psubstream , * csubstream ;
2018-01-29 05:48:52 +03:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( soc_runtime , DRV_NAME ) ;
2016-03-31 21:23:14 +03:00
int ret = - EINVAL ;
2016-02-11 15:17:17 +03:00
size_t size = lpass_platform_pcm_hardware . buffer_bytes_max ;
2015-05-16 15:32:34 +03:00
2016-02-11 15:18:33 +03:00
psubstream = pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream ;
if ( psubstream ) {
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV ,
2018-01-29 05:48:52 +03:00
component - > dev ,
2016-02-11 15:18:33 +03:00
size , & psubstream - > dma_buffer ) ;
if ( ret ) {
2016-10-31 14:25:43 +03:00
dev_err ( soc_runtime - > dev , " Cannot allocate buffer(s) \n " ) ;
return ret ;
2016-02-11 15:18:33 +03:00
}
}
csubstream = pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream ;
if ( csubstream ) {
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV ,
2018-01-29 05:48:52 +03:00
component - > dev ,
2016-02-11 15:18:33 +03:00
size , & csubstream - > dma_buffer ) ;
if ( ret ) {
2016-10-31 14:25:43 +03: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 15:18:33 +03:00
}
2016-10-31 14:25:43 +03:00
2015-03-04 03:21:55 +03:00
}
return 0 ;
}
static void lpass_platform_pcm_free ( struct snd_pcm * pcm )
{
2016-02-11 15:18:33 +03:00
struct snd_pcm_substream * substream ;
2016-10-31 14:25:43 +03:00
int i ;
2016-02-11 15:18:33 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( pcm - > streams ) ; i + + ) {
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-04 03:21:55 +03:00
}
2018-01-29 05:48:52 +03:00
static const struct snd_soc_component_driver lpass_component_driver = {
. name = DRV_NAME ,
2015-03-04 03:21:55 +03:00
. pcm_new = lpass_platform_pcm_new ,
. pcm_free = lpass_platform_pcm_free ,
. ops = & lpass_platform_pcm_ops ,
} ;
int asoc_qcom_lpass_platform_register ( struct platform_device * pdev )
{
struct lpass_data * drvdata = platform_get_drvdata ( pdev ) ;
2015-05-22 00:53:14 +03:00
struct lpass_variant * v = drvdata - > variant ;
int ret ;
2015-03-04 03:21:55 +03:00
drvdata - > lpaif_irq = platform_get_irq_byname ( pdev , " lpass-irq-lpaif " ) ;
2019-07-30 21:15:49 +03:00
if ( drvdata - > lpaif_irq < 0 )
2015-03-04 03:21:55 +03:00
return - ENODEV ;
2015-05-22 00:53:14 +03: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-31 00:03:37 +03:00
dev_err ( & pdev - > dev , " error writing to irqen reg: %d \n " , ret ) ;
2015-05-22 00:53:14 +03: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-31 00:03:37 +03:00
dev_err ( & pdev - > dev , " irq request failed: %d \n " , ret ) ;
2015-05-22 00:53:14 +03:00
return ret ;
}
2018-01-29 05:48:52 +03:00
return devm_snd_soc_register_component ( & pdev - > dev ,
& lpass_component_driver , NULL , 0 ) ;
2015-03-04 03:21:55 +03:00
}
EXPORT_SYMBOL_GPL ( asoc_qcom_lpass_platform_register ) ;
MODULE_DESCRIPTION ( " QTi LPASS Platform Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;