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 ;
2020-07-27 10:38:04 +01:00
struct snd_codec codec ;
2018-11-15 18:13:24 +00:00
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 ;
2020-07-27 10:38:05 +01:00
uint32_t next_track_stream_id ;
bool next_track ;
2020-07-27 10:37:58 +01:00
uint32_t stream_id ;
2018-05-18 13:56:08 +01:00
uint16_t session_id ;
enum stream_state state ;
2020-07-27 10:38:03 +01:00
uint32_t initial_samples_drop ;
uint32_t trailing_samples_drop ;
2020-07-27 10:38:05 +01:00
bool notify_on_drain ;
2018-05-18 13:56:08 +01:00
} ;
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 )
2020-07-27 10:37:58 +01:00
q6asm_write_async ( prtd - > audio_client , prtd - > stream_id ,
2020-07-27 10:37:59 +01:00
prtd - > pcm_count , 0 , 0 , 0 ) ;
2018-05-18 13:56:08 +01:00
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 )
2020-07-27 10:37:58 +01:00
q6asm_write_async ( prtd - > audio_client , prtd - > stream_id ,
2020-07-27 10:37:59 +01:00
prtd - > pcm_count , 0 , 0 , 0 ) ;
2018-05-18 13:56:08 +01:00
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 )
2020-07-27 10:37:58 +01:00
q6asm_read ( prtd - > audio_client , prtd - > stream_id ) ;
2018-05-18 13:56:08 +01:00
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 */
2020-07-27 10:37:58 +01:00
q6asm_cmd ( prtd - > audio_client , prtd - > stream_id , CMD_CLOSE ) ;
2018-05-18 13:56:08 +01:00
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 ) {
2020-07-27 10:37:58 +01:00
ret = q6asm_open_write ( prtd - > audio_client , prtd - > stream_id ,
FORMAT_LINEAR_PCM ,
2020-07-27 10:38:02 +01:00
0 , prtd - > bits_per_sample , false ) ;
2018-05-18 13:56:08 +01:00
} else if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
2020-07-27 10:37:58 +01:00
ret = q6asm_open_read ( prtd - > audio_client , prtd - > stream_id ,
FORMAT_LINEAR_PCM ,
prtd - > bits_per_sample ) ;
2018-05-18 13:56:08 +01:00
}
if ( ret < 0 ) {
2020-07-07 17:36:39 +01:00
dev_err ( dev , " %s: q6asm_open_write failed \n " , __func__ ) ;
2021-11-16 11:47:19 +00:00
goto open_err ;
2018-05-18 13:56:08 +01:00
}
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 ) ;
2021-11-16 11:47:19 +00:00
goto routing_err ;
2018-05-18 13:56:08 +01:00
}
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
ret = q6asm_media_format_block_multi_ch_pcm (
2020-07-27 10:37:58 +01:00
prtd - > audio_client , prtd - > stream_id ,
runtime - > rate , runtime - > channels , NULL ,
2018-05-18 13:56:08 +01:00
prtd - > bits_per_sample ) ;
} else if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) {
ret = q6asm_enc_cfg_blk_pcm_format_support ( prtd - > audio_client ,
2020-07-27 10:37:58 +01:00
prtd - > stream_id ,
runtime - > rate ,
runtime - > channels ,
prtd - > bits_per_sample ) ;
2018-05-18 13:56:08 +01:00
/* Queue the buffers */
for ( i = 0 ; i < runtime - > periods ; i + + )
2020-07-27 10:37:58 +01:00
q6asm_read ( prtd - > audio_client , prtd - > stream_id ) ;
2018-05-18 13:56:08 +01:00
}
if ( ret < 0 )
2020-07-07 17:36:39 +01:00
dev_info ( dev , " %s: CMD Format block failed \n " , __func__ ) ;
2021-11-16 11:47:19 +00:00
else
prtd - > state = Q6ASM_STREAM_RUNNING ;
return ret ;
2018-05-18 13:56:08 +01:00
2021-11-16 11:47:19 +00:00
routing_err :
q6asm_cmd ( prtd - > audio_client , prtd - > stream_id , CMD_CLOSE ) ;
open_err :
q6asm_unmap_memory_regions ( substream - > stream , prtd - > audio_client ) ;
q6asm_audio_client_free ( prtd - > audio_client ) ;
prtd - > audio_client = NULL ;
2018-05-18 13:56:08 +01:00
2021-11-16 11:47:19 +00:00
return ret ;
2018-05-18 13:56:08 +01:00
}
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 :
2020-07-27 10:37:58 +01:00
ret = q6asm_run_nowait ( prtd - > audio_client , prtd - > stream_id ,
0 , 0 , 0 ) ;
2018-05-18 13:56:08 +01:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
prtd - > state = Q6ASM_STREAM_STOPPED ;
2020-07-27 10:37:58 +01:00
ret = q6asm_cmd_nowait ( prtd - > audio_client , prtd - > stream_id ,
CMD_EOS ) ;
2018-05-18 13:56:08 +01:00
break ;
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2020-07-27 10:37:58 +01:00
ret = q6asm_cmd_nowait ( prtd - > audio_client , prtd - > stream_id ,
CMD_PAUSE ) ;
2018-05-18 13:56:08 +01:00
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
}
2020-07-27 10:37:58 +01:00
/* DSP expects stream id from 1 */
prtd - > stream_id = 1 ;
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 ) ;
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 )
2020-07-27 10:37:58 +01:00
q6asm_cmd ( prtd - > audio_client , prtd - > stream_id ,
CMD_CLOSE ) ;
2018-07-04 10:49:44 +01:00
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_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 ;
2020-07-27 10:38:05 +01:00
u32 wflags = 0 ;
2018-11-15 18:13:24 +00:00
uint64_t avail ;
2020-07-27 10:38:05 +01:00
uint32_t bytes_written , bytes_to_write ;
bool is_last_buffer = false ;
2018-11-15 18:13:24 +00:00
switch ( opcode ) {
case ASM_CLIENT_EVENT_CMD_RUN_DONE :
spin_lock_irqsave ( & prtd - > lock , flags ) ;
if ( ! prtd - > bytes_sent ) {
2020-07-27 10:38:05 +01:00
q6asm_stream_remove_initial_silence ( prtd - > audio_client ,
prtd - > stream_id ,
prtd - > initial_samples_drop ) ;
2020-07-27 10:37:58 +01:00
q6asm_write_async ( prtd - > audio_client , prtd - > stream_id ,
2020-07-27 10:37:59 +01:00
prtd - > pcm_count , 0 , 0 , 0 ) ;
2018-11-15 18:13:24 +00:00
prtd - > bytes_sent + = prtd - > pcm_count ;
}
spin_unlock_irqrestore ( & prtd - > lock , flags ) ;
break ;
case ASM_CLIENT_EVENT_CMD_EOS_DONE :
2020-07-27 10:38:05 +01:00
spin_lock_irqsave ( & prtd - > lock , flags ) ;
if ( prtd - > notify_on_drain ) {
if ( substream - > partial_drain ) {
/*
* Close old stream and make it stale , switch
* the active stream now !
*/
q6asm_cmd_nowait ( prtd - > audio_client ,
prtd - > stream_id ,
CMD_CLOSE ) ;
/*
* vaild stream ids start from 1 , So we are
* toggling this between 1 and 2.
*/
prtd - > stream_id = ( prtd - > stream_id = = 1 ? 2 : 1 ) ;
}
snd_compr_drain_notify ( prtd - > cstream ) ;
prtd - > notify_on_drain = false ;
} else {
prtd - > state = Q6ASM_STREAM_STOPPED ;
}
spin_unlock_irqrestore ( & prtd - > lock , flags ) ;
2018-11-15 18:13:24 +00:00
break ;
case ASM_CLIENT_EVENT_DATA_WRITE_DONE :
spin_lock_irqsave ( & prtd - > lock , flags ) ;
2020-07-27 10:38:00 +01:00
bytes_written = token > > ASM_WRITE_TOKEN_LEN_SHIFT ;
prtd - > copied_total + = bytes_written ;
2018-11-15 18:13:24 +00:00
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 ;
2020-07-27 10:38:05 +01:00
if ( avail > prtd - > pcm_count ) {
bytes_to_write = prtd - > pcm_count ;
} else {
if ( substream - > partial_drain | | prtd - > notify_on_drain )
is_last_buffer = true ;
bytes_to_write = avail ;
}
if ( bytes_to_write ) {
if ( substream - > partial_drain & & is_last_buffer ) {
wflags | = ASM_LAST_BUFFER_FLAG ;
q6asm_stream_remove_trailing_silence ( prtd - > audio_client ,
prtd - > stream_id ,
prtd - > trailing_samples_drop ) ;
}
2018-11-15 18:13:24 +00:00
2020-07-27 10:37:58 +01:00
q6asm_write_async ( prtd - > audio_client , prtd - > stream_id ,
2020-07-27 10:38:05 +01:00
bytes_to_write , 0 , 0 , wflags ) ;
prtd - > bytes_sent + = bytes_to_write ;
2018-11-15 18:13:24 +00:00
}
2020-07-27 10:38:05 +01:00
if ( prtd - > notify_on_drain & & is_last_buffer )
q6asm_cmd_nowait ( prtd - > audio_client ,
prtd - > stream_id , CMD_EOS ) ;
2018-11-15 18:13:24 +00:00
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 ;
2020-07-27 10:37:58 +01:00
/* DSP expects stream id from 1 */
prtd - > stream_id = 1 ;
2018-11-15 18:13:24 +00:00
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 ) {
2020-07-27 10:38:05 +01:00
if ( prtd - > state ) {
2020-07-27 10:37:58 +01:00
q6asm_cmd ( prtd - > audio_client , prtd - > stream_id ,
CMD_CLOSE ) ;
2020-07-27 10:38:05 +01:00
if ( prtd - > next_track_stream_id ) {
q6asm_cmd ( prtd - > audio_client ,
prtd - > next_track_stream_id ,
CMD_CLOSE ) ;
}
}
2018-11-15 18:13:24 +00:00
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-07-27 10:38:04 +01:00
static int __q6asm_dai_compr_set_codec_params ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
struct snd_codec * codec ,
int stream_id )
2018-11-15 18:13:24 +00:00
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
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
2020-07-27 10:38:04 +01:00
codec_options = & ( prtd - > codec . options ) ;
2018-11-15 18:13:24 +00:00
2020-07-27 10:38:04 +01:00
memcpy ( & prtd - > codec , codec , sizeof ( * codec ) ) ;
2018-11-15 18:13:24 +00:00
2020-07-27 10:38:04 +01:00
switch ( codec - > id ) {
2019-11-15 15:57:05 +05:30
case SND_AUDIOCODEC_FLAC :
memset ( & flac_cfg , 0x0 , sizeof ( struct q6asm_flac_cfg ) ) ;
flac = & codec_options - > flac_d ;
2020-07-27 10:38:04 +01:00
flac_cfg . ch_cfg = codec - > ch_in ;
flac_cfg . sample_rate = codec - > sample_rate ;
2019-11-15 15:57:05 +05:30
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 ,
2020-07-27 10:38:04 +01:00
stream_id ,
2019-11-15 15:57:05 +05:30
& 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 ) ) ;
2020-07-27 10:38:04 +01:00
wma_cfg . sample_rate = codec - > sample_rate ;
wma_cfg . num_channels = codec - > ch_in ;
wma_cfg . bytes_per_sec = codec - > bit_rate / 8 ;
wma_cfg . block_align = codec - > align ;
2020-03-16 11:22:17 +05:30
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 */
2020-07-27 10:38:04 +01:00
switch ( codec - > profile ) {
2020-03-16 11:22:17 +05:30
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 " ,
2020-07-27 10:38:04 +01:00
codec - > profile ) ;
2020-03-16 11:22:17 +05:30
return - EIO ;
}
if ( wma_v9 )
ret = q6asm_stream_media_format_block_wma_v9 (
2020-07-27 10:38:04 +01:00
prtd - > audio_client , stream_id ,
2020-07-27 10:37:58 +01:00
& wma_cfg ) ;
2020-03-16 11:22:17 +05:30
else
ret = q6asm_stream_media_format_block_wma_v10 (
2020-07-27 10:38:04 +01:00
prtd - > audio_client , stream_id ,
2020-07-27 10:37:58 +01:00
& wma_cfg ) ;
2020-03-16 11:22:17 +05:30
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 ;
2020-07-27 10:38:04 +01:00
alac_cfg . sample_rate = codec - > sample_rate ;
alac_cfg . avg_bit_rate = codec - > bit_rate ;
2020-03-16 11:22:20 +05:30
alac_cfg . bit_depth = prtd - > bits_per_sample ;
2020-07-27 10:38:04 +01:00
alac_cfg . num_channels = codec - > ch_in ;
2020-03-16 11:22:20 +05:30
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 ;
2020-07-27 10:38:04 +01:00
switch ( codec - > ch_in ) {
2020-03-16 11:22:20 +05:30
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 ,
2020-07-27 10:38:04 +01:00
stream_id ,
2020-03-16 11:22:20 +05:30
& 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 ;
2020-07-27 10:38:04 +01:00
ape_cfg . sample_rate = codec - > sample_rate ;
ape_cfg . num_channels = codec - > ch_in ;
2020-03-16 11:22:20 +05:30
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 ,
2020-07-27 10:38:04 +01:00
stream_id ,
2020-03-16 11:22:20 +05:30
& 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 ;
}
2020-07-27 10:38:04 +01:00
return 0 ;
}
static int q6asm_dai_compr_set_params ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
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 ;
struct device * dev = component - > dev ;
int ret ;
pdata = snd_soc_component_get_drvdata ( component ) ;
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 , prtd - > stream_id , params - > codec . id ,
params - > codec . profile , prtd - > bits_per_sample ,
true ) ;
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 ;
}
ret = __q6asm_dai_compr_set_codec_params ( component , stream ,
& params - > codec ,
prtd - > stream_id ) ;
if ( ret ) {
dev_err ( dev , " codec param setup failed ret:%d \n " , ret ) ;
return ret ;
}
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-07-27 10:38:03 +01:00
static int q6asm_dai_compr_set_metadata ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
struct snd_compr_metadata * metadata )
{
struct snd_compr_runtime * runtime = stream - > runtime ;
struct q6asm_dai_rtd * prtd = runtime - > private_data ;
int ret = 0 ;
switch ( metadata - > key ) {
case SNDRV_COMPRESS_ENCODER_PADDING :
prtd - > trailing_samples_drop = metadata - > value [ 0 ] ;
break ;
case SNDRV_COMPRESS_ENCODER_DELAY :
prtd - > initial_samples_drop = metadata - > value [ 0 ] ;
2020-07-27 10:38:05 +01:00
if ( prtd - > next_track_stream_id ) {
ret = q6asm_open_write ( prtd - > audio_client ,
prtd - > next_track_stream_id ,
prtd - > codec . id ,
prtd - > codec . profile ,
prtd - > bits_per_sample ,
true ) ;
if ( ret < 0 ) {
dev_err ( component - > dev , " q6asm_open_write failed \n " ) ;
return ret ;
}
ret = __q6asm_dai_compr_set_codec_params ( component , stream ,
& prtd - > codec ,
prtd - > next_track_stream_id ) ;
if ( ret < 0 ) {
dev_err ( component - > dev , " q6asm_open_write failed \n " ) ;
return ret ;
}
ret = q6asm_stream_remove_initial_silence ( prtd - > audio_client ,
prtd - > next_track_stream_id ,
prtd - > initial_samples_drop ) ;
prtd - > next_track_stream_id = 0 ;
}
2020-07-27 10:38:03 +01:00
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
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 :
2020-07-27 10:37:58 +01:00
ret = q6asm_run_nowait ( prtd - > audio_client , prtd - > stream_id ,
0 , 0 , 0 ) ;
2018-11-15 18:13:24 +00:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
prtd - > state = Q6ASM_STREAM_STOPPED ;
2020-07-27 10:37:58 +01:00
ret = q6asm_cmd_nowait ( prtd - > audio_client , prtd - > stream_id ,
CMD_EOS ) ;
2018-11-15 18:13:24 +00:00
break ;
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2020-07-27 10:37:58 +01:00
ret = q6asm_cmd_nowait ( prtd - > audio_client , prtd - > stream_id ,
CMD_PAUSE ) ;
2018-11-15 18:13:24 +00:00
break ;
2020-07-27 10:38:05 +01:00
case SND_COMPR_TRIGGER_NEXT_TRACK :
prtd - > next_track = true ;
prtd - > next_track_stream_id = ( prtd - > stream_id = = 1 ? 2 : 1 ) ;
break ;
case SND_COMPR_TRIGGER_DRAIN :
case SND_COMPR_TRIGGER_PARTIAL_DRAIN :
prtd - > notify_on_drain = true ;
break ;
2018-11-15 18:13:24 +00:00
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-07-27 10:38:06 +01:00
static int q6asm_compr_copy ( struct snd_soc_component * component ,
struct snd_compr_stream * stream , char __user * buf ,
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 ;
2020-07-27 10:38:06 +01:00
u32 wflags = 0 ;
int avail , bytes_in_flight = 0 ;
void * dstn ;
size_t copy ;
u32 app_pointer ;
u32 bytes_received ;
bytes_received = prtd - > bytes_received ;
/**
* Make sure that next track data pointer is aligned at 32 bit boundary
* This is a Mandatory requirement from DSP data buffers alignment
*/
if ( prtd - > next_track )
bytes_received = ALIGN ( prtd - > bytes_received , prtd - > pcm_count ) ;
app_pointer = bytes_received / prtd - > pcm_size ;
app_pointer = bytes_received - ( app_pointer * prtd - > pcm_size ) ;
dstn = prtd - > dma_buffer . area + app_pointer ;
if ( count < prtd - > pcm_size - app_pointer ) {
if ( copy_from_user ( dstn , buf , count ) )
return - EFAULT ;
} else {
copy = prtd - > pcm_size - app_pointer ;
if ( copy_from_user ( dstn , buf , copy ) )
return - EFAULT ;
if ( copy_from_user ( prtd - > dma_buffer . area , buf + copy ,
count - copy ) )
return - EFAULT ;
}
2018-11-15 18:13:24 +00:00
spin_lock_irqsave ( & prtd - > lock , flags ) ;
2020-07-27 10:38:06 +01:00
bytes_in_flight = prtd - > bytes_received - prtd - > copied_total ;
if ( prtd - > next_track ) {
prtd - > next_track = false ;
prtd - > copied_total = ALIGN ( prtd - > copied_total , prtd - > pcm_count ) ;
prtd - > bytes_sent = ALIGN ( prtd - > bytes_sent , prtd - > pcm_count ) ;
}
prtd - > bytes_received = bytes_received + count ;
/* Kick off the data to dsp if its starving!! */
if ( prtd - > state = = Q6ASM_STREAM_RUNNING & & ( bytes_in_flight = = 0 ) ) {
uint32_t bytes_to_write = prtd - > pcm_count ;
avail = prtd - > bytes_received - prtd - > bytes_sent ;
if ( avail < prtd - > pcm_count )
bytes_to_write = avail ;
q6asm_write_async ( prtd - > audio_client , prtd - > stream_id ,
bytes_to_write , 0 , 0 , wflags ) ;
prtd - > bytes_sent + = bytes_to_write ;
}
2018-11-15 18:13:24 +00:00
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 ;
}
2021-05-27 01:10:12 +02:00
static const 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 ,
2020-07-27 10:38:03 +01:00
. set_metadata = q6asm_dai_compr_set_metadata ,
2018-11-15 18:13:24 +00:00
. 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 ,
2020-07-27 10:38:06 +01:00
. copy = q6asm_compr_copy ,
2018-11-15 18:13:24 +00:00
} ;
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 * pcm = rtd - > pcm ;
2021-08-02 09:28:14 +02:00
size_t size = q6asm_dai_hardware_playback . buffer_bytes_max ;
2018-05-18 13:56:08 +01:00
2021-08-02 09:28:14 +02:00
return snd_pcm_set_fixed_buffer_all ( pcm , SNDRV_DMA_TYPE_DEV ,
component - > dev , size ) ;
2018-05-18 13:56:08 +01:00
}
ASoC: qcom: qdsp6: Move frontend AIFs to q6asm-dai
At the moment it is necessary to set up the DAPM routes between
front-end AIF<->DAI explicitly in the device tree, e.g. using
audio-routing =
"MM_DL1", "MultiMedia1 Playback",
"MM_DL3", "MultiMedia3 Playback",
"MM_DL4", "MultiMedia4 Playback",
"MultiMedia2 Capture", "MM_UL2";
This is prone to mistakes and (sadly) there is no clear error if one
of these routes is missing. :(
Actually, this should not be necessary because the ASoC core normally
automatically links AIF<->DAI within snd_soc_dapm_link_dai_widgets().
This is done using the "stname" parameter of SND_SOC_DAPM_AIF_IN/OUT.
For SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
it should create the route from above: MM_DL1 <-> MultiMedia1 Playback.
This does not work at the moment because the AIF widget (MM_DL1)
and the DAI widget (MultiMedia1 Playback) belong to different
DAPM contexts (q6routing / q6asm-dai).
Fix this by declaring the AIF widgets in the same driver as the DAIs
(q6asm-dai). Now the routes above are created automatically
and no longer need to be specified in the device tree.
This is also more consistent with the back-end AIFs which are already
declared in q6afe-dais instead of q6routing. q6routing should only link
the components together using mixers.
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Fixes: 2a9e92d371db ("ASoC: qdsp6: q6asm: Add q6asm dai driver")
Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
Link: https://lore.kernel.org/r/20201211203255.148246-1-stephan@gerhold.net
Signed-off-by: Mark Brown <broonie@kernel.org>
2020-12-11 21:32:55 +01:00
static const struct snd_soc_dapm_widget q6asm_dapm_widgets [ ] = {
SND_SOC_DAPM_AIF_IN ( " MM_DL1 " , " MultiMedia1 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " MM_DL2 " , " MultiMedia2 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " MM_DL3 " , " MultiMedia3 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " MM_DL4 " , " MultiMedia4 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " MM_DL5 " , " MultiMedia5 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " MM_DL6 " , " MultiMedia6 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " MM_DL7 " , " MultiMedia7 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " MM_DL8 " , " MultiMedia8 Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL1 " , " MultiMedia1 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL2 " , " MultiMedia2 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL3 " , " MultiMedia3 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL4 " , " MultiMedia4 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL5 " , " MultiMedia5 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL6 " , " MultiMedia6 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL7 " , " MultiMedia7 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " MM_UL8 " , " MultiMedia8 Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
} ;
2018-05-18 13:56:08 +01:00
static const struct snd_soc_component_driver q6asm_fe_dai_component = {
2022-06-23 13:51:38 +01:00
. name = DRV_NAME ,
. 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 ,
. pcm_construct = q6asm_dai_pcm_new ,
. compress_ops = & q6asm_dai_compress_ops ,
. dapm_widgets = q6asm_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( q6asm_dapm_widgets ) ,
. legacy_dai_naming = 1 ,
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
}
2020-09-25 17:35:51 +01:00
# ifdef CONFIG_OF
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 ) ;
2020-09-25 17:35:51 +01:00
# endif
2018-06-26 10:20:12 +01:00
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 " ) ;