2018-05-18 13:56:08 +01:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
// Copyright (c) 2018, Linaro Limited
# include <linux/init.h>
# include <linux/err.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/pcm.h>
2018-11-15 18:13:24 +00:00
# include <linux/spinlock.h>
# include <sound/compress_driver.h>
2018-05-18 13:56:08 +01:00
# include <asm/dma.h>
# include <linux/dma-mapping.h>
# include <linux/of_device.h>
# include <sound/pcm_params.h>
# include "q6asm.h"
# include "q6routing.h"
# include "q6dsp-errno.h"
# define DRV_NAME "q6asm-fe-dai"
# define PLAYBACK_MIN_NUM_PERIODS 2
# define PLAYBACK_MAX_NUM_PERIODS 8
# define PLAYBACK_MAX_PERIOD_SIZE 65536
# define PLAYBACK_MIN_PERIOD_SIZE 128
# define CAPTURE_MIN_NUM_PERIODS 2
# define CAPTURE_MAX_NUM_PERIODS 8
# define CAPTURE_MAX_PERIOD_SIZE 4096
# define CAPTURE_MIN_PERIOD_SIZE 320
# define SID_MASK_DEFAULT 0xF
2018-11-15 18:13:24 +00:00
/* Default values used if user space does not set */
# define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
# define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
# define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
# define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)
2020-03-16 11:22:20 +05:30
# define ALAC_CH_LAYOUT_MONO ((101 << 16) | 1)
# define ALAC_CH_LAYOUT_STEREO ((101 << 16) | 2)
2018-05-18 13:56:08 +01:00
enum stream_state {
Q6ASM_STREAM_IDLE = 0 ,
Q6ASM_STREAM_STOPPED ,
Q6ASM_STREAM_RUNNING ,
} ;
struct q6asm_dai_rtd {
struct snd_pcm_substream * substream ;
2018-11-15 18:13:24 +00:00
struct snd_compr_stream * cstream ;
struct snd_compr_params codec_param ;
struct snd_dma_buffer dma_buffer ;
spinlock_t lock ;
2018-05-18 13:56:08 +01:00
phys_addr_t phys ;
unsigned int pcm_size ;
unsigned int pcm_count ;
unsigned int pcm_irq_pos ; /* IRQ position */
unsigned int periods ;
2018-11-15 18:13:24 +00:00
unsigned int bytes_sent ;
unsigned int bytes_received ;
unsigned int copied_total ;
2018-05-18 13:56:08 +01:00
uint16_t bits_per_sample ;
uint16_t source ; /* Encoding source bit mask */
struct audio_client * audio_client ;
uint16_t session_id ;
enum stream_state state ;
} ;
struct q6asm_dai_data {
2020-03-11 18:04:21 +00:00
struct snd_soc_dai_driver * dais ;
int num_dais ;
2018-05-18 13:56:08 +01:00
long long int sid ;
} ;
2020-01-01 08:43:28 +01:00
static const struct snd_pcm_hardware q6asm_dai_hardware_capture = {
2020-03-30 19:52:10 +02:00
. info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH |
2018-05-18 13:56:08 +01:00
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME ) ,
. formats = ( SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE ) ,
. rates = SNDRV_PCM_RATE_8000_48000 ,
. rate_min = 8000 ,
. rate_max = 48000 ,
. channels_min = 1 ,
. channels_max = 4 ,
. buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *
CAPTURE_MAX_PERIOD_SIZE ,
. period_bytes_min = CAPTURE_MIN_PERIOD_SIZE ,
. period_bytes_max = CAPTURE_MAX_PERIOD_SIZE ,
. periods_min = CAPTURE_MIN_NUM_PERIODS ,
. periods_max = CAPTURE_MAX_NUM_PERIODS ,
. fifo_size = 0 ,
} ;
static struct snd_pcm_hardware q6asm_dai_hardware_playback = {
2020-03-30 19:52:10 +02:00
. info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH |
2018-05-18 13:56:08 +01:00
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME ) ,
. formats = ( SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE ) ,
. rates = SNDRV_PCM_RATE_8000_192000 ,
. rate_min = 8000 ,
. rate_max = 192000 ,
. channels_min = 1 ,
. channels_max = 8 ,
. buffer_bytes_max = ( PLAYBACK_MAX_NUM_PERIODS *
PLAYBACK_MAX_PERIOD_SIZE ) ,
. period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE ,
. period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE ,
. periods_min = PLAYBACK_MIN_NUM_PERIODS ,
. periods_max = PLAYBACK_MAX_NUM_PERIODS ,
. fifo_size = 0 ,
} ;
# define Q6ASM_FEDAI_DRIVER(num) { \
. playback = { \
. stream_name = " MultiMedia " # num " Playback " , \
. rates = ( SNDRV_PCM_RATE_8000_192000 | \
SNDRV_PCM_RATE_KNOT ) , \
. formats = ( SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE ) , \
. channels_min = 1 , \
. channels_max = 8 , \
. rate_min = 8000 , \
. rate_max = 192000 , \
} , \
. capture = { \
. stream_name = " MultiMedia " # num " Capture " , \
. rates = ( SNDRV_PCM_RATE_8000_48000 | \
SNDRV_PCM_RATE_KNOT ) , \
. formats = ( SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE ) , \
. channels_min = 1 , \
. channels_max = 4 , \
. rate_min = 8000 , \
. rate_max = 48000 , \
} , \
. name = " MultiMedia " # num , \
. id = MSM_FRONTEND_DAI_MULTIMEDIA # # num , \
}
/* Conventional and unconventional sample rate supported */
static unsigned int supported_sample_rates [ ] = {
8000 , 11025 , 12000 , 16000 , 22050 , 24000 , 32000 , 44100 , 48000 ,
88200 , 96000 , 176400 , 192000
} ;
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
. count = ARRAY_SIZE ( supported_sample_rates ) ,
. list = supported_sample_rates ,
. mask = 0 ,
} ;
2018-11-15 18:13:24 +00:00
static const struct snd_compr_codec_caps q6asm_compr_caps = {
. num_descriptors = 1 ,
. descriptor [ 0 ] . max_ch = 2 ,
. descriptor [ 0 ] . sample_rates = { 8000 , 11025 , 12000 , 16000 , 22050 ,
24000 , 32000 , 44100 , 48000 , 88200 ,
96000 , 176400 , 192000 } ,
. descriptor [ 0 ] . num_sample_rates = 13 ,
. descriptor [ 0 ] . bit_rate [ 0 ] = 320 ,
. descriptor [ 0 ] . bit_rate [ 1 ] = 128 ,
. descriptor [ 0 ] . num_bitrates = 2 ,
. descriptor [ 0 ] . profiles = 0 ,
. descriptor [ 0 ] . modes = SND_AUDIOCHANMODE_MP3_STEREO ,
. descriptor [ 0 ] . formats = 0 ,
} ;
2018-05-18 13:56:08 +01:00
static void event_handler ( uint32_t opcode , uint32_t token ,
2020-05-29 21:38:23 +00:00
void * payload , void * priv )
2018-05-18 13:56:08 +01:00
{
struct q6asm_dai_rtd * prtd = priv ;
struct snd_pcm_substream * substream = prtd - > substream ;
switch ( opcode ) {
case ASM_CLIENT_EVENT_CMD_RUN_DONE :
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
q6asm_write_async ( prtd - > audio_client ,
prtd - > pcm_count , 0 , 0 , NO_TIMESTAMP ) ;
break ;
case ASM_CLIENT_EVENT_CMD_EOS_DONE :
prtd - > state = Q6ASM_STREAM_STOPPED ;
break ;
case ASM_CLIENT_EVENT_DATA_WRITE_DONE : {
prtd - > pcm_irq_pos + = prtd - > pcm_count ;
snd_pcm_period_elapsed ( substream ) ;
if ( prtd - > state = = Q6ASM_STREAM_RUNNING )
q6asm_write_async ( prtd - > audio_client ,
prtd - > pcm_count , 0 , 0 , NO_TIMESTAMP ) ;
break ;
}
case ASM_CLIENT_EVENT_DATA_READ_DONE :
prtd - > pcm_irq_pos + = prtd - > pcm_count ;
snd_pcm_period_elapsed ( substream ) ;
if ( prtd - > state = = Q6ASM_STREAM_RUNNING )
q6asm_read ( prtd - > audio_client ) ;
break ;
default :
break ;
}
}
2019-10-02 14:33:39 +09:00
static int q6asm_dai_prepare ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_prtd = asoc_substream_to_rtd ( substream ) ;
2018-05-18 13:56:08 +01:00
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
struct q6asm_dai_data * pdata ;
2020-07-07 17:36:39 +01:00
struct device * dev = component - > dev ;
2018-05-18 13:56:08 +01:00
int ret , i ;
2019-10-02 14:33:39 +09:00
pdata = snd_soc_component_get_drvdata ( component ) ;
2018-05-18 13:56:08 +01:00
if ( ! pdata )
return - EINVAL ;
if ( ! prtd | | ! prtd - > audio_client ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " %s: private data null or audio client freed \n " ,
2018-05-18 13:56:08 +01:00
__func__ ) ;
return - EINVAL ;
}
prtd - > pcm_count = snd_pcm_lib_period_bytes ( substream ) ;
prtd - > pcm_irq_pos = 0 ;
/* rate and channels are sent to audio driver */
if ( prtd - > state ) {
/* clear the previous setup if any */
q6asm_cmd ( prtd - > audio_client , CMD_CLOSE ) ;
q6asm_unmap_memory_regions ( substream - > stream ,
prtd - > audio_client ) ;
q6routing_stream_close ( soc_prtd - > dai_link - > id ,
substream - > stream ) ;
}
ret = q6asm_map_memory_regions ( substream - > stream , prtd - > audio_client ,
prtd - > phys ,
( prtd - > pcm_size / prtd - > periods ) ,
prtd - > periods ) ;
if ( ret < 0 ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " Audio Start: Buffer Allocation failed rc = %d \n " ,
2018-05-18 13:56:08 +01:00
ret ) ;
return - ENOMEM ;
}
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
ret = q6asm_open_write ( prtd - > audio_client , FORMAT_LINEAR_PCM ,
2020-03-16 11:22:15 +05:30
0 , prtd - > bits_per_sample ) ;
2018-05-18 13:56:08 +01:00
} else if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
ret = q6asm_open_read ( prtd - > audio_client , FORMAT_LINEAR_PCM ,
prtd - > bits_per_sample ) ;
}
if ( ret < 0 ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " %s: q6asm_open_write failed \n " , __func__ ) ;
2018-05-18 13:56:08 +01:00
q6asm_audio_client_free ( prtd - > audio_client ) ;
prtd - > audio_client = NULL ;
return - ENOMEM ;
}
prtd - > session_id = q6asm_get_session_id ( prtd - > audio_client ) ;
ret = q6routing_stream_open ( soc_prtd - > dai_link - > id , LEGACY_PCM_MODE ,
prtd - > session_id , substream - > stream ) ;
if ( ret ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " %s: stream reg failed ret:%d \n " , __func__ , ret ) ;
2018-05-18 13:56:08 +01:00
return ret ;
}
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
ret = q6asm_media_format_block_multi_ch_pcm (
prtd - > audio_client , runtime - > rate ,
runtime - > channels , NULL ,
prtd - > bits_per_sample ) ;
} else if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
ret = q6asm_enc_cfg_blk_pcm_format_support ( prtd - > audio_client ,
runtime - > rate , runtime - > channels ,
prtd - > bits_per_sample ) ;
/* Queue the buffers */
for ( i = 0 ; i < runtime - > periods ; i + + )
q6asm_read ( prtd - > audio_client ) ;
}
if ( ret < 0 )
2020-07-07 17:36:39 +01:00
dev_info ( dev , " %s: CMD Format block failed \n " , __func__ ) ;
2018-05-18 13:56:08 +01:00
prtd - > state = Q6ASM_STREAM_RUNNING ;
return 0 ;
}
2019-10-02 14:33:39 +09:00
static int q6asm_dai_trigger ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream , int cmd )
2018-05-18 13:56:08 +01:00
{
int ret = 0 ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
ret = q6asm_run_nowait ( prtd - > audio_client , 0 , 0 , 0 ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
prtd - > state = Q6ASM_STREAM_STOPPED ;
ret = q6asm_cmd_nowait ( prtd - > audio_client , CMD_EOS ) ;
break ;
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
ret = q6asm_cmd_nowait ( prtd - > audio_client , CMD_PAUSE ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
2019-10-02 14:33:39 +09:00
static int q6asm_dai_open ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_prtd = asoc_substream_to_rtd ( substream ) ;
2020-03-23 14:20:01 +09:00
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( soc_prtd , 0 ) ;
2018-05-18 13:56:08 +01:00
struct q6asm_dai_rtd * prtd ;
struct q6asm_dai_data * pdata ;
2019-10-02 14:33:39 +09:00
struct device * dev = component - > dev ;
2018-05-18 13:56:08 +01:00
int ret = 0 ;
int stream_id ;
stream_id = cpu_dai - > driver - > id ;
2019-10-02 14:33:39 +09:00
pdata = snd_soc_component_get_drvdata ( component ) ;
2018-05-18 13:56:08 +01:00
if ( ! pdata ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " Drv data not found .. \n " ) ;
2018-05-18 13:56:08 +01:00
return - EINVAL ;
}
prtd = kzalloc ( sizeof ( struct q6asm_dai_rtd ) , GFP_KERNEL ) ;
if ( prtd = = NULL )
return - ENOMEM ;
prtd - > substream = substream ;
prtd - > audio_client = q6asm_audio_client_alloc ( dev ,
( q6asm_cb ) event_handler , prtd , stream_id ,
LEGACY_PCM_MODE ) ;
2018-10-01 19:44:30 +03:00
if ( IS_ERR ( prtd - > audio_client ) ) {
2020-07-07 17:36:39 +01:00
dev_info ( dev , " %s: Could not allocate memory \n " , __func__ ) ;
2018-10-01 19:44:30 +03:00
ret = PTR_ERR ( prtd - > audio_client ) ;
2018-05-18 13:56:08 +01:00
kfree ( prtd ) ;
2018-10-01 19:44:30 +03:00
return ret ;
2018-05-18 13:56:08 +01:00
}
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
runtime - > hw = q6asm_dai_hardware_playback ;
else if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
runtime - > hw = q6asm_dai_hardware_capture ;
ret = snd_pcm_hw_constraint_list ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_RATE ,
& constraints_sample_rates ) ;
if ( ret < 0 )
2020-07-07 17:36:39 +01:00
dev_info ( dev , " snd_pcm_hw_constraint_list failed \n " ) ;
2018-05-18 13:56:08 +01:00
/* Ensure that buffer size is a multiple of period size */
ret = snd_pcm_hw_constraint_integer ( runtime ,
SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( ret < 0 )
2020-07-07 17:36:39 +01:00
dev_info ( dev , " snd_pcm_hw_constraint_integer failed \n " ) ;
2018-05-18 13:56:08 +01:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
ret = snd_pcm_hw_constraint_minmax ( runtime ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES ,
PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE ,
PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE ) ;
if ( ret < 0 ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " constraint for buffer bytes min max ret = %d \n " ,
ret ) ;
2018-05-18 13:56:08 +01:00
}
}
ret = snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES , 32 ) ;
if ( ret < 0 ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " constraint for period bytes step ret = %d \n " ,
2018-05-18 13:56:08 +01:00
ret ) ;
}
ret = snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES , 32 ) ;
if ( ret < 0 ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " constraint for buffer bytes step ret = %d \n " ,
2018-05-18 13:56:08 +01:00
ret ) ;
}
runtime - > private_data = prtd ;
snd_soc_set_runtime_hwparams ( substream , & q6asm_dai_hardware_playback ) ;
runtime - > dma_bytes = q6asm_dai_hardware_playback . buffer_bytes_max ;
if ( pdata - > sid < 0 )
prtd - > phys = substream - > dma_buffer . addr ;
else
prtd - > phys = substream - > dma_buffer . addr | ( pdata - > sid < < 32 ) ;
snd_pcm_set_runtime_buffer ( substream , & substream - > dma_buffer ) ;
return 0 ;
}
2019-10-02 14:33:39 +09:00
static int q6asm_dai_close ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2020-07-20 10:19:00 +09:00
struct snd_soc_pcm_runtime * soc_prtd = asoc_substream_to_rtd ( substream ) ;
2018-05-18 13:56:08 +01:00
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
if ( prtd - > audio_client ) {
2018-07-04 10:49:44 +01:00
if ( prtd - > state )
q6asm_cmd ( prtd - > audio_client , CMD_CLOSE ) ;
2018-05-18 13:56:08 +01:00
q6asm_unmap_memory_regions ( substream - > stream ,
prtd - > audio_client ) ;
q6asm_audio_client_free ( prtd - > audio_client ) ;
prtd - > audio_client = NULL ;
}
q6routing_stream_close ( soc_prtd - > dai_link - > id ,
substream - > stream ) ;
kfree ( prtd ) ;
return 0 ;
}
2019-10-02 14:33:39 +09:00
static snd_pcm_uframes_t q6asm_dai_pointer ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
if ( prtd - > pcm_irq_pos > = prtd - > pcm_size )
prtd - > pcm_irq_pos = 0 ;
return bytes_to_frames ( runtime , ( prtd - > pcm_irq_pos ) ) ;
}
2019-10-02 14:33:39 +09:00
static int q6asm_dai_mmap ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream ,
struct vm_area_struct * vma )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
2019-10-02 14:33:39 +09:00
struct device * dev = component - > dev ;
2018-05-18 13:56:08 +01:00
return dma_mmap_coherent ( dev , vma ,
runtime - > dma_area , runtime - > dma_addr ,
runtime - > dma_bytes ) ;
}
2019-10-02 14:33:39 +09:00
static int q6asm_dai_hw_params ( struct snd_soc_component * component ,
struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
prtd - > pcm_size = params_buffer_bytes ( params ) ;
prtd - > periods = params_periods ( params ) ;
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
prtd - > bits_per_sample = 16 ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
prtd - > bits_per_sample = 24 ;
break ;
}
return 0 ;
}
2018-11-15 18:13:24 +00:00
static void compress_event_handler ( uint32_t opcode , uint32_t token ,
2020-05-29 21:38:23 +00:00
void * payload , void * priv )
2018-11-15 18:13:24 +00:00
{
struct q6asm_dai_rtd * prtd = priv ;
struct snd_compr_stream * substream = prtd - > cstream ;
unsigned long flags ;
uint64_t avail ;
switch ( opcode ) {
case ASM_CLIENT_EVENT_CMD_RUN_DONE :
spin_lock_irqsave ( & prtd - > lock , flags ) ;
if ( ! prtd - > bytes_sent ) {
q6asm_write_async ( prtd - > audio_client , prtd - > pcm_count ,
0 , 0 , NO_TIMESTAMP ) ;
prtd - > bytes_sent + = prtd - > pcm_count ;
}
spin_unlock_irqrestore ( & prtd - > lock , flags ) ;
break ;
case ASM_CLIENT_EVENT_CMD_EOS_DONE :
prtd - > state = Q6ASM_STREAM_STOPPED ;
break ;
case ASM_CLIENT_EVENT_DATA_WRITE_DONE :
spin_lock_irqsave ( & prtd - > lock , flags ) ;
prtd - > copied_total + = prtd - > pcm_count ;
snd_compr_fragment_elapsed ( substream ) ;
if ( prtd - > state ! = Q6ASM_STREAM_RUNNING ) {
spin_unlock_irqrestore ( & prtd - > lock , flags ) ;
break ;
}
avail = prtd - > bytes_received - prtd - > bytes_sent ;
if ( avail > = prtd - > pcm_count ) {
q6asm_write_async ( prtd - > audio_client ,
prtd - > pcm_count , 0 , 0 , NO_TIMESTAMP ) ;
prtd - > bytes_sent + = prtd - > pcm_count ;
}
spin_unlock_irqrestore ( & prtd - > lock , flags ) ;
break ;
default :
break ;
}
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_open ( struct snd_soc_component * component ,
struct snd_compr_stream * stream )
2018-11-15 18:13:24 +00:00
{
struct snd_soc_pcm_runtime * rtd = stream - > private_data ;
struct snd_compr_runtime * runtime = stream - > runtime ;
2020-03-23 14:20:01 +09:00
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( rtd , 0 ) ;
2018-11-15 18:13:24 +00:00
struct q6asm_dai_data * pdata ;
2020-04-20 16:08:44 +09:00
struct device * dev = component - > dev ;
2018-11-15 18:13:24 +00:00
struct q6asm_dai_rtd * prtd ;
int stream_id , size , ret ;
stream_id = cpu_dai - > driver - > id ;
2020-04-20 16:08:44 +09:00
pdata = snd_soc_component_get_drvdata ( component ) ;
2018-11-15 18:13:24 +00:00
if ( ! pdata ) {
dev_err ( dev , " Drv data not found .. \n " ) ;
return - EINVAL ;
}
prtd = kzalloc ( sizeof ( * prtd ) , GFP_KERNEL ) ;
if ( ! prtd )
return - ENOMEM ;
prtd - > cstream = stream ;
prtd - > audio_client = q6asm_audio_client_alloc ( dev ,
( q6asm_cb ) compress_event_handler ,
prtd , stream_id , LEGACY_PCM_MODE ) ;
2018-12-21 12:05:16 +03:00
if ( IS_ERR ( prtd - > audio_client ) ) {
2018-11-15 18:13:24 +00:00
dev_err ( dev , " Could not allocate memory \n " ) ;
2018-12-21 12:05:16 +03:00
ret = PTR_ERR ( prtd - > audio_client ) ;
2018-12-21 12:06:10 +03:00
goto free_prtd ;
2018-11-15 18:13:24 +00:00
}
size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE *
COMPR_PLAYBACK_MAX_NUM_FRAGMENTS ;
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , dev , size ,
& prtd - > dma_buffer ) ;
if ( ret ) {
dev_err ( dev , " Cannot allocate buffer(s) \n " ) ;
2018-12-21 12:06:10 +03:00
goto free_client ;
2018-11-15 18:13:24 +00:00
}
if ( pdata - > sid < 0 )
prtd - > phys = prtd - > dma_buffer . addr ;
else
prtd - > phys = prtd - > dma_buffer . addr | ( pdata - > sid < < 32 ) ;
snd_compr_set_runtime_buffer ( stream , & prtd - > dma_buffer ) ;
spin_lock_init ( & prtd - > lock ) ;
runtime - > private_data = prtd ;
return 0 ;
2018-12-21 12:06:10 +03:00
free_client :
q6asm_audio_client_free ( prtd - > audio_client ) ;
free_prtd :
kfree ( prtd ) ;
return ret ;
2018-11-15 18:13:24 +00:00
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_free ( struct snd_soc_component * component ,
struct snd_compr_stream * stream )
2018-11-15 18:13:24 +00:00
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
struct snd_soc_pcm_runtime * rtd = stream - > private_data ;
if ( prtd - > audio_client ) {
if ( prtd - > state )
q6asm_cmd ( prtd - > audio_client , CMD_CLOSE ) ;
snd_dma_free_pages ( & prtd - > dma_buffer ) ;
q6asm_unmap_memory_regions ( stream - > direction ,
prtd - > audio_client ) ;
q6asm_audio_client_free ( prtd - > audio_client ) ;
prtd - > audio_client = NULL ;
}
q6routing_stream_close ( rtd - > dai_link - > id , stream - > direction ) ;
kfree ( prtd ) ;
return 0 ;
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_set_params ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
2018-11-15 18:13:24 +00:00
struct snd_compr_params * params )
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
struct snd_soc_pcm_runtime * rtd = stream - > private_data ;
int dir = stream - > direction ;
struct q6asm_dai_data * pdata ;
2019-11-15 15:57:05 +05:30
struct q6asm_flac_cfg flac_cfg ;
2020-03-16 11:22:17 +05:30
struct q6asm_wma_cfg wma_cfg ;
2020-03-16 11:22:20 +05:30
struct q6asm_alac_cfg alac_cfg ;
struct q6asm_ape_cfg ape_cfg ;
2020-03-16 11:22:17 +05:30
unsigned int wma_v9 = 0 ;
2020-04-20 16:08:44 +09:00
struct device * dev = component - > dev ;
2018-11-15 18:13:24 +00:00
int ret ;
2019-11-15 15:57:05 +05:30
union snd_codec_options * codec_options ;
struct snd_dec_flac * flac ;
2020-03-16 11:22:17 +05:30
struct snd_dec_wma * wma ;
2020-03-16 11:22:20 +05:30
struct snd_dec_alac * alac ;
struct snd_dec_ape * ape ;
2019-11-15 15:57:05 +05:30
codec_options = & ( prtd - > codec_param . codec . options ) ;
2018-11-15 18:13:24 +00:00
memcpy ( & prtd - > codec_param , params , sizeof ( * params ) ) ;
2020-04-20 16:08:44 +09:00
pdata = snd_soc_component_get_drvdata ( component ) ;
2018-11-15 18:13:24 +00:00
if ( ! pdata )
return - EINVAL ;
if ( ! prtd | | ! prtd - > audio_client ) {
dev_err ( dev , " private data null or audio client freed \n " ) ;
return - EINVAL ;
}
prtd - > periods = runtime - > fragments ;
prtd - > pcm_count = runtime - > fragment_size ;
prtd - > pcm_size = runtime - > fragments * runtime - > fragment_size ;
prtd - > bits_per_sample = 16 ;
if ( dir = = SND_COMPRESS_PLAYBACK ) {
ret = q6asm_open_write ( prtd - > audio_client , params - > codec . id ,
2020-03-16 11:22:15 +05:30
params - > codec . profile , prtd - > bits_per_sample ) ;
2018-11-15 18:13:24 +00:00
if ( ret < 0 ) {
dev_err ( dev , " q6asm_open_write failed \n " ) ;
q6asm_audio_client_free ( prtd - > audio_client ) ;
prtd - > audio_client = NULL ;
return ret ;
}
}
prtd - > session_id = q6asm_get_session_id ( prtd - > audio_client ) ;
ret = q6routing_stream_open ( rtd - > dai_link - > id , LEGACY_PCM_MODE ,
prtd - > session_id , dir ) ;
if ( ret ) {
dev_err ( dev , " Stream reg failed ret:%d \n " , ret ) ;
return ret ;
}
2019-11-15 15:57:05 +05:30
switch ( params - > codec . id ) {
case SND_AUDIOCODEC_FLAC :
memset ( & flac_cfg , 0x0 , sizeof ( struct q6asm_flac_cfg ) ) ;
flac = & codec_options - > flac_d ;
flac_cfg . ch_cfg = params - > codec . ch_in ;
flac_cfg . sample_rate = params - > codec . sample_rate ;
flac_cfg . stream_info_present = 1 ;
flac_cfg . sample_size = flac - > sample_size ;
flac_cfg . min_blk_size = flac - > min_blk_size ;
flac_cfg . max_blk_size = flac - > max_blk_size ;
flac_cfg . max_frame_size = flac - > max_frame_size ;
flac_cfg . min_frame_size = flac - > min_frame_size ;
ret = q6asm_stream_media_format_block_flac ( prtd - > audio_client ,
& flac_cfg ) ;
if ( ret < 0 ) {
dev_err ( dev , " FLAC CMD Format block failed:%d \n " , ret ) ;
return - EIO ;
}
break ;
2020-03-16 11:22:17 +05:30
case SND_AUDIOCODEC_WMA :
wma = & codec_options - > wma_d ;
memset ( & wma_cfg , 0x0 , sizeof ( struct q6asm_wma_cfg ) ) ;
wma_cfg . sample_rate = params - > codec . sample_rate ;
wma_cfg . num_channels = params - > codec . ch_in ;
wma_cfg . bytes_per_sec = params - > codec . bit_rate / 8 ;
wma_cfg . block_align = params - > codec . align ;
wma_cfg . bits_per_sample = prtd - > bits_per_sample ;
wma_cfg . enc_options = wma - > encoder_option ;
wma_cfg . adv_enc_options = wma - > adv_encoder_option ;
wma_cfg . adv_enc_options2 = wma - > adv_encoder_option2 ;
if ( wma_cfg . num_channels = = 1 )
wma_cfg . channel_mask = 4 ; /* Mono Center */
else if ( wma_cfg . num_channels = = 2 )
wma_cfg . channel_mask = 3 ; /* Stereo FL/FR */
else
return - EINVAL ;
/* check the codec profile */
switch ( params - > codec . profile ) {
case SND_AUDIOPROFILE_WMA9 :
wma_cfg . fmtag = 0x161 ;
wma_v9 = 1 ;
break ;
case SND_AUDIOPROFILE_WMA10 :
wma_cfg . fmtag = 0x166 ;
break ;
case SND_AUDIOPROFILE_WMA9_PRO :
wma_cfg . fmtag = 0x162 ;
break ;
case SND_AUDIOPROFILE_WMA9_LOSSLESS :
wma_cfg . fmtag = 0x163 ;
break ;
case SND_AUDIOPROFILE_WMA10_LOSSLESS :
wma_cfg . fmtag = 0x167 ;
break ;
default :
dev_err ( dev , " Unknown WMA profile:%x \n " ,
params - > codec . profile ) ;
return - EIO ;
}
if ( wma_v9 )
ret = q6asm_stream_media_format_block_wma_v9 (
prtd - > audio_client , & wma_cfg ) ;
else
ret = q6asm_stream_media_format_block_wma_v10 (
prtd - > audio_client , & wma_cfg ) ;
if ( ret < 0 ) {
dev_err ( dev , " WMA9 CMD failed:%d \n " , ret ) ;
return - EIO ;
}
2020-03-16 11:22:20 +05:30
break ;
case SND_AUDIOCODEC_ALAC :
memset ( & alac_cfg , 0x0 , sizeof ( alac_cfg ) ) ;
alac = & codec_options - > alac_d ;
alac_cfg . sample_rate = params - > codec . sample_rate ;
alac_cfg . avg_bit_rate = params - > codec . bit_rate ;
alac_cfg . bit_depth = prtd - > bits_per_sample ;
alac_cfg . num_channels = params - > codec . ch_in ;
alac_cfg . frame_length = alac - > frame_length ;
alac_cfg . pb = alac - > pb ;
alac_cfg . mb = alac - > mb ;
alac_cfg . kb = alac - > kb ;
alac_cfg . max_run = alac - > max_run ;
alac_cfg . compatible_version = alac - > compatible_version ;
alac_cfg . max_frame_bytes = alac - > max_frame_bytes ;
switch ( params - > codec . ch_in ) {
case 1 :
alac_cfg . channel_layout_tag = ALAC_CH_LAYOUT_MONO ;
break ;
case 2 :
alac_cfg . channel_layout_tag = ALAC_CH_LAYOUT_STEREO ;
break ;
}
ret = q6asm_stream_media_format_block_alac ( prtd - > audio_client ,
& alac_cfg ) ;
if ( ret < 0 ) {
dev_err ( dev , " ALAC CMD Format block failed:%d \n " , ret ) ;
return - EIO ;
}
break ;
case SND_AUDIOCODEC_APE :
memset ( & ape_cfg , 0x0 , sizeof ( ape_cfg ) ) ;
ape = & codec_options - > ape_d ;
ape_cfg . sample_rate = params - > codec . sample_rate ;
ape_cfg . num_channels = params - > codec . ch_in ;
ape_cfg . bits_per_sample = prtd - > bits_per_sample ;
ape_cfg . compatible_version = ape - > compatible_version ;
ape_cfg . compression_level = ape - > compression_level ;
ape_cfg . format_flags = ape - > format_flags ;
ape_cfg . blocks_per_frame = ape - > blocks_per_frame ;
ape_cfg . final_frame_blocks = ape - > final_frame_blocks ;
ape_cfg . total_frames = ape - > total_frames ;
ape_cfg . seek_table_present = ape - > seek_table_present ;
ret = q6asm_stream_media_format_block_ape ( prtd - > audio_client ,
& ape_cfg ) ;
if ( ret < 0 ) {
dev_err ( dev , " APE CMD Format block failed:%d \n " , ret ) ;
return - EIO ;
}
break ;
2019-11-15 15:57:05 +05:30
default :
break ;
}
2018-11-15 18:13:24 +00:00
ret = q6asm_map_memory_regions ( dir , prtd - > audio_client , prtd - > phys ,
( prtd - > pcm_size / prtd - > periods ) ,
prtd - > periods ) ;
if ( ret < 0 ) {
dev_err ( dev , " Buffer Mapping failed ret:%d \n " , ret ) ;
return - ENOMEM ;
}
prtd - > state = Q6ASM_STREAM_RUNNING ;
return 0 ;
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_trigger ( struct snd_soc_component * component ,
struct snd_compr_stream * stream , int cmd )
2018-11-15 18:13:24 +00:00
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
int ret = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
ret = q6asm_run_nowait ( prtd - > audio_client , 0 , 0 , 0 ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
prtd - > state = Q6ASM_STREAM_STOPPED ;
ret = q6asm_cmd_nowait ( prtd - > audio_client , CMD_EOS ) ;
break ;
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
ret = q6asm_cmd_nowait ( prtd - > audio_client , CMD_PAUSE ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_pointer ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
struct snd_compr_tstamp * tstamp )
2018-11-15 18:13:24 +00:00
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
unsigned long flags ;
spin_lock_irqsave ( & prtd - > lock , flags ) ;
tstamp - > copied_total = prtd - > copied_total ;
tstamp - > byte_offset = prtd - > copied_total % prtd - > pcm_size ;
spin_unlock_irqrestore ( & prtd - > lock , flags ) ;
return 0 ;
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_ack ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
size_t count )
2018-11-15 18:13:24 +00:00
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
unsigned long flags ;
spin_lock_irqsave ( & prtd - > lock , flags ) ;
prtd - > bytes_received + = count ;
spin_unlock_irqrestore ( & prtd - > lock , flags ) ;
return count ;
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_mmap ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
struct vm_area_struct * vma )
2018-11-15 18:13:24 +00:00
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
2020-04-20 16:08:44 +09:00
struct device * dev = component - > dev ;
2018-11-15 18:13:24 +00:00
return dma_mmap_coherent ( dev , vma ,
prtd - > dma_buffer . area , prtd - > dma_buffer . addr ,
prtd - > dma_buffer . bytes ) ;
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_get_caps ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
2018-11-15 18:13:24 +00:00
struct snd_compr_caps * caps )
{
caps - > direction = SND_COMPRESS_PLAYBACK ;
caps - > min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE ;
caps - > max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE ;
caps - > min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS ;
caps - > max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS ;
2020-03-16 11:22:20 +05:30
caps - > num_codecs = 5 ;
2018-11-15 18:13:24 +00:00
caps - > codecs [ 0 ] = SND_AUDIOCODEC_MP3 ;
2019-11-15 15:57:05 +05:30
caps - > codecs [ 1 ] = SND_AUDIOCODEC_FLAC ;
2020-03-16 11:22:17 +05:30
caps - > codecs [ 2 ] = SND_AUDIOCODEC_WMA ;
2020-03-16 11:22:20 +05:30
caps - > codecs [ 3 ] = SND_AUDIOCODEC_ALAC ;
caps - > codecs [ 4 ] = SND_AUDIOCODEC_APE ;
2018-11-15 18:13:24 +00:00
return 0 ;
}
2020-04-20 16:08:44 +09:00
static int q6asm_dai_compr_get_codec_caps ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
2018-11-15 18:13:24 +00:00
struct snd_compr_codec_caps * codec )
{
switch ( codec - > codec ) {
case SND_AUDIOCODEC_MP3 :
* codec = q6asm_compr_caps ;
break ;
default :
break ;
}
return 0 ;
}
2020-04-20 16:08:44 +09:00
static struct snd_compress_ops q6asm_dai_compress_ops = {
2018-11-15 18:13:24 +00:00
. open = q6asm_dai_compr_open ,
. free = q6asm_dai_compr_free ,
. set_params = q6asm_dai_compr_set_params ,
. pointer = q6asm_dai_compr_pointer ,
. trigger = q6asm_dai_compr_trigger ,
. get_caps = q6asm_dai_compr_get_caps ,
. get_codec_caps = q6asm_dai_compr_get_codec_caps ,
. mmap = q6asm_dai_compr_mmap ,
. ack = q6asm_dai_compr_ack ,
} ;
2019-10-02 14:33:39 +09:00
static int q6asm_dai_pcm_new ( struct snd_soc_component * component ,
struct snd_soc_pcm_runtime * rtd )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_substream * psubstream , * csubstream ;
struct snd_pcm * pcm = rtd - > pcm ;
struct device * dev ;
int size , ret ;
2019-10-02 14:33:39 +09:00
dev = component - > dev ;
2018-05-18 13:56:08 +01:00
size = q6asm_dai_hardware_playback . buffer_bytes_max ;
psubstream = pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream ;
if ( psubstream ) {
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , dev , size ,
& psubstream - > dma_buffer ) ;
if ( ret ) {
dev_err ( dev , " Cannot allocate buffer(s) \n " ) ;
return ret ;
}
}
csubstream = pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream ;
if ( csubstream ) {
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , dev , size ,
& csubstream - > dma_buffer ) ;
if ( ret ) {
dev_err ( dev , " Cannot allocate buffer(s) \n " ) ;
if ( psubstream )
snd_dma_free_pages ( & psubstream - > dma_buffer ) ;
return ret ;
}
}
2018-09-10 11:40:48 +03:00
return 0 ;
2018-05-18 13:56:08 +01:00
}
2019-10-02 14:33:39 +09:00
static void q6asm_dai_pcm_free ( struct snd_soc_component * component ,
struct snd_pcm * pcm )
2018-05-18 13:56:08 +01:00
{
struct snd_pcm_substream * substream ;
int i ;
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 ;
}
}
}
static const struct snd_soc_component_driver q6asm_fe_dai_component = {
. name = DRV_NAME ,
2019-10-02 14:33:39 +09:00
. open = q6asm_dai_open ,
. hw_params = q6asm_dai_hw_params ,
. close = q6asm_dai_close ,
. prepare = q6asm_dai_prepare ,
. trigger = q6asm_dai_trigger ,
. pointer = q6asm_dai_pointer ,
. mmap = q6asm_dai_mmap ,
. pcm_construct = q6asm_dai_pcm_new ,
. pcm_destruct = q6asm_dai_pcm_free ,
2020-04-20 16:08:44 +09:00
. compress_ops = & q6asm_dai_compress_ops ,
2018-05-18 13:56:08 +01:00
} ;
2020-03-11 18:04:21 +00:00
static struct snd_soc_dai_driver q6asm_fe_dais_template [ ] = {
2018-05-18 13:56:08 +01:00
Q6ASM_FEDAI_DRIVER ( 1 ) ,
Q6ASM_FEDAI_DRIVER ( 2 ) ,
Q6ASM_FEDAI_DRIVER ( 3 ) ,
Q6ASM_FEDAI_DRIVER ( 4 ) ,
Q6ASM_FEDAI_DRIVER ( 5 ) ,
Q6ASM_FEDAI_DRIVER ( 6 ) ,
Q6ASM_FEDAI_DRIVER ( 7 ) ,
Q6ASM_FEDAI_DRIVER ( 8 ) ,
} ;
2018-11-15 18:13:24 +00:00
static int of_q6asm_parse_dai_data ( struct device * dev ,
struct q6asm_dai_data * pdata )
{
2020-03-11 18:04:21 +00:00
struct snd_soc_dai_driver * dai_drv ;
2018-11-15 18:13:24 +00:00
struct snd_soc_pcm_stream empty_stream ;
struct device_node * node ;
2020-03-11 18:04:21 +00:00
int ret , id , dir , idx = 0 ;
pdata - > num_dais = of_get_child_count ( dev - > of_node ) ;
if ( ! pdata - > num_dais ) {
dev_err ( dev , " No dais found in DT \n " ) ;
return - EINVAL ;
}
pdata - > dais = devm_kcalloc ( dev , pdata - > num_dais , sizeof ( * dai_drv ) ,
GFP_KERNEL ) ;
if ( ! pdata - > dais )
return - ENOMEM ;
2018-11-15 18:13:24 +00:00
memset ( & empty_stream , 0 , sizeof ( empty_stream ) ) ;
for_each_child_of_node ( dev - > of_node , node ) {
ret = of_property_read_u32 ( node , " reg " , & id ) ;
2018-12-21 12:04:42 +03:00
if ( ret | | id > = MAX_SESSIONS | | id < 0 ) {
2018-11-15 18:13:24 +00:00
dev_err ( dev , " valid dai id not found:%d \n " , ret ) ;
continue ;
}
2020-03-11 18:04:21 +00:00
dai_drv = & pdata - > dais [ idx + + ] ;
* dai_drv = q6asm_fe_dais_template [ id ] ;
2018-11-15 18:13:24 +00:00
ret = of_property_read_u32 ( node , " direction " , & dir ) ;
if ( ret )
continue ;
if ( dir = = Q6ASM_DAI_RX )
dai_drv - > capture = empty_stream ;
else if ( dir = = Q6ASM_DAI_TX )
dai_drv - > playback = empty_stream ;
if ( of_property_read_bool ( node , " is-compress-dai " ) )
dai_drv - > compress_new = snd_soc_new_compress ;
}
return 0 ;
}
2018-07-13 16:36:30 +01:00
static int q6asm_dai_probe ( struct platform_device * pdev )
2018-05-18 13:56:08 +01:00
{
2018-07-13 16:36:30 +01:00
struct device * dev = & pdev - > dev ;
2018-05-18 13:56:08 +01:00
struct device_node * node = dev - > of_node ;
struct of_phandle_args args ;
struct q6asm_dai_data * pdata ;
int rc ;
2018-07-13 16:36:30 +01:00
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
2018-05-18 13:56:08 +01:00
if ( ! pdata )
return - ENOMEM ;
rc = of_parse_phandle_with_fixed_args ( node , " iommus " , 1 , 0 , & args ) ;
if ( rc < 0 )
pdata - > sid = - 1 ;
else
pdata - > sid = args . args [ 0 ] & SID_MASK_DEFAULT ;
dev_set_drvdata ( dev , pdata ) ;
2020-03-11 18:04:21 +00:00
rc = of_q6asm_parse_dai_data ( dev , pdata ) ;
if ( rc )
return rc ;
2018-11-15 18:13:24 +00:00
2018-07-13 16:36:30 +01:00
return devm_snd_soc_register_component ( dev , & q6asm_fe_dai_component ,
2020-03-11 18:04:21 +00:00
pdata - > dais , pdata - > num_dais ) ;
2018-05-18 13:56:08 +01:00
}
2018-06-26 10:20:12 +01:00
static const struct of_device_id q6asm_dai_device_id [ ] = {
{ . compatible = " qcom,q6asm-dais " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , q6asm_dai_device_id ) ;
2018-05-18 13:56:08 +01:00
static struct platform_driver q6asm_dai_platform_driver = {
. driver = {
. name = " q6asm-dai " ,
2018-06-26 10:20:12 +01:00
. of_match_table = of_match_ptr ( q6asm_dai_device_id ) ,
2018-05-18 13:56:08 +01:00
} ,
. probe = q6asm_dai_probe ,
} ;
module_platform_driver ( q6asm_dai_platform_driver ) ;
MODULE_DESCRIPTION ( " Q6ASM dai driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;