2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-10-11 06:54:02 +04:00
/*
* wm_adsp . c - - Wolfson ADSP support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . com >
*/
2018-08-08 19:13:39 +03:00
# include <linux/ctype.h>
2012-10-11 06:54:02 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/firmware.h>
2013-01-30 10:37:23 +04:00
# include <linux/list.h>
2012-10-11 06:54:02 +04:00
# include <linux/pm.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
2012-11-28 21:20:32 +04:00
# include <linux/regulator/consumer.h>
2012-10-11 06:54:02 +04:00
# include <linux/slab.h>
2022-11-23 19:58:11 +03:00
# include <linux/vmalloc.h>
2013-05-08 17:15:35 +04:00
# include <linux/workqueue.h>
2015-06-11 13:32:32 +03:00
# include <linux/debugfs.h>
2012-10-11 06:54:02 +04:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/jack.h>
# include <sound/initval.h>
# include <sound/tlv.h>
# include "wm_adsp.h"
# define adsp_crit(_dsp, fmt, ...) \
2021-09-13 19:00:55 +03:00
dev_crit ( _dsp - > cs_dsp . dev , " %s: " fmt , _dsp - > cs_dsp . name , # # __VA_ARGS__ )
2012-10-11 06:54:02 +04:00
# define adsp_err(_dsp, fmt, ...) \
2021-09-13 19:00:55 +03:00
dev_err ( _dsp - > cs_dsp . dev , " %s: " fmt , _dsp - > cs_dsp . name , # # __VA_ARGS__ )
2012-10-11 06:54:02 +04:00
# define adsp_warn(_dsp, fmt, ...) \
2021-09-13 19:00:55 +03:00
dev_warn ( _dsp - > cs_dsp . dev , " %s: " fmt , _dsp - > cs_dsp . name , # # __VA_ARGS__ )
2012-10-11 06:54:02 +04:00
# define adsp_info(_dsp, fmt, ...) \
2021-09-13 19:00:55 +03:00
dev_info ( _dsp - > cs_dsp . dev , " %s: " fmt , _dsp - > cs_dsp . name , # # __VA_ARGS__ )
2012-10-11 06:54:02 +04:00
# define adsp_dbg(_dsp, fmt, ...) \
2021-09-13 19:00:55 +03:00
dev_dbg ( _dsp - > cs_dsp . dev , " %s: " fmt , _dsp - > cs_dsp . name , # # __VA_ARGS__ )
2012-10-11 06:54:02 +04:00
2019-02-22 13:04:21 +03:00
# define compr_err(_obj, fmt, ...) \
adsp_err ( _obj - > dsp , " %s: " fmt , _obj - > name ? _obj - > name : " legacy " , \
# #__VA_ARGS__)
# define compr_dbg(_obj, fmt, ...) \
adsp_dbg ( _obj - > dsp , " %s: " fmt , _obj - > name ? _obj - > name : " legacy " , \
# #__VA_ARGS__)
2016-05-02 15:57:36 +03:00
# define ADSP_MAX_STD_CTRL_SIZE 512
2021-09-13 19:00:56 +03:00
static const struct cs_dsp_client_ops wm_adsp1_client_ops ;
static const struct cs_dsp_client_ops wm_adsp2_client_ops ;
2015-11-26 17:01:52 +03:00
# define WM_ADSP_FW_MBC_VSS 0
# define WM_ADSP_FW_HIFI 1
# define WM_ADSP_FW_TX 2
# define WM_ADSP_FW_TX_SPK 3
# define WM_ADSP_FW_RX 4
# define WM_ADSP_FW_RX_ANC 5
# define WM_ADSP_FW_CTRL 6
# define WM_ADSP_FW_ASR 7
# define WM_ADSP_FW_TRACE 8
# define WM_ADSP_FW_SPK_PROT 9
2020-06-20 00:26:51 +03:00
# define WM_ADSP_FW_SPK_CALI 10
# define WM_ADSP_FW_SPK_DIAG 11
# define WM_ADSP_FW_MISC 12
2015-11-26 17:01:52 +03:00
2020-06-20 00:26:51 +03:00
# define WM_ADSP_NUM_FW 13
2013-03-08 11:25:58 +04:00
2013-01-12 02:58:28 +04:00
static const char * wm_adsp_fw_text [ WM_ADSP_NUM_FW ] = {
2015-11-26 17:01:52 +03:00
[ WM_ADSP_FW_MBC_VSS ] = " MBC/VSS " ,
[ WM_ADSP_FW_HIFI ] = " MasterHiFi " ,
[ WM_ADSP_FW_TX ] = " Tx " ,
[ WM_ADSP_FW_TX_SPK ] = " Tx Speaker " ,
[ WM_ADSP_FW_RX ] = " Rx " ,
[ WM_ADSP_FW_RX_ANC ] = " Rx ANC " ,
[ WM_ADSP_FW_CTRL ] = " Voice Ctrl " ,
[ WM_ADSP_FW_ASR ] = " ASR Assist " ,
[ WM_ADSP_FW_TRACE ] = " Dbg Trace " ,
[ WM_ADSP_FW_SPK_PROT ] = " Protection " ,
2020-06-20 00:26:51 +03:00
[ WM_ADSP_FW_SPK_CALI ] = " Calibration " ,
[ WM_ADSP_FW_SPK_DIAG ] = " Diagnostic " ,
2015-11-26 17:01:52 +03:00
[ WM_ADSP_FW_MISC ] = " Misc " ,
2013-01-12 02:58:28 +04:00
} ;
2015-12-15 14:29:46 +03:00
struct wm_adsp_system_config_xm_hdr {
__be32 sys_enable ;
__be32 fw_id ;
__be32 fw_rev ;
__be32 boot_status ;
__be32 watchdog ;
__be32 dma_buffer_size ;
__be32 rdma [ 6 ] ;
__be32 wdma [ 8 ] ;
__be32 build_job_name [ 3 ] ;
__be32 build_job_number ;
2022-03-09 16:00:17 +03:00
} __packed ;
2015-12-15 14:29:46 +03:00
2019-03-19 14:52:13 +03:00
struct wm_halo_system_config_xm_hdr {
__be32 halo_heartbeat ;
__be32 build_job_name [ 3 ] ;
__be32 build_job_number ;
2022-03-09 16:00:17 +03:00
} __packed ;
2019-03-19 14:52:13 +03:00
2015-12-15 14:29:46 +03:00
struct wm_adsp_alg_xm_struct {
__be32 magic ;
__be32 smoothing ;
__be32 threshold ;
__be32 host_buf_ptr ;
__be32 start_seq ;
__be32 high_water_mark ;
__be32 low_water_mark ;
__be64 smoothed_power ;
2022-03-09 16:00:17 +03:00
} __packed ;
2015-12-15 14:29:46 +03:00
2019-02-22 13:04:20 +03:00
struct wm_adsp_host_buf_coeff_v1 {
__be32 host_buf_ptr ; /* Host buffer pointer */
__be32 versions ; /* Version numbers */
__be32 name [ 4 ] ; /* The buffer name */
2022-03-09 16:00:17 +03:00
} __packed ;
2019-02-22 13:04:20 +03:00
2015-12-15 14:29:46 +03:00
struct wm_adsp_buffer {
2018-10-19 15:25:15 +03:00
__be32 buf1_base ; /* Base addr of first buffer area */
__be32 buf1_size ; /* Size of buf1 area in DSP words */
__be32 buf2_base ; /* Base addr of 2nd buffer area */
__be32 buf1_buf2_size ; /* Size of buf1+buf2 in DSP words */
__be32 buf3_base ; /* Base addr of buf3 area */
__be32 buf_total_size ; /* Size of buf1+buf2+buf3 in DSP words */
2015-12-15 14:29:46 +03:00
__be32 high_water_mark ; /* Point at which IRQ is asserted */
__be32 irq_count ; /* bits 1-31 count IRQ assertions */
__be32 irq_ack ; /* acked IRQ count, bit 0 enables IRQ */
__be32 next_write_index ; /* word index of next write */
__be32 next_read_index ; /* word index of next read */
__be32 error ; /* error if any */
__be32 oldest_block_index ; /* word index of oldest surviving */
__be32 requested_rewind ; /* how many blocks rewind was done */
__be32 reserved_space ; /* internal */
__be32 min_free ; /* min free space since stream start */
__be32 blocks_written [ 2 ] ; /* total blocks written (64 bit) */
__be32 words_written [ 2 ] ; /* total words written (64 bit) */
2022-03-09 16:00:17 +03:00
} __packed ;
2015-12-15 14:29:46 +03:00
2016-05-04 19:11:56 +03:00
struct wm_adsp_compr ;
2015-12-15 14:29:46 +03:00
struct wm_adsp_compr_buf {
2019-02-22 13:04:20 +03:00
struct list_head list ;
2015-12-15 14:29:46 +03:00
struct wm_adsp * dsp ;
2016-05-04 19:11:56 +03:00
struct wm_adsp_compr * compr ;
2015-12-15 14:29:46 +03:00
struct wm_adsp_buffer_region * regions ;
u32 host_buf_ptr ;
2016-01-06 15:33:18 +03:00
u32 error ;
u32 irq_count ;
int read_index ;
int avail ;
2019-02-19 20:31:56 +03:00
int host_buf_mem_type ;
2019-02-22 13:04:20 +03:00
char * name ;
2015-12-15 14:29:46 +03:00
} ;
2015-12-15 14:29:45 +03:00
struct wm_adsp_compr {
2019-02-22 13:04:20 +03:00
struct list_head list ;
2015-12-15 14:29:45 +03:00
struct wm_adsp * dsp ;
2015-12-15 14:29:47 +03:00
struct wm_adsp_compr_buf * buf ;
2015-12-15 14:29:45 +03:00
struct snd_compr_stream * stream ;
struct snd_compressed_buffer size ;
2016-01-06 15:33:18 +03:00
2016-01-06 15:33:19 +03:00
u32 * raw_buf ;
2016-01-06 15:33:18 +03:00
unsigned int copied_total ;
2016-02-02 19:41:36 +03:00
unsigned int sample_rate ;
2019-02-22 13:04:20 +03:00
const char * name ;
2015-12-15 14:29:45 +03:00
} ;
# define WM_ADSP_MIN_FRAGMENTS 1
# define WM_ADSP_MAX_FRAGMENTS 256
2022-02-10 20:20:53 +03:00
# define WM_ADSP_MIN_FRAGMENT_SIZE (16 * CS_DSP_DATA_WORD_SIZE)
2021-09-13 19:00:46 +03:00
# define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE)
2015-12-15 14:29:45 +03:00
2015-12-15 14:29:46 +03:00
# define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7
# define HOST_BUFFER_FIELD(field) \
( offsetof ( struct wm_adsp_buffer , field ) / sizeof ( __be32 ) )
# define ALG_XM_FIELD(field) \
( offsetof ( struct wm_adsp_alg_xm_struct , field ) / sizeof ( __be32 ) )
2019-02-22 13:04:20 +03:00
# define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1
# define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00
# define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8
2015-12-15 14:29:46 +03:00
static int wm_adsp_buffer_init ( struct wm_adsp * dsp ) ;
static int wm_adsp_buffer_free ( struct wm_adsp * dsp ) ;
struct wm_adsp_buffer_region {
unsigned int offset ;
unsigned int cumulative_size ;
unsigned int mem_type ;
unsigned int base_addr ;
} ;
struct wm_adsp_buffer_region_def {
unsigned int mem_type ;
unsigned int base_offset ;
unsigned int size_offset ;
} ;
2016-02-01 18:22:34 +03:00
static const struct wm_adsp_buffer_region_def default_regions [ ] = {
2015-12-15 14:29:46 +03:00
{
. mem_type = WMFW_ADSP2_XM ,
2018-10-19 15:25:15 +03:00
. base_offset = HOST_BUFFER_FIELD ( buf1_base ) ,
. size_offset = HOST_BUFFER_FIELD ( buf1_size ) ,
2015-12-15 14:29:46 +03:00
} ,
{
. mem_type = WMFW_ADSP2_XM ,
2018-10-19 15:25:15 +03:00
. base_offset = HOST_BUFFER_FIELD ( buf2_base ) ,
. size_offset = HOST_BUFFER_FIELD ( buf1_buf2_size ) ,
2015-12-15 14:29:46 +03:00
} ,
{
. mem_type = WMFW_ADSP2_YM ,
2018-10-19 15:25:15 +03:00
. base_offset = HOST_BUFFER_FIELD ( buf3_base ) ,
. size_offset = HOST_BUFFER_FIELD ( buf_total_size ) ,
2015-12-15 14:29:46 +03:00
} ,
} ;
2015-12-15 14:29:45 +03:00
struct wm_adsp_fw_caps {
u32 id ;
struct snd_codec_desc desc ;
2015-12-15 14:29:46 +03:00
int num_regions ;
2016-02-01 18:22:34 +03:00
const struct wm_adsp_buffer_region_def * region_defs ;
2015-12-15 14:29:45 +03:00
} ;
2016-01-21 20:52:58 +03:00
static const struct wm_adsp_fw_caps ctrl_caps [ ] = {
2015-12-15 14:29:45 +03:00
{
. id = SND_AUDIOCODEC_BESPOKE ,
. desc = {
2018-07-19 13:50:38 +03:00
. max_ch = 8 ,
2015-12-15 14:29:45 +03:00
. sample_rates = { 16000 } ,
. num_sample_rates = 1 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
2016-01-21 20:52:58 +03:00
. num_regions = ARRAY_SIZE ( default_regions ) ,
. region_defs = default_regions ,
2015-12-15 14:29:45 +03:00
} ,
} ;
2016-01-21 20:52:59 +03:00
static const struct wm_adsp_fw_caps trace_caps [ ] = {
{
. id = SND_AUDIOCODEC_BESPOKE ,
. desc = {
. max_ch = 8 ,
. sample_rates = {
4000 , 8000 , 11025 , 12000 , 16000 , 22050 ,
24000 , 32000 , 44100 , 48000 , 64000 , 88200 ,
96000 , 176400 , 192000
} ,
. num_sample_rates = 15 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
. num_regions = ARRAY_SIZE ( default_regions ) ,
. region_defs = default_regions ,
2015-12-15 14:29:45 +03:00
} ,
} ;
static const struct {
2013-01-12 02:58:28 +04:00
const char * file ;
2015-12-15 14:29:45 +03:00
int compr_direction ;
int num_caps ;
const struct wm_adsp_fw_caps * caps ;
2016-05-13 18:45:17 +03:00
bool voice_trigger ;
2013-01-12 02:58:28 +04:00
} wm_adsp_fw [ WM_ADSP_NUM_FW ] = {
2015-11-26 17:01:52 +03:00
[ WM_ADSP_FW_MBC_VSS ] = { . file = " mbc-vss " } ,
[ WM_ADSP_FW_HIFI ] = { . file = " hifi " } ,
[ WM_ADSP_FW_TX ] = { . file = " tx " } ,
[ WM_ADSP_FW_TX_SPK ] = { . file = " tx-spk " } ,
[ WM_ADSP_FW_RX ] = { . file = " rx " } ,
[ WM_ADSP_FW_RX_ANC ] = { . file = " rx-anc " } ,
2015-12-15 14:29:45 +03:00
[ WM_ADSP_FW_CTRL ] = {
. file = " ctrl " ,
. compr_direction = SND_COMPRESS_CAPTURE ,
2016-01-21 20:52:58 +03:00
. num_caps = ARRAY_SIZE ( ctrl_caps ) ,
. caps = ctrl_caps ,
2016-05-13 18:45:17 +03:00
. voice_trigger = true ,
2015-12-15 14:29:45 +03:00
} ,
2015-11-26 17:01:52 +03:00
[ WM_ADSP_FW_ASR ] = { . file = " asr " } ,
2016-01-21 20:52:59 +03:00
[ WM_ADSP_FW_TRACE ] = {
. file = " trace " ,
. compr_direction = SND_COMPRESS_CAPTURE ,
. num_caps = ARRAY_SIZE ( trace_caps ) ,
. caps = trace_caps ,
} ,
2022-02-10 20:20:53 +03:00
[ WM_ADSP_FW_SPK_PROT ] = {
. file = " spk-prot " ,
. compr_direction = SND_COMPRESS_CAPTURE ,
. num_caps = ARRAY_SIZE ( trace_caps ) ,
. caps = trace_caps ,
} ,
2020-06-20 00:26:51 +03:00
[ WM_ADSP_FW_SPK_CALI ] = { . file = " spk-cali " } ,
[ WM_ADSP_FW_SPK_DIAG ] = { . file = " spk-diag " } ,
2015-11-26 17:01:52 +03:00
[ WM_ADSP_FW_MISC ] = { . file = " misc " } ,
2013-01-12 02:58:28 +04:00
} ;
2013-05-08 17:15:35 +04:00
struct wm_coeff_ctl {
const char * name ;
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl ;
2016-05-02 15:57:36 +03:00
struct soc_bytes_ext bytes_ext ;
2021-09-13 19:00:45 +03:00
struct work_struct work ;
2013-05-08 17:15:35 +04:00
} ;
2018-08-08 19:13:38 +03:00
int wm_adsp_fw_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2013-01-12 02:58:28 +04:00
{
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
2013-01-12 02:58:28 +04:00
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
2018-02-13 05:03:12 +03:00
struct wm_adsp * dsp = snd_soc_component_get_drvdata ( component ) ;
2013-01-12 02:58:28 +04:00
2016-02-29 20:01:18 +03:00
ucontrol - > value . enumerated . item [ 0 ] = dsp [ e - > shift_l ] . fw ;
2013-01-12 02:58:28 +04:00
return 0 ;
}
2018-08-08 19:13:38 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp_fw_get ) ;
2013-01-12 02:58:28 +04:00
2018-08-08 19:13:38 +03:00
int wm_adsp_fw_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2013-01-12 02:58:28 +04:00
{
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
2013-01-12 02:58:28 +04:00
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
2018-02-13 05:03:12 +03:00
struct wm_adsp * dsp = snd_soc_component_get_drvdata ( component ) ;
2022-06-03 14:50:03 +03:00
int ret = 1 ;
2013-01-12 02:58:28 +04:00
2016-02-29 20:01:18 +03:00
if ( ucontrol - > value . enumerated . item [ 0 ] = = dsp [ e - > shift_l ] . fw )
2013-01-12 02:58:28 +04:00
return 0 ;
2016-02-29 20:01:18 +03:00
if ( ucontrol - > value . enumerated . item [ 0 ] > = WM_ADSP_NUM_FW )
2013-01-12 02:58:28 +04:00
return - EINVAL ;
2021-09-13 19:00:55 +03:00
mutex_lock ( & dsp [ e - > shift_l ] . cs_dsp . pwr_lock ) ;
2015-12-08 19:08:28 +03:00
2021-09-13 19:00:55 +03:00
if ( dsp [ e - > shift_l ] . cs_dsp . booted | | ! list_empty ( & dsp [ e - > shift_l ] . compr_list ) )
2015-12-08 19:08:28 +03:00
ret = - EBUSY ;
else
2016-02-29 20:01:18 +03:00
dsp [ e - > shift_l ] . fw = ucontrol - > value . enumerated . item [ 0 ] ;
2013-01-12 02:58:28 +04:00
2021-09-13 19:00:55 +03:00
mutex_unlock ( & dsp [ e - > shift_l ] . cs_dsp . pwr_lock ) ;
2013-01-12 02:58:28 +04:00
2015-12-08 19:08:28 +03:00
return ret ;
2013-01-12 02:58:28 +04:00
}
2018-08-08 19:13:38 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp_fw_put ) ;
2013-01-12 02:58:28 +04:00
2018-08-08 19:13:38 +03:00
const struct soc_enum wm_adsp_fw_enum [ ] = {
2013-01-12 02:58:28 +04:00
SOC_ENUM_SINGLE ( 0 , 0 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 1 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 2 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 3 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
2017-04-05 13:07:59 +03:00
SOC_ENUM_SINGLE ( 0 , 4 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 5 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 6 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
2013-01-12 02:58:28 +04:00
} ;
2018-08-08 19:13:38 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp_fw_enum ) ;
2012-10-11 06:54:02 +04:00
2016-05-02 15:57:36 +03:00
static inline struct wm_coeff_ctl * bytes_ext_to_ctl ( struct soc_bytes_ext * ext )
{
return container_of ( ext , struct wm_coeff_ctl , bytes_ext ) ;
}
2015-12-08 19:08:25 +03:00
static int wm_coeff_info ( struct snd_kcontrol * kctl ,
2013-05-08 17:15:35 +04:00
struct snd_ctl_elem_info * uinfo )
{
2016-05-02 15:57:36 +03:00
struct soc_bytes_ext * bytes_ext =
( struct soc_bytes_ext * ) kctl - > private_value ;
struct wm_coeff_ctl * ctl = bytes_ext_to_ctl ( bytes_ext ) ;
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl = ctl - > cs_ctl ;
2013-05-08 17:15:35 +04:00
2021-09-13 19:00:51 +03:00
switch ( cs_ctl - > type ) {
2016-11-09 20:14:18 +03:00
case WMFW_CTL_TYPE_ACKED :
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
2021-09-13 19:00:46 +03:00
uinfo - > value . integer . min = CS_DSP_ACKED_CTL_MIN_VALUE ;
uinfo - > value . integer . max = CS_DSP_ACKED_CTL_MAX_VALUE ;
2016-11-09 20:14:18 +03:00
uinfo - > value . integer . step = 1 ;
uinfo - > count = 1 ;
break ;
default :
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BYTES ;
2021-09-13 19:00:51 +03:00
uinfo - > count = cs_ctl - > len ;
2016-11-09 20:14:18 +03:00
break ;
}
2013-05-08 17:15:35 +04:00
return 0 ;
}
2015-12-08 19:08:25 +03:00
static int wm_coeff_put ( struct snd_kcontrol * kctl ,
2013-05-08 17:15:35 +04:00
struct snd_ctl_elem_value * ucontrol )
{
2016-05-02 15:57:36 +03:00
struct soc_bytes_ext * bytes_ext =
( struct soc_bytes_ext * ) kctl - > private_value ;
struct wm_coeff_ctl * ctl = bytes_ext_to_ctl ( bytes_ext ) ;
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl = ctl - > cs_ctl ;
2013-05-08 17:15:35 +04:00
char * p = ucontrol - > value . bytes . data ;
2015-12-08 19:08:27 +03:00
int ret = 0 ;
2021-09-13 19:00:51 +03:00
mutex_lock ( & cs_ctl - > dsp - > pwr_lock ) ;
2021-11-17 16:22:59 +03:00
ret = cs_dsp_coeff_write_ctrl ( cs_ctl , 0 , p , cs_ctl - > len ) ;
2021-09-13 19:00:51 +03:00
mutex_unlock ( & cs_ctl - > dsp - > pwr_lock ) ;
2015-12-08 19:08:27 +03:00
return ret ;
2013-05-08 17:15:35 +04:00
}
2016-05-02 15:57:36 +03:00
static int wm_coeff_tlv_put ( struct snd_kcontrol * kctl ,
const unsigned int __user * bytes , unsigned int size )
{
struct soc_bytes_ext * bytes_ext =
( struct soc_bytes_ext * ) kctl - > private_value ;
struct wm_coeff_ctl * ctl = bytes_ext_to_ctl ( bytes_ext ) ;
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl = ctl - > cs_ctl ;
2022-11-23 19:58:11 +03:00
void * scratch ;
2016-05-02 15:57:36 +03:00
int ret = 0 ;
2022-11-23 19:58:11 +03:00
scratch = vmalloc ( size ) ;
if ( ! scratch )
return - ENOMEM ;
2016-05-02 15:57:36 +03:00
2022-11-23 19:58:11 +03:00
if ( copy_from_user ( scratch , bytes , size ) ) {
2016-05-02 15:57:36 +03:00
ret = - EFAULT ;
2022-11-23 19:58:11 +03:00
} else {
mutex_lock ( & cs_ctl - > dsp - > pwr_lock ) ;
ret = cs_dsp_coeff_write_ctrl ( cs_ctl , 0 , scratch , size ) ;
mutex_unlock ( & cs_ctl - > dsp - > pwr_lock ) ;
}
vfree ( scratch ) ;
2016-05-02 15:57:36 +03:00
return ret ;
}
2016-11-09 20:14:18 +03:00
static int wm_coeff_put_acked ( struct snd_kcontrol * kctl ,
struct snd_ctl_elem_value * ucontrol )
{
struct soc_bytes_ext * bytes_ext =
( struct soc_bytes_ext * ) kctl - > private_value ;
struct wm_coeff_ctl * ctl = bytes_ext_to_ctl ( bytes_ext ) ;
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl = ctl - > cs_ctl ;
2016-11-09 20:14:18 +03:00
unsigned int val = ucontrol - > value . integer . value [ 0 ] ;
int ret ;
if ( val = = 0 )
return 0 ; /* 0 means no event */
2021-09-13 19:00:51 +03:00
mutex_lock ( & cs_ctl - > dsp - > pwr_lock ) ;
2016-11-09 20:14:18 +03:00
2021-09-13 19:00:52 +03:00
if ( cs_ctl - > enabled )
2021-09-13 19:00:51 +03:00
ret = cs_dsp_coeff_write_acked_control ( cs_ctl , val ) ;
2016-11-09 20:14:18 +03:00
else
ret = - EPERM ;
2021-09-13 19:00:51 +03:00
mutex_unlock ( & cs_ctl - > dsp - > pwr_lock ) ;
2016-11-09 20:14:18 +03:00
2022-11-23 19:58:11 +03:00
if ( ret < 0 )
return ret ;
return 1 ;
2016-11-09 20:14:18 +03:00
}
2021-09-13 19:00:57 +03:00
static int wm_coeff_get ( struct snd_kcontrol * kctl ,
struct snd_ctl_elem_value * ucontrol )
2013-05-08 17:15:35 +04:00
{
2021-09-13 19:00:57 +03:00
struct soc_bytes_ext * bytes_ext =
( struct soc_bytes_ext * ) kctl - > private_value ;
struct wm_coeff_ctl * ctl = bytes_ext_to_ctl ( bytes_ext ) ;
struct cs_dsp_coeff_ctl * cs_ctl = ctl - > cs_ctl ;
char * p = ucontrol - > value . bytes . data ;
2020-01-14 19:18:38 +03:00
int ret ;
2021-09-13 19:00:51 +03:00
mutex_lock ( & cs_ctl - > dsp - > pwr_lock ) ;
2021-11-17 16:22:59 +03:00
ret = cs_dsp_coeff_read_ctrl ( cs_ctl , 0 , p , cs_ctl - > len ) ;
2021-09-13 19:00:51 +03:00
mutex_unlock ( & cs_ctl - > dsp - > pwr_lock ) ;
2015-04-20 15:52:45 +03:00
2015-12-08 19:08:27 +03:00
return ret ;
2013-05-08 17:15:35 +04:00
}
2016-05-02 15:57:36 +03:00
static int wm_coeff_tlv_get ( struct snd_kcontrol * kctl ,
unsigned int __user * bytes , unsigned int size )
{
struct soc_bytes_ext * bytes_ext =
( struct soc_bytes_ext * ) kctl - > private_value ;
struct wm_coeff_ctl * ctl = bytes_ext_to_ctl ( bytes_ext ) ;
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl = ctl - > cs_ctl ;
2016-05-02 15:57:36 +03:00
int ret = 0 ;
2021-09-13 19:00:51 +03:00
mutex_lock ( & cs_ctl - > dsp - > pwr_lock ) ;
2016-05-02 15:57:36 +03:00
2021-11-17 16:22:59 +03:00
ret = cs_dsp_coeff_read_ctrl ( cs_ctl , 0 , cs_ctl - > cache , size ) ;
2016-05-02 15:57:36 +03:00
2021-09-13 19:00:51 +03:00
if ( ! ret & & copy_to_user ( bytes , cs_ctl - > cache , size ) )
2016-05-02 15:57:36 +03:00
ret = - EFAULT ;
2021-09-13 19:00:51 +03:00
mutex_unlock ( & cs_ctl - > dsp - > pwr_lock ) ;
2016-05-02 15:57:36 +03:00
return ret ;
}
2016-11-09 20:14:18 +03:00
static int wm_coeff_get_acked ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
/*
* Although it ' s not useful to read an acked control , we must satisfy
* user - side assumptions that all controls are readable and that a
* write of the same value should be filtered out ( it ' s valid to send
* the same event number again to the firmware ) . We therefore return 0 ,
* meaning " no event " so valid event numbers will always be a change
*/
ucontrol - > value . integer . value [ 0 ] = 0 ;
return 0 ;
}
2016-05-02 15:57:36 +03:00
static unsigned int wmfw_convert_flags ( unsigned int in , unsigned int len )
{
unsigned int out , rd , wr , vol ;
if ( len > ADSP_MAX_STD_CTRL_SIZE ) {
rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ ;
wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE ;
vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE ;
out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK ;
} else {
rd = SNDRV_CTL_ELEM_ACCESS_READ ;
wr = SNDRV_CTL_ELEM_ACCESS_WRITE ;
vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE ;
out = 0 ;
}
if ( in ) {
2019-10-02 11:42:40 +03:00
out | = rd ;
2016-05-02 15:57:36 +03:00
if ( in & WMFW_CTL_FLAG_WRITEABLE )
out | = wr ;
if ( in & WMFW_CTL_FLAG_VOLATILE )
out | = vol ;
} else {
out | = rd | wr | vol ;
}
return out ;
}
2021-11-17 16:22:51 +03:00
static void wm_adsp_ctl_work ( struct work_struct * work )
2013-05-08 17:15:35 +04:00
{
2021-11-17 16:22:51 +03:00
struct wm_coeff_ctl * ctl = container_of ( work ,
struct wm_coeff_ctl ,
work ) ;
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl = ctl - > cs_ctl ;
2021-11-17 16:22:51 +03:00
struct wm_adsp * dsp = container_of ( cs_ctl - > dsp ,
struct wm_adsp ,
cs_dsp ) ;
2013-05-08 17:15:35 +04:00
struct snd_kcontrol_new * kcontrol ;
kcontrol = kzalloc ( sizeof ( * kcontrol ) , GFP_KERNEL ) ;
if ( ! kcontrol )
2021-11-17 16:22:51 +03:00
return ;
2013-05-08 17:15:35 +04:00
kcontrol - > name = ctl - > name ;
kcontrol - > info = wm_coeff_info ;
2016-05-02 15:57:36 +03:00
kcontrol - > iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
kcontrol - > tlv . c = snd_soc_bytes_tlv_callback ;
kcontrol - > private_value = ( unsigned long ) & ctl - > bytes_ext ;
2021-09-13 19:00:51 +03:00
kcontrol - > access = wmfw_convert_flags ( cs_ctl - > flags , cs_ctl - > len ) ;
2013-05-08 17:15:35 +04:00
2021-09-13 19:00:51 +03:00
switch ( cs_ctl - > type ) {
2016-11-09 20:14:18 +03:00
case WMFW_CTL_TYPE_ACKED :
kcontrol - > get = wm_coeff_get_acked ;
kcontrol - > put = wm_coeff_put_acked ;
break ;
default :
2018-02-28 13:31:10 +03:00
if ( kcontrol - > access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK ) {
2021-09-13 19:00:51 +03:00
ctl - > bytes_ext . max = cs_ctl - > len ;
2018-02-28 13:31:10 +03:00
ctl - > bytes_ext . get = wm_coeff_tlv_get ;
ctl - > bytes_ext . put = wm_coeff_tlv_put ;
} else {
kcontrol - > get = wm_coeff_get ;
kcontrol - > put = wm_coeff_put ;
}
2016-11-09 20:14:18 +03:00
break ;
}
2015-04-20 15:52:45 +03:00
2021-11-17 16:22:51 +03:00
snd_soc_add_component_controls ( dsp - > component , kcontrol , 1 ) ;
2013-05-08 17:15:35 +04:00
kfree ( kcontrol ) ;
2015-04-13 15:28:01 +03:00
}
2021-09-13 19:00:51 +03:00
static int wm_adsp_control_add ( struct cs_dsp_coeff_ctl * cs_ctl )
2015-04-13 15:28:01 +03:00
{
2021-09-13 19:00:55 +03:00
struct wm_adsp * dsp = container_of ( cs_ctl - > dsp , struct wm_adsp , cs_dsp ) ;
struct cs_dsp * cs_dsp = & dsp - > cs_dsp ;
2015-04-13 15:28:01 +03:00
struct wm_coeff_ctl * ctl ;
char name [ SNDRV_CTL_ELEM_ID_NAME_MAXLEN ] ;
2016-11-09 20:14:15 +03:00
const char * region_name ;
2015-04-13 15:28:01 +03:00
int ret ;
2021-09-13 19:00:51 +03:00
if ( cs_ctl - > flags & WMFW_CTL_FLAG_SYS )
return 0 ;
2021-09-13 19:00:43 +03:00
2021-09-13 19:00:51 +03:00
region_name = cs_dsp_mem_region_name ( cs_ctl - > alg_region . type ) ;
2016-11-09 20:14:15 +03:00
if ( ! region_name ) {
2021-09-13 19:00:51 +03:00
adsp_err ( dsp , " Unknown region type: %d \n " , cs_ctl - > alg_region . type ) ;
2015-04-13 15:28:01 +03:00
return - EINVAL ;
}
2021-09-13 19:00:55 +03:00
switch ( cs_dsp - > fw_ver ) {
2015-04-13 15:28:04 +03:00
case 0 :
case 1 :
2021-11-15 15:01:54 +03:00
ret = scnprintf ( name , SNDRV_CTL_ELEM_ID_NAME_MAXLEN ,
" %s %s %x " , cs_dsp - > name , region_name ,
cs_ctl - > alg_region . alg ) ;
2015-04-13 15:28:04 +03:00
break ;
2019-03-19 14:52:13 +03:00
case 2 :
2020-03-13 16:03:34 +03:00
ret = scnprintf ( name , SNDRV_CTL_ELEM_ID_NAME_MAXLEN ,
2021-09-13 19:00:55 +03:00
" %s%c %.12s %x " , cs_dsp - > name , * region_name ,
2021-09-13 19:00:51 +03:00
wm_adsp_fw_text [ dsp - > fw ] , cs_ctl - > alg_region . alg ) ;
2019-03-19 14:52:13 +03:00
break ;
default :
2020-03-13 16:03:34 +03:00
ret = scnprintf ( name , SNDRV_CTL_ELEM_ID_NAME_MAXLEN ,
2021-09-13 19:00:55 +03:00
" %s %.12s %x " , cs_dsp - > name ,
2021-09-13 19:00:51 +03:00
wm_adsp_fw_text [ dsp - > fw ] , cs_ctl - > alg_region . alg ) ;
2019-03-19 14:52:13 +03:00
break ;
}
2015-04-13 15:28:04 +03:00
2021-09-13 19:00:51 +03:00
if ( cs_ctl - > subname ) {
2019-03-19 14:52:13 +03:00
int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2 ;
int skip = 0 ;
2015-04-13 15:28:04 +03:00
2019-03-19 14:52:13 +03:00
if ( dsp - > component - > name_prefix )
avail - = strlen ( dsp - > component - > name_prefix ) + 1 ;
2018-07-19 13:50:36 +03:00
2019-03-19 14:52:13 +03:00
/* Truncate the subname from the start if it is too long */
2021-09-13 19:00:51 +03:00
if ( cs_ctl - > subname_len > avail )
skip = cs_ctl - > subname_len - avail ;
2015-04-13 15:28:04 +03:00
2019-03-19 14:52:13 +03:00
snprintf ( name + ret , SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret ,
2021-09-13 19:00:51 +03:00
" %.*s " , cs_ctl - > subname_len - skip , cs_ctl - > subname + skip ) ;
2015-04-13 15:28:04 +03:00
}
2015-04-13 15:28:01 +03:00
ctl = kzalloc ( sizeof ( * ctl ) , GFP_KERNEL ) ;
if ( ! ctl )
return - ENOMEM ;
2021-09-13 19:00:51 +03:00
ctl - > cs_ctl = cs_ctl ;
2015-04-13 15:28:01 +03:00
ctl - > name = kmemdup ( name , strlen ( name ) + 1 , GFP_KERNEL ) ;
if ( ! ctl - > name ) {
ret = - ENOMEM ;
goto err_ctl ;
}
2021-09-13 19:00:51 +03:00
cs_ctl - > priv = ctl ;
INIT_WORK ( & ctl - > work , wm_adsp_ctl_work ) ;
schedule_work ( & ctl - > work ) ;
return 0 ;
err_ctl :
kfree ( ctl ) ;
return ret ;
}
static void wm_adsp_control_remove ( struct cs_dsp_coeff_ctl * cs_ctl )
{
struct wm_coeff_ctl * ctl = cs_ctl - > priv ;
cancel_work_sync ( & ctl - > work ) ;
kfree ( ctl - > name ) ;
kfree ( ctl ) ;
}
2021-09-13 19:00:57 +03:00
int wm_adsp_write_ctl ( struct wm_adsp * dsp , const char * name , int type ,
unsigned int alg , void * buf , size_t len )
2021-09-13 19:00:51 +03:00
{
2022-06-30 13:14:59 +03:00
struct cs_dsp_coeff_ctl * cs_ctl = cs_dsp_get_ctl ( & dsp - > cs_dsp , name , type , alg ) ;
2021-09-13 19:00:57 +03:00
struct wm_coeff_ctl * ctl ;
2021-09-13 19:00:51 +03:00
int ret ;
2021-11-17 16:22:59 +03:00
ret = cs_dsp_coeff_write_ctrl ( cs_ctl , 0 , buf , len ) ;
2022-11-23 19:58:11 +03:00
if ( ret < 0 )
2021-09-13 19:00:57 +03:00
return ret ;
2015-04-13 15:28:04 +03:00
2022-11-23 19:58:11 +03:00
if ( ret = = 0 | | ( cs_ctl - > flags & WMFW_CTL_FLAG_SYS ) )
2015-04-13 15:28:04 +03:00
return 0 ;
2015-04-13 15:28:02 +03:00
2022-06-30 13:14:59 +03:00
ctl = cs_ctl - > priv ;
2023-05-12 15:28:32 +03:00
return snd_soc_component_notify_control ( dsp - > component , ctl - > name ) ;
2015-04-13 15:28:02 +03:00
}
2021-09-13 19:00:57 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp_write_ctl ) ;
2015-04-13 15:28:02 +03:00
2021-09-13 19:00:57 +03:00
int wm_adsp_read_ctl ( struct wm_adsp * dsp , const char * name , int type ,
unsigned int alg , void * buf , size_t len )
2019-03-19 14:52:12 +03:00
{
2022-06-30 13:14:59 +03:00
return cs_dsp_coeff_read_ctrl ( cs_dsp_get_ctl ( & dsp - > cs_dsp , name , type , alg ) ,
0 , buf , len ) ;
2019-03-19 14:52:12 +03:00
}
2021-09-13 19:00:57 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp_read_ctl ) ;
2019-03-19 14:52:12 +03:00
2021-09-13 19:00:54 +03:00
static void wm_adsp_release_firmware_files ( struct wm_adsp * dsp ,
const struct firmware * wmfw_firmware ,
char * wmfw_filename ,
const struct firmware * coeff_firmware ,
char * coeff_filename )
{
if ( wmfw_firmware )
release_firmware ( wmfw_firmware ) ;
kfree ( wmfw_filename ) ;
if ( coeff_firmware )
release_firmware ( coeff_firmware ) ;
kfree ( coeff_filename ) ;
}
static int wm_adsp_request_firmware_file ( struct wm_adsp * dsp ,
2022-03-03 18:50:16 +03:00
const struct firmware * * firmware , char * * filename ,
const char * dir , const char * system_name ,
const char * asoc_component_prefix ,
const char * filetype )
2021-09-13 19:00:54 +03:00
{
2021-09-13 19:00:55 +03:00
struct cs_dsp * cs_dsp = & dsp - > cs_dsp ;
2022-03-03 18:50:16 +03:00
char * s , c ;
2021-09-13 19:00:54 +03:00
int ret = 0 ;
2022-03-03 18:50:16 +03:00
if ( system_name & & asoc_component_prefix )
* filename = kasprintf ( GFP_KERNEL , " %s%s-%s-%s-%s-%s.%s " , dir , dsp - > part ,
dsp - > fwf_name , wm_adsp_fw [ dsp - > fw ] . file , system_name ,
asoc_component_prefix , filetype ) ;
else if ( system_name )
* filename = kasprintf ( GFP_KERNEL , " %s%s-%s-%s-%s.%s " , dir , dsp - > part ,
dsp - > fwf_name , wm_adsp_fw [ dsp - > fw ] . file , system_name ,
filetype ) ;
else
* filename = kasprintf ( GFP_KERNEL , " %s%s-%s-%s.%s " , dir , dsp - > part , dsp - > fwf_name ,
wm_adsp_fw [ dsp - > fw ] . file , filetype ) ;
2021-09-13 19:00:54 +03:00
if ( * filename = = NULL )
return - ENOMEM ;
2022-03-03 18:50:16 +03:00
/*
* Make sure that filename is lower - case and any non alpha - numeric
* characters except full stop and forward slash are replaced with
* hyphens .
*/
s = * filename ;
while ( * s ) {
c = * s ;
if ( isalnum ( c ) )
* s = tolower ( c ) ;
else if ( ( c ! = ' . ' ) & & ( c ! = ' / ' ) )
* s = ' - ' ;
s + + ;
}
ret = firmware_request_nowarn ( firmware , * filename , cs_dsp - > dev ) ;
2021-09-13 19:00:54 +03:00
if ( ret ! = 0 ) {
2022-03-03 18:50:16 +03:00
adsp_dbg ( dsp , " Failed to request '%s' \n " , * filename ) ;
2021-09-13 19:00:54 +03:00
kfree ( * filename ) ;
* filename = NULL ;
2023-03-20 14:22:44 +03:00
} else {
adsp_dbg ( dsp , " Found '%s' \n " , * filename ) ;
2021-09-13 19:00:54 +03:00
}
return ret ;
}
2022-03-03 18:50:16 +03:00
static const char * cirrus_dir = " cirrus/ " ;
2021-09-13 19:00:54 +03:00
static int wm_adsp_request_firmware_files ( struct wm_adsp * dsp ,
const struct firmware * * wmfw_firmware ,
char * * wmfw_filename ,
const struct firmware * * coeff_firmware ,
char * * coeff_filename )
{
2022-03-03 18:50:16 +03:00
const char * system_name = dsp - > system_name ;
const char * asoc_component_prefix = dsp - > component - > name_prefix ;
2021-09-13 19:00:54 +03:00
int ret = 0 ;
2022-03-03 18:50:16 +03:00
if ( system_name & & asoc_component_prefix ) {
if ( ! wm_adsp_request_firmware_file ( dsp , wmfw_firmware , wmfw_filename ,
cirrus_dir , system_name ,
asoc_component_prefix , " wmfw " ) ) {
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
cirrus_dir , system_name ,
asoc_component_prefix , " bin " ) ;
return 0 ;
}
}
2021-09-13 19:00:54 +03:00
2022-03-03 18:50:16 +03:00
if ( system_name ) {
if ( ! wm_adsp_request_firmware_file ( dsp , wmfw_firmware , wmfw_filename ,
cirrus_dir , system_name ,
NULL , " wmfw " ) ) {
if ( asoc_component_prefix )
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
cirrus_dir , system_name ,
asoc_component_prefix , " bin " ) ;
if ( ! * coeff_firmware )
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
cirrus_dir , system_name ,
NULL , " bin " ) ;
return 0 ;
}
}
2021-09-13 19:00:54 +03:00
2022-03-03 18:50:16 +03:00
if ( ! wm_adsp_request_firmware_file ( dsp , wmfw_firmware , wmfw_filename ,
" " , NULL , NULL , " wmfw " ) ) {
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
" " , NULL , NULL , " bin " ) ;
return 0 ;
}
ret = wm_adsp_request_firmware_file ( dsp , wmfw_firmware , wmfw_filename ,
cirrus_dir , NULL , NULL , " wmfw " ) ;
if ( ! ret ) {
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
cirrus_dir , NULL , NULL , " bin " ) ;
return 0 ;
}
2023-03-20 14:22:43 +03:00
if ( dsp - > wmfw_optional ) {
if ( system_name ) {
if ( asoc_component_prefix )
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
cirrus_dir , system_name ,
asoc_component_prefix , " bin " ) ;
if ( ! * coeff_firmware )
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
cirrus_dir , system_name ,
NULL , " bin " ) ;
}
if ( ! * coeff_firmware )
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
" " , NULL , NULL , " bin " ) ;
if ( ! * coeff_firmware )
wm_adsp_request_firmware_file ( dsp , coeff_firmware , coeff_filename ,
cirrus_dir , NULL , NULL , " bin " ) ;
2023-03-20 14:22:41 +03:00
return 0 ;
2023-03-20 14:22:43 +03:00
}
2023-03-20 14:22:41 +03:00
2022-03-03 18:50:16 +03:00
adsp_err ( dsp , " Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw \n " ,
cirrus_dir , dsp - > part , dsp - > fwf_name , wm_adsp_fw [ dsp - > fw ] . file ,
system_name , asoc_component_prefix ) ;
return - ENOENT ;
2021-09-13 19:00:54 +03:00
}
2021-09-13 19:00:57 +03:00
static int wm_adsp_common_init ( struct wm_adsp * dsp )
2019-03-19 14:52:12 +03:00
{
2021-09-13 19:00:57 +03:00
char * p ;
2019-03-19 14:52:12 +03:00
2021-09-13 19:00:57 +03:00
INIT_LIST_HEAD ( & dsp - > compr_list ) ;
INIT_LIST_HEAD ( & dsp - > buffer_list ) ;
2019-03-19 14:52:12 +03:00
2021-09-13 19:00:57 +03:00
if ( ! dsp - > fwf_name ) {
p = devm_kstrdup ( dsp - > cs_dsp . dev , dsp - > cs_dsp . name , GFP_KERNEL ) ;
if ( ! p )
return - ENOMEM ;
2019-03-19 14:52:12 +03:00
2021-09-13 19:00:57 +03:00
dsp - > fwf_name = p ;
for ( ; * p ! = 0 ; + + p )
* p = tolower ( * p ) ;
2019-03-19 14:52:12 +03:00
}
2021-09-13 19:00:57 +03:00
return 0 ;
2019-03-19 14:52:13 +03:00
}
2021-09-13 19:00:57 +03:00
int wm_adsp1_init ( struct wm_adsp * dsp )
2012-10-11 06:54:02 +04:00
{
2021-09-13 19:00:57 +03:00
int ret ;
2012-10-11 06:54:02 +04:00
2021-09-13 19:00:57 +03:00
dsp - > cs_dsp . client_ops = & wm_adsp1_client_ops ;
2012-10-11 06:54:02 +04:00
2021-09-13 19:00:57 +03:00
ret = cs_dsp_adsp1_init ( & dsp - > cs_dsp ) ;
if ( ret )
return ret ;
2012-10-11 06:54:02 +04:00
2021-09-13 19:00:57 +03:00
return wm_adsp_common_init ( dsp ) ;
}
EXPORT_SYMBOL_GPL ( wm_adsp1_init ) ;
2012-10-11 06:54:02 +04:00
2021-09-13 19:00:57 +03:00
int wm_adsp1_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol ,
int event )
{
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct wm_adsp * dsps = snd_soc_component_get_drvdata ( component ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
int ret = 0 ;
char * wmfw_filename = NULL ;
const struct firmware * wmfw_firmware = NULL ;
char * coeff_filename = NULL ;
const struct firmware * coeff_firmware = NULL ;
2012-10-11 06:54:02 +04:00
2021-09-13 19:00:57 +03:00
dsp - > component = component ;
2021-09-13 19:00:49 +03:00
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
2021-09-13 19:00:54 +03:00
ret = wm_adsp_request_firmware_files ( dsp ,
& wmfw_firmware , & wmfw_filename ,
& coeff_firmware , & coeff_filename ) ;
2021-09-13 19:00:57 +03:00
if ( ret )
break ;
2019-03-19 14:52:13 +03:00
2021-09-13 19:00:57 +03:00
ret = cs_dsp_adsp1_power_up ( & dsp - > cs_dsp ,
wmfw_firmware , wmfw_filename ,
coeff_firmware , coeff_filename ,
wm_adsp_fw_text [ dsp - > fw ] ) ;
2016-01-21 20:53:02 +03:00
2021-09-13 19:00:57 +03:00
wm_adsp_release_firmware_files ( dsp ,
wmfw_firmware , wmfw_filename ,
coeff_firmware , coeff_filename ) ;
break ;
case SND_SOC_DAPM_PRE_PMD :
cs_dsp_adsp1_power_down ( & dsp - > cs_dsp ) ;
break ;
default :
break ;
}
2019-03-19 14:52:09 +03:00
return ret ;
2016-01-21 20:53:02 +03:00
}
2021-09-13 19:00:57 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp1_event ) ;
2021-09-13 19:00:48 +03:00
int wm_adsp2_set_dspclk ( struct snd_soc_dapm_widget * w , unsigned int freq )
{
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct wm_adsp * dsps = snd_soc_component_get_drvdata ( component ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
2021-09-13 19:00:55 +03:00
return cs_dsp_set_dspclk ( & dsp - > cs_dsp , freq ) ;
2021-09-13 19:00:48 +03:00
}
2019-03-19 14:52:09 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp2_set_dspclk ) ;
2016-01-21 20:53:02 +03:00
2017-01-06 17:24:41 +03:00
int wm_adsp2_preloader_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
2018-08-07 20:30:42 +03:00
struct wm_adsp * dsps = snd_soc_component_get_drvdata ( component ) ;
struct soc_mixer_control * mc =
( struct soc_mixer_control * ) kcontrol - > private_value ;
struct wm_adsp * dsp = & dsps [ mc - > shift - 1 ] ;
2017-01-06 17:24:41 +03:00
ucontrol - > value . integer . value [ 0 ] = dsp - > preloaded ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp2_preloader_get ) ;
int wm_adsp2_preloader_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
2018-08-07 20:30:42 +03:00
struct wm_adsp * dsps = snd_soc_component_get_drvdata ( component ) ;
2018-02-13 05:03:12 +03:00
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( component ) ;
2017-01-06 17:24:41 +03:00
struct soc_mixer_control * mc =
( struct soc_mixer_control * ) kcontrol - > private_value ;
2018-08-07 20:30:42 +03:00
struct wm_adsp * dsp = & dsps [ mc - > shift - 1 ] ;
2017-01-06 17:24:41 +03:00
char preload [ 32 ] ;
2022-01-05 14:30:24 +03:00
if ( dsp - > preloaded = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
2017-01-06 17:24:41 +03:00
2022-01-05 14:30:24 +03:00
snprintf ( preload , ARRAY_SIZE ( preload ) , " %s Preload " , dsp - > cs_dsp . name ) ;
2017-01-06 17:24:41 +03:00
2022-01-05 14:30:24 +03:00
if ( ucontrol - > value . integer . value [ 0 ] | | dsp - > toggle_preload )
2018-04-24 18:53:09 +03:00
snd_soc_component_force_enable_pin ( component , preload ) ;
2017-01-06 17:24:41 +03:00
else
2018-04-24 18:53:09 +03:00
snd_soc_component_disable_pin ( component , preload ) ;
2017-01-06 17:24:41 +03:00
snd_soc_dapm_sync ( dapm ) ;
2018-07-19 13:50:37 +03:00
flush_work ( & dsp - > boot_work ) ;
2022-01-05 14:30:24 +03:00
dsp - > preloaded = ucontrol - > value . integer . value [ 0 ] ;
if ( dsp - > toggle_preload ) {
snd_soc_component_disable_pin ( component , preload ) ;
snd_soc_dapm_sync ( dapm ) ;
}
2022-06-21 13:20:38 +03:00
return 1 ;
2017-01-06 17:24:41 +03:00
}
EXPORT_SYMBOL_GPL ( wm_adsp2_preloader_put ) ;
2023-03-20 14:22:42 +03:00
int wm_adsp_power_up ( struct wm_adsp * dsp )
2021-09-13 19:00:49 +03:00
{
2021-09-13 19:00:54 +03:00
int ret = 0 ;
char * wmfw_filename = NULL ;
const struct firmware * wmfw_firmware = NULL ;
char * coeff_filename = NULL ;
const struct firmware * coeff_firmware = NULL ;
ret = wm_adsp_request_firmware_files ( dsp ,
& wmfw_firmware , & wmfw_filename ,
& coeff_firmware , & coeff_filename ) ;
if ( ret )
2023-03-20 14:22:42 +03:00
return ret ;
2021-09-13 19:00:49 +03:00
2023-03-20 14:22:42 +03:00
ret = cs_dsp_power_up ( & dsp - > cs_dsp ,
wmfw_firmware , wmfw_filename ,
coeff_firmware , coeff_filename ,
wm_adsp_fw_text [ dsp - > fw ] ) ;
2021-09-13 19:00:54 +03:00
wm_adsp_release_firmware_files ( dsp ,
wmfw_firmware , wmfw_filename ,
coeff_firmware , coeff_filename ) ;
2023-03-20 14:22:42 +03:00
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_power_up ) ;
static void wm_adsp_boot_work ( struct work_struct * work )
{
struct wm_adsp * dsp = container_of ( work ,
struct wm_adsp ,
boot_work ) ;
wm_adsp_power_up ( dsp ) ;
2021-09-13 19:00:49 +03:00
}
2019-03-19 14:52:12 +03:00
int wm_adsp_early_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
2014-01-08 21:42:19 +04:00
{
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct wm_adsp * dsps = snd_soc_component_get_drvdata ( component ) ;
2014-01-08 21:42:19 +04:00
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
queue_work ( system_unbound_wq , & dsp - > boot_work ) ;
break ;
2016-09-26 12:15:24 +03:00
case SND_SOC_DAPM_PRE_PMD :
2021-09-13 19:00:55 +03:00
cs_dsp_power_down ( & dsp - > cs_dsp ) ;
2016-09-26 12:15:24 +03:00
break ;
2014-01-08 21:42:19 +04:00
default :
break ;
2014-04-17 16:42:54 +04:00
}
2014-01-08 21:42:19 +04:00
return 0 ;
}
2019-03-19 14:52:12 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp_early_event ) ;
2014-01-08 21:42:19 +04:00
2022-11-09 19:53:29 +03:00
static int wm_adsp_pre_run ( struct cs_dsp * cs_dsp )
{
struct wm_adsp * dsp = container_of ( cs_dsp , struct wm_adsp , cs_dsp ) ;
if ( ! dsp - > pre_run )
return 0 ;
return ( * dsp - > pre_run ) ( dsp ) ;
}
2021-09-13 19:00:55 +03:00
static int wm_adsp_event_post_run ( struct cs_dsp * cs_dsp )
2021-09-13 19:00:49 +03:00
{
2021-09-13 19:00:55 +03:00
struct wm_adsp * dsp = container_of ( cs_dsp , struct wm_adsp , cs_dsp ) ;
2021-09-13 19:00:49 +03:00
if ( wm_adsp_fw [ dsp - > fw ] . num_caps ! = 0 )
return wm_adsp_buffer_init ( dsp ) ;
return 0 ;
}
2021-09-13 19:00:55 +03:00
static void wm_adsp_event_post_stop ( struct cs_dsp * cs_dsp )
2021-09-13 19:00:49 +03:00
{
2021-09-13 19:00:55 +03:00
struct wm_adsp * dsp = container_of ( cs_dsp , struct wm_adsp , cs_dsp ) ;
2021-09-13 19:00:49 +03:00
if ( wm_adsp_fw [ dsp - > fw ] . num_caps ! = 0 )
wm_adsp_buffer_free ( dsp ) ;
dsp - > fatal_error = false ;
}
int wm_adsp_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct wm_adsp * dsps = snd_soc_component_get_drvdata ( component ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
int ret = 0 ;
2012-10-11 06:54:02 +04:00
2021-09-13 19:00:49 +03:00
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
flush_work ( & dsp - > boot_work ) ;
2021-09-13 19:00:55 +03:00
ret = cs_dsp_run ( & dsp - > cs_dsp ) ;
2021-09-13 19:00:49 +03:00
break ;
case SND_SOC_DAPM_PRE_PMD :
2021-09-13 19:00:55 +03:00
cs_dsp_stop ( & dsp - > cs_dsp ) ;
2021-09-13 19:00:49 +03:00
break ;
2012-10-11 06:54:02 +04:00
default :
break ;
}
return ret ;
}
2019-03-19 14:52:12 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp_event ) ;
2012-11-28 21:20:32 +04:00
2018-02-13 05:03:12 +03:00
int wm_adsp2_component_probe ( struct wm_adsp * dsp , struct snd_soc_component * component )
2015-06-11 13:32:30 +03:00
{
2017-01-06 17:24:41 +03:00
char preload [ 32 ] ;
2023-03-20 14:22:39 +03:00
if ( ! dsp - > cs_dsp . no_core_startstop ) {
snprintf ( preload , ARRAY_SIZE ( preload ) , " %s Preload " , dsp - > cs_dsp . name ) ;
snd_soc_component_disable_pin ( component , preload ) ;
}
2016-11-22 19:58:57 +03:00
2021-09-13 19:00:55 +03:00
cs_dsp_init_debugfs ( & dsp - > cs_dsp , component - > debugfs_root ) ;
2015-06-11 13:32:32 +03:00
2018-02-13 05:03:12 +03:00
dsp - > component = component ;
2017-01-06 17:24:41 +03:00
2018-08-08 19:13:38 +03:00
return 0 ;
2015-06-11 13:32:30 +03:00
}
2018-02-13 05:03:12 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp2_component_probe ) ;
2015-06-11 13:32:30 +03:00
2018-02-13 05:03:12 +03:00
int wm_adsp2_component_remove ( struct wm_adsp * dsp , struct snd_soc_component * component )
2015-06-11 13:32:30 +03:00
{
2021-09-13 19:00:55 +03:00
cs_dsp_cleanup_debugfs ( & dsp - > cs_dsp ) ;
2015-06-11 13:32:32 +03:00
2015-06-11 13:32:30 +03:00
return 0 ;
}
2018-02-13 05:03:12 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp2_component_remove ) ;
2015-06-11 13:32:30 +03:00
2021-09-13 19:00:48 +03:00
int wm_adsp2_init ( struct wm_adsp * dsp )
{
2021-09-13 19:00:55 +03:00
int ret ;
2019-03-19 14:52:12 +03:00
INIT_WORK ( & dsp - > boot_work , wm_adsp_boot_work ) ;
2013-05-08 17:15:35 +04:00
2021-09-13 19:00:50 +03:00
dsp - > sys_config_size = sizeof ( struct wm_adsp_system_config_xm_hdr ) ;
2021-09-13 19:00:56 +03:00
dsp - > cs_dsp . client_ops = & wm_adsp2_client_ops ;
2021-09-13 19:00:50 +03:00
2021-09-13 19:00:55 +03:00
ret = cs_dsp_adsp2_init ( & dsp - > cs_dsp ) ;
if ( ret )
return ret ;
2021-09-13 19:00:48 +03:00
2021-09-13 19:00:55 +03:00
return wm_adsp_common_init ( dsp ) ;
2012-11-28 21:20:32 +04:00
}
EXPORT_SYMBOL_GPL ( wm_adsp2_init ) ;
2014-07-04 09:47:41 +04:00
2021-09-13 19:00:48 +03:00
int wm_halo_init ( struct wm_adsp * dsp )
{
2021-09-13 19:00:55 +03:00
int ret ;
2019-03-19 14:52:13 +03:00
INIT_WORK ( & dsp - > boot_work , wm_adsp_boot_work ) ;
2021-09-13 19:00:50 +03:00
dsp - > sys_config_size = sizeof ( struct wm_halo_system_config_xm_hdr ) ;
2021-09-13 19:00:56 +03:00
dsp - > cs_dsp . client_ops = & wm_adsp2_client_ops ;
2021-09-13 19:00:50 +03:00
2021-09-13 19:00:55 +03:00
ret = cs_dsp_halo_init ( & dsp - > cs_dsp ) ;
if ( ret )
return ret ;
2021-09-13 19:00:48 +03:00
2021-09-13 19:00:55 +03:00
return wm_adsp_common_init ( dsp ) ;
2019-03-19 14:52:13 +03:00
}
EXPORT_SYMBOL_GPL ( wm_halo_init ) ;
2021-09-13 19:00:48 +03:00
void wm_adsp2_remove ( struct wm_adsp * dsp )
{
2021-09-13 19:00:55 +03:00
cs_dsp_remove ( & dsp - > cs_dsp ) ;
2021-09-13 19:00:48 +03:00
}
2016-04-27 16:58:27 +03:00
EXPORT_SYMBOL_GPL ( wm_adsp2_remove ) ;
2016-05-04 19:11:55 +03:00
static inline int wm_adsp_compr_attached ( struct wm_adsp_compr * compr )
{
return compr - > buf ! = NULL ;
}
static int wm_adsp_compr_attach ( struct wm_adsp_compr * compr )
{
2019-02-22 13:04:20 +03:00
struct wm_adsp_compr_buf * buf = NULL , * tmp ;
2019-03-19 14:52:07 +03:00
if ( compr - > dsp - > fatal_error )
return - EINVAL ;
2019-02-22 13:04:20 +03:00
list_for_each_entry ( tmp , & compr - > dsp - > buffer_list , list ) {
if ( ! tmp - > name | | ! strcmp ( compr - > name , tmp - > name ) ) {
buf = tmp ;
break ;
}
}
if ( ! buf )
2016-05-04 19:11:55 +03:00
return - EINVAL ;
2019-02-22 13:04:20 +03:00
compr - > buf = buf ;
2019-04-02 15:49:16 +03:00
buf - > compr = compr ;
2016-05-04 19:11:55 +03:00
return 0 ;
}
2016-05-04 19:11:56 +03:00
static void wm_adsp_compr_detach ( struct wm_adsp_compr * compr )
{
if ( ! compr )
return ;
/* Wake the poll so it can see buffer is no longer attached */
if ( compr - > stream )
snd_compr_fragment_elapsed ( compr - > stream ) ;
if ( wm_adsp_compr_attached ( compr ) ) {
compr - > buf - > compr = NULL ;
compr - > buf = NULL ;
}
}
2015-12-15 14:29:45 +03:00
int wm_adsp_compr_open ( struct wm_adsp * dsp , struct snd_compr_stream * stream )
{
2019-02-22 13:04:20 +03:00
struct wm_adsp_compr * compr , * tmp ;
struct snd_soc_pcm_runtime * rtd = stream - > private_data ;
2015-12-15 14:29:45 +03:00
int ret = 0 ;
2021-09-13 19:00:55 +03:00
mutex_lock ( & dsp - > cs_dsp . pwr_lock ) ;
2015-12-15 14:29:45 +03:00
if ( wm_adsp_fw [ dsp - > fw ] . num_caps = = 0 ) {
2019-02-22 13:04:21 +03:00
adsp_err ( dsp , " %s: Firmware does not support compressed API \n " ,
2020-03-23 08:21:56 +03:00
asoc_rtd_to_codec ( rtd , 0 ) - > name ) ;
2015-12-15 14:29:45 +03:00
ret = - ENXIO ;
goto out ;
}
if ( wm_adsp_fw [ dsp - > fw ] . compr_direction ! = stream - > direction ) {
2019-02-22 13:04:21 +03:00
adsp_err ( dsp , " %s: Firmware does not support stream direction \n " ,
2020-03-23 08:21:56 +03:00
asoc_rtd_to_codec ( rtd , 0 ) - > name ) ;
2015-12-15 14:29:45 +03:00
ret = - EINVAL ;
goto out ;
}
2019-02-22 13:04:20 +03:00
list_for_each_entry ( tmp , & dsp - > compr_list , list ) {
2020-03-23 08:21:56 +03:00
if ( ! strcmp ( tmp - > name , asoc_rtd_to_codec ( rtd , 0 ) - > name ) ) {
2019-02-22 13:04:21 +03:00
adsp_err ( dsp , " %s: Only a single stream supported per dai \n " ,
2020-03-23 08:21:56 +03:00
asoc_rtd_to_codec ( rtd , 0 ) - > name ) ;
2019-02-22 13:04:20 +03:00
ret = - EBUSY ;
goto out ;
}
2015-12-15 14:29:47 +03:00
}
2015-12-15 14:29:45 +03:00
compr = kzalloc ( sizeof ( * compr ) , GFP_KERNEL ) ;
if ( ! compr ) {
ret = - ENOMEM ;
goto out ;
}
compr - > dsp = dsp ;
compr - > stream = stream ;
2020-03-23 08:21:56 +03:00
compr - > name = asoc_rtd_to_codec ( rtd , 0 ) - > name ;
2015-12-15 14:29:45 +03:00
2019-02-22 13:04:20 +03:00
list_add_tail ( & compr - > list , & dsp - > compr_list ) ;
2015-12-15 14:29:45 +03:00
stream - > runtime - > private_data = compr ;
out :
2021-09-13 19:00:55 +03:00
mutex_unlock ( & dsp - > cs_dsp . pwr_lock ) ;
2015-12-15 14:29:45 +03:00
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_open ) ;
2020-04-20 10:08:13 +03:00
int wm_adsp_compr_free ( struct snd_soc_component * component ,
struct snd_compr_stream * stream )
2015-12-15 14:29:45 +03:00
{
struct wm_adsp_compr * compr = stream - > runtime - > private_data ;
struct wm_adsp * dsp = compr - > dsp ;
2021-09-13 19:00:55 +03:00
mutex_lock ( & dsp - > cs_dsp . pwr_lock ) ;
2015-12-15 14:29:45 +03:00
2016-05-04 19:11:56 +03:00
wm_adsp_compr_detach ( compr ) ;
2019-02-22 13:04:20 +03:00
list_del ( & compr - > list ) ;
2015-12-15 14:29:45 +03:00
2016-01-06 15:33:19 +03:00
kfree ( compr - > raw_buf ) ;
2015-12-15 14:29:45 +03:00
kfree ( compr ) ;
2021-09-13 19:00:55 +03:00
mutex_unlock ( & dsp - > cs_dsp . pwr_lock ) ;
2015-12-15 14:29:45 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_free ) ;
static int wm_adsp_compr_check_params ( struct snd_compr_stream * stream ,
struct snd_compr_params * params )
{
struct wm_adsp_compr * compr = stream - > runtime - > private_data ;
struct wm_adsp * dsp = compr - > dsp ;
const struct wm_adsp_fw_caps * caps ;
const struct snd_codec_desc * desc ;
int i , j ;
if ( params - > buffer . fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE | |
params - > buffer . fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE | |
params - > buffer . fragments < WM_ADSP_MIN_FRAGMENTS | |
params - > buffer . fragments > WM_ADSP_MAX_FRAGMENTS | |
2021-09-13 19:00:46 +03:00
params - > buffer . fragment_size % CS_DSP_DATA_WORD_SIZE ) {
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Invalid buffer fragsize=%d fragments=%d \n " ,
params - > buffer . fragment_size ,
params - > buffer . fragments ) ;
2015-12-15 14:29:45 +03:00
return - EINVAL ;
}
for ( i = 0 ; i < wm_adsp_fw [ dsp - > fw ] . num_caps ; i + + ) {
caps = & wm_adsp_fw [ dsp - > fw ] . caps [ i ] ;
desc = & caps - > desc ;
if ( caps - > id ! = params - > codec . id )
continue ;
if ( stream - > direction = = SND_COMPRESS_PLAYBACK ) {
if ( desc - > max_ch < params - > codec . ch_out )
continue ;
} else {
if ( desc - > max_ch < params - > codec . ch_in )
continue ;
}
if ( ! ( desc - > formats & ( 1 < < params - > codec . format ) ) )
continue ;
for ( j = 0 ; j < desc - > num_sample_rates ; + + j )
if ( desc - > sample_rates [ j ] = = params - > codec . sample_rate )
return 0 ;
}
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Invalid params id=%u ch=%u,%u rate=%u fmt=%u \n " ,
params - > codec . id , params - > codec . ch_in , params - > codec . ch_out ,
params - > codec . sample_rate , params - > codec . format ) ;
2015-12-15 14:29:45 +03:00
return - EINVAL ;
}
2016-01-06 15:33:18 +03:00
static inline unsigned int wm_adsp_compr_frag_words ( struct wm_adsp_compr * compr )
{
2021-09-13 19:00:46 +03:00
return compr - > size . fragment_size / CS_DSP_DATA_WORD_SIZE ;
2016-01-06 15:33:18 +03:00
}
2020-04-20 10:08:13 +03:00
int wm_adsp_compr_set_params ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
2015-12-15 14:29:45 +03:00
struct snd_compr_params * params )
{
struct wm_adsp_compr * compr = stream - > runtime - > private_data ;
2016-01-06 15:33:19 +03:00
unsigned int size ;
2015-12-15 14:29:45 +03:00
int ret ;
ret = wm_adsp_compr_check_params ( stream , params ) ;
if ( ret )
return ret ;
compr - > size = params - > buffer ;
2019-02-22 13:04:21 +03:00
compr_dbg ( compr , " fragment_size=%d fragments=%d \n " ,
compr - > size . fragment_size , compr - > size . fragments ) ;
2015-12-15 14:29:45 +03:00
2016-01-06 15:33:19 +03:00
size = wm_adsp_compr_frag_words ( compr ) * sizeof ( * compr - > raw_buf ) ;
compr - > raw_buf = kmalloc ( size , GFP_DMA | GFP_KERNEL ) ;
if ( ! compr - > raw_buf )
return - ENOMEM ;
2016-02-02 19:41:36 +03:00
compr - > sample_rate = params - > codec . sample_rate ;
2015-12-15 14:29:45 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_set_params ) ;
2020-04-20 10:08:13 +03:00
int wm_adsp_compr_get_caps ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
2015-12-15 14:29:45 +03:00
struct snd_compr_caps * caps )
{
struct wm_adsp_compr * compr = stream - > runtime - > private_data ;
int fw = compr - > dsp - > fw ;
int i ;
if ( wm_adsp_fw [ fw ] . caps ) {
for ( i = 0 ; i < wm_adsp_fw [ fw ] . num_caps ; i + + )
caps - > codecs [ i ] = wm_adsp_fw [ fw ] . caps [ i ] . id ;
caps - > num_codecs = i ;
caps - > direction = wm_adsp_fw [ fw ] . compr_direction ;
caps - > min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE ;
caps - > max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE ;
caps - > min_fragments = WM_ADSP_MIN_FRAGMENTS ;
caps - > max_fragments = WM_ADSP_MAX_FRAGMENTS ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_get_caps ) ;
2015-12-15 14:29:46 +03:00
static inline int wm_adsp_buffer_read ( struct wm_adsp_compr_buf * buf ,
unsigned int field_offset , u32 * data )
{
2021-09-13 19:00:55 +03:00
return cs_dsp_read_data_word ( & buf - > dsp - > cs_dsp , buf - > host_buf_mem_type ,
2021-09-13 19:00:46 +03:00
buf - > host_buf_ptr + field_offset , data ) ;
2015-12-15 14:29:46 +03:00
}
static inline int wm_adsp_buffer_write ( struct wm_adsp_compr_buf * buf ,
unsigned int field_offset , u32 data )
{
2021-09-13 19:00:55 +03:00
return cs_dsp_write_data_word ( & buf - > dsp - > cs_dsp , buf - > host_buf_mem_type ,
buf - > host_buf_ptr + field_offset ,
data ) ;
2015-12-15 14:29:46 +03:00
}
2019-02-22 13:04:18 +03:00
static int wm_adsp_buffer_populate ( struct wm_adsp_compr_buf * buf )
{
const struct wm_adsp_fw_caps * caps = wm_adsp_fw [ buf - > dsp - > fw ] . caps ;
struct wm_adsp_buffer_region * region ;
u32 offset = 0 ;
int i , ret ;
2019-02-22 13:04:19 +03:00
buf - > regions = kcalloc ( caps - > num_regions , sizeof ( * buf - > regions ) ,
GFP_KERNEL ) ;
if ( ! buf - > regions )
return - ENOMEM ;
2019-02-22 13:04:18 +03:00
for ( i = 0 ; i < caps - > num_regions ; + + i ) {
region = & buf - > regions [ i ] ;
region - > offset = offset ;
region - > mem_type = caps - > region_defs [ i ] . mem_type ;
ret = wm_adsp_buffer_read ( buf , caps - > region_defs [ i ] . base_offset ,
& region - > base_addr ) ;
if ( ret < 0 )
return ret ;
ret = wm_adsp_buffer_read ( buf , caps - > region_defs [ i ] . size_offset ,
& offset ) ;
if ( ret < 0 )
return ret ;
region - > cumulative_size = offset ;
2019-02-22 13:04:21 +03:00
compr_dbg ( buf ,
" region=%d type=%d base=%08x off=%08x size=%08x \n " ,
i , region - > mem_type , region - > base_addr ,
region - > offset , region - > cumulative_size ) ;
2019-02-22 13:04:18 +03:00
}
return 0 ;
}
static void wm_adsp_buffer_clear ( struct wm_adsp_compr_buf * buf )
{
buf - > irq_count = 0xFFFFFFFF ;
buf - > read_index = - 1 ;
buf - > avail = 0 ;
}
2019-02-22 13:04:19 +03:00
static struct wm_adsp_compr_buf * wm_adsp_buffer_alloc ( struct wm_adsp * dsp )
{
struct wm_adsp_compr_buf * buf ;
buf = kzalloc ( sizeof ( * buf ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > dsp = dsp ;
wm_adsp_buffer_clear ( buf ) ;
return buf ;
}
static int wm_adsp_buffer_parse_legacy ( struct wm_adsp * dsp )
2015-12-15 14:29:46 +03:00
{
2021-09-13 19:00:46 +03:00
struct cs_dsp_alg_region * alg_region ;
2019-02-22 13:04:19 +03:00
struct wm_adsp_compr_buf * buf ;
2015-12-15 14:29:46 +03:00
u32 xmalg , addr , magic ;
int i , ret ;
2021-09-13 19:00:55 +03:00
alg_region = cs_dsp_find_alg_region ( & dsp - > cs_dsp , WMFW_ADSP2_XM , dsp - > cs_dsp . fw_id ) ;
2019-10-01 16:09:11 +03:00
if ( ! alg_region ) {
adsp_err ( dsp , " No algorithm region found \n " ) ;
return - EINVAL ;
}
2021-09-13 19:00:50 +03:00
xmalg = dsp - > sys_config_size / sizeof ( __be32 ) ;
2015-12-15 14:29:46 +03:00
addr = alg_region - > base + xmalg + ALG_XM_FIELD ( magic ) ;
2021-09-13 19:00:55 +03:00
ret = cs_dsp_read_data_word ( & dsp - > cs_dsp , WMFW_ADSP2_XM , addr , & magic ) ;
2015-12-15 14:29:46 +03:00
if ( ret < 0 )
return ret ;
if ( magic ! = WM_ADSP_ALG_XM_STRUCT_MAGIC )
2019-02-22 13:04:19 +03:00
return - ENODEV ;
2015-12-15 14:29:46 +03:00
2022-02-10 20:20:52 +03:00
buf = wm_adsp_buffer_alloc ( dsp ) ;
if ( ! buf )
return - ENOMEM ;
2015-12-15 14:29:46 +03:00
addr = alg_region - > base + xmalg + ALG_XM_FIELD ( host_buf_ptr ) ;
for ( i = 0 ; i < 5 ; + + i ) {
2021-09-13 19:00:55 +03:00
ret = cs_dsp_read_data_word ( & dsp - > cs_dsp , WMFW_ADSP2_XM , addr ,
2021-09-13 19:00:46 +03:00
& buf - > host_buf_ptr ) ;
2015-12-15 14:29:46 +03:00
if ( ret < 0 )
2022-02-10 20:20:52 +03:00
goto err ;
2015-12-15 14:29:46 +03:00
if ( buf - > host_buf_ptr )
break ;
usleep_range ( 1000 , 2000 ) ;
}
2022-02-10 20:20:52 +03:00
if ( ! buf - > host_buf_ptr ) {
ret = - EIO ;
goto err ;
}
2015-12-15 14:29:46 +03:00
2019-02-19 20:31:56 +03:00
buf - > host_buf_mem_type = WMFW_ADSP2_XM ;
2019-02-22 13:04:19 +03:00
ret = wm_adsp_buffer_populate ( buf ) ;
if ( ret < 0 )
2022-02-10 20:20:52 +03:00
goto err ;
list_add_tail ( & buf - > list , & dsp - > buffer_list ) ;
2018-07-19 13:50:39 +03:00
2019-02-22 13:04:21 +03:00
compr_dbg ( buf , " legacy host_buf_ptr=%x \n " , buf - > host_buf_ptr ) ;
2018-07-19 13:50:39 +03:00
2019-02-22 13:04:19 +03:00
return 0 ;
2022-02-10 20:20:52 +03:00
err :
kfree ( buf ) ;
return ret ;
2018-07-19 13:50:39 +03:00
}
2021-09-13 19:00:51 +03:00
static int wm_adsp_buffer_parse_coeff ( struct cs_dsp_coeff_ctl * cs_ctl )
2018-07-19 13:50:39 +03:00
{
2019-02-22 13:04:20 +03:00
struct wm_adsp_host_buf_coeff_v1 coeff_v1 ;
2019-02-22 13:04:19 +03:00
struct wm_adsp_compr_buf * buf ;
2021-09-13 19:00:55 +03:00
struct wm_adsp * dsp = container_of ( cs_ctl - > dsp , struct wm_adsp , cs_dsp ) ;
2022-02-10 20:20:52 +03:00
unsigned int version = 0 ;
2019-02-22 13:04:19 +03:00
int ret , i ;
2018-07-19 13:50:39 +03:00
for ( i = 0 ; i < 5 ; + + i ) {
2022-02-10 20:20:51 +03:00
ret = cs_dsp_coeff_read_ctrl ( cs_ctl , 0 , & coeff_v1 ,
min ( cs_ctl - > len , sizeof ( coeff_v1 ) ) ) ;
2018-07-19 13:50:39 +03:00
if ( ret < 0 )
return ret ;
2021-09-13 19:00:44 +03:00
if ( coeff_v1 . host_buf_ptr )
2018-07-19 13:50:39 +03:00
break ;
usleep_range ( 1000 , 2000 ) ;
}
2021-09-13 19:00:44 +03:00
if ( ! coeff_v1 . host_buf_ptr ) {
2021-09-13 19:00:51 +03:00
adsp_err ( dsp , " Failed to acquire host buffer \n " ) ;
2018-07-19 13:50:39 +03:00
return - EIO ;
2019-02-22 13:04:19 +03:00
}
2021-09-13 19:00:51 +03:00
buf = wm_adsp_buffer_alloc ( dsp ) ;
2019-02-22 13:04:19 +03:00
if ( ! buf )
return - ENOMEM ;
2018-07-19 13:50:39 +03:00
2021-09-13 19:00:51 +03:00
buf - > host_buf_mem_type = cs_ctl - > alg_region . type ;
2021-09-13 19:00:44 +03:00
buf - > host_buf_ptr = be32_to_cpu ( coeff_v1 . host_buf_ptr ) ;
2019-02-22 13:04:19 +03:00
ret = wm_adsp_buffer_populate ( buf ) ;
if ( ret < 0 )
2022-02-10 20:20:52 +03:00
goto err ;
2019-02-22 13:04:19 +03:00
2019-02-22 13:04:20 +03:00
/*
* v0 host_buffer coefficients didn ' t have versioning , so if the
* control is one word , assume version 0.
*/
2022-02-10 20:20:52 +03:00
if ( cs_ctl - > len = = 4 )
goto done ;
2019-02-22 13:04:20 +03:00
2020-12-30 20:24:26 +03:00
version = be32_to_cpu ( coeff_v1 . versions ) & HOST_BUF_COEFF_COMPAT_VER_MASK ;
version > > = HOST_BUF_COEFF_COMPAT_VER_SHIFT ;
2018-07-19 13:50:39 +03:00
2020-12-30 20:24:26 +03:00
if ( version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER ) {
2021-09-13 19:00:51 +03:00
adsp_err ( dsp ,
2019-02-22 13:04:20 +03:00
" Host buffer coeff ver %u > supported version %u \n " ,
2020-12-30 20:24:26 +03:00
version , HOST_BUF_COEFF_SUPPORTED_COMPAT_VER ) ;
2022-02-10 20:20:52 +03:00
ret = - EINVAL ;
goto err ;
2019-02-22 13:04:20 +03:00
}
2021-09-13 19:00:46 +03:00
cs_dsp_remove_padding ( ( u32 * ) & coeff_v1 . name , ARRAY_SIZE ( coeff_v1 . name ) ) ;
2019-02-22 13:04:20 +03:00
2021-09-13 19:00:51 +03:00
buf - > name = kasprintf ( GFP_KERNEL , " %s-dsp-%s " , dsp - > part ,
2019-02-22 13:04:20 +03:00
( char * ) & coeff_v1 . name ) ;
2022-02-10 20:20:52 +03:00
done :
list_add_tail ( & buf - > list , & dsp - > buffer_list ) ;
2019-02-22 13:04:21 +03:00
compr_dbg ( buf , " host_buf_ptr=%x coeff version %u \n " ,
2020-12-30 20:24:26 +03:00
buf - > host_buf_ptr , version ) ;
2019-02-22 13:04:20 +03:00
2020-12-30 20:24:26 +03:00
return version ;
2022-02-10 20:20:52 +03:00
err :
kfree ( buf ) ;
return ret ;
2018-07-19 13:50:39 +03:00
}
2015-12-15 14:29:46 +03:00
static int wm_adsp_buffer_init ( struct wm_adsp * dsp )
{
2021-09-13 19:00:51 +03:00
struct cs_dsp_coeff_ctl * cs_ctl ;
2015-12-15 14:29:46 +03:00
int ret ;
2021-09-13 19:00:55 +03:00
list_for_each_entry ( cs_ctl , & dsp - > cs_dsp . ctl_list , list ) {
2021-09-13 19:00:51 +03:00
if ( cs_ctl - > type ! = WMFW_CTL_TYPE_HOST_BUFFER )
2019-02-22 13:04:19 +03:00
continue ;
2018-02-26 13:49:47 +03:00
2021-09-13 19:00:51 +03:00
if ( ! cs_ctl - > enabled )
2019-02-22 13:04:19 +03:00
continue ;
2015-12-15 14:29:46 +03:00
2021-09-13 19:00:51 +03:00
ret = wm_adsp_buffer_parse_coeff ( cs_ctl ) ;
2019-02-22 13:04:19 +03:00
if ( ret < 0 ) {
adsp_err ( dsp , " Failed to parse coeff: %d \n " , ret ) ;
goto error ;
2019-02-22 13:04:20 +03:00
} else if ( ret = = 0 ) {
/* Only one buffer supported for version 0 */
return 0 ;
2019-02-22 13:04:19 +03:00
}
2015-12-15 14:29:46 +03:00
}
2019-02-22 13:04:20 +03:00
if ( list_empty ( & dsp - > buffer_list ) ) {
2019-02-22 13:04:19 +03:00
/* Fall back to legacy support */
ret = wm_adsp_buffer_parse_legacy ( dsp ) ;
2022-08-26 01:05:30 +03:00
if ( ret = = - ENODEV )
adsp_info ( dsp , " Legacy support not available \n " ) ;
else if ( ret )
2022-02-10 20:20:52 +03:00
adsp_warn ( dsp , " Failed to parse legacy: %d \n " , ret ) ;
2015-12-15 14:29:46 +03:00
}
return 0 ;
2019-02-22 13:04:19 +03:00
error :
wm_adsp_buffer_free ( dsp ) ;
2015-12-15 14:29:46 +03:00
return ret ;
}
static int wm_adsp_buffer_free ( struct wm_adsp * dsp )
{
2019-02-22 13:04:20 +03:00
struct wm_adsp_compr_buf * buf , * tmp ;
2016-05-04 19:11:56 +03:00
2019-02-22 13:04:20 +03:00
list_for_each_entry_safe ( buf , tmp , & dsp - > buffer_list , list ) {
2019-04-02 15:49:15 +03:00
wm_adsp_compr_detach ( buf - > compr ) ;
2015-12-15 14:29:46 +03:00
2019-02-22 13:04:20 +03:00
kfree ( buf - > name ) ;
kfree ( buf - > regions ) ;
list_del ( & buf - > list ) ;
kfree ( buf ) ;
2015-12-15 14:29:46 +03:00
}
return 0 ;
}
2019-02-19 20:31:57 +03:00
static int wm_adsp_buffer_get_error ( struct wm_adsp_compr_buf * buf )
{
int ret ;
ret = wm_adsp_buffer_read ( buf , HOST_BUFFER_FIELD ( error ) , & buf - > error ) ;
if ( ret < 0 ) {
2019-03-19 14:52:05 +03:00
compr_err ( buf , " Failed to check buffer error: %d \n " , ret ) ;
2019-02-19 20:31:57 +03:00
return ret ;
}
if ( buf - > error ! = 0 ) {
2019-03-19 14:52:05 +03:00
compr_err ( buf , " Buffer error occurred: %d \n " , buf - > error ) ;
2019-02-19 20:31:57 +03:00
return - EIO ;
}
return 0 ;
}
2020-04-20 10:08:13 +03:00
int wm_adsp_compr_trigger ( struct snd_soc_component * component ,
struct snd_compr_stream * stream , int cmd )
2015-12-15 14:29:47 +03:00
{
struct wm_adsp_compr * compr = stream - > runtime - > private_data ;
struct wm_adsp * dsp = compr - > dsp ;
int ret = 0 ;
2019-02-22 13:04:21 +03:00
compr_dbg ( compr , " Trigger: %d \n " , cmd ) ;
2015-12-15 14:29:47 +03:00
2021-09-13 19:00:55 +03:00
mutex_lock ( & dsp - > cs_dsp . pwr_lock ) ;
2015-12-15 14:29:47 +03:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
2018-02-26 13:49:47 +03:00
if ( ! wm_adsp_compr_attached ( compr ) ) {
ret = wm_adsp_compr_attach ( compr ) ;
if ( ret < 0 ) {
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Failed to link buffer and stream: %d \n " ,
ret ) ;
2018-02-26 13:49:47 +03:00
break ;
}
2015-12-15 14:29:47 +03:00
}
2016-01-06 15:33:18 +03:00
2019-02-19 20:31:57 +03:00
ret = wm_adsp_buffer_get_error ( compr - > buf ) ;
if ( ret < 0 )
break ;
2016-01-06 15:33:18 +03:00
/* Trigger the IRQ at one fragment of data */
ret = wm_adsp_buffer_write ( compr - > buf ,
HOST_BUFFER_FIELD ( high_water_mark ) ,
wm_adsp_compr_frag_words ( compr ) ) ;
if ( ret < 0 ) {
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Failed to set high water mark: %d \n " ,
ret ) ;
2016-01-06 15:33:18 +03:00
break ;
}
2015-12-15 14:29:47 +03:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
2019-04-02 15:49:14 +03:00
if ( wm_adsp_compr_attached ( compr ) )
wm_adsp_buffer_clear ( compr - > buf ) ;
2015-12-15 14:29:47 +03:00
break ;
default :
ret = - EINVAL ;
break ;
}
2021-09-13 19:00:55 +03:00
mutex_unlock ( & dsp - > cs_dsp . pwr_lock ) ;
2015-12-15 14:29:47 +03:00
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_trigger ) ;
2016-01-06 15:33:18 +03:00
static inline int wm_adsp_buffer_size ( struct wm_adsp_compr_buf * buf )
{
int last_region = wm_adsp_fw [ buf - > dsp - > fw ] . caps - > num_regions - 1 ;
return buf - > regions [ last_region ] . cumulative_size ;
}
static int wm_adsp_buffer_update_avail ( struct wm_adsp_compr_buf * buf )
{
u32 next_read_index , next_write_index ;
int write_index , read_index , avail ;
int ret ;
/* Only sync read index if we haven't already read a valid index */
if ( buf - > read_index < 0 ) {
ret = wm_adsp_buffer_read ( buf ,
HOST_BUFFER_FIELD ( next_read_index ) ,
& next_read_index ) ;
if ( ret < 0 )
return ret ;
read_index = sign_extend32 ( next_read_index , 23 ) ;
if ( read_index < 0 ) {
2019-02-22 13:04:21 +03:00
compr_dbg ( buf , " Avail check on unstarted stream \n " ) ;
2016-01-06 15:33:18 +03:00
return 0 ;
}
buf - > read_index = read_index ;
}
ret = wm_adsp_buffer_read ( buf , HOST_BUFFER_FIELD ( next_write_index ) ,
& next_write_index ) ;
if ( ret < 0 )
return ret ;
write_index = sign_extend32 ( next_write_index , 23 ) ;
avail = write_index - buf - > read_index ;
if ( avail < 0 )
avail + = wm_adsp_buffer_size ( buf ) ;
2019-02-22 13:04:21 +03:00
compr_dbg ( buf , " readindex=0x%x, writeindex=0x%x, avail=%d \n " ,
2021-09-13 19:00:46 +03:00
buf - > read_index , write_index , avail * CS_DSP_DATA_WORD_SIZE ) ;
2016-01-06 15:33:18 +03:00
buf - > avail = avail ;
return 0 ;
}
int wm_adsp_compr_handle_irq ( struct wm_adsp * dsp )
{
2016-03-28 16:29:22 +03:00
struct wm_adsp_compr_buf * buf ;
struct wm_adsp_compr * compr ;
2016-01-06 15:33:18 +03:00
int ret = 0 ;
2021-09-13 19:00:55 +03:00
mutex_lock ( & dsp - > cs_dsp . pwr_lock ) ;
2016-01-06 15:33:18 +03:00
2019-02-22 13:04:20 +03:00
if ( list_empty ( & dsp - > buffer_list ) ) {
2016-01-06 15:33:18 +03:00
ret = - ENODEV ;
goto out ;
}
2019-02-22 13:04:21 +03:00
2016-01-06 15:33:18 +03:00
adsp_dbg ( dsp , " Handling buffer IRQ \n " ) ;
2019-02-22 13:04:20 +03:00
list_for_each_entry ( buf , & dsp - > buffer_list , list ) {
compr = buf - > compr ;
2016-01-06 15:33:18 +03:00
2019-02-22 13:04:20 +03:00
ret = wm_adsp_buffer_get_error ( buf ) ;
if ( ret < 0 )
goto out_notify ; /* Wake poll to report error */
2016-01-06 15:33:18 +03:00
2019-02-22 13:04:20 +03:00
ret = wm_adsp_buffer_read ( buf , HOST_BUFFER_FIELD ( irq_count ) ,
& buf - > irq_count ) ;
if ( ret < 0 ) {
2019-02-22 13:04:21 +03:00
compr_err ( buf , " Failed to get irq_count: %d \n " , ret ) ;
2019-02-22 13:04:20 +03:00
goto out ;
}
2016-01-06 15:33:18 +03:00
2019-02-22 13:04:20 +03:00
ret = wm_adsp_buffer_update_avail ( buf ) ;
if ( ret < 0 ) {
2019-02-22 13:04:21 +03:00
compr_err ( buf , " Error reading avail: %d \n " , ret ) ;
2019-02-22 13:04:20 +03:00
goto out ;
}
if ( wm_adsp_fw [ dsp - > fw ] . voice_trigger & & buf - > irq_count = = 2 )
ret = WM_ADSP_COMPR_VOICE_TRIGGER ;
2016-05-13 18:45:17 +03:00
2016-04-06 13:21:54 +03:00
out_notify :
2019-02-22 13:04:20 +03:00
if ( compr & & compr - > stream )
snd_compr_fragment_elapsed ( compr - > stream ) ;
}
2016-01-06 15:33:19 +03:00
2016-01-06 15:33:18 +03:00
out :
2021-09-13 19:00:55 +03:00
mutex_unlock ( & dsp - > cs_dsp . pwr_lock ) ;
2016-01-06 15:33:18 +03:00
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_handle_irq ) ;
static int wm_adsp_buffer_reenable_irq ( struct wm_adsp_compr_buf * buf )
{
if ( buf - > irq_count & 0x01 )
return 0 ;
2019-02-22 13:04:21 +03:00
compr_dbg ( buf , " Enable IRQ(0x%x) for next fragment \n " , buf - > irq_count ) ;
2016-01-06 15:33:18 +03:00
buf - > irq_count | = 0x01 ;
return wm_adsp_buffer_write ( buf , HOST_BUFFER_FIELD ( irq_ack ) ,
buf - > irq_count ) ;
}
2020-04-20 10:08:13 +03:00
int wm_adsp_compr_pointer ( struct snd_soc_component * component ,
struct snd_compr_stream * stream ,
2016-01-06 15:33:18 +03:00
struct snd_compr_tstamp * tstamp )
{
struct wm_adsp_compr * compr = stream - > runtime - > private_data ;
struct wm_adsp * dsp = compr - > dsp ;
2016-03-28 16:29:22 +03:00
struct wm_adsp_compr_buf * buf ;
2016-01-06 15:33:18 +03:00
int ret = 0 ;
2019-02-22 13:04:21 +03:00
compr_dbg ( compr , " Pointer request \n " ) ;
2016-01-06 15:33:18 +03:00
2021-09-13 19:00:55 +03:00
mutex_lock ( & dsp - > cs_dsp . pwr_lock ) ;
2016-01-06 15:33:18 +03:00
2016-03-28 16:29:22 +03:00
buf = compr - > buf ;
2019-04-04 15:56:01 +03:00
if ( dsp - > fatal_error | | ! buf | | buf - > error ) {
2016-06-13 16:17:11 +03:00
snd_compr_stop_error ( stream , SNDRV_PCM_STATE_XRUN ) ;
2016-01-06 15:33:18 +03:00
ret = - EIO ;
goto out ;
}
if ( buf - > avail < wm_adsp_compr_frag_words ( compr ) ) {
ret = wm_adsp_buffer_update_avail ( buf ) ;
if ( ret < 0 ) {
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Error reading avail: %d \n " , ret ) ;
2016-01-06 15:33:18 +03:00
goto out ;
}
/*
* If we really have less than 1 fragment available tell the
* DSP to inform us once a whole fragment is available .
*/
if ( buf - > avail < wm_adsp_compr_frag_words ( compr ) ) {
2016-04-06 13:21:54 +03:00
ret = wm_adsp_buffer_get_error ( buf ) ;
2016-06-13 16:17:11 +03:00
if ( ret < 0 ) {
2019-04-02 15:49:16 +03:00
if ( buf - > error )
2016-06-13 16:17:11 +03:00
snd_compr_stop_error ( stream ,
SNDRV_PCM_STATE_XRUN ) ;
2016-04-06 13:21:54 +03:00
goto out ;
2016-06-13 16:17:11 +03:00
}
2016-04-06 13:21:54 +03:00
2016-01-06 15:33:18 +03:00
ret = wm_adsp_buffer_reenable_irq ( buf ) ;
if ( ret < 0 ) {
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Failed to re-enable buffer IRQ: %d \n " ,
ret ) ;
2016-01-06 15:33:18 +03:00
goto out ;
}
}
}
tstamp - > copied_total = compr - > copied_total ;
2021-09-13 19:00:46 +03:00
tstamp - > copied_total + = buf - > avail * CS_DSP_DATA_WORD_SIZE ;
2016-02-02 19:41:36 +03:00
tstamp - > sampling_rate = compr - > sample_rate ;
2016-01-06 15:33:18 +03:00
out :
2021-09-13 19:00:55 +03:00
mutex_unlock ( & dsp - > cs_dsp . pwr_lock ) ;
2016-01-06 15:33:18 +03:00
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_pointer ) ;
2016-01-06 15:33:19 +03:00
static int wm_adsp_buffer_capture_block ( struct wm_adsp_compr * compr , int target )
{
struct wm_adsp_compr_buf * buf = compr - > buf ;
unsigned int adsp_addr ;
int mem_type , nwords , max_read ;
2019-02-22 13:04:17 +03:00
int i , ret ;
2016-01-06 15:33:19 +03:00
/* Calculate read parameters */
for ( i = 0 ; i < wm_adsp_fw [ buf - > dsp - > fw ] . caps - > num_regions ; + + i )
if ( buf - > read_index < buf - > regions [ i ] . cumulative_size )
break ;
if ( i = = wm_adsp_fw [ buf - > dsp - > fw ] . caps - > num_regions )
return - EINVAL ;
mem_type = buf - > regions [ i ] . mem_type ;
adsp_addr = buf - > regions [ i ] . base_addr +
( buf - > read_index - buf - > regions [ i ] . offset ) ;
max_read = wm_adsp_compr_frag_words ( compr ) ;
nwords = buf - > regions [ i ] . cumulative_size - buf - > read_index ;
if ( nwords > target )
nwords = target ;
if ( nwords > buf - > avail )
nwords = buf - > avail ;
if ( nwords > max_read )
nwords = max_read ;
if ( ! nwords )
return 0 ;
/* Read data from DSP */
2021-09-13 19:00:55 +03:00
ret = cs_dsp_read_raw_data_block ( & buf - > dsp - > cs_dsp , mem_type , adsp_addr ,
2021-09-13 19:00:46 +03:00
nwords , ( __be32 * ) compr - > raw_buf ) ;
2016-01-06 15:33:19 +03:00
if ( ret < 0 )
return ret ;
2021-09-13 19:00:46 +03:00
cs_dsp_remove_padding ( compr - > raw_buf , nwords ) ;
2016-01-06 15:33:19 +03:00
/* update read index to account for words read */
buf - > read_index + = nwords ;
if ( buf - > read_index = = wm_adsp_buffer_size ( buf ) )
buf - > read_index = 0 ;
ret = wm_adsp_buffer_write ( buf , HOST_BUFFER_FIELD ( next_read_index ) ,
buf - > read_index ) ;
if ( ret < 0 )
return ret ;
/* update avail to account for words read */
buf - > avail - = nwords ;
return nwords ;
}
static int wm_adsp_compr_read ( struct wm_adsp_compr * compr ,
char __user * buf , size_t count )
{
2019-04-04 15:56:01 +03:00
struct wm_adsp * dsp = compr - > dsp ;
2016-01-06 15:33:19 +03:00
int ntotal = 0 ;
int nwords , nbytes ;
2019-02-22 13:04:21 +03:00
compr_dbg ( compr , " Requested read of %zu bytes \n " , count ) ;
2016-01-06 15:33:19 +03:00
2019-04-04 15:56:01 +03:00
if ( dsp - > fatal_error | | ! compr - > buf | | compr - > buf - > error ) {
2016-06-13 16:17:11 +03:00
snd_compr_stop_error ( compr - > stream , SNDRV_PCM_STATE_XRUN ) ;
2016-01-06 15:33:19 +03:00
return - EIO ;
2016-06-13 16:17:11 +03:00
}
2016-01-06 15:33:19 +03:00
2021-09-13 19:00:46 +03:00
count / = CS_DSP_DATA_WORD_SIZE ;
2016-01-06 15:33:19 +03:00
do {
nwords = wm_adsp_buffer_capture_block ( compr , count ) ;
if ( nwords < 0 ) {
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Failed to capture block: %d \n " ,
nwords ) ;
2016-01-06 15:33:19 +03:00
return nwords ;
}
2021-09-13 19:00:46 +03:00
nbytes = nwords * CS_DSP_DATA_WORD_SIZE ;
2016-01-06 15:33:19 +03:00
2019-02-22 13:04:21 +03:00
compr_dbg ( compr , " Read %d bytes \n " , nbytes ) ;
2016-01-06 15:33:19 +03:00
if ( copy_to_user ( buf + ntotal , compr - > raw_buf , nbytes ) ) {
2019-02-22 13:04:21 +03:00
compr_err ( compr , " Failed to copy data to user: %d, %d \n " ,
ntotal , nbytes ) ;
2016-01-06 15:33:19 +03:00
return - EFAULT ;
}
count - = nwords ;
ntotal + = nbytes ;
} while ( nwords > 0 & & count > 0 ) ;
compr - > copied_total + = ntotal ;
return ntotal ;
}
2020-04-20 10:08:13 +03:00
int wm_adsp_compr_copy ( struct snd_soc_component * component ,
struct snd_compr_stream * stream , char __user * buf ,
2016-01-06 15:33:19 +03:00
size_t count )
{
struct wm_adsp_compr * compr = stream - > runtime - > private_data ;
struct wm_adsp * dsp = compr - > dsp ;
int ret ;
2021-09-13 19:00:55 +03:00
mutex_lock ( & dsp - > cs_dsp . pwr_lock ) ;
2016-01-06 15:33:19 +03:00
if ( stream - > direction = = SND_COMPRESS_CAPTURE )
ret = wm_adsp_compr_read ( compr , buf , count ) ;
else
ret = - ENOTSUPP ;
2021-09-13 19:00:55 +03:00
mutex_unlock ( & dsp - > cs_dsp . pwr_lock ) ;
2016-01-06 15:33:19 +03:00
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp_compr_copy ) ;
2021-09-13 19:00:55 +03:00
static void wm_adsp_fatal_error ( struct cs_dsp * cs_dsp )
2019-03-19 14:52:07 +03:00
{
2021-09-13 19:00:55 +03:00
struct wm_adsp * dsp = container_of ( cs_dsp , struct wm_adsp , cs_dsp ) ;
2019-03-19 14:52:07 +03:00
struct wm_adsp_compr * compr ;
dsp - > fatal_error = true ;
list_for_each_entry ( compr , & dsp - > compr_list , list ) {
2019-04-04 15:56:01 +03:00
if ( compr - > stream )
2019-03-19 14:52:07 +03:00
snd_compr_fragment_elapsed ( compr - > stream ) ;
}
}
2021-09-13 19:00:48 +03:00
irqreturn_t wm_adsp2_bus_error ( int irq , void * data )
{
struct wm_adsp * dsp = ( struct wm_adsp * ) data ;
2021-09-13 19:00:55 +03:00
cs_dsp_adsp2_bus_error ( & dsp - > cs_dsp ) ;
2019-03-19 14:52:06 +03:00
2017-04-05 13:08:00 +03:00
return IRQ_HANDLED ;
}
EXPORT_SYMBOL_GPL ( wm_adsp2_bus_error ) ;
2021-09-13 19:00:48 +03:00
irqreturn_t wm_halo_bus_error ( int irq , void * data )
{
struct wm_adsp * dsp = ( struct wm_adsp * ) data ;
2021-09-13 19:00:55 +03:00
cs_dsp_halo_bus_error ( & dsp - > cs_dsp ) ;
2019-03-19 14:52:14 +03:00
return IRQ_HANDLED ;
}
EXPORT_SYMBOL_GPL ( wm_halo_bus_error ) ;
2021-09-13 19:00:48 +03:00
irqreturn_t wm_halo_wdt_expire ( int irq , void * data )
{
struct wm_adsp * dsp = data ;
2021-09-13 19:00:55 +03:00
cs_dsp_halo_wdt_expire ( & dsp - > cs_dsp ) ;
2019-03-19 14:52:15 +03:00
return IRQ_HANDLED ;
}
EXPORT_SYMBOL_GPL ( wm_halo_wdt_expire ) ;
2021-09-13 19:00:56 +03:00
static const struct cs_dsp_client_ops wm_adsp1_client_ops = {
. control_add = wm_adsp_control_add ,
. control_remove = wm_adsp_control_remove ,
} ;
static const struct cs_dsp_client_ops wm_adsp2_client_ops = {
. control_add = wm_adsp_control_add ,
. control_remove = wm_adsp_control_remove ,
2022-11-09 19:53:29 +03:00
. pre_run = wm_adsp_pre_run ,
2021-09-13 19:00:56 +03:00
. post_run = wm_adsp_event_post_run ,
. post_stop = wm_adsp_event_post_stop ,
. watchdog_expired = wm_adsp_fatal_error ,
} ;
2014-07-04 09:47:41 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2022-11-24 16:45:56 +03:00
MODULE_IMPORT_NS ( FW_CS_DSP ) ;