xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision 2abdf9f8)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22159ad93SMark Brown /*
32159ad93SMark Brown  * wm_adsp.c  --  Wolfson ADSP support
42159ad93SMark Brown  *
52159ad93SMark Brown  * Copyright 2012 Wolfson Microelectronics plc
62159ad93SMark Brown  *
72159ad93SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
82159ad93SMark Brown  */
92159ad93SMark Brown 
10605391d0SRichard Fitzgerald #include <linux/ctype.h>
112159ad93SMark Brown #include <linux/module.h>
122159ad93SMark Brown #include <linux/moduleparam.h>
132159ad93SMark Brown #include <linux/init.h>
142159ad93SMark Brown #include <linux/delay.h>
152159ad93SMark Brown #include <linux/firmware.h>
16cf17c83cSMark Brown #include <linux/list.h>
172159ad93SMark Brown #include <linux/pm.h>
182159ad93SMark Brown #include <linux/pm_runtime.h>
192159ad93SMark Brown #include <linux/regmap.h>
20973838a0SMark Brown #include <linux/regulator/consumer.h>
212159ad93SMark Brown #include <linux/slab.h>
226ab2b7b4SDimitris Papastamos #include <linux/workqueue.h>
23f9f55e31SRichard Fitzgerald #include <linux/debugfs.h>
242159ad93SMark Brown #include <sound/core.h>
252159ad93SMark Brown #include <sound/pcm.h>
262159ad93SMark Brown #include <sound/pcm_params.h>
272159ad93SMark Brown #include <sound/soc.h>
282159ad93SMark Brown #include <sound/jack.h>
292159ad93SMark Brown #include <sound/initval.h>
302159ad93SMark Brown #include <sound/tlv.h>
312159ad93SMark Brown 
322159ad93SMark Brown #include "wm_adsp.h"
332159ad93SMark Brown 
342159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \
35e1468202SSimon Trimmer 	dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
362159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \
37e1468202SSimon Trimmer 	dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
382159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \
39e1468202SSimon Trimmer 	dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
402159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \
41e1468202SSimon Trimmer 	dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
422159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \
43e1468202SSimon Trimmer 	dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
442159ad93SMark Brown 
450d3fba3eSCharles Keepax #define compr_err(_obj, fmt, ...) \
460d3fba3eSCharles Keepax 	adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
470d3fba3eSCharles Keepax 		 ##__VA_ARGS__)
480d3fba3eSCharles Keepax #define compr_dbg(_obj, fmt, ...) \
490d3fba3eSCharles Keepax 	adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
500d3fba3eSCharles Keepax 		 ##__VA_ARGS__)
510d3fba3eSCharles Keepax 
529ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE               512
539ee78757SCharles Keepax 
542dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp1_client_ops;
552dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp2_client_ops;
562dd04464SSimon Trimmer 
57dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS  0
5804d1300fSCharles Keepax #define WM_ADSP_FW_HIFI     1
5904d1300fSCharles Keepax #define WM_ADSP_FW_TX       2
6004d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK   3
6104d1300fSCharles Keepax #define WM_ADSP_FW_RX       4
6204d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC   5
6304d1300fSCharles Keepax #define WM_ADSP_FW_CTRL     6
6404d1300fSCharles Keepax #define WM_ADSP_FW_ASR      7
6504d1300fSCharles Keepax #define WM_ADSP_FW_TRACE    8
6604d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9
67d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_CALI 10
68d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_DIAG 11
69d6fea46eSVlad Karpovich #define WM_ADSP_FW_MISC     12
7004d1300fSCharles Keepax 
71d6fea46eSVlad Karpovich #define WM_ADSP_NUM_FW      13
72dd84f925SMark Brown 
731023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
74dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] =  "MBC/VSS",
7504d1300fSCharles Keepax 	[WM_ADSP_FW_HIFI] =     "MasterHiFi",
76dd84f925SMark Brown 	[WM_ADSP_FW_TX] =       "Tx",
77dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =   "Tx Speaker",
7804d1300fSCharles Keepax 	[WM_ADSP_FW_RX] =       "Rx",
79dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =   "Rx ANC",
8004d1300fSCharles Keepax 	[WM_ADSP_FW_CTRL] =     "Voice Ctrl",
8104d1300fSCharles Keepax 	[WM_ADSP_FW_ASR] =      "ASR Assist",
8204d1300fSCharles Keepax 	[WM_ADSP_FW_TRACE] =    "Dbg Trace",
8304d1300fSCharles Keepax 	[WM_ADSP_FW_SPK_PROT] = "Protection",
84d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_CALI] = "Calibration",
85d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_DIAG] = "Diagnostic",
8604d1300fSCharles Keepax 	[WM_ADSP_FW_MISC] =     "Misc",
871023dbd9SMark Brown };
881023dbd9SMark Brown 
892cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr {
902cd19bdbSCharles Keepax 	__be32 sys_enable;
912cd19bdbSCharles Keepax 	__be32 fw_id;
922cd19bdbSCharles Keepax 	__be32 fw_rev;
932cd19bdbSCharles Keepax 	__be32 boot_status;
942cd19bdbSCharles Keepax 	__be32 watchdog;
952cd19bdbSCharles Keepax 	__be32 dma_buffer_size;
962cd19bdbSCharles Keepax 	__be32 rdma[6];
972cd19bdbSCharles Keepax 	__be32 wdma[8];
982cd19bdbSCharles Keepax 	__be32 build_job_name[3];
992cd19bdbSCharles Keepax 	__be32 build_job_number;
1002cd19bdbSCharles Keepax };
1012cd19bdbSCharles Keepax 
102170b1e12SWen Shi struct wm_halo_system_config_xm_hdr {
103170b1e12SWen Shi 	__be32 halo_heartbeat;
104170b1e12SWen Shi 	__be32 build_job_name[3];
105170b1e12SWen Shi 	__be32 build_job_number;
106170b1e12SWen Shi };
107170b1e12SWen Shi 
1082cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct {
1092cd19bdbSCharles Keepax 	__be32 magic;
1102cd19bdbSCharles Keepax 	__be32 smoothing;
1112cd19bdbSCharles Keepax 	__be32 threshold;
1122cd19bdbSCharles Keepax 	__be32 host_buf_ptr;
1132cd19bdbSCharles Keepax 	__be32 start_seq;
1142cd19bdbSCharles Keepax 	__be32 high_water_mark;
1152cd19bdbSCharles Keepax 	__be32 low_water_mark;
1162cd19bdbSCharles Keepax 	__be64 smoothed_power;
1172cd19bdbSCharles Keepax };
1182cd19bdbSCharles Keepax 
1194f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 {
1204f2d4eabSStuart Henderson 	__be32 host_buf_ptr;		/* Host buffer pointer */
1214f2d4eabSStuart Henderson 	__be32 versions;		/* Version numbers */
1224f2d4eabSStuart Henderson 	__be32 name[4];			/* The buffer name */
1234f2d4eabSStuart Henderson };
1244f2d4eabSStuart Henderson 
1252cd19bdbSCharles Keepax struct wm_adsp_buffer {
1262a2aefa4SRichard Fitzgerald 	__be32 buf1_base;		/* Base addr of first buffer area */
1272a2aefa4SRichard Fitzgerald 	__be32 buf1_size;		/* Size of buf1 area in DSP words */
1282a2aefa4SRichard Fitzgerald 	__be32 buf2_base;		/* Base addr of 2nd buffer area */
1292a2aefa4SRichard Fitzgerald 	__be32 buf1_buf2_size;		/* Size of buf1+buf2 in DSP words */
1302a2aefa4SRichard Fitzgerald 	__be32 buf3_base;		/* Base addr of buf3 area */
1312a2aefa4SRichard Fitzgerald 	__be32 buf_total_size;		/* Size of buf1+buf2+buf3 in DSP words */
1322cd19bdbSCharles Keepax 	__be32 high_water_mark;		/* Point at which IRQ is asserted */
1332cd19bdbSCharles Keepax 	__be32 irq_count;		/* bits 1-31 count IRQ assertions */
1342cd19bdbSCharles Keepax 	__be32 irq_ack;			/* acked IRQ count, bit 0 enables IRQ */
1352cd19bdbSCharles Keepax 	__be32 next_write_index;	/* word index of next write */
1362cd19bdbSCharles Keepax 	__be32 next_read_index;		/* word index of next read */
1372cd19bdbSCharles Keepax 	__be32 error;			/* error if any */
1382cd19bdbSCharles Keepax 	__be32 oldest_block_index;	/* word index of oldest surviving */
1392cd19bdbSCharles Keepax 	__be32 requested_rewind;	/* how many blocks rewind was done */
1402cd19bdbSCharles Keepax 	__be32 reserved_space;		/* internal */
1412cd19bdbSCharles Keepax 	__be32 min_free;		/* min free space since stream start */
1422cd19bdbSCharles Keepax 	__be32 blocks_written[2];	/* total blocks written (64 bit) */
1432cd19bdbSCharles Keepax 	__be32 words_written[2];	/* total words written (64 bit) */
1442cd19bdbSCharles Keepax };
1452cd19bdbSCharles Keepax 
146721be3beSCharles Keepax struct wm_adsp_compr;
147721be3beSCharles Keepax 
1482cd19bdbSCharles Keepax struct wm_adsp_compr_buf {
1494f2d4eabSStuart Henderson 	struct list_head list;
1502cd19bdbSCharles Keepax 	struct wm_adsp *dsp;
151721be3beSCharles Keepax 	struct wm_adsp_compr *compr;
1522cd19bdbSCharles Keepax 
1532cd19bdbSCharles Keepax 	struct wm_adsp_buffer_region *regions;
1542cd19bdbSCharles Keepax 	u32 host_buf_ptr;
155565ace46SCharles Keepax 
156565ace46SCharles Keepax 	u32 error;
157565ace46SCharles Keepax 	u32 irq_count;
158565ace46SCharles Keepax 	int read_index;
159565ace46SCharles Keepax 	int avail;
160fb13f19dSAndrew Ford 	int host_buf_mem_type;
1614f2d4eabSStuart Henderson 
1624f2d4eabSStuart Henderson 	char *name;
1632cd19bdbSCharles Keepax };
1642cd19bdbSCharles Keepax 
165406abc95SCharles Keepax struct wm_adsp_compr {
1664f2d4eabSStuart Henderson 	struct list_head list;
167406abc95SCharles Keepax 	struct wm_adsp *dsp;
16895fe9597SCharles Keepax 	struct wm_adsp_compr_buf *buf;
169406abc95SCharles Keepax 
170406abc95SCharles Keepax 	struct snd_compr_stream *stream;
171406abc95SCharles Keepax 	struct snd_compressed_buffer size;
172565ace46SCharles Keepax 
17383a40ce9SCharles Keepax 	u32 *raw_buf;
174565ace46SCharles Keepax 	unsigned int copied_total;
175da2b3358SCharles Keepax 
176da2b3358SCharles Keepax 	unsigned int sample_rate;
1774f2d4eabSStuart Henderson 
1784f2d4eabSStuart Henderson 	const char *name;
179406abc95SCharles Keepax };
180406abc95SCharles Keepax 
181406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS          1
182406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS          256
183c55b3e46SVlad Karpovich #define WM_ADSP_MIN_FRAGMENT_SIZE      (16 * CS_DSP_DATA_WORD_SIZE)
1845beb8eeaSSimon Trimmer #define WM_ADSP_MAX_FRAGMENT_SIZE      (4096 * CS_DSP_DATA_WORD_SIZE)
185406abc95SCharles Keepax 
1862cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC    0x49aec7
1872cd19bdbSCharles Keepax 
1882cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \
1892cd19bdbSCharles Keepax 	(offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))
1902cd19bdbSCharles Keepax 
1912cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \
1922cd19bdbSCharles Keepax 	(offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))
1932cd19bdbSCharles Keepax 
1944f2d4eabSStuart Henderson #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER	1
1954f2d4eabSStuart Henderson 
1964f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_MASK		0xFF00
1974f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_SHIFT		8
1984f2d4eabSStuart Henderson 
1992cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp);
2002cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp);
2012cd19bdbSCharles Keepax 
2022cd19bdbSCharles Keepax struct wm_adsp_buffer_region {
2032cd19bdbSCharles Keepax 	unsigned int offset;
2042cd19bdbSCharles Keepax 	unsigned int cumulative_size;
2052cd19bdbSCharles Keepax 	unsigned int mem_type;
2062cd19bdbSCharles Keepax 	unsigned int base_addr;
2072cd19bdbSCharles Keepax };
2082cd19bdbSCharles Keepax 
2092cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def {
2102cd19bdbSCharles Keepax 	unsigned int mem_type;
2112cd19bdbSCharles Keepax 	unsigned int base_offset;
2122cd19bdbSCharles Keepax 	unsigned int size_offset;
2132cd19bdbSCharles Keepax };
2142cd19bdbSCharles Keepax 
2153a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = {
2162cd19bdbSCharles Keepax 	{
2172cd19bdbSCharles Keepax 		.mem_type = WMFW_ADSP2_XM,
2182a2aefa4SRichard Fitzgerald 		.base_offset = HOST_BUFFER_FIELD(buf1_base),
2192a2aefa4SRichard Fitzgerald 		.size_offset = HOST_BUFFER_FIELD(buf1_size),
2202cd19bdbSCharles Keepax 	},
2212cd19bdbSCharles Keepax 	{
2222cd19bdbSCharles Keepax 		.mem_type = WMFW_ADSP2_XM,
2232a2aefa4SRichard Fitzgerald 		.base_offset = HOST_BUFFER_FIELD(buf2_base),
2242a2aefa4SRichard Fitzgerald 		.size_offset = HOST_BUFFER_FIELD(buf1_buf2_size),
2252cd19bdbSCharles Keepax 	},
2262cd19bdbSCharles Keepax 	{
2272cd19bdbSCharles Keepax 		.mem_type = WMFW_ADSP2_YM,
2282a2aefa4SRichard Fitzgerald 		.base_offset = HOST_BUFFER_FIELD(buf3_base),
2292a2aefa4SRichard Fitzgerald 		.size_offset = HOST_BUFFER_FIELD(buf_total_size),
2302cd19bdbSCharles Keepax 	},
2312cd19bdbSCharles Keepax };
2322cd19bdbSCharles Keepax 
233406abc95SCharles Keepax struct wm_adsp_fw_caps {
234406abc95SCharles Keepax 	u32 id;
235406abc95SCharles Keepax 	struct snd_codec_desc desc;
2362cd19bdbSCharles Keepax 	int num_regions;
2373a9686c4SCharles Keepax 	const struct wm_adsp_buffer_region_def *region_defs;
238406abc95SCharles Keepax };
239406abc95SCharles Keepax 
240e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = {
241406abc95SCharles Keepax 	{
242406abc95SCharles Keepax 		.id = SND_AUDIOCODEC_BESPOKE,
243406abc95SCharles Keepax 		.desc = {
2443bbc2705SRichard Fitzgerald 			.max_ch = 8,
245406abc95SCharles Keepax 			.sample_rates = { 16000 },
246406abc95SCharles Keepax 			.num_sample_rates = 1,
247406abc95SCharles Keepax 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
248406abc95SCharles Keepax 		},
249e6d00f34SCharles Keepax 		.num_regions = ARRAY_SIZE(default_regions),
250e6d00f34SCharles Keepax 		.region_defs = default_regions,
251406abc95SCharles Keepax 	},
252406abc95SCharles Keepax };
253406abc95SCharles Keepax 
2547ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = {
2557ce4283cSCharles Keepax 	{
2567ce4283cSCharles Keepax 		.id = SND_AUDIOCODEC_BESPOKE,
2577ce4283cSCharles Keepax 		.desc = {
2587ce4283cSCharles Keepax 			.max_ch = 8,
2597ce4283cSCharles Keepax 			.sample_rates = {
2607ce4283cSCharles Keepax 				4000, 8000, 11025, 12000, 16000, 22050,
2617ce4283cSCharles Keepax 				24000, 32000, 44100, 48000, 64000, 88200,
2627ce4283cSCharles Keepax 				96000, 176400, 192000
2637ce4283cSCharles Keepax 			},
2647ce4283cSCharles Keepax 			.num_sample_rates = 15,
2657ce4283cSCharles Keepax 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
2667ce4283cSCharles Keepax 		},
2677ce4283cSCharles Keepax 		.num_regions = ARRAY_SIZE(default_regions),
2687ce4283cSCharles Keepax 		.region_defs = default_regions,
269406abc95SCharles Keepax 	},
270406abc95SCharles Keepax };
271406abc95SCharles Keepax 
272406abc95SCharles Keepax static const struct {
2731023dbd9SMark Brown 	const char *file;
274406abc95SCharles Keepax 	int compr_direction;
275406abc95SCharles Keepax 	int num_caps;
276406abc95SCharles Keepax 	const struct wm_adsp_fw_caps *caps;
27720b7f7c5SCharles Keepax 	bool voice_trigger;
2781023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = {
279dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] =  { .file = "mbc-vss" },
28004d1300fSCharles Keepax 	[WM_ADSP_FW_HIFI] =     { .file = "hifi" },
281dd84f925SMark Brown 	[WM_ADSP_FW_TX] =       { .file = "tx" },
282dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =   { .file = "tx-spk" },
28304d1300fSCharles Keepax 	[WM_ADSP_FW_RX] =       { .file = "rx" },
284dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =   { .file = "rx-anc" },
285406abc95SCharles Keepax 	[WM_ADSP_FW_CTRL] =     {
286406abc95SCharles Keepax 		.file = "ctrl",
287406abc95SCharles Keepax 		.compr_direction = SND_COMPRESS_CAPTURE,
288e6d00f34SCharles Keepax 		.num_caps = ARRAY_SIZE(ctrl_caps),
289e6d00f34SCharles Keepax 		.caps = ctrl_caps,
29020b7f7c5SCharles Keepax 		.voice_trigger = true,
291406abc95SCharles Keepax 	},
29204d1300fSCharles Keepax 	[WM_ADSP_FW_ASR] =      { .file = "asr" },
2937ce4283cSCharles Keepax 	[WM_ADSP_FW_TRACE] =    {
2947ce4283cSCharles Keepax 		.file = "trace",
2957ce4283cSCharles Keepax 		.compr_direction = SND_COMPRESS_CAPTURE,
2967ce4283cSCharles Keepax 		.num_caps = ARRAY_SIZE(trace_caps),
2977ce4283cSCharles Keepax 		.caps = trace_caps,
2987ce4283cSCharles Keepax 	},
299c55b3e46SVlad Karpovich 	[WM_ADSP_FW_SPK_PROT] = {
300c55b3e46SVlad Karpovich 		.file = "spk-prot",
301c55b3e46SVlad Karpovich 		.compr_direction = SND_COMPRESS_CAPTURE,
302c55b3e46SVlad Karpovich 		.num_caps = ARRAY_SIZE(trace_caps),
303c55b3e46SVlad Karpovich 		.caps = trace_caps,
304c55b3e46SVlad Karpovich 	},
305d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" },
306d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" },
30704d1300fSCharles Keepax 	[WM_ADSP_FW_MISC] =     { .file = "misc" },
3081023dbd9SMark Brown };
3091023dbd9SMark Brown 
3106ab2b7b4SDimitris Papastamos struct wm_coeff_ctl {
3116ab2b7b4SDimitris Papastamos 	const char *name;
3120700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
3139ee78757SCharles Keepax 	struct soc_bytes_ext bytes_ext;
314df6c505cSSimon Trimmer 	struct work_struct work;
3156ab2b7b4SDimitris Papastamos };
3166ab2b7b4SDimitris Papastamos 
3170a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
3181023dbd9SMark Brown 		   struct snd_ctl_elem_value *ucontrol)
3191023dbd9SMark Brown {
3200fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
3211023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
3220fe1daa6SKuninori Morimoto 	struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
3231023dbd9SMark Brown 
32415c66570STakashi Iwai 	ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
3251023dbd9SMark Brown 
3261023dbd9SMark Brown 	return 0;
3271023dbd9SMark Brown }
3280a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get);
3291023dbd9SMark Brown 
3300a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
3311023dbd9SMark Brown 		   struct snd_ctl_elem_value *ucontrol)
3321023dbd9SMark Brown {
3330fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
3341023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
3350fe1daa6SKuninori Morimoto 	struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
336*2abdf9f8SMark Brown 	int ret = 1;
3371023dbd9SMark Brown 
33815c66570STakashi Iwai 	if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
3391023dbd9SMark Brown 		return 0;
3401023dbd9SMark Brown 
34115c66570STakashi Iwai 	if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
3421023dbd9SMark Brown 		return -EINVAL;
3431023dbd9SMark Brown 
344e1468202SSimon Trimmer 	mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock);
3451023dbd9SMark Brown 
346e1468202SSimon Trimmer 	if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list))
347d27c5e15SCharles Keepax 		ret = -EBUSY;
348d27c5e15SCharles Keepax 	else
34915c66570STakashi Iwai 		dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
3501023dbd9SMark Brown 
351e1468202SSimon Trimmer 	mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock);
352d27c5e15SCharles Keepax 
353d27c5e15SCharles Keepax 	return ret;
3541023dbd9SMark Brown }
3550a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put);
3561023dbd9SMark Brown 
3570a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = {
3581023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3591023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3601023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3611023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
362e1ea1879SRichard Fitzgerald 	SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
363e1ea1879SRichard Fitzgerald 	SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
364e1ea1879SRichard Fitzgerald 	SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3651023dbd9SMark Brown };
3660a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
3672159ad93SMark Brown 
3689ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
3699ee78757SCharles Keepax {
3709ee78757SCharles Keepax 	return container_of(ext, struct wm_coeff_ctl, bytes_ext);
3719ee78757SCharles Keepax }
3729ee78757SCharles Keepax 
3737585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl,
3746ab2b7b4SDimitris Papastamos 			 struct snd_ctl_elem_info *uinfo)
3756ab2b7b4SDimitris Papastamos {
3769ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
3779ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
3789ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
3790700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
3806ab2b7b4SDimitris Papastamos 
3810700bc2fSSimon Trimmer 	switch (cs_ctl->type) {
382a23ebba8SRichard Fitzgerald 	case WMFW_CTL_TYPE_ACKED:
383a23ebba8SRichard Fitzgerald 		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3845beb8eeaSSimon Trimmer 		uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE;
3855beb8eeaSSimon Trimmer 		uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE;
386a23ebba8SRichard Fitzgerald 		uinfo->value.integer.step = 1;
387a23ebba8SRichard Fitzgerald 		uinfo->count = 1;
388a23ebba8SRichard Fitzgerald 		break;
389a23ebba8SRichard Fitzgerald 	default:
3906ab2b7b4SDimitris Papastamos 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
3910700bc2fSSimon Trimmer 		uinfo->count = cs_ctl->len;
392a23ebba8SRichard Fitzgerald 		break;
393a23ebba8SRichard Fitzgerald 	}
394a23ebba8SRichard Fitzgerald 
3956ab2b7b4SDimitris Papastamos 	return 0;
3966ab2b7b4SDimitris Papastamos }
3976ab2b7b4SDimitris Papastamos 
3987585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl,
3996ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4006ab2b7b4SDimitris Papastamos {
4019ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4029ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4039ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4040700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4056ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
406168d10e7SCharles Keepax 	int ret = 0;
407168d10e7SCharles Keepax 
4080700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
409f444da38SCharles Keepax 	ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
4100700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
411168d10e7SCharles Keepax 
412168d10e7SCharles Keepax 	return ret;
4136ab2b7b4SDimitris Papastamos }
4146ab2b7b4SDimitris Papastamos 
4159ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
4169ee78757SCharles Keepax 			    const unsigned int __user *bytes, unsigned int size)
4179ee78757SCharles Keepax {
4189ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4199ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4209ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4210700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4229ee78757SCharles Keepax 	int ret = 0;
4239ee78757SCharles Keepax 
4240700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
4259ee78757SCharles Keepax 
4260700bc2fSSimon Trimmer 	if (copy_from_user(cs_ctl->cache, bytes, size))
4279ee78757SCharles Keepax 		ret = -EFAULT;
42873ecf1a6SCharles Keepax 	else
429f444da38SCharles Keepax 		ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size);
4309ee78757SCharles Keepax 
4310700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
4329ee78757SCharles Keepax 
4339ee78757SCharles Keepax 	return ret;
4349ee78757SCharles Keepax }
4359ee78757SCharles Keepax 
436a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
437a23ebba8SRichard Fitzgerald 			      struct snd_ctl_elem_value *ucontrol)
438a23ebba8SRichard Fitzgerald {
439a23ebba8SRichard Fitzgerald 	struct soc_bytes_ext *bytes_ext =
440a23ebba8SRichard Fitzgerald 		(struct soc_bytes_ext *)kctl->private_value;
441a23ebba8SRichard Fitzgerald 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4420700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
443a23ebba8SRichard Fitzgerald 	unsigned int val = ucontrol->value.integer.value[0];
444a23ebba8SRichard Fitzgerald 	int ret;
445a23ebba8SRichard Fitzgerald 
446a23ebba8SRichard Fitzgerald 	if (val == 0)
447a23ebba8SRichard Fitzgerald 		return 0;	/* 0 means no event */
448a23ebba8SRichard Fitzgerald 
4490700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
450a23ebba8SRichard Fitzgerald 
451edb1d6d7SSimon Trimmer 	if (cs_ctl->enabled)
4520700bc2fSSimon Trimmer 		ret = cs_dsp_coeff_write_acked_control(cs_ctl, val);
453a23ebba8SRichard Fitzgerald 	else
454a23ebba8SRichard Fitzgerald 		ret = -EPERM;
455a23ebba8SRichard Fitzgerald 
4560700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
457a23ebba8SRichard Fitzgerald 
458a23ebba8SRichard Fitzgerald 	return ret;
459a23ebba8SRichard Fitzgerald }
460a23ebba8SRichard Fitzgerald 
4617585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl,
4626ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4636ab2b7b4SDimitris Papastamos {
4649ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4659ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4669ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4670700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4686ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
46973ecf1a6SCharles Keepax 	int ret;
470168d10e7SCharles Keepax 
4710700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
472f444da38SCharles Keepax 	ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
4730700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
47426c22a19SCharles Keepax 
475168d10e7SCharles Keepax 	return ret;
4766ab2b7b4SDimitris Papastamos }
4776ab2b7b4SDimitris Papastamos 
4789ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
4799ee78757SCharles Keepax 			    unsigned int __user *bytes, unsigned int size)
4809ee78757SCharles Keepax {
4819ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4829ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4839ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4840700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4859ee78757SCharles Keepax 	int ret = 0;
4869ee78757SCharles Keepax 
4870700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
4889ee78757SCharles Keepax 
489f444da38SCharles Keepax 	ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size);
4909ee78757SCharles Keepax 
4910700bc2fSSimon Trimmer 	if (!ret && copy_to_user(bytes, cs_ctl->cache, size))
4929ee78757SCharles Keepax 		ret = -EFAULT;
4939ee78757SCharles Keepax 
4940700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
4959ee78757SCharles Keepax 
4969ee78757SCharles Keepax 	return ret;
4979ee78757SCharles Keepax }
4989ee78757SCharles Keepax 
499a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
500a23ebba8SRichard Fitzgerald 			      struct snd_ctl_elem_value *ucontrol)
501a23ebba8SRichard Fitzgerald {
502a23ebba8SRichard Fitzgerald 	/*
503a23ebba8SRichard Fitzgerald 	 * Although it's not useful to read an acked control, we must satisfy
504a23ebba8SRichard Fitzgerald 	 * user-side assumptions that all controls are readable and that a
505a23ebba8SRichard Fitzgerald 	 * write of the same value should be filtered out (it's valid to send
506a23ebba8SRichard Fitzgerald 	 * the same event number again to the firmware). We therefore return 0,
507a23ebba8SRichard Fitzgerald 	 * meaning "no event" so valid event numbers will always be a change
508a23ebba8SRichard Fitzgerald 	 */
509a23ebba8SRichard Fitzgerald 	ucontrol->value.integer.value[0] = 0;
510a23ebba8SRichard Fitzgerald 
511a23ebba8SRichard Fitzgerald 	return 0;
512a23ebba8SRichard Fitzgerald }
513a23ebba8SRichard Fitzgerald 
5149ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
5159ee78757SCharles Keepax {
5169ee78757SCharles Keepax 	unsigned int out, rd, wr, vol;
5179ee78757SCharles Keepax 
5189ee78757SCharles Keepax 	if (len > ADSP_MAX_STD_CTRL_SIZE) {
5199ee78757SCharles Keepax 		rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
5209ee78757SCharles Keepax 		wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
5219ee78757SCharles Keepax 		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
5229ee78757SCharles Keepax 
5239ee78757SCharles Keepax 		out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
5249ee78757SCharles Keepax 	} else {
5259ee78757SCharles Keepax 		rd = SNDRV_CTL_ELEM_ACCESS_READ;
5269ee78757SCharles Keepax 		wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
5279ee78757SCharles Keepax 		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
5289ee78757SCharles Keepax 
5299ee78757SCharles Keepax 		out = 0;
5309ee78757SCharles Keepax 	}
5319ee78757SCharles Keepax 
5329ee78757SCharles Keepax 	if (in) {
5339ee78757SCharles Keepax 		out |= rd;
5349ee78757SCharles Keepax 		if (in & WMFW_CTL_FLAG_WRITEABLE)
5359ee78757SCharles Keepax 			out |= wr;
5369ee78757SCharles Keepax 		if (in & WMFW_CTL_FLAG_VOLATILE)
5379ee78757SCharles Keepax 			out |= vol;
5389ee78757SCharles Keepax 	} else {
5399ee78757SCharles Keepax 		out |= rd | wr | vol;
5409ee78757SCharles Keepax 	}
5419ee78757SCharles Keepax 
5429ee78757SCharles Keepax 	return out;
5439ee78757SCharles Keepax }
5449ee78757SCharles Keepax 
54556717d72SCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work)
5466ab2b7b4SDimitris Papastamos {
54756717d72SCharles Keepax 	struct wm_coeff_ctl *ctl = container_of(work,
54856717d72SCharles Keepax 						struct wm_coeff_ctl,
54956717d72SCharles Keepax 						work);
5500700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
55156717d72SCharles Keepax 	struct wm_adsp *dsp = container_of(cs_ctl->dsp,
55256717d72SCharles Keepax 					   struct wm_adsp,
55356717d72SCharles Keepax 					   cs_dsp);
5546ab2b7b4SDimitris Papastamos 	struct snd_kcontrol_new *kcontrol;
5556ab2b7b4SDimitris Papastamos 
5566ab2b7b4SDimitris Papastamos 	kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
5576ab2b7b4SDimitris Papastamos 	if (!kcontrol)
55856717d72SCharles Keepax 		return;
5596ab2b7b4SDimitris Papastamos 
5606ab2b7b4SDimitris Papastamos 	kcontrol->name = ctl->name;
5616ab2b7b4SDimitris Papastamos 	kcontrol->info = wm_coeff_info;
5629ee78757SCharles Keepax 	kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
5639ee78757SCharles Keepax 	kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
5649ee78757SCharles Keepax 	kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
5650700bc2fSSimon Trimmer 	kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);
566a23ebba8SRichard Fitzgerald 
5670700bc2fSSimon Trimmer 	switch (cs_ctl->type) {
568a23ebba8SRichard Fitzgerald 	case WMFW_CTL_TYPE_ACKED:
569a23ebba8SRichard Fitzgerald 		kcontrol->get = wm_coeff_get_acked;
570a23ebba8SRichard Fitzgerald 		kcontrol->put = wm_coeff_put_acked;
571a23ebba8SRichard Fitzgerald 		break;
572a23ebba8SRichard Fitzgerald 	default:
573d7789f5bSRichard Fitzgerald 		if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
5740700bc2fSSimon Trimmer 			ctl->bytes_ext.max = cs_ctl->len;
5759ee78757SCharles Keepax 			ctl->bytes_ext.get = wm_coeff_tlv_get;
5769ee78757SCharles Keepax 			ctl->bytes_ext.put = wm_coeff_tlv_put;
577d7789f5bSRichard Fitzgerald 		} else {
578d7789f5bSRichard Fitzgerald 			kcontrol->get = wm_coeff_get;
579d7789f5bSRichard Fitzgerald 			kcontrol->put = wm_coeff_put;
580d7789f5bSRichard Fitzgerald 		}
581a23ebba8SRichard Fitzgerald 		break;
582a23ebba8SRichard Fitzgerald 	}
58326c22a19SCharles Keepax 
58456717d72SCharles Keepax 	snd_soc_add_component_controls(dsp->component, kcontrol, 1);
5856ab2b7b4SDimitris Papastamos 
5866ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
587b21acc1cSCharles Keepax }
588b21acc1cSCharles Keepax 
5890700bc2fSSimon Trimmer static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
5900700bc2fSSimon Trimmer {
591e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
592e1468202SSimon Trimmer 	struct cs_dsp *cs_dsp = &dsp->cs_dsp;
5930700bc2fSSimon Trimmer 	struct wm_coeff_ctl *ctl;
5940700bc2fSSimon Trimmer 	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
5950700bc2fSSimon Trimmer 	const char *region_name;
5960700bc2fSSimon Trimmer 	int ret;
5970700bc2fSSimon Trimmer 
5980700bc2fSSimon Trimmer 	if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
5990700bc2fSSimon Trimmer 		return 0;
6000700bc2fSSimon Trimmer 
6010700bc2fSSimon Trimmer 	region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
6020700bc2fSSimon Trimmer 	if (!region_name) {
6030700bc2fSSimon Trimmer 		adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type);
6040700bc2fSSimon Trimmer 		return -EINVAL;
6050700bc2fSSimon Trimmer 	}
6060700bc2fSSimon Trimmer 
607e1468202SSimon Trimmer 	switch (cs_dsp->fw_ver) {
6080700bc2fSSimon Trimmer 	case 0:
6090700bc2fSSimon Trimmer 	case 1:
610a6e849d0SSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
611a6e849d0SSimon Trimmer 				"%s %s %x", cs_dsp->name, region_name,
612a6e849d0SSimon Trimmer 				cs_ctl->alg_region.alg);
6130700bc2fSSimon Trimmer 		break;
6140700bc2fSSimon Trimmer 	case 2:
6150700bc2fSSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
616e1468202SSimon Trimmer 				"%s%c %.12s %x", cs_dsp->name, *region_name,
6170700bc2fSSimon Trimmer 				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
6180700bc2fSSimon Trimmer 		break;
6190700bc2fSSimon Trimmer 	default:
6200700bc2fSSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
621e1468202SSimon Trimmer 				"%s %.12s %x", cs_dsp->name,
6220700bc2fSSimon Trimmer 				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
6230700bc2fSSimon Trimmer 		break;
6240700bc2fSSimon Trimmer 	}
6250700bc2fSSimon Trimmer 
6260700bc2fSSimon Trimmer 	if (cs_ctl->subname) {
6270700bc2fSSimon Trimmer 		int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
6280700bc2fSSimon Trimmer 		int skip = 0;
6290700bc2fSSimon Trimmer 
6300700bc2fSSimon Trimmer 		if (dsp->component->name_prefix)
6310700bc2fSSimon Trimmer 			avail -= strlen(dsp->component->name_prefix) + 1;
6320700bc2fSSimon Trimmer 
6330700bc2fSSimon Trimmer 		/* Truncate the subname from the start if it is too long */
6340700bc2fSSimon Trimmer 		if (cs_ctl->subname_len > avail)
6350700bc2fSSimon Trimmer 			skip = cs_ctl->subname_len - avail;
6360700bc2fSSimon Trimmer 
6370700bc2fSSimon Trimmer 		snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
6380700bc2fSSimon Trimmer 			 " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
6390700bc2fSSimon Trimmer 	}
6400700bc2fSSimon Trimmer 
6410700bc2fSSimon Trimmer 	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
6420700bc2fSSimon Trimmer 	if (!ctl)
6430700bc2fSSimon Trimmer 		return -ENOMEM;
6440700bc2fSSimon Trimmer 	ctl->cs_ctl = cs_ctl;
6450700bc2fSSimon Trimmer 
6460700bc2fSSimon Trimmer 	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
6470700bc2fSSimon Trimmer 	if (!ctl->name) {
6480700bc2fSSimon Trimmer 		ret = -ENOMEM;
6490700bc2fSSimon Trimmer 		goto err_ctl;
6500700bc2fSSimon Trimmer 	}
6510700bc2fSSimon Trimmer 
6520700bc2fSSimon Trimmer 	cs_ctl->priv = ctl;
6530700bc2fSSimon Trimmer 
6540700bc2fSSimon Trimmer 	INIT_WORK(&ctl->work, wm_adsp_ctl_work);
6550700bc2fSSimon Trimmer 	schedule_work(&ctl->work);
6560700bc2fSSimon Trimmer 
6570700bc2fSSimon Trimmer 	return 0;
6580700bc2fSSimon Trimmer 
6590700bc2fSSimon Trimmer err_ctl:
6600700bc2fSSimon Trimmer 	kfree(ctl);
6610700bc2fSSimon Trimmer 
6620700bc2fSSimon Trimmer 	return ret;
6630700bc2fSSimon Trimmer }
6640700bc2fSSimon Trimmer 
6650700bc2fSSimon Trimmer static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
6660700bc2fSSimon Trimmer {
6670700bc2fSSimon Trimmer 	struct wm_coeff_ctl *ctl = cs_ctl->priv;
6680700bc2fSSimon Trimmer 
669df6c505cSSimon Trimmer 	cancel_work_sync(&ctl->work);
670df6c505cSSimon Trimmer 
67166225e98SRichard Fitzgerald 	kfree(ctl->name);
67266225e98SRichard Fitzgerald 	kfree(ctl);
67366225e98SRichard Fitzgerald }
67466225e98SRichard Fitzgerald 
675eb65ccdbSLi Xu int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
676eb65ccdbSLi Xu 		      unsigned int alg, void *buf, size_t len)
677eb65ccdbSLi Xu {
6780700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
679eb65ccdbSLi Xu 	struct wm_coeff_ctl *ctl;
680eb65ccdbSLi Xu 	struct snd_kcontrol *kcontrol;
68120441614SAdam Brickman 	char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
682eb65ccdbSLi Xu 	int ret;
683eb65ccdbSLi Xu 
684e1468202SSimon Trimmer 	cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
6850700bc2fSSimon Trimmer 	if (!cs_ctl)
686eb65ccdbSLi Xu 		return -EINVAL;
687eb65ccdbSLi Xu 
6880700bc2fSSimon Trimmer 	ctl = cs_ctl->priv;
6890700bc2fSSimon Trimmer 
6900700bc2fSSimon Trimmer 	if (len > cs_ctl->len)
691eb65ccdbSLi Xu 		return -EINVAL;
692eb65ccdbSLi Xu 
693f444da38SCharles Keepax 	ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
69420441614SAdam Brickman 	if (ret)
69520441614SAdam Brickman 		return ret;
696eb65ccdbSLi Xu 
6970700bc2fSSimon Trimmer 	if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
69820441614SAdam Brickman 		return 0;
69920441614SAdam Brickman 
70020441614SAdam Brickman 	if (dsp->component->name_prefix)
70120441614SAdam Brickman 		snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s",
70220441614SAdam Brickman 			 dsp->component->name_prefix, ctl->name);
70320441614SAdam Brickman 	else
70420441614SAdam Brickman 		snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s",
70520441614SAdam Brickman 			 ctl->name);
70620441614SAdam Brickman 
70720441614SAdam Brickman 	kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name);
70820441614SAdam Brickman 	if (!kcontrol) {
70920441614SAdam Brickman 		adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name);
71020441614SAdam Brickman 		return -EINVAL;
71120441614SAdam Brickman 	}
71220441614SAdam Brickman 
713eb65ccdbSLi Xu 	snd_ctl_notify(dsp->component->card->snd_card,
714eb65ccdbSLi Xu 		       SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id);
715eb65ccdbSLi Xu 
716492df5b0SPierre-Louis Bossart 	return 0;
717eb65ccdbSLi Xu }
718eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
719eb65ccdbSLi Xu 
720eb65ccdbSLi Xu int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
721eb65ccdbSLi Xu 		     unsigned int alg, void *buf, size_t len)
722eb65ccdbSLi Xu {
7230700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
724eb65ccdbSLi Xu 
725e1468202SSimon Trimmer 	cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
7260700bc2fSSimon Trimmer 	if (!cs_ctl)
727eb65ccdbSLi Xu 		return -EINVAL;
728eb65ccdbSLi Xu 
7290700bc2fSSimon Trimmer 	if (len > cs_ctl->len)
730eb65ccdbSLi Xu 		return -EINVAL;
731eb65ccdbSLi Xu 
732f444da38SCharles Keepax 	return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len);
733eb65ccdbSLi Xu }
734eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
735eb65ccdbSLi Xu 
736f6bc909eSSimon Trimmer static void wm_adsp_release_firmware_files(struct wm_adsp *dsp,
737f6bc909eSSimon Trimmer 					   const struct firmware *wmfw_firmware,
738f6bc909eSSimon Trimmer 					   char *wmfw_filename,
739f6bc909eSSimon Trimmer 					   const struct firmware *coeff_firmware,
740f6bc909eSSimon Trimmer 					   char *coeff_filename)
7412323736dSCharles Keepax {
742f6bc909eSSimon Trimmer 	if (wmfw_firmware)
743f6bc909eSSimon Trimmer 		release_firmware(wmfw_firmware);
744f6bc909eSSimon Trimmer 	kfree(wmfw_filename);
7452323736dSCharles Keepax 
746f6bc909eSSimon Trimmer 	if (coeff_firmware)
747f6bc909eSSimon Trimmer 		release_firmware(coeff_firmware);
748f6bc909eSSimon Trimmer 	kfree(coeff_filename);
7492323736dSCharles Keepax }
7502323736dSCharles Keepax 
751f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
752b6b62d94SSimon Trimmer 					 const struct firmware **firmware, char **filename,
753b6b62d94SSimon Trimmer 					 const char *dir, const char *system_name,
754b6b62d94SSimon Trimmer 					 const char *asoc_component_prefix,
755b6b62d94SSimon Trimmer 					 const char *filetype)
756db40517cSMark Brown {
757f6bc909eSSimon Trimmer 	struct cs_dsp *cs_dsp = &dsp->cs_dsp;
758b6b62d94SSimon Trimmer 	char *s, c;
759f6bc909eSSimon Trimmer 	int ret = 0;
760db40517cSMark Brown 
761b6b62d94SSimon Trimmer 	if (system_name && asoc_component_prefix)
762b6b62d94SSimon Trimmer 		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part,
763b6b62d94SSimon Trimmer 				      dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name,
764b6b62d94SSimon Trimmer 				      asoc_component_prefix, filetype);
765b6b62d94SSimon Trimmer 	else if (system_name)
766b6b62d94SSimon Trimmer 		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part,
767b6b62d94SSimon Trimmer 				      dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name,
768b6b62d94SSimon Trimmer 				      filetype);
769b6b62d94SSimon Trimmer 	else
770b6b62d94SSimon Trimmer 		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, dsp->fwf_name,
771b6b62d94SSimon Trimmer 				      wm_adsp_fw[dsp->fw].file, filetype);
772b6b62d94SSimon Trimmer 
773f6bc909eSSimon Trimmer 	if (*filename == NULL)
774605391d0SRichard Fitzgerald 		return -ENOMEM;
775f6bc909eSSimon Trimmer 
776b6b62d94SSimon Trimmer 	/*
777b6b62d94SSimon Trimmer 	 * Make sure that filename is lower-case and any non alpha-numeric
778b6b62d94SSimon Trimmer 	 * characters except full stop and forward slash are replaced with
779b6b62d94SSimon Trimmer 	 * hyphens.
780b6b62d94SSimon Trimmer 	 */
781b6b62d94SSimon Trimmer 	s = *filename;
782b6b62d94SSimon Trimmer 	while (*s) {
783b6b62d94SSimon Trimmer 		c = *s;
784b6b62d94SSimon Trimmer 		if (isalnum(c))
785b6b62d94SSimon Trimmer 			*s = tolower(c);
786b6b62d94SSimon Trimmer 		else if ((c != '.') && (c != '/'))
787b6b62d94SSimon Trimmer 			*s = '-';
788b6b62d94SSimon Trimmer 		s++;
789b6b62d94SSimon Trimmer 	}
790b6b62d94SSimon Trimmer 
791b6b62d94SSimon Trimmer 	ret = firmware_request_nowarn(firmware, *filename, cs_dsp->dev);
792f6bc909eSSimon Trimmer 	if (ret != 0) {
793b6b62d94SSimon Trimmer 		adsp_dbg(dsp, "Failed to request '%s'\n", *filename);
794f6bc909eSSimon Trimmer 		kfree(*filename);
795f6bc909eSSimon Trimmer 		*filename = NULL;
796605391d0SRichard Fitzgerald 	}
797605391d0SRichard Fitzgerald 
798f6bc909eSSimon Trimmer 	return ret;
799605391d0SRichard Fitzgerald }
800605391d0SRichard Fitzgerald 
801b6b62d94SSimon Trimmer static const char *cirrus_dir = "cirrus/";
802f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
803f6bc909eSSimon Trimmer 					  const struct firmware **wmfw_firmware,
804f6bc909eSSimon Trimmer 					  char **wmfw_filename,
805f6bc909eSSimon Trimmer 					  const struct firmware **coeff_firmware,
806f6bc909eSSimon Trimmer 					  char **coeff_filename)
8075e7a7a22SMark Brown {
808b6b62d94SSimon Trimmer 	const char *system_name = dsp->system_name;
809b6b62d94SSimon Trimmer 	const char *asoc_component_prefix = dsp->component->name_prefix;
810f6bc909eSSimon Trimmer 	int ret = 0;
811605391d0SRichard Fitzgerald 
812b6b62d94SSimon Trimmer 	if (system_name && asoc_component_prefix) {
813b6b62d94SSimon Trimmer 		if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
814b6b62d94SSimon Trimmer 						   cirrus_dir, system_name,
815b6b62d94SSimon Trimmer 						   asoc_component_prefix, "wmfw")) {
816b6b62d94SSimon Trimmer 			adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
817b6b62d94SSimon Trimmer 			wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
818b6b62d94SSimon Trimmer 						      cirrus_dir, system_name,
819b6b62d94SSimon Trimmer 						      asoc_component_prefix, "bin");
8205e7a7a22SMark Brown 			return 0;
8215e7a7a22SMark Brown 		}
822b6b62d94SSimon Trimmer 	}
823b6b62d94SSimon Trimmer 
824b6b62d94SSimon Trimmer 	if (system_name) {
825b6b62d94SSimon Trimmer 		if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
826b6b62d94SSimon Trimmer 						   cirrus_dir, system_name,
827b6b62d94SSimon Trimmer 						   NULL, "wmfw")) {
828b6b62d94SSimon Trimmer 			adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
829b6b62d94SSimon Trimmer 			if (asoc_component_prefix)
830b6b62d94SSimon Trimmer 				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
831b6b62d94SSimon Trimmer 							      cirrus_dir, system_name,
832b6b62d94SSimon Trimmer 							      asoc_component_prefix, "bin");
833b6b62d94SSimon Trimmer 
834b6b62d94SSimon Trimmer 			if (!*coeff_firmware)
835b6b62d94SSimon Trimmer 				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
836b6b62d94SSimon Trimmer 							      cirrus_dir, system_name,
837b6b62d94SSimon Trimmer 							      NULL, "bin");
838b6b62d94SSimon Trimmer 			return 0;
839b6b62d94SSimon Trimmer 		}
840b6b62d94SSimon Trimmer 	}
841b6b62d94SSimon Trimmer 
842b6b62d94SSimon Trimmer 	if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
843b6b62d94SSimon Trimmer 					   "", NULL, NULL, "wmfw")) {
844b6b62d94SSimon Trimmer 		adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
845b6b62d94SSimon Trimmer 		wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
846b6b62d94SSimon Trimmer 					      "", NULL, NULL, "bin");
847b6b62d94SSimon Trimmer 		return 0;
848b6b62d94SSimon Trimmer 	}
849b6b62d94SSimon Trimmer 
850b6b62d94SSimon Trimmer 	ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
851b6b62d94SSimon Trimmer 					    cirrus_dir, NULL, NULL, "wmfw");
852b6b62d94SSimon Trimmer 	if (!ret) {
853b6b62d94SSimon Trimmer 		adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
854b6b62d94SSimon Trimmer 		wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
855b6b62d94SSimon Trimmer 					      cirrus_dir, NULL, NULL, "bin");
856b6b62d94SSimon Trimmer 		return 0;
857b6b62d94SSimon Trimmer 	}
858b6b62d94SSimon Trimmer 
859b6b62d94SSimon Trimmer 	adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
860b6b62d94SSimon Trimmer 		 cirrus_dir, dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file,
861b6b62d94SSimon Trimmer 		 system_name, asoc_component_prefix);
862b6b62d94SSimon Trimmer 
863b6b62d94SSimon Trimmer 	return -ENOENT;
864b6b62d94SSimon Trimmer }
865dcad34f8SRichard Fitzgerald 
866e1468202SSimon Trimmer static int wm_adsp_common_init(struct wm_adsp *dsp)
86725ca837bSSimon Trimmer {
868e1468202SSimon Trimmer 	char *p;
869e1468202SSimon Trimmer 
87025ca837bSSimon Trimmer 	INIT_LIST_HEAD(&dsp->compr_list);
87125ca837bSSimon Trimmer 	INIT_LIST_HEAD(&dsp->buffer_list);
872e1468202SSimon Trimmer 
873e1468202SSimon Trimmer 	if (!dsp->fwf_name) {
874e1468202SSimon Trimmer 		p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL);
875e1468202SSimon Trimmer 		if (!p)
876e1468202SSimon Trimmer 			return -ENOMEM;
877e1468202SSimon Trimmer 
878e1468202SSimon Trimmer 		dsp->fwf_name = p;
879e1468202SSimon Trimmer 		for (; *p != 0; ++p)
880e1468202SSimon Trimmer 			*p = tolower(*p);
88125ca837bSSimon Trimmer 	}
88225ca837bSSimon Trimmer 
883e1468202SSimon Trimmer 	return 0;
884e1468202SSimon Trimmer }
885e1468202SSimon Trimmer 
88625ca837bSSimon Trimmer int wm_adsp1_init(struct wm_adsp *dsp)
88725ca837bSSimon Trimmer {
888e1468202SSimon Trimmer 	int ret;
88925ca837bSSimon Trimmer 
8902dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp1_client_ops;
8912dd04464SSimon Trimmer 
892e1468202SSimon Trimmer 	ret = cs_dsp_adsp1_init(&dsp->cs_dsp);
893e1468202SSimon Trimmer 	if (ret)
894e1468202SSimon Trimmer 		return ret;
895e1468202SSimon Trimmer 
896e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
897dcad34f8SRichard Fitzgerald }
8985e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
8995e7a7a22SMark Brown 
900186152dfSSimon Trimmer int wm_adsp1_event(struct snd_soc_dapm_widget *w,
901186152dfSSimon Trimmer 		   struct snd_kcontrol *kcontrol,
902186152dfSSimon Trimmer 		   int event)
903186152dfSSimon Trimmer {
904186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
905186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
906186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
907186152dfSSimon Trimmer 	int ret = 0;
908a828056fSSimon Trimmer 	char *wmfw_filename = NULL;
909a828056fSSimon Trimmer 	const struct firmware *wmfw_firmware = NULL;
910a828056fSSimon Trimmer 	char *coeff_filename = NULL;
911a828056fSSimon Trimmer 	const struct firmware *coeff_firmware = NULL;
912186152dfSSimon Trimmer 
913186152dfSSimon Trimmer 	dsp->component = component;
914186152dfSSimon Trimmer 
915186152dfSSimon Trimmer 	switch (event) {
916186152dfSSimon Trimmer 	case SND_SOC_DAPM_POST_PMU:
917a828056fSSimon Trimmer 		ret = wm_adsp_request_firmware_files(dsp,
918a828056fSSimon Trimmer 						     &wmfw_firmware, &wmfw_filename,
919a828056fSSimon Trimmer 						     &coeff_firmware, &coeff_filename);
920a828056fSSimon Trimmer 		if (ret)
921a828056fSSimon Trimmer 			break;
922a828056fSSimon Trimmer 
923e1468202SSimon Trimmer 		ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp,
924a828056fSSimon Trimmer 					    wmfw_firmware, wmfw_filename,
925a828056fSSimon Trimmer 					    coeff_firmware, coeff_filename,
9262169f2f1SSimon Trimmer 					    wm_adsp_fw_text[dsp->fw]);
927a828056fSSimon Trimmer 
928a828056fSSimon Trimmer 		wm_adsp_release_firmware_files(dsp,
929a828056fSSimon Trimmer 					       wmfw_firmware, wmfw_filename,
930a828056fSSimon Trimmer 					       coeff_firmware, coeff_filename);
931186152dfSSimon Trimmer 		break;
932186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
933e1468202SSimon Trimmer 		cs_dsp_adsp1_power_down(&dsp->cs_dsp);
934186152dfSSimon Trimmer 		break;
9352159ad93SMark Brown 	default:
9362159ad93SMark Brown 		break;
9372159ad93SMark Brown 	}
9382159ad93SMark Brown 
9392159ad93SMark Brown 	return ret;
9402159ad93SMark Brown }
9412159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
9422159ad93SMark Brown 
94325ca837bSSimon Trimmer int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq)
94425ca837bSSimon Trimmer {
94525ca837bSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
94625ca837bSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
94725ca837bSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
94825ca837bSSimon Trimmer 
949e1468202SSimon Trimmer 	return cs_dsp_set_dspclk(&dsp->cs_dsp, freq);
95025ca837bSSimon Trimmer }
951b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk);
952d82d767fSCharles Keepax 
953af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
954af813a6fSCharles Keepax 			   struct snd_ctl_elem_value *ucontrol)
955af813a6fSCharles Keepax {
9560fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
957b1470d4cSAjit Pandey 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
958b1470d4cSAjit Pandey 	struct soc_mixer_control *mc =
959b1470d4cSAjit Pandey 		(struct soc_mixer_control *)kcontrol->private_value;
960b1470d4cSAjit Pandey 	struct wm_adsp *dsp = &dsps[mc->shift - 1];
961af813a6fSCharles Keepax 
962af813a6fSCharles Keepax 	ucontrol->value.integer.value[0] = dsp->preloaded;
963af813a6fSCharles Keepax 
964af813a6fSCharles Keepax 	return 0;
965af813a6fSCharles Keepax }
966af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);
967af813a6fSCharles Keepax 
968af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
969af813a6fSCharles Keepax 			   struct snd_ctl_elem_value *ucontrol)
970af813a6fSCharles Keepax {
9710fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
972b1470d4cSAjit Pandey 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
9730fe1daa6SKuninori Morimoto 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
974af813a6fSCharles Keepax 	struct soc_mixer_control *mc =
975af813a6fSCharles Keepax 		(struct soc_mixer_control *)kcontrol->private_value;
976b1470d4cSAjit Pandey 	struct wm_adsp *dsp = &dsps[mc->shift - 1];
977af813a6fSCharles Keepax 	char preload[32];
978af813a6fSCharles Keepax 
979ba235634SCharles Keepax 	if (dsp->preloaded == ucontrol->value.integer.value[0])
980ba235634SCharles Keepax 		return 0;
981ba235634SCharles Keepax 
982e1468202SSimon Trimmer 	snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
983af813a6fSCharles Keepax 
984ba235634SCharles Keepax 	if (ucontrol->value.integer.value[0] || dsp->toggle_preload)
98595a594d0SCharles Keepax 		snd_soc_component_force_enable_pin(component, preload);
986af813a6fSCharles Keepax 	else
98795a594d0SCharles Keepax 		snd_soc_component_disable_pin(component, preload);
988af813a6fSCharles Keepax 
989af813a6fSCharles Keepax 	snd_soc_dapm_sync(dapm);
990af813a6fSCharles Keepax 
991868e49a4SStuart Henderson 	flush_work(&dsp->boot_work);
992868e49a4SStuart Henderson 
993ba235634SCharles Keepax 	dsp->preloaded = ucontrol->value.integer.value[0];
994ba235634SCharles Keepax 
995ba235634SCharles Keepax 	if (dsp->toggle_preload) {
996ba235634SCharles Keepax 		snd_soc_component_disable_pin(component, preload);
997ba235634SCharles Keepax 		snd_soc_dapm_sync(dapm);
998ba235634SCharles Keepax 	}
999ba235634SCharles Keepax 
1000af813a6fSCharles Keepax 	return 0;
1001af813a6fSCharles Keepax }
1002af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
1003af813a6fSCharles Keepax 
1004186152dfSSimon Trimmer static void wm_adsp_boot_work(struct work_struct *work)
1005186152dfSSimon Trimmer {
1006186152dfSSimon Trimmer 	struct wm_adsp *dsp = container_of(work,
1007186152dfSSimon Trimmer 					   struct wm_adsp,
1008186152dfSSimon Trimmer 					   boot_work);
1009a828056fSSimon Trimmer 	int ret = 0;
1010a828056fSSimon Trimmer 	char *wmfw_filename = NULL;
1011a828056fSSimon Trimmer 	const struct firmware *wmfw_firmware = NULL;
1012a828056fSSimon Trimmer 	char *coeff_filename = NULL;
1013a828056fSSimon Trimmer 	const struct firmware *coeff_firmware = NULL;
1014a828056fSSimon Trimmer 
1015a828056fSSimon Trimmer 	ret = wm_adsp_request_firmware_files(dsp,
1016a828056fSSimon Trimmer 					     &wmfw_firmware, &wmfw_filename,
1017a828056fSSimon Trimmer 					     &coeff_firmware, &coeff_filename);
1018a828056fSSimon Trimmer 	if (ret)
1019a828056fSSimon Trimmer 		return;
1020186152dfSSimon Trimmer 
1021e1468202SSimon Trimmer 	cs_dsp_power_up(&dsp->cs_dsp,
1022a828056fSSimon Trimmer 			wmfw_firmware, wmfw_filename,
1023a828056fSSimon Trimmer 			coeff_firmware, coeff_filename,
10242169f2f1SSimon Trimmer 			wm_adsp_fw_text[dsp->fw]);
1025a828056fSSimon Trimmer 
1026a828056fSSimon Trimmer 	wm_adsp_release_firmware_files(dsp,
1027a828056fSSimon Trimmer 				       wmfw_firmware, wmfw_filename,
1028a828056fSSimon Trimmer 				       coeff_firmware, coeff_filename);
1029186152dfSSimon Trimmer }
1030186152dfSSimon Trimmer 
1031186152dfSSimon Trimmer int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
1032186152dfSSimon Trimmer 			struct snd_kcontrol *kcontrol, int event)
1033186152dfSSimon Trimmer {
1034186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
1035186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
1036186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
1037186152dfSSimon Trimmer 
1038186152dfSSimon Trimmer 	switch (event) {
1039186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMU:
1040186152dfSSimon Trimmer 		queue_work(system_unbound_wq, &dsp->boot_work);
1041186152dfSSimon Trimmer 		break;
1042186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
1043e1468202SSimon Trimmer 		cs_dsp_power_down(&dsp->cs_dsp);
104457a60cc3SCharles Keepax 		break;
104512db5eddSCharles Keepax 	default:
104612db5eddSCharles Keepax 		break;
1047cab27258SCharles Keepax 	}
104812db5eddSCharles Keepax 
104912db5eddSCharles Keepax 	return 0;
105012db5eddSCharles Keepax }
10514e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event);
105212db5eddSCharles Keepax 
1053e1468202SSimon Trimmer static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp)
1054d8a64d6aSCharles Keepax {
1055e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
1056e1468202SSimon Trimmer 
1057186152dfSSimon Trimmer 	if (wm_adsp_fw[dsp->fw].num_caps != 0)
1058186152dfSSimon Trimmer 		return wm_adsp_buffer_init(dsp);
1059d8a64d6aSCharles Keepax 
1060186152dfSSimon Trimmer 	return 0;
1061186152dfSSimon Trimmer }
1062186152dfSSimon Trimmer 
1063e1468202SSimon Trimmer static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp)
1064186152dfSSimon Trimmer {
1065e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
1066e1468202SSimon Trimmer 
1067186152dfSSimon Trimmer 	if (wm_adsp_fw[dsp->fw].num_caps != 0)
1068186152dfSSimon Trimmer 		wm_adsp_buffer_free(dsp);
1069186152dfSSimon Trimmer 
1070186152dfSSimon Trimmer 	dsp->fatal_error = false;
1071186152dfSSimon Trimmer }
1072186152dfSSimon Trimmer 
1073186152dfSSimon Trimmer int wm_adsp_event(struct snd_soc_dapm_widget *w,
1074186152dfSSimon Trimmer 		  struct snd_kcontrol *kcontrol, int event)
1075186152dfSSimon Trimmer {
1076186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
1077186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
1078186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
1079186152dfSSimon Trimmer 	int ret = 0;
1080186152dfSSimon Trimmer 
1081186152dfSSimon Trimmer 	switch (event) {
1082186152dfSSimon Trimmer 	case SND_SOC_DAPM_POST_PMU:
1083186152dfSSimon Trimmer 		flush_work(&dsp->boot_work);
1084e1468202SSimon Trimmer 		ret = cs_dsp_run(&dsp->cs_dsp);
1085186152dfSSimon Trimmer 		break;
1086186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
1087e1468202SSimon Trimmer 		cs_dsp_stop(&dsp->cs_dsp);
1088186152dfSSimon Trimmer 		break;
10892159ad93SMark Brown 	default:
10902159ad93SMark Brown 		break;
10912159ad93SMark Brown 	}
10922159ad93SMark Brown 
10932159ad93SMark Brown 	return ret;
10942159ad93SMark Brown }
10954e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event);
1096973838a0SMark Brown 
10970fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
1098f5e2ce92SRichard Fitzgerald {
1099af813a6fSCharles Keepax 	char preload[32];
1100af813a6fSCharles Keepax 
1101e1468202SSimon Trimmer 	snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
110295a594d0SCharles Keepax 	snd_soc_component_disable_pin(component, preload);
1103685f51a5SRichard Fitzgerald 
1104e1468202SSimon Trimmer 	cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);
1105f9f55e31SRichard Fitzgerald 
11060fe1daa6SKuninori Morimoto 	dsp->component = component;
1107af813a6fSCharles Keepax 
11080a047f07SRichard Fitzgerald 	return 0;
1109f5e2ce92SRichard Fitzgerald }
11100fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
1111f5e2ce92SRichard Fitzgerald 
11120fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
1113f5e2ce92SRichard Fitzgerald {
1114e1468202SSimon Trimmer 	cs_dsp_cleanup_debugfs(&dsp->cs_dsp);
1115f9f55e31SRichard Fitzgerald 
1116f5e2ce92SRichard Fitzgerald 	return 0;
1117f5e2ce92SRichard Fitzgerald }
11180fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove);
1119f5e2ce92SRichard Fitzgerald 
112025ca837bSSimon Trimmer int wm_adsp2_init(struct wm_adsp *dsp)
112125ca837bSSimon Trimmer {
1122e1468202SSimon Trimmer 	int ret;
1123e1468202SSimon Trimmer 
11244e08d50dSCharles Keepax 	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
11256ab2b7b4SDimitris Papastamos 
11266092be2dSCharles Keepax 	dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr);
11272dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
11286092be2dSCharles Keepax 
1129e1468202SSimon Trimmer 	ret = cs_dsp_adsp2_init(&dsp->cs_dsp);
1130e1468202SSimon Trimmer 	if (ret)
1131e1468202SSimon Trimmer 		return ret;
113225ca837bSSimon Trimmer 
1133e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
1134973838a0SMark Brown }
1135973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
11360a37c6efSPraveen Diwakar 
113725ca837bSSimon Trimmer int wm_halo_init(struct wm_adsp *dsp)
113825ca837bSSimon Trimmer {
1139e1468202SSimon Trimmer 	int ret;
1140e1468202SSimon Trimmer 
1141170b1e12SWen Shi 	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
1142170b1e12SWen Shi 
11436092be2dSCharles Keepax 	dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr);
11442dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
11456092be2dSCharles Keepax 
1146e1468202SSimon Trimmer 	ret = cs_dsp_halo_init(&dsp->cs_dsp);
1147e1468202SSimon Trimmer 	if (ret)
1148e1468202SSimon Trimmer 		return ret;
114925ca837bSSimon Trimmer 
1150e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
1151170b1e12SWen Shi }
1152170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init);
1153170b1e12SWen Shi 
115425ca837bSSimon Trimmer void wm_adsp2_remove(struct wm_adsp *dsp)
115525ca837bSSimon Trimmer {
1156e1468202SSimon Trimmer 	cs_dsp_remove(&dsp->cs_dsp);
115725ca837bSSimon Trimmer }
115866225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove);
115966225e98SRichard Fitzgerald 
1160edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
1161edd71350SCharles Keepax {
1162edd71350SCharles Keepax 	return compr->buf != NULL;
1163edd71350SCharles Keepax }
1164edd71350SCharles Keepax 
1165edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
1166edd71350SCharles Keepax {
11674f2d4eabSStuart Henderson 	struct wm_adsp_compr_buf *buf = NULL, *tmp;
11684f2d4eabSStuart Henderson 
1169a2bcbc1bSCharles Keepax 	if (compr->dsp->fatal_error)
1170a2bcbc1bSCharles Keepax 		return -EINVAL;
1171a2bcbc1bSCharles Keepax 
11724f2d4eabSStuart Henderson 	list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
11734f2d4eabSStuart Henderson 		if (!tmp->name || !strcmp(compr->name, tmp->name)) {
11744f2d4eabSStuart Henderson 			buf = tmp;
11754f2d4eabSStuart Henderson 			break;
11764f2d4eabSStuart Henderson 		}
11774f2d4eabSStuart Henderson 	}
11784f2d4eabSStuart Henderson 
11794f2d4eabSStuart Henderson 	if (!buf)
1180edd71350SCharles Keepax 		return -EINVAL;
1181edd71350SCharles Keepax 
11824f2d4eabSStuart Henderson 	compr->buf = buf;
1183789b930aSCharles Keepax 	buf->compr = compr;
1184edd71350SCharles Keepax 
1185edd71350SCharles Keepax 	return 0;
1186edd71350SCharles Keepax }
1187edd71350SCharles Keepax 
1188721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
1189721be3beSCharles Keepax {
1190721be3beSCharles Keepax 	if (!compr)
1191721be3beSCharles Keepax 		return;
1192721be3beSCharles Keepax 
1193721be3beSCharles Keepax 	/* Wake the poll so it can see buffer is no longer attached */
1194721be3beSCharles Keepax 	if (compr->stream)
1195721be3beSCharles Keepax 		snd_compr_fragment_elapsed(compr->stream);
1196721be3beSCharles Keepax 
1197721be3beSCharles Keepax 	if (wm_adsp_compr_attached(compr)) {
1198721be3beSCharles Keepax 		compr->buf->compr = NULL;
1199721be3beSCharles Keepax 		compr->buf = NULL;
1200721be3beSCharles Keepax 	}
1201721be3beSCharles Keepax }
1202721be3beSCharles Keepax 
1203406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
1204406abc95SCharles Keepax {
12054f2d4eabSStuart Henderson 	struct wm_adsp_compr *compr, *tmp;
12064f2d4eabSStuart Henderson 	struct snd_soc_pcm_runtime *rtd = stream->private_data;
1207406abc95SCharles Keepax 	int ret = 0;
1208406abc95SCharles Keepax 
1209e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1210406abc95SCharles Keepax 
1211406abc95SCharles Keepax 	if (wm_adsp_fw[dsp->fw].num_caps == 0) {
12120d3fba3eSCharles Keepax 		adsp_err(dsp, "%s: Firmware does not support compressed API\n",
1213b5cb8558SKuninori Morimoto 			 asoc_rtd_to_codec(rtd, 0)->name);
1214406abc95SCharles Keepax 		ret = -ENXIO;
1215406abc95SCharles Keepax 		goto out;
1216406abc95SCharles Keepax 	}
1217406abc95SCharles Keepax 
1218406abc95SCharles Keepax 	if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
12190d3fba3eSCharles Keepax 		adsp_err(dsp, "%s: Firmware does not support stream direction\n",
1220b5cb8558SKuninori Morimoto 			 asoc_rtd_to_codec(rtd, 0)->name);
1221406abc95SCharles Keepax 		ret = -EINVAL;
1222406abc95SCharles Keepax 		goto out;
1223406abc95SCharles Keepax 	}
1224406abc95SCharles Keepax 
12254f2d4eabSStuart Henderson 	list_for_each_entry(tmp, &dsp->compr_list, list) {
1226b5cb8558SKuninori Morimoto 		if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) {
12270d3fba3eSCharles Keepax 			adsp_err(dsp, "%s: Only a single stream supported per dai\n",
1228b5cb8558SKuninori Morimoto 				 asoc_rtd_to_codec(rtd, 0)->name);
122995fe9597SCharles Keepax 			ret = -EBUSY;
123095fe9597SCharles Keepax 			goto out;
123195fe9597SCharles Keepax 		}
12324f2d4eabSStuart Henderson 	}
123395fe9597SCharles Keepax 
1234406abc95SCharles Keepax 	compr = kzalloc(sizeof(*compr), GFP_KERNEL);
1235406abc95SCharles Keepax 	if (!compr) {
1236406abc95SCharles Keepax 		ret = -ENOMEM;
1237406abc95SCharles Keepax 		goto out;
1238406abc95SCharles Keepax 	}
1239406abc95SCharles Keepax 
1240406abc95SCharles Keepax 	compr->dsp = dsp;
1241406abc95SCharles Keepax 	compr->stream = stream;
1242b5cb8558SKuninori Morimoto 	compr->name = asoc_rtd_to_codec(rtd, 0)->name;
1243406abc95SCharles Keepax 
12444f2d4eabSStuart Henderson 	list_add_tail(&compr->list, &dsp->compr_list);
1245406abc95SCharles Keepax 
1246406abc95SCharles Keepax 	stream->runtime->private_data = compr;
1247406abc95SCharles Keepax 
1248406abc95SCharles Keepax out:
1249e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1250406abc95SCharles Keepax 
1251406abc95SCharles Keepax 	return ret;
1252406abc95SCharles Keepax }
1253406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
1254406abc95SCharles Keepax 
12553a5ccf25SKuninori Morimoto int wm_adsp_compr_free(struct snd_soc_component *component,
12563a5ccf25SKuninori Morimoto 		       struct snd_compr_stream *stream)
1257406abc95SCharles Keepax {
1258406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1259406abc95SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1260406abc95SCharles Keepax 
1261e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1262406abc95SCharles Keepax 
1263721be3beSCharles Keepax 	wm_adsp_compr_detach(compr);
12644f2d4eabSStuart Henderson 	list_del(&compr->list);
1265406abc95SCharles Keepax 
126683a40ce9SCharles Keepax 	kfree(compr->raw_buf);
1267406abc95SCharles Keepax 	kfree(compr);
1268406abc95SCharles Keepax 
1269e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1270406abc95SCharles Keepax 
1271406abc95SCharles Keepax 	return 0;
1272406abc95SCharles Keepax }
1273406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free);
1274406abc95SCharles Keepax 
1275406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
1276406abc95SCharles Keepax 				      struct snd_compr_params *params)
1277406abc95SCharles Keepax {
1278406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1279406abc95SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1280406abc95SCharles Keepax 	const struct wm_adsp_fw_caps *caps;
1281406abc95SCharles Keepax 	const struct snd_codec_desc *desc;
1282406abc95SCharles Keepax 	int i, j;
1283406abc95SCharles Keepax 
1284406abc95SCharles Keepax 	if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||
1285406abc95SCharles Keepax 	    params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
1286406abc95SCharles Keepax 	    params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
1287406abc95SCharles Keepax 	    params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
12885beb8eeaSSimon Trimmer 	    params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) {
12890d3fba3eSCharles Keepax 		compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n",
1290406abc95SCharles Keepax 			  params->buffer.fragment_size,
1291406abc95SCharles Keepax 			  params->buffer.fragments);
1292406abc95SCharles Keepax 
1293406abc95SCharles Keepax 		return -EINVAL;
1294406abc95SCharles Keepax 	}
1295406abc95SCharles Keepax 
1296406abc95SCharles Keepax 	for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
1297406abc95SCharles Keepax 		caps = &wm_adsp_fw[dsp->fw].caps[i];
1298406abc95SCharles Keepax 		desc = &caps->desc;
1299406abc95SCharles Keepax 
1300406abc95SCharles Keepax 		if (caps->id != params->codec.id)
1301406abc95SCharles Keepax 			continue;
1302406abc95SCharles Keepax 
1303406abc95SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
1304406abc95SCharles Keepax 			if (desc->max_ch < params->codec.ch_out)
1305406abc95SCharles Keepax 				continue;
1306406abc95SCharles Keepax 		} else {
1307406abc95SCharles Keepax 			if (desc->max_ch < params->codec.ch_in)
1308406abc95SCharles Keepax 				continue;
1309406abc95SCharles Keepax 		}
1310406abc95SCharles Keepax 
1311406abc95SCharles Keepax 		if (!(desc->formats & (1 << params->codec.format)))
1312406abc95SCharles Keepax 			continue;
1313406abc95SCharles Keepax 
1314406abc95SCharles Keepax 		for (j = 0; j < desc->num_sample_rates; ++j)
1315406abc95SCharles Keepax 			if (desc->sample_rates[j] == params->codec.sample_rate)
1316406abc95SCharles Keepax 				return 0;
1317406abc95SCharles Keepax 	}
1318406abc95SCharles Keepax 
13190d3fba3eSCharles Keepax 	compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
1320406abc95SCharles Keepax 		  params->codec.id, params->codec.ch_in, params->codec.ch_out,
1321406abc95SCharles Keepax 		  params->codec.sample_rate, params->codec.format);
1322406abc95SCharles Keepax 	return -EINVAL;
1323406abc95SCharles Keepax }
1324406abc95SCharles Keepax 
1325565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
1326565ace46SCharles Keepax {
13275beb8eeaSSimon Trimmer 	return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE;
1328565ace46SCharles Keepax }
1329565ace46SCharles Keepax 
13303a5ccf25SKuninori Morimoto int wm_adsp_compr_set_params(struct snd_soc_component *component,
13313a5ccf25SKuninori Morimoto 			     struct snd_compr_stream *stream,
1332406abc95SCharles Keepax 			     struct snd_compr_params *params)
1333406abc95SCharles Keepax {
1334406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
133583a40ce9SCharles Keepax 	unsigned int size;
1336406abc95SCharles Keepax 	int ret;
1337406abc95SCharles Keepax 
1338406abc95SCharles Keepax 	ret = wm_adsp_compr_check_params(stream, params);
1339406abc95SCharles Keepax 	if (ret)
1340406abc95SCharles Keepax 		return ret;
1341406abc95SCharles Keepax 
1342406abc95SCharles Keepax 	compr->size = params->buffer;
1343406abc95SCharles Keepax 
13440d3fba3eSCharles Keepax 	compr_dbg(compr, "fragment_size=%d fragments=%d\n",
1345406abc95SCharles Keepax 		  compr->size.fragment_size, compr->size.fragments);
1346406abc95SCharles Keepax 
134783a40ce9SCharles Keepax 	size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
134883a40ce9SCharles Keepax 	compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
134983a40ce9SCharles Keepax 	if (!compr->raw_buf)
135083a40ce9SCharles Keepax 		return -ENOMEM;
135183a40ce9SCharles Keepax 
1352da2b3358SCharles Keepax 	compr->sample_rate = params->codec.sample_rate;
1353da2b3358SCharles Keepax 
1354406abc95SCharles Keepax 	return 0;
1355406abc95SCharles Keepax }
1356406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
1357406abc95SCharles Keepax 
13583a5ccf25SKuninori Morimoto int wm_adsp_compr_get_caps(struct snd_soc_component *component,
13593a5ccf25SKuninori Morimoto 			   struct snd_compr_stream *stream,
1360406abc95SCharles Keepax 			   struct snd_compr_caps *caps)
1361406abc95SCharles Keepax {
1362406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1363406abc95SCharles Keepax 	int fw = compr->dsp->fw;
1364406abc95SCharles Keepax 	int i;
1365406abc95SCharles Keepax 
1366406abc95SCharles Keepax 	if (wm_adsp_fw[fw].caps) {
1367406abc95SCharles Keepax 		for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
1368406abc95SCharles Keepax 			caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
1369406abc95SCharles Keepax 
1370406abc95SCharles Keepax 		caps->num_codecs = i;
1371406abc95SCharles Keepax 		caps->direction = wm_adsp_fw[fw].compr_direction;
1372406abc95SCharles Keepax 
1373406abc95SCharles Keepax 		caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
1374406abc95SCharles Keepax 		caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
1375406abc95SCharles Keepax 		caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;
1376406abc95SCharles Keepax 		caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;
1377406abc95SCharles Keepax 	}
1378406abc95SCharles Keepax 
1379406abc95SCharles Keepax 	return 0;
1380406abc95SCharles Keepax }
1381406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
1382406abc95SCharles Keepax 
13832cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
13842cd19bdbSCharles Keepax 				      unsigned int field_offset, u32 *data)
13852cd19bdbSCharles Keepax {
1386e1468202SSimon Trimmer 	return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
13872cd19bdbSCharles Keepax 				     buf->host_buf_ptr + field_offset, data);
13882cd19bdbSCharles Keepax }
13892cd19bdbSCharles Keepax 
13902cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
13912cd19bdbSCharles Keepax 				       unsigned int field_offset, u32 data)
13922cd19bdbSCharles Keepax {
1393e1468202SSimon Trimmer 	return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
1394e1468202SSimon Trimmer 				      buf->host_buf_ptr + field_offset,
1395e1468202SSimon Trimmer 				      data);
13962cd19bdbSCharles Keepax }
13972cd19bdbSCharles Keepax 
13981e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
13991e38f069SCharles Keepax {
14001e38f069SCharles Keepax 	const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
14011e38f069SCharles Keepax 	struct wm_adsp_buffer_region *region;
14021e38f069SCharles Keepax 	u32 offset = 0;
14031e38f069SCharles Keepax 	int i, ret;
14041e38f069SCharles Keepax 
1405a792af69SCharles Keepax 	buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions),
1406a792af69SCharles Keepax 			       GFP_KERNEL);
1407a792af69SCharles Keepax 	if (!buf->regions)
1408a792af69SCharles Keepax 		return -ENOMEM;
1409a792af69SCharles Keepax 
14101e38f069SCharles Keepax 	for (i = 0; i < caps->num_regions; ++i) {
14111e38f069SCharles Keepax 		region = &buf->regions[i];
14121e38f069SCharles Keepax 
14131e38f069SCharles Keepax 		region->offset = offset;
14141e38f069SCharles Keepax 		region->mem_type = caps->region_defs[i].mem_type;
14151e38f069SCharles Keepax 
14161e38f069SCharles Keepax 		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
14171e38f069SCharles Keepax 					  &region->base_addr);
14181e38f069SCharles Keepax 		if (ret < 0)
14191e38f069SCharles Keepax 			return ret;
14201e38f069SCharles Keepax 
14211e38f069SCharles Keepax 		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
14221e38f069SCharles Keepax 					  &offset);
14231e38f069SCharles Keepax 		if (ret < 0)
14241e38f069SCharles Keepax 			return ret;
14251e38f069SCharles Keepax 
14261e38f069SCharles Keepax 		region->cumulative_size = offset;
14271e38f069SCharles Keepax 
14280d3fba3eSCharles Keepax 		compr_dbg(buf,
14291e38f069SCharles Keepax 			  "region=%d type=%d base=%08x off=%08x size=%08x\n",
14301e38f069SCharles Keepax 			  i, region->mem_type, region->base_addr,
14311e38f069SCharles Keepax 			  region->offset, region->cumulative_size);
14321e38f069SCharles Keepax 	}
14331e38f069SCharles Keepax 
14341e38f069SCharles Keepax 	return 0;
14351e38f069SCharles Keepax }
14361e38f069SCharles Keepax 
14371e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf)
14381e38f069SCharles Keepax {
14391e38f069SCharles Keepax 	buf->irq_count = 0xFFFFFFFF;
14401e38f069SCharles Keepax 	buf->read_index = -1;
14411e38f069SCharles Keepax 	buf->avail = 0;
14421e38f069SCharles Keepax }
14431e38f069SCharles Keepax 
1444a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)
1445a792af69SCharles Keepax {
1446a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1447a792af69SCharles Keepax 
1448a792af69SCharles Keepax 	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
1449a792af69SCharles Keepax 	if (!buf)
1450a792af69SCharles Keepax 		return NULL;
1451a792af69SCharles Keepax 
1452a792af69SCharles Keepax 	buf->dsp = dsp;
1453a792af69SCharles Keepax 
1454a792af69SCharles Keepax 	wm_adsp_buffer_clear(buf);
1455a792af69SCharles Keepax 
1456a792af69SCharles Keepax 	return buf;
1457a792af69SCharles Keepax }
1458a792af69SCharles Keepax 
1459a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
14602cd19bdbSCharles Keepax {
14615beb8eeaSSimon Trimmer 	struct cs_dsp_alg_region *alg_region;
1462a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
14632cd19bdbSCharles Keepax 	u32 xmalg, addr, magic;
14642cd19bdbSCharles Keepax 	int i, ret;
14652cd19bdbSCharles Keepax 
1466e1468202SSimon Trimmer 	alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id);
14679daf4fd0SLi Xu 	if (!alg_region) {
14689daf4fd0SLi Xu 		adsp_err(dsp, "No algorithm region found\n");
14699daf4fd0SLi Xu 		return -EINVAL;
14709daf4fd0SLi Xu 	}
14719daf4fd0SLi Xu 
14726092be2dSCharles Keepax 	xmalg = dsp->sys_config_size / sizeof(__be32);
14732cd19bdbSCharles Keepax 
14742cd19bdbSCharles Keepax 	addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
1475e1468202SSimon Trimmer 	ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic);
14762cd19bdbSCharles Keepax 	if (ret < 0)
14772cd19bdbSCharles Keepax 		return ret;
14782cd19bdbSCharles Keepax 
14792cd19bdbSCharles Keepax 	if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
1480a792af69SCharles Keepax 		return -ENODEV;
14812cd19bdbSCharles Keepax 
14820f1d41a8SCharles Keepax 	buf = wm_adsp_buffer_alloc(dsp);
14830f1d41a8SCharles Keepax 	if (!buf)
14840f1d41a8SCharles Keepax 		return -ENOMEM;
14850f1d41a8SCharles Keepax 
14862cd19bdbSCharles Keepax 	addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
14872cd19bdbSCharles Keepax 	for (i = 0; i < 5; ++i) {
1488e1468202SSimon Trimmer 		ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr,
14892cd19bdbSCharles Keepax 					    &buf->host_buf_ptr);
14902cd19bdbSCharles Keepax 		if (ret < 0)
14910f1d41a8SCharles Keepax 			goto err;
14922cd19bdbSCharles Keepax 
14932cd19bdbSCharles Keepax 		if (buf->host_buf_ptr)
14942cd19bdbSCharles Keepax 			break;
14952cd19bdbSCharles Keepax 
14962cd19bdbSCharles Keepax 		usleep_range(1000, 2000);
14972cd19bdbSCharles Keepax 	}
14982cd19bdbSCharles Keepax 
14990f1d41a8SCharles Keepax 	if (!buf->host_buf_ptr) {
15000f1d41a8SCharles Keepax 		ret = -EIO;
15010f1d41a8SCharles Keepax 		goto err;
15020f1d41a8SCharles Keepax 	}
15032cd19bdbSCharles Keepax 
1504fb13f19dSAndrew Ford 	buf->host_buf_mem_type = WMFW_ADSP2_XM;
1505fb13f19dSAndrew Ford 
1506a792af69SCharles Keepax 	ret = wm_adsp_buffer_populate(buf);
1507a792af69SCharles Keepax 	if (ret < 0)
15080f1d41a8SCharles Keepax 		goto err;
15090f1d41a8SCharles Keepax 
15100f1d41a8SCharles Keepax 	list_add_tail(&buf->list, &dsp->buffer_list);
1511a792af69SCharles Keepax 
15120d3fba3eSCharles Keepax 	compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr);
15132cd19bdbSCharles Keepax 
15142cd19bdbSCharles Keepax 	return 0;
15150f1d41a8SCharles Keepax 
15160f1d41a8SCharles Keepax err:
15170f1d41a8SCharles Keepax 	kfree(buf);
15180f1d41a8SCharles Keepax 
15190f1d41a8SCharles Keepax 	return ret;
15202cd19bdbSCharles Keepax }
15212cd19bdbSCharles Keepax 
15220700bc2fSSimon Trimmer static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)
1523d52ed4b0SRichard Fitzgerald {
15244f2d4eabSStuart Henderson 	struct wm_adsp_host_buf_coeff_v1 coeff_v1;
1525a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1526e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
15270f1d41a8SCharles Keepax 	unsigned int version = 0;
1528a792af69SCharles Keepax 	int ret, i;
1529d52ed4b0SRichard Fitzgerald 
1530d52ed4b0SRichard Fitzgerald 	for (i = 0; i < 5; ++i) {
1531a887f9c7SCharles Keepax 		ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1,
1532a887f9c7SCharles Keepax 					     min(cs_ctl->len, sizeof(coeff_v1)));
1533d52ed4b0SRichard Fitzgerald 		if (ret < 0)
1534d52ed4b0SRichard Fitzgerald 			return ret;
1535d52ed4b0SRichard Fitzgerald 
153604ae0859SCharles Keepax 		if (coeff_v1.host_buf_ptr)
1537d52ed4b0SRichard Fitzgerald 			break;
1538d52ed4b0SRichard Fitzgerald 
1539d52ed4b0SRichard Fitzgerald 		usleep_range(1000, 2000);
1540d52ed4b0SRichard Fitzgerald 	}
1541d52ed4b0SRichard Fitzgerald 
154204ae0859SCharles Keepax 	if (!coeff_v1.host_buf_ptr) {
15430700bc2fSSimon Trimmer 		adsp_err(dsp, "Failed to acquire host buffer\n");
1544d52ed4b0SRichard Fitzgerald 		return -EIO;
1545d52ed4b0SRichard Fitzgerald 	}
1546d52ed4b0SRichard Fitzgerald 
15470700bc2fSSimon Trimmer 	buf = wm_adsp_buffer_alloc(dsp);
15482cd19bdbSCharles Keepax 	if (!buf)
15492cd19bdbSCharles Keepax 		return -ENOMEM;
15502cd19bdbSCharles Keepax 
15510700bc2fSSimon Trimmer 	buf->host_buf_mem_type = cs_ctl->alg_region.type;
155204ae0859SCharles Keepax 	buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr);
15532cd19bdbSCharles Keepax 
15542cd19bdbSCharles Keepax 	ret = wm_adsp_buffer_populate(buf);
1555a792af69SCharles Keepax 	if (ret < 0)
15560f1d41a8SCharles Keepax 		goto err;
1557a792af69SCharles Keepax 
15584f2d4eabSStuart Henderson 	/*
15594f2d4eabSStuart Henderson 	 * v0 host_buffer coefficients didn't have versioning, so if the
15604f2d4eabSStuart Henderson 	 * control is one word, assume version 0.
15614f2d4eabSStuart Henderson 	 */
15620f1d41a8SCharles Keepax 	if (cs_ctl->len == 4)
15630f1d41a8SCharles Keepax 		goto done;
15642cd19bdbSCharles Keepax 
1565a0b653e8SRichard Fitzgerald 	version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK;
1566a0b653e8SRichard Fitzgerald 	version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;
15674f2d4eabSStuart Henderson 
1568a0b653e8SRichard Fitzgerald 	if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {
15690700bc2fSSimon Trimmer 		adsp_err(dsp,
15704f2d4eabSStuart Henderson 			 "Host buffer coeff ver %u > supported version %u\n",
1571a0b653e8SRichard Fitzgerald 			 version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);
15720f1d41a8SCharles Keepax 		ret = -EINVAL;
15730f1d41a8SCharles Keepax 		goto err;
15744f2d4eabSStuart Henderson 	}
15754f2d4eabSStuart Henderson 
15765beb8eeaSSimon Trimmer 	cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name));
15774f2d4eabSStuart Henderson 
15780700bc2fSSimon Trimmer 	buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part,
15794f2d4eabSStuart Henderson 			      (char *)&coeff_v1.name);
15804f2d4eabSStuart Henderson 
15810f1d41a8SCharles Keepax done:
15820f1d41a8SCharles Keepax 	list_add_tail(&buf->list, &dsp->buffer_list);
15830f1d41a8SCharles Keepax 
15840d3fba3eSCharles Keepax 	compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n",
1585a0b653e8SRichard Fitzgerald 		  buf->host_buf_ptr, version);
15864f2d4eabSStuart Henderson 
1587a0b653e8SRichard Fitzgerald 	return version;
15880f1d41a8SCharles Keepax 
15890f1d41a8SCharles Keepax err:
15900f1d41a8SCharles Keepax 	kfree(buf);
15910f1d41a8SCharles Keepax 
15920f1d41a8SCharles Keepax 	return ret;
15934f2d4eabSStuart Henderson }
15944f2d4eabSStuart Henderson 
1595a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp)
1596a792af69SCharles Keepax {
15970700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
1598a792af69SCharles Keepax 	int ret;
1599a792af69SCharles Keepax 
1600e1468202SSimon Trimmer 	list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) {
16010700bc2fSSimon Trimmer 		if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
1602a792af69SCharles Keepax 			continue;
1603a792af69SCharles Keepax 
16040700bc2fSSimon Trimmer 		if (!cs_ctl->enabled)
1605a792af69SCharles Keepax 			continue;
1606a792af69SCharles Keepax 
16070700bc2fSSimon Trimmer 		ret = wm_adsp_buffer_parse_coeff(cs_ctl);
1608a792af69SCharles Keepax 		if (ret < 0) {
1609a792af69SCharles Keepax 			adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
1610a792af69SCharles Keepax 			goto error;
16114f2d4eabSStuart Henderson 		} else if (ret == 0) {
16124f2d4eabSStuart Henderson 			/* Only one buffer supported for version 0 */
1613a792af69SCharles Keepax 			return 0;
1614a792af69SCharles Keepax 		}
16154f2d4eabSStuart Henderson 	}
1616a792af69SCharles Keepax 
16174f2d4eabSStuart Henderson 	if (list_empty(&dsp->buffer_list)) {
1618a792af69SCharles Keepax 		/* Fall back to legacy support */
1619a792af69SCharles Keepax 		ret = wm_adsp_buffer_parse_legacy(dsp);
16200f1d41a8SCharles Keepax 		if (ret)
16210f1d41a8SCharles Keepax 			adsp_warn(dsp, "Failed to parse legacy: %d\n", ret);
1622a792af69SCharles Keepax 	}
16232cd19bdbSCharles Keepax 
16242cd19bdbSCharles Keepax 	return 0;
16252cd19bdbSCharles Keepax 
1626a792af69SCharles Keepax error:
1627a792af69SCharles Keepax 	wm_adsp_buffer_free(dsp);
16282cd19bdbSCharles Keepax 	return ret;
16292cd19bdbSCharles Keepax }
16302cd19bdbSCharles Keepax 
16312cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp)
16322cd19bdbSCharles Keepax {
16334f2d4eabSStuart Henderson 	struct wm_adsp_compr_buf *buf, *tmp;
1634721be3beSCharles Keepax 
16354f2d4eabSStuart Henderson 	list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {
16364f2d4eabSStuart Henderson 		wm_adsp_compr_detach(buf->compr);
16372cd19bdbSCharles Keepax 
16384f2d4eabSStuart Henderson 		kfree(buf->name);
16394f2d4eabSStuart Henderson 		kfree(buf->regions);
16404f2d4eabSStuart Henderson 		list_del(&buf->list);
16414f2d4eabSStuart Henderson 		kfree(buf);
16422cd19bdbSCharles Keepax 	}
16432cd19bdbSCharles Keepax 
16442cd19bdbSCharles Keepax 	return 0;
16452cd19bdbSCharles Keepax }
16462cd19bdbSCharles Keepax 
1647f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
1648f938f348SStuart Henderson {
1649f938f348SStuart Henderson 	int ret;
1650f938f348SStuart Henderson 
1651f938f348SStuart Henderson 	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
1652f938f348SStuart Henderson 	if (ret < 0) {
165348ead31cSCharles Keepax 		compr_err(buf, "Failed to check buffer error: %d\n", ret);
1654f938f348SStuart Henderson 		return ret;
1655f938f348SStuart Henderson 	}
1656f938f348SStuart Henderson 	if (buf->error != 0) {
165748ead31cSCharles Keepax 		compr_err(buf, "Buffer error occurred: %d\n", buf->error);
1658f938f348SStuart Henderson 		return -EIO;
1659f938f348SStuart Henderson 	}
1660f938f348SStuart Henderson 
1661f938f348SStuart Henderson 	return 0;
1662f938f348SStuart Henderson }
1663f938f348SStuart Henderson 
16643a5ccf25SKuninori Morimoto int wm_adsp_compr_trigger(struct snd_soc_component *component,
16653a5ccf25SKuninori Morimoto 			  struct snd_compr_stream *stream, int cmd)
166695fe9597SCharles Keepax {
166795fe9597SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
166895fe9597SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
166995fe9597SCharles Keepax 	int ret = 0;
167095fe9597SCharles Keepax 
16710d3fba3eSCharles Keepax 	compr_dbg(compr, "Trigger: %d\n", cmd);
167295fe9597SCharles Keepax 
1673e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
167495fe9597SCharles Keepax 
167595fe9597SCharles Keepax 	switch (cmd) {
167695fe9597SCharles Keepax 	case SNDRV_PCM_TRIGGER_START:
167761fc060cSCharles Keepax 		if (!wm_adsp_compr_attached(compr)) {
167895fe9597SCharles Keepax 			ret = wm_adsp_compr_attach(compr);
167995fe9597SCharles Keepax 			if (ret < 0) {
16800d3fba3eSCharles Keepax 				compr_err(compr, "Failed to link buffer and stream: %d\n",
168195fe9597SCharles Keepax 					  ret);
168295fe9597SCharles Keepax 				break;
168395fe9597SCharles Keepax 			}
168461fc060cSCharles Keepax 		}
168561fc060cSCharles Keepax 
1686f938f348SStuart Henderson 		ret = wm_adsp_buffer_get_error(compr->buf);
1687f938f348SStuart Henderson 		if (ret < 0)
1688f938f348SStuart Henderson 			break;
1689f938f348SStuart Henderson 
1690565ace46SCharles Keepax 		/* Trigger the IRQ at one fragment of data */
1691565ace46SCharles Keepax 		ret = wm_adsp_buffer_write(compr->buf,
1692565ace46SCharles Keepax 					   HOST_BUFFER_FIELD(high_water_mark),
1693565ace46SCharles Keepax 					   wm_adsp_compr_frag_words(compr));
1694565ace46SCharles Keepax 		if (ret < 0) {
16950d3fba3eSCharles Keepax 			compr_err(compr, "Failed to set high water mark: %d\n",
1696565ace46SCharles Keepax 				  ret);
1697565ace46SCharles Keepax 			break;
1698565ace46SCharles Keepax 		}
169995fe9597SCharles Keepax 		break;
170095fe9597SCharles Keepax 	case SNDRV_PCM_TRIGGER_STOP:
170143d147beSCharles Keepax 		if (wm_adsp_compr_attached(compr))
1702639e5eb3SCharles Keepax 			wm_adsp_buffer_clear(compr->buf);
170395fe9597SCharles Keepax 		break;
170495fe9597SCharles Keepax 	default:
170595fe9597SCharles Keepax 		ret = -EINVAL;
170695fe9597SCharles Keepax 		break;
170795fe9597SCharles Keepax 	}
170895fe9597SCharles Keepax 
1709e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
171095fe9597SCharles Keepax 
171195fe9597SCharles Keepax 	return ret;
171295fe9597SCharles Keepax }
171395fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
171495fe9597SCharles Keepax 
1715565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
1716565ace46SCharles Keepax {
1717565ace46SCharles Keepax 	int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
1718565ace46SCharles Keepax 
1719565ace46SCharles Keepax 	return buf->regions[last_region].cumulative_size;
1720565ace46SCharles Keepax }
1721565ace46SCharles Keepax 
1722565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
1723565ace46SCharles Keepax {
1724565ace46SCharles Keepax 	u32 next_read_index, next_write_index;
1725565ace46SCharles Keepax 	int write_index, read_index, avail;
1726565ace46SCharles Keepax 	int ret;
1727565ace46SCharles Keepax 
1728565ace46SCharles Keepax 	/* Only sync read index if we haven't already read a valid index */
1729565ace46SCharles Keepax 	if (buf->read_index < 0) {
1730565ace46SCharles Keepax 		ret = wm_adsp_buffer_read(buf,
1731565ace46SCharles Keepax 				HOST_BUFFER_FIELD(next_read_index),
1732565ace46SCharles Keepax 				&next_read_index);
1733565ace46SCharles Keepax 		if (ret < 0)
1734565ace46SCharles Keepax 			return ret;
1735565ace46SCharles Keepax 
1736565ace46SCharles Keepax 		read_index = sign_extend32(next_read_index, 23);
1737565ace46SCharles Keepax 
1738565ace46SCharles Keepax 		if (read_index < 0) {
17390d3fba3eSCharles Keepax 			compr_dbg(buf, "Avail check on unstarted stream\n");
1740565ace46SCharles Keepax 			return 0;
1741565ace46SCharles Keepax 		}
1742565ace46SCharles Keepax 
1743565ace46SCharles Keepax 		buf->read_index = read_index;
1744565ace46SCharles Keepax 	}
1745565ace46SCharles Keepax 
1746565ace46SCharles Keepax 	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
1747565ace46SCharles Keepax 			&next_write_index);
1748565ace46SCharles Keepax 	if (ret < 0)
1749565ace46SCharles Keepax 		return ret;
1750565ace46SCharles Keepax 
1751565ace46SCharles Keepax 	write_index = sign_extend32(next_write_index, 23);
1752565ace46SCharles Keepax 
1753565ace46SCharles Keepax 	avail = write_index - buf->read_index;
1754565ace46SCharles Keepax 	if (avail < 0)
1755565ace46SCharles Keepax 		avail += wm_adsp_buffer_size(buf);
1756565ace46SCharles Keepax 
17570d3fba3eSCharles Keepax 	compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
17585beb8eeaSSimon Trimmer 		  buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE);
1759565ace46SCharles Keepax 
1760565ace46SCharles Keepax 	buf->avail = avail;
1761565ace46SCharles Keepax 
1762565ace46SCharles Keepax 	return 0;
1763565ace46SCharles Keepax }
1764565ace46SCharles Keepax 
1765565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
1766565ace46SCharles Keepax {
1767612047f0SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1768612047f0SCharles Keepax 	struct wm_adsp_compr *compr;
1769565ace46SCharles Keepax 	int ret = 0;
1770565ace46SCharles Keepax 
1771e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1772565ace46SCharles Keepax 
17734f2d4eabSStuart Henderson 	if (list_empty(&dsp->buffer_list)) {
1774565ace46SCharles Keepax 		ret = -ENODEV;
1775565ace46SCharles Keepax 		goto out;
1776565ace46SCharles Keepax 	}
17770d3fba3eSCharles Keepax 
1778565ace46SCharles Keepax 	adsp_dbg(dsp, "Handling buffer IRQ\n");
1779565ace46SCharles Keepax 
17804f2d4eabSStuart Henderson 	list_for_each_entry(buf, &dsp->buffer_list, list) {
17814f2d4eabSStuart Henderson 		compr = buf->compr;
17824f2d4eabSStuart Henderson 
17839771b18aSCharles Keepax 		ret = wm_adsp_buffer_get_error(buf);
17849771b18aSCharles Keepax 		if (ret < 0)
17855847609eSCharles Keepax 			goto out_notify; /* Wake poll to report error */
1786565ace46SCharles Keepax 
1787565ace46SCharles Keepax 		ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
1788565ace46SCharles Keepax 					  &buf->irq_count);
1789565ace46SCharles Keepax 		if (ret < 0) {
17900d3fba3eSCharles Keepax 			compr_err(buf, "Failed to get irq_count: %d\n", ret);
1791565ace46SCharles Keepax 			goto out;
1792565ace46SCharles Keepax 		}
1793565ace46SCharles Keepax 
1794565ace46SCharles Keepax 		ret = wm_adsp_buffer_update_avail(buf);
1795565ace46SCharles Keepax 		if (ret < 0) {
17960d3fba3eSCharles Keepax 			compr_err(buf, "Error reading avail: %d\n", ret);
1797565ace46SCharles Keepax 			goto out;
1798565ace46SCharles Keepax 		}
1799565ace46SCharles Keepax 
180020b7f7c5SCharles Keepax 		if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
180120b7f7c5SCharles Keepax 			ret = WM_ADSP_COMPR_VOICE_TRIGGER;
180220b7f7c5SCharles Keepax 
18035847609eSCharles Keepax out_notify:
1804c7dae7c4SCharles Keepax 		if (compr && compr->stream)
180583a40ce9SCharles Keepax 			snd_compr_fragment_elapsed(compr->stream);
18064f2d4eabSStuart Henderson 	}
180783a40ce9SCharles Keepax 
1808565ace46SCharles Keepax out:
1809e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1810565ace46SCharles Keepax 
1811565ace46SCharles Keepax 	return ret;
1812565ace46SCharles Keepax }
1813565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
1814565ace46SCharles Keepax 
1815565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
1816565ace46SCharles Keepax {
1817565ace46SCharles Keepax 	if (buf->irq_count & 0x01)
1818565ace46SCharles Keepax 		return 0;
1819565ace46SCharles Keepax 
18200d3fba3eSCharles Keepax 	compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count);
1821565ace46SCharles Keepax 
1822565ace46SCharles Keepax 	buf->irq_count |= 0x01;
1823565ace46SCharles Keepax 
1824565ace46SCharles Keepax 	return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
1825565ace46SCharles Keepax 				    buf->irq_count);
1826565ace46SCharles Keepax }
1827565ace46SCharles Keepax 
18283a5ccf25SKuninori Morimoto int wm_adsp_compr_pointer(struct snd_soc_component *component,
18293a5ccf25SKuninori Morimoto 			  struct snd_compr_stream *stream,
1830565ace46SCharles Keepax 			  struct snd_compr_tstamp *tstamp)
1831565ace46SCharles Keepax {
1832565ace46SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1833565ace46SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1834612047f0SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1835565ace46SCharles Keepax 	int ret = 0;
1836565ace46SCharles Keepax 
18370d3fba3eSCharles Keepax 	compr_dbg(compr, "Pointer request\n");
1838565ace46SCharles Keepax 
1839e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1840565ace46SCharles Keepax 
1841612047f0SCharles Keepax 	buf = compr->buf;
1842612047f0SCharles Keepax 
1843aa612f2bSCharles Keepax 	if (dsp->fatal_error || !buf || buf->error) {
18448d280664SCharles Keepax 		snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
1845565ace46SCharles Keepax 		ret = -EIO;
1846565ace46SCharles Keepax 		goto out;
1847565ace46SCharles Keepax 	}
1848565ace46SCharles Keepax 
1849565ace46SCharles Keepax 	if (buf->avail < wm_adsp_compr_frag_words(compr)) {
1850565ace46SCharles Keepax 		ret = wm_adsp_buffer_update_avail(buf);
1851565ace46SCharles Keepax 		if (ret < 0) {
18520d3fba3eSCharles Keepax 			compr_err(compr, "Error reading avail: %d\n", ret);
1853565ace46SCharles Keepax 			goto out;
1854565ace46SCharles Keepax 		}
1855565ace46SCharles Keepax 
1856565ace46SCharles Keepax 		/*
1857565ace46SCharles Keepax 		 * If we really have less than 1 fragment available tell the
1858565ace46SCharles Keepax 		 * DSP to inform us once a whole fragment is available.
1859565ace46SCharles Keepax 		 */
1860565ace46SCharles Keepax 		if (buf->avail < wm_adsp_compr_frag_words(compr)) {
18615847609eSCharles Keepax 			ret = wm_adsp_buffer_get_error(buf);
18628d280664SCharles Keepax 			if (ret < 0) {
1863789b930aSCharles Keepax 				if (buf->error)
18648d280664SCharles Keepax 					snd_compr_stop_error(stream,
18658d280664SCharles Keepax 							SNDRV_PCM_STATE_XRUN);
18665847609eSCharles Keepax 				goto out;
18678d280664SCharles Keepax 			}
18685847609eSCharles Keepax 
1869565ace46SCharles Keepax 			ret = wm_adsp_buffer_reenable_irq(buf);
1870565ace46SCharles Keepax 			if (ret < 0) {
18710d3fba3eSCharles Keepax 				compr_err(compr, "Failed to re-enable buffer IRQ: %d\n",
1872565ace46SCharles Keepax 					  ret);
1873565ace46SCharles Keepax 				goto out;
1874565ace46SCharles Keepax 			}
1875565ace46SCharles Keepax 		}
1876565ace46SCharles Keepax 	}
1877565ace46SCharles Keepax 
1878565ace46SCharles Keepax 	tstamp->copied_total = compr->copied_total;
18795beb8eeaSSimon Trimmer 	tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE;
1880da2b3358SCharles Keepax 	tstamp->sampling_rate = compr->sample_rate;
1881565ace46SCharles Keepax 
1882565ace46SCharles Keepax out:
1883e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1884565ace46SCharles Keepax 
1885565ace46SCharles Keepax 	return ret;
1886565ace46SCharles Keepax }
1887565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
1888565ace46SCharles Keepax 
188983a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
189083a40ce9SCharles Keepax {
189183a40ce9SCharles Keepax 	struct wm_adsp_compr_buf *buf = compr->buf;
189283a40ce9SCharles Keepax 	unsigned int adsp_addr;
189383a40ce9SCharles Keepax 	int mem_type, nwords, max_read;
1894cc7d6ce9SCharles Keepax 	int i, ret;
189583a40ce9SCharles Keepax 
189683a40ce9SCharles Keepax 	/* Calculate read parameters */
189783a40ce9SCharles Keepax 	for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
189883a40ce9SCharles Keepax 		if (buf->read_index < buf->regions[i].cumulative_size)
189983a40ce9SCharles Keepax 			break;
190083a40ce9SCharles Keepax 
190183a40ce9SCharles Keepax 	if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
190283a40ce9SCharles Keepax 		return -EINVAL;
190383a40ce9SCharles Keepax 
190483a40ce9SCharles Keepax 	mem_type = buf->regions[i].mem_type;
190583a40ce9SCharles Keepax 	adsp_addr = buf->regions[i].base_addr +
190683a40ce9SCharles Keepax 		    (buf->read_index - buf->regions[i].offset);
190783a40ce9SCharles Keepax 
190883a40ce9SCharles Keepax 	max_read = wm_adsp_compr_frag_words(compr);
190983a40ce9SCharles Keepax 	nwords = buf->regions[i].cumulative_size - buf->read_index;
191083a40ce9SCharles Keepax 
191183a40ce9SCharles Keepax 	if (nwords > target)
191283a40ce9SCharles Keepax 		nwords = target;
191383a40ce9SCharles Keepax 	if (nwords > buf->avail)
191483a40ce9SCharles Keepax 		nwords = buf->avail;
191583a40ce9SCharles Keepax 	if (nwords > max_read)
191683a40ce9SCharles Keepax 		nwords = max_read;
191783a40ce9SCharles Keepax 	if (!nwords)
191883a40ce9SCharles Keepax 		return 0;
191983a40ce9SCharles Keepax 
192083a40ce9SCharles Keepax 	/* Read data from DSP */
1921e1468202SSimon Trimmer 	ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr,
1922a0b653e8SRichard Fitzgerald 					 nwords, (__be32 *)compr->raw_buf);
192383a40ce9SCharles Keepax 	if (ret < 0)
192483a40ce9SCharles Keepax 		return ret;
192583a40ce9SCharles Keepax 
19265beb8eeaSSimon Trimmer 	cs_dsp_remove_padding(compr->raw_buf, nwords);
192783a40ce9SCharles Keepax 
192883a40ce9SCharles Keepax 	/* update read index to account for words read */
192983a40ce9SCharles Keepax 	buf->read_index += nwords;
193083a40ce9SCharles Keepax 	if (buf->read_index == wm_adsp_buffer_size(buf))
193183a40ce9SCharles Keepax 		buf->read_index = 0;
193283a40ce9SCharles Keepax 
193383a40ce9SCharles Keepax 	ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
193483a40ce9SCharles Keepax 				   buf->read_index);
193583a40ce9SCharles Keepax 	if (ret < 0)
193683a40ce9SCharles Keepax 		return ret;
193783a40ce9SCharles Keepax 
193883a40ce9SCharles Keepax 	/* update avail to account for words read */
193983a40ce9SCharles Keepax 	buf->avail -= nwords;
194083a40ce9SCharles Keepax 
194183a40ce9SCharles Keepax 	return nwords;
194283a40ce9SCharles Keepax }
194383a40ce9SCharles Keepax 
194483a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
194583a40ce9SCharles Keepax 			      char __user *buf, size_t count)
194683a40ce9SCharles Keepax {
1947aa612f2bSCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
194883a40ce9SCharles Keepax 	int ntotal = 0;
194983a40ce9SCharles Keepax 	int nwords, nbytes;
195083a40ce9SCharles Keepax 
19510d3fba3eSCharles Keepax 	compr_dbg(compr, "Requested read of %zu bytes\n", count);
195283a40ce9SCharles Keepax 
1953aa612f2bSCharles Keepax 	if (dsp->fatal_error || !compr->buf || compr->buf->error) {
19548d280664SCharles Keepax 		snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
195583a40ce9SCharles Keepax 		return -EIO;
19568d280664SCharles Keepax 	}
195783a40ce9SCharles Keepax 
19585beb8eeaSSimon Trimmer 	count /= CS_DSP_DATA_WORD_SIZE;
195983a40ce9SCharles Keepax 
196083a40ce9SCharles Keepax 	do {
196183a40ce9SCharles Keepax 		nwords = wm_adsp_buffer_capture_block(compr, count);
196283a40ce9SCharles Keepax 		if (nwords < 0) {
19630d3fba3eSCharles Keepax 			compr_err(compr, "Failed to capture block: %d\n",
19640d3fba3eSCharles Keepax 				  nwords);
196583a40ce9SCharles Keepax 			return nwords;
196683a40ce9SCharles Keepax 		}
196783a40ce9SCharles Keepax 
19685beb8eeaSSimon Trimmer 		nbytes = nwords * CS_DSP_DATA_WORD_SIZE;
196983a40ce9SCharles Keepax 
19700d3fba3eSCharles Keepax 		compr_dbg(compr, "Read %d bytes\n", nbytes);
197183a40ce9SCharles Keepax 
197283a40ce9SCharles Keepax 		if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
19730d3fba3eSCharles Keepax 			compr_err(compr, "Failed to copy data to user: %d, %d\n",
197483a40ce9SCharles Keepax 				  ntotal, nbytes);
197583a40ce9SCharles Keepax 			return -EFAULT;
197683a40ce9SCharles Keepax 		}
197783a40ce9SCharles Keepax 
197883a40ce9SCharles Keepax 		count -= nwords;
197983a40ce9SCharles Keepax 		ntotal += nbytes;
198083a40ce9SCharles Keepax 	} while (nwords > 0 && count > 0);
198183a40ce9SCharles Keepax 
198283a40ce9SCharles Keepax 	compr->copied_total += ntotal;
198383a40ce9SCharles Keepax 
198483a40ce9SCharles Keepax 	return ntotal;
198583a40ce9SCharles Keepax }
198683a40ce9SCharles Keepax 
19873a5ccf25SKuninori Morimoto int wm_adsp_compr_copy(struct snd_soc_component *component,
19883a5ccf25SKuninori Morimoto 		       struct snd_compr_stream *stream, char __user *buf,
198983a40ce9SCharles Keepax 		       size_t count)
199083a40ce9SCharles Keepax {
199183a40ce9SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
199283a40ce9SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
199383a40ce9SCharles Keepax 	int ret;
199483a40ce9SCharles Keepax 
1995e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
199683a40ce9SCharles Keepax 
199783a40ce9SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
199883a40ce9SCharles Keepax 		ret = wm_adsp_compr_read(compr, buf, count);
199983a40ce9SCharles Keepax 	else
200083a40ce9SCharles Keepax 		ret = -ENOTSUPP;
200183a40ce9SCharles Keepax 
2002e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
200383a40ce9SCharles Keepax 
200483a40ce9SCharles Keepax 	return ret;
200583a40ce9SCharles Keepax }
200683a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
200783a40ce9SCharles Keepax 
2008e1468202SSimon Trimmer static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp)
2009a2bcbc1bSCharles Keepax {
2010e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
2011a2bcbc1bSCharles Keepax 	struct wm_adsp_compr *compr;
2012a2bcbc1bSCharles Keepax 
2013a2bcbc1bSCharles Keepax 	dsp->fatal_error = true;
2014a2bcbc1bSCharles Keepax 
2015a2bcbc1bSCharles Keepax 	list_for_each_entry(compr, &dsp->compr_list, list) {
2016aa612f2bSCharles Keepax 		if (compr->stream)
2017a2bcbc1bSCharles Keepax 			snd_compr_fragment_elapsed(compr->stream);
2018a2bcbc1bSCharles Keepax 	}
2019a2bcbc1bSCharles Keepax }
2020a2bcbc1bSCharles Keepax 
202125ca837bSSimon Trimmer irqreturn_t wm_adsp2_bus_error(int irq, void *data)
202225ca837bSSimon Trimmer {
202325ca837bSSimon Trimmer 	struct wm_adsp *dsp = (struct wm_adsp *)data;
202425ca837bSSimon Trimmer 
2025e1468202SSimon Trimmer 	cs_dsp_adsp2_bus_error(&dsp->cs_dsp);
2026a2225a6dSCharles Keepax 
202751a2c944SMayuresh Kulkarni 	return IRQ_HANDLED;
202851a2c944SMayuresh Kulkarni }
202951a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
203051a2c944SMayuresh Kulkarni 
203125ca837bSSimon Trimmer irqreturn_t wm_halo_bus_error(int irq, void *data)
203225ca837bSSimon Trimmer {
203325ca837bSSimon Trimmer 	struct wm_adsp *dsp = (struct wm_adsp *)data;
203425ca837bSSimon Trimmer 
2035e1468202SSimon Trimmer 	cs_dsp_halo_bus_error(&dsp->cs_dsp);
20362ae58138SRichard Fitzgerald 
20372ae58138SRichard Fitzgerald 	return IRQ_HANDLED;
20382ae58138SRichard Fitzgerald }
20392ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error);
20402ae58138SRichard Fitzgerald 
204125ca837bSSimon Trimmer irqreturn_t wm_halo_wdt_expire(int irq, void *data)
204225ca837bSSimon Trimmer {
204325ca837bSSimon Trimmer 	struct wm_adsp *dsp = data;
204425ca837bSSimon Trimmer 
2045e1468202SSimon Trimmer 	cs_dsp_halo_wdt_expire(&dsp->cs_dsp);
20468bc144f9SStuart Henderson 
20478bc144f9SStuart Henderson 	return IRQ_HANDLED;
20488bc144f9SStuart Henderson }
20498bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);
20508bc144f9SStuart Henderson 
20512dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp1_client_ops = {
20522dd04464SSimon Trimmer 	.control_add = wm_adsp_control_add,
20532dd04464SSimon Trimmer 	.control_remove = wm_adsp_control_remove,
20542dd04464SSimon Trimmer };
20552dd04464SSimon Trimmer 
20562dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp2_client_ops = {
20572dd04464SSimon Trimmer 	.control_add = wm_adsp_control_add,
20582dd04464SSimon Trimmer 	.control_remove = wm_adsp_control_remove,
20592dd04464SSimon Trimmer 	.post_run = wm_adsp_event_post_run,
20602dd04464SSimon Trimmer 	.post_stop = wm_adsp_event_post_stop,
20612dd04464SSimon Trimmer 	.watchdog_expired = wm_adsp_fatal_error,
20622dd04464SSimon Trimmer };
20632dd04464SSimon Trimmer 
20640a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2");
2065