xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision 3e1a29fb)
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/regmap.h>
19973838a0SMark Brown #include <linux/regulator/consumer.h>
202159ad93SMark Brown #include <linux/slab.h>
217406bdbcSSimon Trimmer #include <linux/vmalloc.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;
100353bb6a5SSimon Trimmer } __packed;
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;
106353bb6a5SSimon Trimmer } __packed;
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;
117353bb6a5SSimon Trimmer } __packed;
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 */
123353bb6a5SSimon Trimmer } __packed;
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) */
144353bb6a5SSimon Trimmer } __packed;
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 
wm_adsp_fw_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
wm_adsp_fw_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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);
3362abdf9f8SMark 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 
bytes_ext_to_ctl(struct soc_bytes_ext * ext)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 
wm_coeff_info(struct snd_kcontrol * kctl,struct snd_ctl_elem_info * uinfo)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 
wm_coeff_put(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)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 
wm_coeff_tlv_put(struct snd_kcontrol * kctl,const unsigned int __user * bytes,unsigned int size)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;
4227406bdbcSSimon Trimmer 	void *scratch;
4239ee78757SCharles Keepax 	int ret = 0;
4249ee78757SCharles Keepax 
4257406bdbcSSimon Trimmer 	scratch = vmalloc(size);
4267406bdbcSSimon Trimmer 	if (!scratch)
4277406bdbcSSimon Trimmer 		return -ENOMEM;
4289ee78757SCharles Keepax 
4297406bdbcSSimon Trimmer 	if (copy_from_user(scratch, bytes, size)) {
4309ee78757SCharles Keepax 		ret = -EFAULT;
4317406bdbcSSimon Trimmer 	} else {
4327406bdbcSSimon Trimmer 		mutex_lock(&cs_ctl->dsp->pwr_lock);
4337406bdbcSSimon Trimmer 		ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, scratch, size);
4340700bc2fSSimon Trimmer 		mutex_unlock(&cs_ctl->dsp->pwr_lock);
4357406bdbcSSimon Trimmer 	}
4367406bdbcSSimon Trimmer 	vfree(scratch);
4379ee78757SCharles Keepax 
4389ee78757SCharles Keepax 	return ret;
4399ee78757SCharles Keepax }
4409ee78757SCharles Keepax 
wm_coeff_put_acked(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)441a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
442a23ebba8SRichard Fitzgerald 			      struct snd_ctl_elem_value *ucontrol)
443a23ebba8SRichard Fitzgerald {
444a23ebba8SRichard Fitzgerald 	struct soc_bytes_ext *bytes_ext =
445a23ebba8SRichard Fitzgerald 		(struct soc_bytes_ext *)kctl->private_value;
446a23ebba8SRichard Fitzgerald 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4470700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
448a23ebba8SRichard Fitzgerald 	unsigned int val = ucontrol->value.integer.value[0];
449a23ebba8SRichard Fitzgerald 	int ret;
450a23ebba8SRichard Fitzgerald 
451a23ebba8SRichard Fitzgerald 	if (val == 0)
452a23ebba8SRichard Fitzgerald 		return 0;	/* 0 means no event */
453a23ebba8SRichard Fitzgerald 
4540700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
455a23ebba8SRichard Fitzgerald 
456edb1d6d7SSimon Trimmer 	if (cs_ctl->enabled)
4570700bc2fSSimon Trimmer 		ret = cs_dsp_coeff_write_acked_control(cs_ctl, val);
458a23ebba8SRichard Fitzgerald 	else
459a23ebba8SRichard Fitzgerald 		ret = -EPERM;
460a23ebba8SRichard Fitzgerald 
4610700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
462a23ebba8SRichard Fitzgerald 
4637406bdbcSSimon Trimmer 	if (ret < 0)
464a23ebba8SRichard Fitzgerald 		return ret;
4657406bdbcSSimon Trimmer 
4667406bdbcSSimon Trimmer 	return 1;
467a23ebba8SRichard Fitzgerald }
468a23ebba8SRichard Fitzgerald 
wm_coeff_get(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)4697585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl,
4706ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4716ab2b7b4SDimitris Papastamos {
4729ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4739ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4749ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4750700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4766ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
47773ecf1a6SCharles Keepax 	int ret;
478168d10e7SCharles Keepax 
4790700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
480f444da38SCharles Keepax 	ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
4810700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
48226c22a19SCharles Keepax 
483168d10e7SCharles Keepax 	return ret;
4846ab2b7b4SDimitris Papastamos }
4856ab2b7b4SDimitris Papastamos 
wm_coeff_tlv_get(struct snd_kcontrol * kctl,unsigned int __user * bytes,unsigned int size)4869ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
4879ee78757SCharles Keepax 			    unsigned int __user *bytes, unsigned int size)
4889ee78757SCharles Keepax {
4899ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4909ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4919ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4920700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4939ee78757SCharles Keepax 	int ret = 0;
4949ee78757SCharles Keepax 
4950700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
4969ee78757SCharles Keepax 
497f444da38SCharles Keepax 	ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size);
4989ee78757SCharles Keepax 
4990700bc2fSSimon Trimmer 	if (!ret && copy_to_user(bytes, cs_ctl->cache, size))
5009ee78757SCharles Keepax 		ret = -EFAULT;
5019ee78757SCharles Keepax 
5020700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
5039ee78757SCharles Keepax 
5049ee78757SCharles Keepax 	return ret;
5059ee78757SCharles Keepax }
5069ee78757SCharles Keepax 
wm_coeff_get_acked(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)507a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
508a23ebba8SRichard Fitzgerald 			      struct snd_ctl_elem_value *ucontrol)
509a23ebba8SRichard Fitzgerald {
510a23ebba8SRichard Fitzgerald 	/*
511a23ebba8SRichard Fitzgerald 	 * Although it's not useful to read an acked control, we must satisfy
512a23ebba8SRichard Fitzgerald 	 * user-side assumptions that all controls are readable and that a
513a23ebba8SRichard Fitzgerald 	 * write of the same value should be filtered out (it's valid to send
514a23ebba8SRichard Fitzgerald 	 * the same event number again to the firmware). We therefore return 0,
515a23ebba8SRichard Fitzgerald 	 * meaning "no event" so valid event numbers will always be a change
516a23ebba8SRichard Fitzgerald 	 */
517a23ebba8SRichard Fitzgerald 	ucontrol->value.integer.value[0] = 0;
518a23ebba8SRichard Fitzgerald 
519a23ebba8SRichard Fitzgerald 	return 0;
520a23ebba8SRichard Fitzgerald }
521a23ebba8SRichard Fitzgerald 
wmfw_convert_flags(unsigned int in,unsigned int len)5229ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
5239ee78757SCharles Keepax {
5249ee78757SCharles Keepax 	unsigned int out, rd, wr, vol;
5259ee78757SCharles Keepax 
5269ee78757SCharles Keepax 	if (len > ADSP_MAX_STD_CTRL_SIZE) {
5279ee78757SCharles Keepax 		rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
5289ee78757SCharles Keepax 		wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
5299ee78757SCharles Keepax 		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
5309ee78757SCharles Keepax 
5319ee78757SCharles Keepax 		out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
5329ee78757SCharles Keepax 	} else {
5339ee78757SCharles Keepax 		rd = SNDRV_CTL_ELEM_ACCESS_READ;
5349ee78757SCharles Keepax 		wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
5359ee78757SCharles Keepax 		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
5369ee78757SCharles Keepax 
5379ee78757SCharles Keepax 		out = 0;
5389ee78757SCharles Keepax 	}
5399ee78757SCharles Keepax 
5409ee78757SCharles Keepax 	if (in) {
5419ee78757SCharles Keepax 		out |= rd;
5429ee78757SCharles Keepax 		if (in & WMFW_CTL_FLAG_WRITEABLE)
5439ee78757SCharles Keepax 			out |= wr;
5449ee78757SCharles Keepax 		if (in & WMFW_CTL_FLAG_VOLATILE)
5459ee78757SCharles Keepax 			out |= vol;
5469ee78757SCharles Keepax 	} else {
5479ee78757SCharles Keepax 		out |= rd | wr | vol;
5489ee78757SCharles Keepax 	}
5499ee78757SCharles Keepax 
5509ee78757SCharles Keepax 	return out;
5519ee78757SCharles Keepax }
5529ee78757SCharles Keepax 
wm_adsp_ctl_work(struct work_struct * work)55356717d72SCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work)
5546ab2b7b4SDimitris Papastamos {
55556717d72SCharles Keepax 	struct wm_coeff_ctl *ctl = container_of(work,
55656717d72SCharles Keepax 						struct wm_coeff_ctl,
55756717d72SCharles Keepax 						work);
5580700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
55956717d72SCharles Keepax 	struct wm_adsp *dsp = container_of(cs_ctl->dsp,
56056717d72SCharles Keepax 					   struct wm_adsp,
56156717d72SCharles Keepax 					   cs_dsp);
5626ab2b7b4SDimitris Papastamos 	struct snd_kcontrol_new *kcontrol;
5636ab2b7b4SDimitris Papastamos 
5646ab2b7b4SDimitris Papastamos 	kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
5656ab2b7b4SDimitris Papastamos 	if (!kcontrol)
56656717d72SCharles Keepax 		return;
5676ab2b7b4SDimitris Papastamos 
5686ab2b7b4SDimitris Papastamos 	kcontrol->name = ctl->name;
5696ab2b7b4SDimitris Papastamos 	kcontrol->info = wm_coeff_info;
5709ee78757SCharles Keepax 	kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
5719ee78757SCharles Keepax 	kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
5729ee78757SCharles Keepax 	kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
5730700bc2fSSimon Trimmer 	kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);
574a23ebba8SRichard Fitzgerald 
5750700bc2fSSimon Trimmer 	switch (cs_ctl->type) {
576a23ebba8SRichard Fitzgerald 	case WMFW_CTL_TYPE_ACKED:
577a23ebba8SRichard Fitzgerald 		kcontrol->get = wm_coeff_get_acked;
578a23ebba8SRichard Fitzgerald 		kcontrol->put = wm_coeff_put_acked;
579a23ebba8SRichard Fitzgerald 		break;
580a23ebba8SRichard Fitzgerald 	default:
581d7789f5bSRichard Fitzgerald 		if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
5820700bc2fSSimon Trimmer 			ctl->bytes_ext.max = cs_ctl->len;
5839ee78757SCharles Keepax 			ctl->bytes_ext.get = wm_coeff_tlv_get;
5849ee78757SCharles Keepax 			ctl->bytes_ext.put = wm_coeff_tlv_put;
585d7789f5bSRichard Fitzgerald 		} else {
586d7789f5bSRichard Fitzgerald 			kcontrol->get = wm_coeff_get;
587d7789f5bSRichard Fitzgerald 			kcontrol->put = wm_coeff_put;
588d7789f5bSRichard Fitzgerald 		}
589a23ebba8SRichard Fitzgerald 		break;
590a23ebba8SRichard Fitzgerald 	}
59126c22a19SCharles Keepax 
59256717d72SCharles Keepax 	snd_soc_add_component_controls(dsp->component, kcontrol, 1);
5936ab2b7b4SDimitris Papastamos 
5946ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
595b21acc1cSCharles Keepax }
596b21acc1cSCharles Keepax 
wm_adsp_control_add(struct cs_dsp_coeff_ctl * cs_ctl)5970700bc2fSSimon Trimmer static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
5980700bc2fSSimon Trimmer {
599e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
600e1468202SSimon Trimmer 	struct cs_dsp *cs_dsp = &dsp->cs_dsp;
6010700bc2fSSimon Trimmer 	struct wm_coeff_ctl *ctl;
6020700bc2fSSimon Trimmer 	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
6030700bc2fSSimon Trimmer 	const char *region_name;
6040700bc2fSSimon Trimmer 	int ret;
6050700bc2fSSimon Trimmer 
6060700bc2fSSimon Trimmer 	if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
6070700bc2fSSimon Trimmer 		return 0;
6080700bc2fSSimon Trimmer 
6090700bc2fSSimon Trimmer 	region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
6100700bc2fSSimon Trimmer 	if (!region_name) {
6110700bc2fSSimon Trimmer 		adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type);
6120700bc2fSSimon Trimmer 		return -EINVAL;
6130700bc2fSSimon Trimmer 	}
6140700bc2fSSimon Trimmer 
615e1468202SSimon Trimmer 	switch (cs_dsp->fw_ver) {
6160700bc2fSSimon Trimmer 	case 0:
6170700bc2fSSimon Trimmer 	case 1:
618a6e849d0SSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
619a6e849d0SSimon Trimmer 				"%s %s %x", cs_dsp->name, region_name,
620a6e849d0SSimon Trimmer 				cs_ctl->alg_region.alg);
6210700bc2fSSimon Trimmer 		break;
6220700bc2fSSimon Trimmer 	case 2:
6230700bc2fSSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
624e1468202SSimon Trimmer 				"%s%c %.12s %x", cs_dsp->name, *region_name,
6250700bc2fSSimon Trimmer 				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
6260700bc2fSSimon Trimmer 		break;
6270700bc2fSSimon Trimmer 	default:
6280700bc2fSSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
629e1468202SSimon Trimmer 				"%s %.12s %x", cs_dsp->name,
6300700bc2fSSimon Trimmer 				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
6310700bc2fSSimon Trimmer 		break;
6320700bc2fSSimon Trimmer 	}
6330700bc2fSSimon Trimmer 
6340700bc2fSSimon Trimmer 	if (cs_ctl->subname) {
6350700bc2fSSimon Trimmer 		int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
6360700bc2fSSimon Trimmer 		int skip = 0;
6370700bc2fSSimon Trimmer 
6380700bc2fSSimon Trimmer 		if (dsp->component->name_prefix)
6390700bc2fSSimon Trimmer 			avail -= strlen(dsp->component->name_prefix) + 1;
6400700bc2fSSimon Trimmer 
6410700bc2fSSimon Trimmer 		/* Truncate the subname from the start if it is too long */
6420700bc2fSSimon Trimmer 		if (cs_ctl->subname_len > avail)
6430700bc2fSSimon Trimmer 			skip = cs_ctl->subname_len - avail;
6440700bc2fSSimon Trimmer 
6450700bc2fSSimon Trimmer 		snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
6460700bc2fSSimon Trimmer 			 " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
6470700bc2fSSimon Trimmer 	}
6480700bc2fSSimon Trimmer 
6490700bc2fSSimon Trimmer 	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
6500700bc2fSSimon Trimmer 	if (!ctl)
6510700bc2fSSimon Trimmer 		return -ENOMEM;
6520700bc2fSSimon Trimmer 	ctl->cs_ctl = cs_ctl;
6530700bc2fSSimon Trimmer 
6540700bc2fSSimon Trimmer 	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
6550700bc2fSSimon Trimmer 	if (!ctl->name) {
6560700bc2fSSimon Trimmer 		ret = -ENOMEM;
6570700bc2fSSimon Trimmer 		goto err_ctl;
6580700bc2fSSimon Trimmer 	}
6590700bc2fSSimon Trimmer 
6600700bc2fSSimon Trimmer 	cs_ctl->priv = ctl;
6610700bc2fSSimon Trimmer 
6620700bc2fSSimon Trimmer 	INIT_WORK(&ctl->work, wm_adsp_ctl_work);
6630700bc2fSSimon Trimmer 	schedule_work(&ctl->work);
6640700bc2fSSimon Trimmer 
6650700bc2fSSimon Trimmer 	return 0;
6660700bc2fSSimon Trimmer 
6670700bc2fSSimon Trimmer err_ctl:
6680700bc2fSSimon Trimmer 	kfree(ctl);
6690700bc2fSSimon Trimmer 
6700700bc2fSSimon Trimmer 	return ret;
6710700bc2fSSimon Trimmer }
6720700bc2fSSimon Trimmer 
wm_adsp_control_remove(struct cs_dsp_coeff_ctl * cs_ctl)6730700bc2fSSimon Trimmer static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
6740700bc2fSSimon Trimmer {
6750700bc2fSSimon Trimmer 	struct wm_coeff_ctl *ctl = cs_ctl->priv;
6760700bc2fSSimon Trimmer 
677df6c505cSSimon Trimmer 	cancel_work_sync(&ctl->work);
678df6c505cSSimon Trimmer 
67966225e98SRichard Fitzgerald 	kfree(ctl->name);
68066225e98SRichard Fitzgerald 	kfree(ctl);
68166225e98SRichard Fitzgerald }
68266225e98SRichard Fitzgerald 
wm_adsp_write_ctl(struct wm_adsp * dsp,const char * name,int type,unsigned int alg,void * buf,size_t len)683eb65ccdbSLi Xu int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
684eb65ccdbSLi Xu 		      unsigned int alg, void *buf, size_t len)
685eb65ccdbSLi Xu {
686*3e1a29fbSRichard Fitzgerald 	struct cs_dsp_coeff_ctl *cs_ctl;
687eb65ccdbSLi Xu 	struct wm_coeff_ctl *ctl;
688eb65ccdbSLi Xu 	int ret;
689eb65ccdbSLi Xu 
690781118bcSRichard Fitzgerald 	mutex_lock(&dsp->cs_dsp.pwr_lock);
691*3e1a29fbSRichard Fitzgerald 	cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
692f444da38SCharles Keepax 	ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
693781118bcSRichard Fitzgerald 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
694781118bcSRichard Fitzgerald 
6957406bdbcSSimon Trimmer 	if (ret < 0)
69620441614SAdam Brickman 		return ret;
697eb65ccdbSLi Xu 
6987406bdbcSSimon Trimmer 	if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS))
69920441614SAdam Brickman 		return 0;
70020441614SAdam Brickman 
701e8010efcSCharles Keepax 	ctl = cs_ctl->priv;
702e8010efcSCharles Keepax 
70395d06196SCharles Keepax 	return snd_soc_component_notify_control(dsp->component, ctl->name);
704eb65ccdbSLi Xu }
705eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
706eb65ccdbSLi Xu 
wm_adsp_read_ctl(struct wm_adsp * dsp,const char * name,int type,unsigned int alg,void * buf,size_t len)707eb65ccdbSLi Xu int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
708eb65ccdbSLi Xu 		     unsigned int alg, void *buf, size_t len)
709eb65ccdbSLi Xu {
710781118bcSRichard Fitzgerald 	int ret;
711781118bcSRichard Fitzgerald 
712781118bcSRichard Fitzgerald 	mutex_lock(&dsp->cs_dsp.pwr_lock);
713781118bcSRichard Fitzgerald 	ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg),
714e8010efcSCharles Keepax 				     0, buf, len);
715781118bcSRichard Fitzgerald 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
716781118bcSRichard Fitzgerald 
717781118bcSRichard Fitzgerald 	return ret;
718eb65ccdbSLi Xu }
719eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
720eb65ccdbSLi Xu 
wm_adsp_release_firmware_files(struct wm_adsp * dsp,const struct firmware * wmfw_firmware,char * wmfw_filename,const struct firmware * coeff_firmware,char * coeff_filename)721f6bc909eSSimon Trimmer static void wm_adsp_release_firmware_files(struct wm_adsp *dsp,
722f6bc909eSSimon Trimmer 					   const struct firmware *wmfw_firmware,
723f6bc909eSSimon Trimmer 					   char *wmfw_filename,
724f6bc909eSSimon Trimmer 					   const struct firmware *coeff_firmware,
725f6bc909eSSimon Trimmer 					   char *coeff_filename)
7262323736dSCharles Keepax {
727f6bc909eSSimon Trimmer 	if (wmfw_firmware)
728f6bc909eSSimon Trimmer 		release_firmware(wmfw_firmware);
729f6bc909eSSimon Trimmer 	kfree(wmfw_filename);
7302323736dSCharles Keepax 
731f6bc909eSSimon Trimmer 	if (coeff_firmware)
732f6bc909eSSimon Trimmer 		release_firmware(coeff_firmware);
733f6bc909eSSimon Trimmer 	kfree(coeff_filename);
7342323736dSCharles Keepax }
7352323736dSCharles Keepax 
wm_adsp_request_firmware_file(struct wm_adsp * dsp,const struct firmware ** firmware,char ** filename,const char * dir,const char * system_name,const char * asoc_component_prefix,const char * filetype)736f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
737b6b62d94SSimon Trimmer 					 const struct firmware **firmware, char **filename,
738b6b62d94SSimon Trimmer 					 const char *dir, const char *system_name,
739b6b62d94SSimon Trimmer 					 const char *asoc_component_prefix,
740b6b62d94SSimon Trimmer 					 const char *filetype)
741db40517cSMark Brown {
742f6bc909eSSimon Trimmer 	struct cs_dsp *cs_dsp = &dsp->cs_dsp;
743fabab199SRichard Fitzgerald 	const char *fwf;
744b6b62d94SSimon Trimmer 	char *s, c;
745f6bc909eSSimon Trimmer 	int ret = 0;
746db40517cSMark Brown 
747fabab199SRichard Fitzgerald 	if (dsp->fwf_name)
748fabab199SRichard Fitzgerald 		fwf = dsp->fwf_name;
749fabab199SRichard Fitzgerald 	else
750fabab199SRichard Fitzgerald 		fwf = dsp->cs_dsp.name;
751fabab199SRichard Fitzgerald 
752b6b62d94SSimon Trimmer 	if (system_name && asoc_component_prefix)
753b6b62d94SSimon Trimmer 		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part,
754fabab199SRichard Fitzgerald 				      fwf, wm_adsp_fw[dsp->fw].file, system_name,
755b6b62d94SSimon Trimmer 				      asoc_component_prefix, filetype);
756b6b62d94SSimon Trimmer 	else if (system_name)
757b6b62d94SSimon Trimmer 		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part,
758fabab199SRichard Fitzgerald 				      fwf, wm_adsp_fw[dsp->fw].file, system_name,
759b6b62d94SSimon Trimmer 				      filetype);
760b6b62d94SSimon Trimmer 	else
761fabab199SRichard Fitzgerald 		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf,
762b6b62d94SSimon Trimmer 				      wm_adsp_fw[dsp->fw].file, filetype);
763b6b62d94SSimon Trimmer 
764f6bc909eSSimon Trimmer 	if (*filename == NULL)
765605391d0SRichard Fitzgerald 		return -ENOMEM;
766f6bc909eSSimon Trimmer 
767b6b62d94SSimon Trimmer 	/*
768b6b62d94SSimon Trimmer 	 * Make sure that filename is lower-case and any non alpha-numeric
769b6b62d94SSimon Trimmer 	 * characters except full stop and forward slash are replaced with
770b6b62d94SSimon Trimmer 	 * hyphens.
771b6b62d94SSimon Trimmer 	 */
772b6b62d94SSimon Trimmer 	s = *filename;
773b6b62d94SSimon Trimmer 	while (*s) {
774b6b62d94SSimon Trimmer 		c = *s;
775b6b62d94SSimon Trimmer 		if (isalnum(c))
776b6b62d94SSimon Trimmer 			*s = tolower(c);
777b6b62d94SSimon Trimmer 		else if ((c != '.') && (c != '/'))
778b6b62d94SSimon Trimmer 			*s = '-';
779b6b62d94SSimon Trimmer 		s++;
780b6b62d94SSimon Trimmer 	}
781b6b62d94SSimon Trimmer 
782b6b62d94SSimon Trimmer 	ret = firmware_request_nowarn(firmware, *filename, cs_dsp->dev);
783f6bc909eSSimon Trimmer 	if (ret != 0) {
784b6b62d94SSimon Trimmer 		adsp_dbg(dsp, "Failed to request '%s'\n", *filename);
785f6bc909eSSimon Trimmer 		kfree(*filename);
786f6bc909eSSimon Trimmer 		*filename = NULL;
787991b1de8SSimon Trimmer 	} else {
788991b1de8SSimon Trimmer 		adsp_dbg(dsp, "Found '%s'\n", *filename);
789605391d0SRichard Fitzgerald 	}
790605391d0SRichard Fitzgerald 
791f6bc909eSSimon Trimmer 	return ret;
792605391d0SRichard Fitzgerald }
793605391d0SRichard Fitzgerald 
794b6b62d94SSimon Trimmer static const char *cirrus_dir = "cirrus/";
wm_adsp_request_firmware_files(struct wm_adsp * dsp,const struct firmware ** wmfw_firmware,char ** wmfw_filename,const struct firmware ** coeff_firmware,char ** coeff_filename)795f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
796f6bc909eSSimon Trimmer 					  const struct firmware **wmfw_firmware,
797f6bc909eSSimon Trimmer 					  char **wmfw_filename,
798f6bc909eSSimon Trimmer 					  const struct firmware **coeff_firmware,
799f6bc909eSSimon Trimmer 					  char **coeff_filename)
8005e7a7a22SMark Brown {
801b6b62d94SSimon Trimmer 	const char *system_name = dsp->system_name;
802b6b62d94SSimon Trimmer 	const char *asoc_component_prefix = dsp->component->name_prefix;
803f6bc909eSSimon Trimmer 	int ret = 0;
804605391d0SRichard Fitzgerald 
805b6b62d94SSimon Trimmer 	if (system_name && asoc_component_prefix) {
806b6b62d94SSimon Trimmer 		if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
807b6b62d94SSimon Trimmer 						   cirrus_dir, system_name,
808b6b62d94SSimon Trimmer 						   asoc_component_prefix, "wmfw")) {
809b6b62d94SSimon Trimmer 			wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
810b6b62d94SSimon Trimmer 						      cirrus_dir, system_name,
811b6b62d94SSimon Trimmer 						      asoc_component_prefix, "bin");
8125e7a7a22SMark Brown 			return 0;
8135e7a7a22SMark Brown 		}
814b6b62d94SSimon Trimmer 	}
815b6b62d94SSimon Trimmer 
816b6b62d94SSimon Trimmer 	if (system_name) {
817b6b62d94SSimon Trimmer 		if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
818b6b62d94SSimon Trimmer 						   cirrus_dir, system_name,
819b6b62d94SSimon Trimmer 						   NULL, "wmfw")) {
820b6b62d94SSimon Trimmer 			if (asoc_component_prefix)
821b6b62d94SSimon Trimmer 				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
822b6b62d94SSimon Trimmer 							      cirrus_dir, system_name,
823b6b62d94SSimon Trimmer 							      asoc_component_prefix, "bin");
824b6b62d94SSimon Trimmer 
825b6b62d94SSimon Trimmer 			if (!*coeff_firmware)
826b6b62d94SSimon Trimmer 				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
827b6b62d94SSimon Trimmer 							      cirrus_dir, system_name,
828b6b62d94SSimon Trimmer 							      NULL, "bin");
829b6b62d94SSimon Trimmer 			return 0;
830b6b62d94SSimon Trimmer 		}
831b6b62d94SSimon Trimmer 	}
832b6b62d94SSimon Trimmer 
833b6b62d94SSimon Trimmer 	if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
834b6b62d94SSimon Trimmer 					   "", NULL, NULL, "wmfw")) {
835b6b62d94SSimon Trimmer 		wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
836b6b62d94SSimon Trimmer 					      "", NULL, NULL, "bin");
837b6b62d94SSimon Trimmer 		return 0;
838b6b62d94SSimon Trimmer 	}
839b6b62d94SSimon Trimmer 
840b6b62d94SSimon Trimmer 	ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
841b6b62d94SSimon Trimmer 					    cirrus_dir, NULL, NULL, "wmfw");
842b6b62d94SSimon Trimmer 	if (!ret) {
843b6b62d94SSimon Trimmer 		wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
844b6b62d94SSimon Trimmer 					      cirrus_dir, NULL, NULL, "bin");
845b6b62d94SSimon Trimmer 		return 0;
846b6b62d94SSimon Trimmer 	}
847b6b62d94SSimon Trimmer 
8480e7d82cbSSimon Trimmer 	if (dsp->wmfw_optional) {
8490e7d82cbSSimon Trimmer 		if (system_name) {
8500e7d82cbSSimon Trimmer 			if (asoc_component_prefix)
8510e7d82cbSSimon Trimmer 				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
8520e7d82cbSSimon Trimmer 							      cirrus_dir, system_name,
8530e7d82cbSSimon Trimmer 							      asoc_component_prefix, "bin");
8540e7d82cbSSimon Trimmer 
8550e7d82cbSSimon Trimmer 			if (!*coeff_firmware)
8560e7d82cbSSimon Trimmer 				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
8570e7d82cbSSimon Trimmer 							      cirrus_dir, system_name,
8580e7d82cbSSimon Trimmer 							      NULL, "bin");
8590e7d82cbSSimon Trimmer 		}
8600e7d82cbSSimon Trimmer 
8610e7d82cbSSimon Trimmer 		if (!*coeff_firmware)
8620e7d82cbSSimon Trimmer 			wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
8630e7d82cbSSimon Trimmer 						      "", NULL, NULL, "bin");
8640e7d82cbSSimon Trimmer 
8650e7d82cbSSimon Trimmer 		if (!*coeff_firmware)
8660e7d82cbSSimon Trimmer 			wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
8670e7d82cbSSimon Trimmer 						      cirrus_dir, NULL, NULL, "bin");
8680e7d82cbSSimon Trimmer 
86965a314b7SSimon Trimmer 		return 0;
8700e7d82cbSSimon Trimmer 	}
87165a314b7SSimon Trimmer 
872b6b62d94SSimon Trimmer 	adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
873fabab199SRichard Fitzgerald 		 cirrus_dir, dsp->part,
874fabab199SRichard Fitzgerald 		 dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name,
875fabab199SRichard Fitzgerald 		 wm_adsp_fw[dsp->fw].file, system_name, asoc_component_prefix);
876b6b62d94SSimon Trimmer 
877b6b62d94SSimon Trimmer 	return -ENOENT;
878b6b62d94SSimon Trimmer }
879dcad34f8SRichard Fitzgerald 
wm_adsp_common_init(struct wm_adsp * dsp)880e1468202SSimon Trimmer static int wm_adsp_common_init(struct wm_adsp *dsp)
88125ca837bSSimon Trimmer {
88225ca837bSSimon Trimmer 	INIT_LIST_HEAD(&dsp->compr_list);
88325ca837bSSimon Trimmer 	INIT_LIST_HEAD(&dsp->buffer_list);
884e1468202SSimon Trimmer 
885e1468202SSimon Trimmer 	return 0;
886e1468202SSimon Trimmer }
887e1468202SSimon Trimmer 
wm_adsp1_init(struct wm_adsp * dsp)88825ca837bSSimon Trimmer int wm_adsp1_init(struct wm_adsp *dsp)
88925ca837bSSimon Trimmer {
890e1468202SSimon Trimmer 	int ret;
89125ca837bSSimon Trimmer 
8922dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp1_client_ops;
8932dd04464SSimon Trimmer 
894e1468202SSimon Trimmer 	ret = cs_dsp_adsp1_init(&dsp->cs_dsp);
895e1468202SSimon Trimmer 	if (ret)
896e1468202SSimon Trimmer 		return ret;
897e1468202SSimon Trimmer 
898e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
899dcad34f8SRichard Fitzgerald }
9005e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
9015e7a7a22SMark Brown 
wm_adsp1_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)902186152dfSSimon Trimmer int wm_adsp1_event(struct snd_soc_dapm_widget *w,
903186152dfSSimon Trimmer 		   struct snd_kcontrol *kcontrol,
904186152dfSSimon Trimmer 		   int event)
905186152dfSSimon Trimmer {
906186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
907186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
908186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
909186152dfSSimon Trimmer 	int ret = 0;
910a828056fSSimon Trimmer 	char *wmfw_filename = NULL;
911a828056fSSimon Trimmer 	const struct firmware *wmfw_firmware = NULL;
912a828056fSSimon Trimmer 	char *coeff_filename = NULL;
913a828056fSSimon Trimmer 	const struct firmware *coeff_firmware = NULL;
914186152dfSSimon Trimmer 
915186152dfSSimon Trimmer 	dsp->component = component;
916186152dfSSimon Trimmer 
917186152dfSSimon Trimmer 	switch (event) {
918186152dfSSimon Trimmer 	case SND_SOC_DAPM_POST_PMU:
919a828056fSSimon Trimmer 		ret = wm_adsp_request_firmware_files(dsp,
920a828056fSSimon Trimmer 						     &wmfw_firmware, &wmfw_filename,
921a828056fSSimon Trimmer 						     &coeff_firmware, &coeff_filename);
922a828056fSSimon Trimmer 		if (ret)
923a828056fSSimon Trimmer 			break;
924a828056fSSimon Trimmer 
925e1468202SSimon Trimmer 		ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp,
926a828056fSSimon Trimmer 					    wmfw_firmware, wmfw_filename,
927a828056fSSimon Trimmer 					    coeff_firmware, coeff_filename,
9282169f2f1SSimon Trimmer 					    wm_adsp_fw_text[dsp->fw]);
929a828056fSSimon Trimmer 
930a828056fSSimon Trimmer 		wm_adsp_release_firmware_files(dsp,
931a828056fSSimon Trimmer 					       wmfw_firmware, wmfw_filename,
932a828056fSSimon Trimmer 					       coeff_firmware, coeff_filename);
933186152dfSSimon Trimmer 		break;
934186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
935e1468202SSimon Trimmer 		cs_dsp_adsp1_power_down(&dsp->cs_dsp);
936186152dfSSimon Trimmer 		break;
9372159ad93SMark Brown 	default:
9382159ad93SMark Brown 		break;
9392159ad93SMark Brown 	}
9402159ad93SMark Brown 
9412159ad93SMark Brown 	return ret;
9422159ad93SMark Brown }
9432159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
9442159ad93SMark Brown 
wm_adsp2_set_dspclk(struct snd_soc_dapm_widget * w,unsigned int freq)94525ca837bSSimon Trimmer int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq)
94625ca837bSSimon Trimmer {
94725ca837bSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
94825ca837bSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
94925ca837bSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
95025ca837bSSimon Trimmer 
951e1468202SSimon Trimmer 	return cs_dsp_set_dspclk(&dsp->cs_dsp, freq);
95225ca837bSSimon Trimmer }
953b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk);
954d82d767fSCharles Keepax 
wm_adsp2_preloader_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)955af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
956af813a6fSCharles Keepax 			   struct snd_ctl_elem_value *ucontrol)
957af813a6fSCharles Keepax {
9580fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
959b1470d4cSAjit Pandey 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
960b1470d4cSAjit Pandey 	struct soc_mixer_control *mc =
961b1470d4cSAjit Pandey 		(struct soc_mixer_control *)kcontrol->private_value;
962b1470d4cSAjit Pandey 	struct wm_adsp *dsp = &dsps[mc->shift - 1];
963af813a6fSCharles Keepax 
964af813a6fSCharles Keepax 	ucontrol->value.integer.value[0] = dsp->preloaded;
965af813a6fSCharles Keepax 
966af813a6fSCharles Keepax 	return 0;
967af813a6fSCharles Keepax }
968af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);
969af813a6fSCharles Keepax 
wm_adsp2_preloader_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)970af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
971af813a6fSCharles Keepax 			   struct snd_ctl_elem_value *ucontrol)
972af813a6fSCharles Keepax {
9730fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
974b1470d4cSAjit Pandey 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
9750fe1daa6SKuninori Morimoto 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
976af813a6fSCharles Keepax 	struct soc_mixer_control *mc =
977af813a6fSCharles Keepax 		(struct soc_mixer_control *)kcontrol->private_value;
978b1470d4cSAjit Pandey 	struct wm_adsp *dsp = &dsps[mc->shift - 1];
979af813a6fSCharles Keepax 	char preload[32];
980af813a6fSCharles Keepax 
981ba235634SCharles Keepax 	if (dsp->preloaded == ucontrol->value.integer.value[0])
982ba235634SCharles Keepax 		return 0;
983ba235634SCharles Keepax 
984e1468202SSimon Trimmer 	snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
985af813a6fSCharles Keepax 
986ba235634SCharles Keepax 	if (ucontrol->value.integer.value[0] || dsp->toggle_preload)
98795a594d0SCharles Keepax 		snd_soc_component_force_enable_pin(component, preload);
988af813a6fSCharles Keepax 	else
98995a594d0SCharles Keepax 		snd_soc_component_disable_pin(component, preload);
990af813a6fSCharles Keepax 
991af813a6fSCharles Keepax 	snd_soc_dapm_sync(dapm);
992af813a6fSCharles Keepax 
993868e49a4SStuart Henderson 	flush_work(&dsp->boot_work);
994868e49a4SStuart Henderson 
995ba235634SCharles Keepax 	dsp->preloaded = ucontrol->value.integer.value[0];
996ba235634SCharles Keepax 
997ba235634SCharles Keepax 	if (dsp->toggle_preload) {
998ba235634SCharles Keepax 		snd_soc_component_disable_pin(component, preload);
999ba235634SCharles Keepax 		snd_soc_dapm_sync(dapm);
1000ba235634SCharles Keepax 	}
1001ba235634SCharles Keepax 
100281d74ddaSCharles Keepax 	return 1;
1003af813a6fSCharles Keepax }
1004af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
1005af813a6fSCharles Keepax 
wm_adsp_power_up(struct wm_adsp * dsp,bool load_firmware)100662ddad42SRichard Fitzgerald int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware)
1007186152dfSSimon Trimmer {
1008a828056fSSimon Trimmer 	int ret = 0;
1009a828056fSSimon Trimmer 	char *wmfw_filename = NULL;
1010a828056fSSimon Trimmer 	const struct firmware *wmfw_firmware = NULL;
1011a828056fSSimon Trimmer 	char *coeff_filename = NULL;
1012a828056fSSimon Trimmer 	const struct firmware *coeff_firmware = NULL;
1013a828056fSSimon Trimmer 
101462ddad42SRichard Fitzgerald 	if (load_firmware) {
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)
101911520b87SSimon Trimmer 			return ret;
102062ddad42SRichard Fitzgerald 	}
1021186152dfSSimon Trimmer 
102211520b87SSimon Trimmer 	ret = cs_dsp_power_up(&dsp->cs_dsp,
1023a828056fSSimon Trimmer 			      wmfw_firmware, wmfw_filename,
1024a828056fSSimon Trimmer 			      coeff_firmware, coeff_filename,
10252169f2f1SSimon Trimmer 			      wm_adsp_fw_text[dsp->fw]);
1026a828056fSSimon Trimmer 
1027a828056fSSimon Trimmer 	wm_adsp_release_firmware_files(dsp,
1028a828056fSSimon Trimmer 				       wmfw_firmware, wmfw_filename,
1029a828056fSSimon Trimmer 				       coeff_firmware, coeff_filename);
103011520b87SSimon Trimmer 
103111520b87SSimon Trimmer 	return ret;
103211520b87SSimon Trimmer }
103311520b87SSimon Trimmer EXPORT_SYMBOL_GPL(wm_adsp_power_up);
103411520b87SSimon Trimmer 
wm_adsp_power_down(struct wm_adsp * dsp)1035d0a3a6adSSimon Trimmer void wm_adsp_power_down(struct wm_adsp *dsp)
1036d0a3a6adSSimon Trimmer {
1037d0a3a6adSSimon Trimmer 	cs_dsp_power_down(&dsp->cs_dsp);
1038d0a3a6adSSimon Trimmer }
1039d0a3a6adSSimon Trimmer EXPORT_SYMBOL_GPL(wm_adsp_power_down);
1040d0a3a6adSSimon Trimmer 
wm_adsp_boot_work(struct work_struct * work)104111520b87SSimon Trimmer static void wm_adsp_boot_work(struct work_struct *work)
104211520b87SSimon Trimmer {
104311520b87SSimon Trimmer 	struct wm_adsp *dsp = container_of(work,
104411520b87SSimon Trimmer 					   struct wm_adsp,
104511520b87SSimon Trimmer 					   boot_work);
104611520b87SSimon Trimmer 
104762ddad42SRichard Fitzgerald 	wm_adsp_power_up(dsp, true);
1048186152dfSSimon Trimmer }
1049186152dfSSimon Trimmer 
wm_adsp_early_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)1050186152dfSSimon Trimmer int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
1051186152dfSSimon Trimmer 			struct snd_kcontrol *kcontrol, int event)
1052186152dfSSimon Trimmer {
1053186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
1054186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
1055186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
1056186152dfSSimon Trimmer 
1057186152dfSSimon Trimmer 	switch (event) {
1058186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMU:
1059186152dfSSimon Trimmer 		queue_work(system_unbound_wq, &dsp->boot_work);
1060186152dfSSimon Trimmer 		break;
1061186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
1062d0a3a6adSSimon Trimmer 		wm_adsp_power_down(dsp);
106357a60cc3SCharles Keepax 		break;
106412db5eddSCharles Keepax 	default:
106512db5eddSCharles Keepax 		break;
1066cab27258SCharles Keepax 	}
106712db5eddSCharles Keepax 
106812db5eddSCharles Keepax 	return 0;
106912db5eddSCharles Keepax }
10704e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event);
107112db5eddSCharles Keepax 
wm_adsp_pre_run(struct cs_dsp * cs_dsp)1072fe071308SRichard Fitzgerald static int wm_adsp_pre_run(struct cs_dsp *cs_dsp)
1073fe071308SRichard Fitzgerald {
1074fe071308SRichard Fitzgerald 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
1075fe071308SRichard Fitzgerald 
1076fe071308SRichard Fitzgerald 	if (!dsp->pre_run)
1077fe071308SRichard Fitzgerald 		return 0;
1078fe071308SRichard Fitzgerald 
1079fe071308SRichard Fitzgerald 	return (*dsp->pre_run)(dsp);
1080fe071308SRichard Fitzgerald }
1081fe071308SRichard Fitzgerald 
wm_adsp_event_post_run(struct cs_dsp * cs_dsp)1082e1468202SSimon Trimmer static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp)
1083d8a64d6aSCharles Keepax {
1084e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
1085e1468202SSimon Trimmer 
1086186152dfSSimon Trimmer 	if (wm_adsp_fw[dsp->fw].num_caps != 0)
1087186152dfSSimon Trimmer 		return wm_adsp_buffer_init(dsp);
1088d8a64d6aSCharles Keepax 
1089186152dfSSimon Trimmer 	return 0;
1090186152dfSSimon Trimmer }
1091186152dfSSimon Trimmer 
wm_adsp_event_post_stop(struct cs_dsp * cs_dsp)1092e1468202SSimon Trimmer static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp)
1093186152dfSSimon Trimmer {
1094e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
1095e1468202SSimon Trimmer 
1096186152dfSSimon Trimmer 	if (wm_adsp_fw[dsp->fw].num_caps != 0)
1097186152dfSSimon Trimmer 		wm_adsp_buffer_free(dsp);
1098186152dfSSimon Trimmer 
1099186152dfSSimon Trimmer 	dsp->fatal_error = false;
1100186152dfSSimon Trimmer }
1101186152dfSSimon Trimmer 
wm_adsp_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)1102186152dfSSimon Trimmer int wm_adsp_event(struct snd_soc_dapm_widget *w,
1103186152dfSSimon Trimmer 		  struct snd_kcontrol *kcontrol, int event)
1104186152dfSSimon Trimmer {
1105186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
1106186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
1107186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
1108186152dfSSimon Trimmer 	int ret = 0;
1109186152dfSSimon Trimmer 
1110186152dfSSimon Trimmer 	switch (event) {
1111186152dfSSimon Trimmer 	case SND_SOC_DAPM_POST_PMU:
1112186152dfSSimon Trimmer 		flush_work(&dsp->boot_work);
1113e1468202SSimon Trimmer 		ret = cs_dsp_run(&dsp->cs_dsp);
1114186152dfSSimon Trimmer 		break;
1115186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
1116e1468202SSimon Trimmer 		cs_dsp_stop(&dsp->cs_dsp);
1117186152dfSSimon Trimmer 		break;
11182159ad93SMark Brown 	default:
11192159ad93SMark Brown 		break;
11202159ad93SMark Brown 	}
11212159ad93SMark Brown 
11222159ad93SMark Brown 	return ret;
11232159ad93SMark Brown }
11244e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event);
1125973838a0SMark Brown 
wm_adsp2_component_probe(struct wm_adsp * dsp,struct snd_soc_component * component)11260fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
1127f5e2ce92SRichard Fitzgerald {
1128af813a6fSCharles Keepax 	char preload[32];
1129af813a6fSCharles Keepax 
11300cd1fd57SSimon Trimmer 	if (!dsp->cs_dsp.no_core_startstop) {
1131e1468202SSimon Trimmer 		snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
113295a594d0SCharles Keepax 		snd_soc_component_disable_pin(component, preload);
11330cd1fd57SSimon Trimmer 	}
1134685f51a5SRichard Fitzgerald 
1135e1468202SSimon Trimmer 	cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);
1136f9f55e31SRichard Fitzgerald 
11370fe1daa6SKuninori Morimoto 	dsp->component = component;
1138af813a6fSCharles Keepax 
11390a047f07SRichard Fitzgerald 	return 0;
1140f5e2ce92SRichard Fitzgerald }
11410fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
1142f5e2ce92SRichard Fitzgerald 
wm_adsp2_component_remove(struct wm_adsp * dsp,struct snd_soc_component * component)11430fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
1144f5e2ce92SRichard Fitzgerald {
1145e1468202SSimon Trimmer 	cs_dsp_cleanup_debugfs(&dsp->cs_dsp);
1146f9f55e31SRichard Fitzgerald 
1147f5e2ce92SRichard Fitzgerald 	return 0;
1148f5e2ce92SRichard Fitzgerald }
11490fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove);
1150f5e2ce92SRichard Fitzgerald 
wm_adsp2_init(struct wm_adsp * dsp)115125ca837bSSimon Trimmer int wm_adsp2_init(struct wm_adsp *dsp)
115225ca837bSSimon Trimmer {
1153e1468202SSimon Trimmer 	int ret;
1154e1468202SSimon Trimmer 
11554e08d50dSCharles Keepax 	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
11566ab2b7b4SDimitris Papastamos 
11576092be2dSCharles Keepax 	dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr);
11582dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
11596092be2dSCharles Keepax 
1160e1468202SSimon Trimmer 	ret = cs_dsp_adsp2_init(&dsp->cs_dsp);
1161e1468202SSimon Trimmer 	if (ret)
1162e1468202SSimon Trimmer 		return ret;
116325ca837bSSimon Trimmer 
1164e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
1165973838a0SMark Brown }
1166973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
11670a37c6efSPraveen Diwakar 
wm_halo_init(struct wm_adsp * dsp)116825ca837bSSimon Trimmer int wm_halo_init(struct wm_adsp *dsp)
116925ca837bSSimon Trimmer {
1170e1468202SSimon Trimmer 	int ret;
1171e1468202SSimon Trimmer 
1172170b1e12SWen Shi 	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
1173170b1e12SWen Shi 
11746092be2dSCharles Keepax 	dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr);
11752dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
11766092be2dSCharles Keepax 
1177e1468202SSimon Trimmer 	ret = cs_dsp_halo_init(&dsp->cs_dsp);
1178e1468202SSimon Trimmer 	if (ret)
1179e1468202SSimon Trimmer 		return ret;
118025ca837bSSimon Trimmer 
1181e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
1182170b1e12SWen Shi }
1183170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init);
1184170b1e12SWen Shi 
wm_adsp2_remove(struct wm_adsp * dsp)118525ca837bSSimon Trimmer void wm_adsp2_remove(struct wm_adsp *dsp)
118625ca837bSSimon Trimmer {
1187e1468202SSimon Trimmer 	cs_dsp_remove(&dsp->cs_dsp);
118825ca837bSSimon Trimmer }
118966225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove);
119066225e98SRichard Fitzgerald 
wm_adsp_compr_attached(struct wm_adsp_compr * compr)1191edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
1192edd71350SCharles Keepax {
1193edd71350SCharles Keepax 	return compr->buf != NULL;
1194edd71350SCharles Keepax }
1195edd71350SCharles Keepax 
wm_adsp_compr_attach(struct wm_adsp_compr * compr)1196edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
1197edd71350SCharles Keepax {
11984f2d4eabSStuart Henderson 	struct wm_adsp_compr_buf *buf = NULL, *tmp;
11994f2d4eabSStuart Henderson 
1200a2bcbc1bSCharles Keepax 	if (compr->dsp->fatal_error)
1201a2bcbc1bSCharles Keepax 		return -EINVAL;
1202a2bcbc1bSCharles Keepax 
12034f2d4eabSStuart Henderson 	list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
12044f2d4eabSStuart Henderson 		if (!tmp->name || !strcmp(compr->name, tmp->name)) {
12054f2d4eabSStuart Henderson 			buf = tmp;
12064f2d4eabSStuart Henderson 			break;
12074f2d4eabSStuart Henderson 		}
12084f2d4eabSStuart Henderson 	}
12094f2d4eabSStuart Henderson 
12104f2d4eabSStuart Henderson 	if (!buf)
1211edd71350SCharles Keepax 		return -EINVAL;
1212edd71350SCharles Keepax 
12134f2d4eabSStuart Henderson 	compr->buf = buf;
1214789b930aSCharles Keepax 	buf->compr = compr;
1215edd71350SCharles Keepax 
1216edd71350SCharles Keepax 	return 0;
1217edd71350SCharles Keepax }
1218edd71350SCharles Keepax 
wm_adsp_compr_detach(struct wm_adsp_compr * compr)1219721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
1220721be3beSCharles Keepax {
1221721be3beSCharles Keepax 	if (!compr)
1222721be3beSCharles Keepax 		return;
1223721be3beSCharles Keepax 
1224721be3beSCharles Keepax 	/* Wake the poll so it can see buffer is no longer attached */
1225721be3beSCharles Keepax 	if (compr->stream)
1226721be3beSCharles Keepax 		snd_compr_fragment_elapsed(compr->stream);
1227721be3beSCharles Keepax 
1228721be3beSCharles Keepax 	if (wm_adsp_compr_attached(compr)) {
1229721be3beSCharles Keepax 		compr->buf->compr = NULL;
1230721be3beSCharles Keepax 		compr->buf = NULL;
1231721be3beSCharles Keepax 	}
1232721be3beSCharles Keepax }
1233721be3beSCharles Keepax 
wm_adsp_compr_open(struct wm_adsp * dsp,struct snd_compr_stream * stream)1234406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
1235406abc95SCharles Keepax {
12364f2d4eabSStuart Henderson 	struct wm_adsp_compr *compr, *tmp;
12374f2d4eabSStuart Henderson 	struct snd_soc_pcm_runtime *rtd = stream->private_data;
1238406abc95SCharles Keepax 	int ret = 0;
1239406abc95SCharles Keepax 
1240e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1241406abc95SCharles Keepax 
1242406abc95SCharles Keepax 	if (wm_adsp_fw[dsp->fw].num_caps == 0) {
12430d3fba3eSCharles Keepax 		adsp_err(dsp, "%s: Firmware does not support compressed API\n",
1244b5cb8558SKuninori Morimoto 			 asoc_rtd_to_codec(rtd, 0)->name);
1245406abc95SCharles Keepax 		ret = -ENXIO;
1246406abc95SCharles Keepax 		goto out;
1247406abc95SCharles Keepax 	}
1248406abc95SCharles Keepax 
1249406abc95SCharles Keepax 	if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
12500d3fba3eSCharles Keepax 		adsp_err(dsp, "%s: Firmware does not support stream direction\n",
1251b5cb8558SKuninori Morimoto 			 asoc_rtd_to_codec(rtd, 0)->name);
1252406abc95SCharles Keepax 		ret = -EINVAL;
1253406abc95SCharles Keepax 		goto out;
1254406abc95SCharles Keepax 	}
1255406abc95SCharles Keepax 
12564f2d4eabSStuart Henderson 	list_for_each_entry(tmp, &dsp->compr_list, list) {
1257b5cb8558SKuninori Morimoto 		if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) {
12580d3fba3eSCharles Keepax 			adsp_err(dsp, "%s: Only a single stream supported per dai\n",
1259b5cb8558SKuninori Morimoto 				 asoc_rtd_to_codec(rtd, 0)->name);
126095fe9597SCharles Keepax 			ret = -EBUSY;
126195fe9597SCharles Keepax 			goto out;
126295fe9597SCharles Keepax 		}
12634f2d4eabSStuart Henderson 	}
126495fe9597SCharles Keepax 
1265406abc95SCharles Keepax 	compr = kzalloc(sizeof(*compr), GFP_KERNEL);
1266406abc95SCharles Keepax 	if (!compr) {
1267406abc95SCharles Keepax 		ret = -ENOMEM;
1268406abc95SCharles Keepax 		goto out;
1269406abc95SCharles Keepax 	}
1270406abc95SCharles Keepax 
1271406abc95SCharles Keepax 	compr->dsp = dsp;
1272406abc95SCharles Keepax 	compr->stream = stream;
1273b5cb8558SKuninori Morimoto 	compr->name = asoc_rtd_to_codec(rtd, 0)->name;
1274406abc95SCharles Keepax 
12754f2d4eabSStuart Henderson 	list_add_tail(&compr->list, &dsp->compr_list);
1276406abc95SCharles Keepax 
1277406abc95SCharles Keepax 	stream->runtime->private_data = compr;
1278406abc95SCharles Keepax 
1279406abc95SCharles Keepax out:
1280e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1281406abc95SCharles Keepax 
1282406abc95SCharles Keepax 	return ret;
1283406abc95SCharles Keepax }
1284406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
1285406abc95SCharles Keepax 
wm_adsp_compr_free(struct snd_soc_component * component,struct snd_compr_stream * stream)12863a5ccf25SKuninori Morimoto int wm_adsp_compr_free(struct snd_soc_component *component,
12873a5ccf25SKuninori Morimoto 		       struct snd_compr_stream *stream)
1288406abc95SCharles Keepax {
1289406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1290406abc95SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1291406abc95SCharles Keepax 
1292e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1293406abc95SCharles Keepax 
1294721be3beSCharles Keepax 	wm_adsp_compr_detach(compr);
12954f2d4eabSStuart Henderson 	list_del(&compr->list);
1296406abc95SCharles Keepax 
129783a40ce9SCharles Keepax 	kfree(compr->raw_buf);
1298406abc95SCharles Keepax 	kfree(compr);
1299406abc95SCharles Keepax 
1300e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1301406abc95SCharles Keepax 
1302406abc95SCharles Keepax 	return 0;
1303406abc95SCharles Keepax }
1304406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free);
1305406abc95SCharles Keepax 
wm_adsp_compr_check_params(struct snd_compr_stream * stream,struct snd_compr_params * params)1306406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
1307406abc95SCharles Keepax 				      struct snd_compr_params *params)
1308406abc95SCharles Keepax {
1309406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1310406abc95SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1311406abc95SCharles Keepax 	const struct wm_adsp_fw_caps *caps;
1312406abc95SCharles Keepax 	const struct snd_codec_desc *desc;
1313406abc95SCharles Keepax 	int i, j;
1314406abc95SCharles Keepax 
1315406abc95SCharles Keepax 	if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||
1316406abc95SCharles Keepax 	    params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
1317406abc95SCharles Keepax 	    params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
1318406abc95SCharles Keepax 	    params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
13195beb8eeaSSimon Trimmer 	    params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) {
13200d3fba3eSCharles Keepax 		compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n",
1321406abc95SCharles Keepax 			  params->buffer.fragment_size,
1322406abc95SCharles Keepax 			  params->buffer.fragments);
1323406abc95SCharles Keepax 
1324406abc95SCharles Keepax 		return -EINVAL;
1325406abc95SCharles Keepax 	}
1326406abc95SCharles Keepax 
1327406abc95SCharles Keepax 	for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
1328406abc95SCharles Keepax 		caps = &wm_adsp_fw[dsp->fw].caps[i];
1329406abc95SCharles Keepax 		desc = &caps->desc;
1330406abc95SCharles Keepax 
1331406abc95SCharles Keepax 		if (caps->id != params->codec.id)
1332406abc95SCharles Keepax 			continue;
1333406abc95SCharles Keepax 
1334406abc95SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
1335406abc95SCharles Keepax 			if (desc->max_ch < params->codec.ch_out)
1336406abc95SCharles Keepax 				continue;
1337406abc95SCharles Keepax 		} else {
1338406abc95SCharles Keepax 			if (desc->max_ch < params->codec.ch_in)
1339406abc95SCharles Keepax 				continue;
1340406abc95SCharles Keepax 		}
1341406abc95SCharles Keepax 
1342406abc95SCharles Keepax 		if (!(desc->formats & (1 << params->codec.format)))
1343406abc95SCharles Keepax 			continue;
1344406abc95SCharles Keepax 
1345406abc95SCharles Keepax 		for (j = 0; j < desc->num_sample_rates; ++j)
1346406abc95SCharles Keepax 			if (desc->sample_rates[j] == params->codec.sample_rate)
1347406abc95SCharles Keepax 				return 0;
1348406abc95SCharles Keepax 	}
1349406abc95SCharles Keepax 
13500d3fba3eSCharles Keepax 	compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
1351406abc95SCharles Keepax 		  params->codec.id, params->codec.ch_in, params->codec.ch_out,
1352406abc95SCharles Keepax 		  params->codec.sample_rate, params->codec.format);
1353406abc95SCharles Keepax 	return -EINVAL;
1354406abc95SCharles Keepax }
1355406abc95SCharles Keepax 
wm_adsp_compr_frag_words(struct wm_adsp_compr * compr)1356565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
1357565ace46SCharles Keepax {
13585beb8eeaSSimon Trimmer 	return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE;
1359565ace46SCharles Keepax }
1360565ace46SCharles Keepax 
wm_adsp_compr_set_params(struct snd_soc_component * component,struct snd_compr_stream * stream,struct snd_compr_params * params)13613a5ccf25SKuninori Morimoto int wm_adsp_compr_set_params(struct snd_soc_component *component,
13623a5ccf25SKuninori Morimoto 			     struct snd_compr_stream *stream,
1363406abc95SCharles Keepax 			     struct snd_compr_params *params)
1364406abc95SCharles Keepax {
1365406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
136683a40ce9SCharles Keepax 	unsigned int size;
1367406abc95SCharles Keepax 	int ret;
1368406abc95SCharles Keepax 
1369406abc95SCharles Keepax 	ret = wm_adsp_compr_check_params(stream, params);
1370406abc95SCharles Keepax 	if (ret)
1371406abc95SCharles Keepax 		return ret;
1372406abc95SCharles Keepax 
1373406abc95SCharles Keepax 	compr->size = params->buffer;
1374406abc95SCharles Keepax 
13750d3fba3eSCharles Keepax 	compr_dbg(compr, "fragment_size=%d fragments=%d\n",
1376406abc95SCharles Keepax 		  compr->size.fragment_size, compr->size.fragments);
1377406abc95SCharles Keepax 
137883a40ce9SCharles Keepax 	size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
137983a40ce9SCharles Keepax 	compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
138083a40ce9SCharles Keepax 	if (!compr->raw_buf)
138183a40ce9SCharles Keepax 		return -ENOMEM;
138283a40ce9SCharles Keepax 
1383da2b3358SCharles Keepax 	compr->sample_rate = params->codec.sample_rate;
1384da2b3358SCharles Keepax 
1385406abc95SCharles Keepax 	return 0;
1386406abc95SCharles Keepax }
1387406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
1388406abc95SCharles Keepax 
wm_adsp_compr_get_caps(struct snd_soc_component * component,struct snd_compr_stream * stream,struct snd_compr_caps * caps)13893a5ccf25SKuninori Morimoto int wm_adsp_compr_get_caps(struct snd_soc_component *component,
13903a5ccf25SKuninori Morimoto 			   struct snd_compr_stream *stream,
1391406abc95SCharles Keepax 			   struct snd_compr_caps *caps)
1392406abc95SCharles Keepax {
1393406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1394406abc95SCharles Keepax 	int fw = compr->dsp->fw;
1395406abc95SCharles Keepax 	int i;
1396406abc95SCharles Keepax 
1397406abc95SCharles Keepax 	if (wm_adsp_fw[fw].caps) {
1398406abc95SCharles Keepax 		for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
1399406abc95SCharles Keepax 			caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
1400406abc95SCharles Keepax 
1401406abc95SCharles Keepax 		caps->num_codecs = i;
1402406abc95SCharles Keepax 		caps->direction = wm_adsp_fw[fw].compr_direction;
1403406abc95SCharles Keepax 
1404406abc95SCharles Keepax 		caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
1405406abc95SCharles Keepax 		caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
1406406abc95SCharles Keepax 		caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;
1407406abc95SCharles Keepax 		caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;
1408406abc95SCharles Keepax 	}
1409406abc95SCharles Keepax 
1410406abc95SCharles Keepax 	return 0;
1411406abc95SCharles Keepax }
1412406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
1413406abc95SCharles Keepax 
wm_adsp_buffer_read(struct wm_adsp_compr_buf * buf,unsigned int field_offset,u32 * data)14142cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
14152cd19bdbSCharles Keepax 				      unsigned int field_offset, u32 *data)
14162cd19bdbSCharles Keepax {
1417e1468202SSimon Trimmer 	return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
14182cd19bdbSCharles Keepax 				     buf->host_buf_ptr + field_offset, data);
14192cd19bdbSCharles Keepax }
14202cd19bdbSCharles Keepax 
wm_adsp_buffer_write(struct wm_adsp_compr_buf * buf,unsigned int field_offset,u32 data)14212cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
14222cd19bdbSCharles Keepax 				       unsigned int field_offset, u32 data)
14232cd19bdbSCharles Keepax {
1424e1468202SSimon Trimmer 	return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
1425e1468202SSimon Trimmer 				      buf->host_buf_ptr + field_offset,
1426e1468202SSimon Trimmer 				      data);
14272cd19bdbSCharles Keepax }
14282cd19bdbSCharles Keepax 
wm_adsp_buffer_populate(struct wm_adsp_compr_buf * buf)14291e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
14301e38f069SCharles Keepax {
14311e38f069SCharles Keepax 	const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
14321e38f069SCharles Keepax 	struct wm_adsp_buffer_region *region;
14331e38f069SCharles Keepax 	u32 offset = 0;
14341e38f069SCharles Keepax 	int i, ret;
14351e38f069SCharles Keepax 
1436a792af69SCharles Keepax 	buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions),
1437a792af69SCharles Keepax 			       GFP_KERNEL);
1438a792af69SCharles Keepax 	if (!buf->regions)
1439a792af69SCharles Keepax 		return -ENOMEM;
1440a792af69SCharles Keepax 
14411e38f069SCharles Keepax 	for (i = 0; i < caps->num_regions; ++i) {
14421e38f069SCharles Keepax 		region = &buf->regions[i];
14431e38f069SCharles Keepax 
14441e38f069SCharles Keepax 		region->offset = offset;
14451e38f069SCharles Keepax 		region->mem_type = caps->region_defs[i].mem_type;
14461e38f069SCharles Keepax 
14471e38f069SCharles Keepax 		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
14481e38f069SCharles Keepax 					  &region->base_addr);
14491e38f069SCharles Keepax 		if (ret < 0)
1450b8668fe7SDinghao Liu 			goto err;
14511e38f069SCharles Keepax 
14521e38f069SCharles Keepax 		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
14531e38f069SCharles Keepax 					  &offset);
14541e38f069SCharles Keepax 		if (ret < 0)
1455b8668fe7SDinghao Liu 			goto err;
14561e38f069SCharles Keepax 
14571e38f069SCharles Keepax 		region->cumulative_size = offset;
14581e38f069SCharles Keepax 
14590d3fba3eSCharles Keepax 		compr_dbg(buf,
14601e38f069SCharles Keepax 			  "region=%d type=%d base=%08x off=%08x size=%08x\n",
14611e38f069SCharles Keepax 			  i, region->mem_type, region->base_addr,
14621e38f069SCharles Keepax 			  region->offset, region->cumulative_size);
14631e38f069SCharles Keepax 	}
14641e38f069SCharles Keepax 
14651e38f069SCharles Keepax 	return 0;
1466b8668fe7SDinghao Liu 
1467b8668fe7SDinghao Liu err:
1468b8668fe7SDinghao Liu 	kfree(buf->regions);
1469b8668fe7SDinghao Liu 	return ret;
14701e38f069SCharles Keepax }
14711e38f069SCharles Keepax 
wm_adsp_buffer_clear(struct wm_adsp_compr_buf * buf)14721e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf)
14731e38f069SCharles Keepax {
14741e38f069SCharles Keepax 	buf->irq_count = 0xFFFFFFFF;
14751e38f069SCharles Keepax 	buf->read_index = -1;
14761e38f069SCharles Keepax 	buf->avail = 0;
14771e38f069SCharles Keepax }
14781e38f069SCharles Keepax 
wm_adsp_buffer_alloc(struct wm_adsp * dsp)1479a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)
1480a792af69SCharles Keepax {
1481a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1482a792af69SCharles Keepax 
1483a792af69SCharles Keepax 	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
1484a792af69SCharles Keepax 	if (!buf)
1485a792af69SCharles Keepax 		return NULL;
1486a792af69SCharles Keepax 
1487a792af69SCharles Keepax 	buf->dsp = dsp;
1488a792af69SCharles Keepax 
1489a792af69SCharles Keepax 	wm_adsp_buffer_clear(buf);
1490a792af69SCharles Keepax 
1491a792af69SCharles Keepax 	return buf;
1492a792af69SCharles Keepax }
1493a792af69SCharles Keepax 
wm_adsp_buffer_parse_legacy(struct wm_adsp * dsp)1494a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
14952cd19bdbSCharles Keepax {
14965beb8eeaSSimon Trimmer 	struct cs_dsp_alg_region *alg_region;
1497a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
14982cd19bdbSCharles Keepax 	u32 xmalg, addr, magic;
14992cd19bdbSCharles Keepax 	int i, ret;
15002cd19bdbSCharles Keepax 
1501e1468202SSimon Trimmer 	alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id);
15029daf4fd0SLi Xu 	if (!alg_region) {
15039daf4fd0SLi Xu 		adsp_err(dsp, "No algorithm region found\n");
15049daf4fd0SLi Xu 		return -EINVAL;
15059daf4fd0SLi Xu 	}
15069daf4fd0SLi Xu 
15076092be2dSCharles Keepax 	xmalg = dsp->sys_config_size / sizeof(__be32);
15082cd19bdbSCharles Keepax 
15092cd19bdbSCharles Keepax 	addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
1510e1468202SSimon Trimmer 	ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic);
15112cd19bdbSCharles Keepax 	if (ret < 0)
15122cd19bdbSCharles Keepax 		return ret;
15132cd19bdbSCharles Keepax 
15142cd19bdbSCharles Keepax 	if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
1515a792af69SCharles Keepax 		return -ENODEV;
15162cd19bdbSCharles Keepax 
15170f1d41a8SCharles Keepax 	buf = wm_adsp_buffer_alloc(dsp);
15180f1d41a8SCharles Keepax 	if (!buf)
15190f1d41a8SCharles Keepax 		return -ENOMEM;
15200f1d41a8SCharles Keepax 
15212cd19bdbSCharles Keepax 	addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
15222cd19bdbSCharles Keepax 	for (i = 0; i < 5; ++i) {
1523e1468202SSimon Trimmer 		ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr,
15242cd19bdbSCharles Keepax 					    &buf->host_buf_ptr);
15252cd19bdbSCharles Keepax 		if (ret < 0)
15260f1d41a8SCharles Keepax 			goto err;
15272cd19bdbSCharles Keepax 
15282cd19bdbSCharles Keepax 		if (buf->host_buf_ptr)
15292cd19bdbSCharles Keepax 			break;
15302cd19bdbSCharles Keepax 
15312cd19bdbSCharles Keepax 		usleep_range(1000, 2000);
15322cd19bdbSCharles Keepax 	}
15332cd19bdbSCharles Keepax 
15340f1d41a8SCharles Keepax 	if (!buf->host_buf_ptr) {
15350f1d41a8SCharles Keepax 		ret = -EIO;
15360f1d41a8SCharles Keepax 		goto err;
15370f1d41a8SCharles Keepax 	}
15382cd19bdbSCharles Keepax 
1539fb13f19dSAndrew Ford 	buf->host_buf_mem_type = WMFW_ADSP2_XM;
1540fb13f19dSAndrew Ford 
1541a792af69SCharles Keepax 	ret = wm_adsp_buffer_populate(buf);
1542a792af69SCharles Keepax 	if (ret < 0)
15430f1d41a8SCharles Keepax 		goto err;
15440f1d41a8SCharles Keepax 
15450f1d41a8SCharles Keepax 	list_add_tail(&buf->list, &dsp->buffer_list);
1546a792af69SCharles Keepax 
15470d3fba3eSCharles Keepax 	compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr);
15482cd19bdbSCharles Keepax 
15492cd19bdbSCharles Keepax 	return 0;
15500f1d41a8SCharles Keepax 
15510f1d41a8SCharles Keepax err:
15520f1d41a8SCharles Keepax 	kfree(buf);
15530f1d41a8SCharles Keepax 
15540f1d41a8SCharles Keepax 	return ret;
15552cd19bdbSCharles Keepax }
15562cd19bdbSCharles Keepax 
wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl * cs_ctl)15570700bc2fSSimon Trimmer static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)
1558d52ed4b0SRichard Fitzgerald {
15594f2d4eabSStuart Henderson 	struct wm_adsp_host_buf_coeff_v1 coeff_v1;
1560a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1561e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
15620f1d41a8SCharles Keepax 	unsigned int version = 0;
1563a792af69SCharles Keepax 	int ret, i;
1564d52ed4b0SRichard Fitzgerald 
1565d52ed4b0SRichard Fitzgerald 	for (i = 0; i < 5; ++i) {
1566a887f9c7SCharles Keepax 		ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1,
1567a887f9c7SCharles Keepax 					     min(cs_ctl->len, sizeof(coeff_v1)));
1568d52ed4b0SRichard Fitzgerald 		if (ret < 0)
1569d52ed4b0SRichard Fitzgerald 			return ret;
1570d52ed4b0SRichard Fitzgerald 
157104ae0859SCharles Keepax 		if (coeff_v1.host_buf_ptr)
1572d52ed4b0SRichard Fitzgerald 			break;
1573d52ed4b0SRichard Fitzgerald 
1574d52ed4b0SRichard Fitzgerald 		usleep_range(1000, 2000);
1575d52ed4b0SRichard Fitzgerald 	}
1576d52ed4b0SRichard Fitzgerald 
157704ae0859SCharles Keepax 	if (!coeff_v1.host_buf_ptr) {
15780700bc2fSSimon Trimmer 		adsp_err(dsp, "Failed to acquire host buffer\n");
1579d52ed4b0SRichard Fitzgerald 		return -EIO;
1580d52ed4b0SRichard Fitzgerald 	}
1581d52ed4b0SRichard Fitzgerald 
15820700bc2fSSimon Trimmer 	buf = wm_adsp_buffer_alloc(dsp);
15832cd19bdbSCharles Keepax 	if (!buf)
15842cd19bdbSCharles Keepax 		return -ENOMEM;
15852cd19bdbSCharles Keepax 
15860700bc2fSSimon Trimmer 	buf->host_buf_mem_type = cs_ctl->alg_region.type;
158704ae0859SCharles Keepax 	buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr);
15882cd19bdbSCharles Keepax 
15892cd19bdbSCharles Keepax 	ret = wm_adsp_buffer_populate(buf);
1590a792af69SCharles Keepax 	if (ret < 0)
15910f1d41a8SCharles Keepax 		goto err;
1592a792af69SCharles Keepax 
15934f2d4eabSStuart Henderson 	/*
15944f2d4eabSStuart Henderson 	 * v0 host_buffer coefficients didn't have versioning, so if the
15954f2d4eabSStuart Henderson 	 * control is one word, assume version 0.
15964f2d4eabSStuart Henderson 	 */
15970f1d41a8SCharles Keepax 	if (cs_ctl->len == 4)
15980f1d41a8SCharles Keepax 		goto done;
15992cd19bdbSCharles Keepax 
1600a0b653e8SRichard Fitzgerald 	version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK;
1601a0b653e8SRichard Fitzgerald 	version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;
16024f2d4eabSStuart Henderson 
1603a0b653e8SRichard Fitzgerald 	if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {
16040700bc2fSSimon Trimmer 		adsp_err(dsp,
16054f2d4eabSStuart Henderson 			 "Host buffer coeff ver %u > supported version %u\n",
1606a0b653e8SRichard Fitzgerald 			 version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);
16070f1d41a8SCharles Keepax 		ret = -EINVAL;
16080f1d41a8SCharles Keepax 		goto err;
16094f2d4eabSStuart Henderson 	}
16104f2d4eabSStuart Henderson 
16115beb8eeaSSimon Trimmer 	cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name));
16124f2d4eabSStuart Henderson 
16130700bc2fSSimon Trimmer 	buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part,
16144f2d4eabSStuart Henderson 			      (char *)&coeff_v1.name);
16154f2d4eabSStuart Henderson 
16160f1d41a8SCharles Keepax done:
16170f1d41a8SCharles Keepax 	list_add_tail(&buf->list, &dsp->buffer_list);
16180f1d41a8SCharles Keepax 
16190d3fba3eSCharles Keepax 	compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n",
1620a0b653e8SRichard Fitzgerald 		  buf->host_buf_ptr, version);
16214f2d4eabSStuart Henderson 
1622a0b653e8SRichard Fitzgerald 	return version;
16230f1d41a8SCharles Keepax 
16240f1d41a8SCharles Keepax err:
16250f1d41a8SCharles Keepax 	kfree(buf);
16260f1d41a8SCharles Keepax 
16270f1d41a8SCharles Keepax 	return ret;
16284f2d4eabSStuart Henderson }
16294f2d4eabSStuart Henderson 
wm_adsp_buffer_init(struct wm_adsp * dsp)1630a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp)
1631a792af69SCharles Keepax {
16320700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
1633a792af69SCharles Keepax 	int ret;
1634a792af69SCharles Keepax 
1635e1468202SSimon Trimmer 	list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) {
16360700bc2fSSimon Trimmer 		if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
1637a792af69SCharles Keepax 			continue;
1638a792af69SCharles Keepax 
16390700bc2fSSimon Trimmer 		if (!cs_ctl->enabled)
1640a792af69SCharles Keepax 			continue;
1641a792af69SCharles Keepax 
16420700bc2fSSimon Trimmer 		ret = wm_adsp_buffer_parse_coeff(cs_ctl);
1643a792af69SCharles Keepax 		if (ret < 0) {
1644a792af69SCharles Keepax 			adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
1645a792af69SCharles Keepax 			goto error;
16464f2d4eabSStuart Henderson 		} else if (ret == 0) {
16474f2d4eabSStuart Henderson 			/* Only one buffer supported for version 0 */
1648a792af69SCharles Keepax 			return 0;
1649a792af69SCharles Keepax 		}
16504f2d4eabSStuart Henderson 	}
1651a792af69SCharles Keepax 
16524f2d4eabSStuart Henderson 	if (list_empty(&dsp->buffer_list)) {
1653a792af69SCharles Keepax 		/* Fall back to legacy support */
1654a792af69SCharles Keepax 		ret = wm_adsp_buffer_parse_legacy(dsp);
165535c8ae25SCristian Ciocaltea 		if (ret == -ENODEV)
165635c8ae25SCristian Ciocaltea 			adsp_info(dsp, "Legacy support not available\n");
165735c8ae25SCristian Ciocaltea 		else if (ret)
16580f1d41a8SCharles Keepax 			adsp_warn(dsp, "Failed to parse legacy: %d\n", ret);
1659a792af69SCharles Keepax 	}
16602cd19bdbSCharles Keepax 
16612cd19bdbSCharles Keepax 	return 0;
16622cd19bdbSCharles Keepax 
1663a792af69SCharles Keepax error:
1664a792af69SCharles Keepax 	wm_adsp_buffer_free(dsp);
16652cd19bdbSCharles Keepax 	return ret;
16662cd19bdbSCharles Keepax }
16672cd19bdbSCharles Keepax 
wm_adsp_buffer_free(struct wm_adsp * dsp)16682cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp)
16692cd19bdbSCharles Keepax {
16704f2d4eabSStuart Henderson 	struct wm_adsp_compr_buf *buf, *tmp;
1671721be3beSCharles Keepax 
16724f2d4eabSStuart Henderson 	list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {
16734f2d4eabSStuart Henderson 		wm_adsp_compr_detach(buf->compr);
16742cd19bdbSCharles Keepax 
16754f2d4eabSStuart Henderson 		kfree(buf->name);
16764f2d4eabSStuart Henderson 		kfree(buf->regions);
16774f2d4eabSStuart Henderson 		list_del(&buf->list);
16784f2d4eabSStuart Henderson 		kfree(buf);
16792cd19bdbSCharles Keepax 	}
16802cd19bdbSCharles Keepax 
16812cd19bdbSCharles Keepax 	return 0;
16822cd19bdbSCharles Keepax }
16832cd19bdbSCharles Keepax 
wm_adsp_buffer_get_error(struct wm_adsp_compr_buf * buf)1684f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
1685f938f348SStuart Henderson {
1686f938f348SStuart Henderson 	int ret;
1687f938f348SStuart Henderson 
1688f938f348SStuart Henderson 	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
1689f938f348SStuart Henderson 	if (ret < 0) {
169048ead31cSCharles Keepax 		compr_err(buf, "Failed to check buffer error: %d\n", ret);
1691f938f348SStuart Henderson 		return ret;
1692f938f348SStuart Henderson 	}
1693f938f348SStuart Henderson 	if (buf->error != 0) {
169448ead31cSCharles Keepax 		compr_err(buf, "Buffer error occurred: %d\n", buf->error);
1695f938f348SStuart Henderson 		return -EIO;
1696f938f348SStuart Henderson 	}
1697f938f348SStuart Henderson 
1698f938f348SStuart Henderson 	return 0;
1699f938f348SStuart Henderson }
1700f938f348SStuart Henderson 
wm_adsp_compr_trigger(struct snd_soc_component * component,struct snd_compr_stream * stream,int cmd)17013a5ccf25SKuninori Morimoto int wm_adsp_compr_trigger(struct snd_soc_component *component,
17023a5ccf25SKuninori Morimoto 			  struct snd_compr_stream *stream, int cmd)
170395fe9597SCharles Keepax {
170495fe9597SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
170595fe9597SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
170695fe9597SCharles Keepax 	int ret = 0;
170795fe9597SCharles Keepax 
17080d3fba3eSCharles Keepax 	compr_dbg(compr, "Trigger: %d\n", cmd);
170995fe9597SCharles Keepax 
1710e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
171195fe9597SCharles Keepax 
171295fe9597SCharles Keepax 	switch (cmd) {
171395fe9597SCharles Keepax 	case SNDRV_PCM_TRIGGER_START:
171461fc060cSCharles Keepax 		if (!wm_adsp_compr_attached(compr)) {
171595fe9597SCharles Keepax 			ret = wm_adsp_compr_attach(compr);
171695fe9597SCharles Keepax 			if (ret < 0) {
17170d3fba3eSCharles Keepax 				compr_err(compr, "Failed to link buffer and stream: %d\n",
171895fe9597SCharles Keepax 					  ret);
171995fe9597SCharles Keepax 				break;
172095fe9597SCharles Keepax 			}
172161fc060cSCharles Keepax 		}
172261fc060cSCharles Keepax 
1723f938f348SStuart Henderson 		ret = wm_adsp_buffer_get_error(compr->buf);
1724f938f348SStuart Henderson 		if (ret < 0)
1725f938f348SStuart Henderson 			break;
1726f938f348SStuart Henderson 
1727565ace46SCharles Keepax 		/* Trigger the IRQ at one fragment of data */
1728565ace46SCharles Keepax 		ret = wm_adsp_buffer_write(compr->buf,
1729565ace46SCharles Keepax 					   HOST_BUFFER_FIELD(high_water_mark),
1730565ace46SCharles Keepax 					   wm_adsp_compr_frag_words(compr));
1731565ace46SCharles Keepax 		if (ret < 0) {
17320d3fba3eSCharles Keepax 			compr_err(compr, "Failed to set high water mark: %d\n",
1733565ace46SCharles Keepax 				  ret);
1734565ace46SCharles Keepax 			break;
1735565ace46SCharles Keepax 		}
173695fe9597SCharles Keepax 		break;
173795fe9597SCharles Keepax 	case SNDRV_PCM_TRIGGER_STOP:
173843d147beSCharles Keepax 		if (wm_adsp_compr_attached(compr))
1739639e5eb3SCharles Keepax 			wm_adsp_buffer_clear(compr->buf);
174095fe9597SCharles Keepax 		break;
174195fe9597SCharles Keepax 	default:
174295fe9597SCharles Keepax 		ret = -EINVAL;
174395fe9597SCharles Keepax 		break;
174495fe9597SCharles Keepax 	}
174595fe9597SCharles Keepax 
1746e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
174795fe9597SCharles Keepax 
174895fe9597SCharles Keepax 	return ret;
174995fe9597SCharles Keepax }
175095fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
175195fe9597SCharles Keepax 
wm_adsp_buffer_size(struct wm_adsp_compr_buf * buf)1752565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
1753565ace46SCharles Keepax {
1754565ace46SCharles Keepax 	int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
1755565ace46SCharles Keepax 
1756565ace46SCharles Keepax 	return buf->regions[last_region].cumulative_size;
1757565ace46SCharles Keepax }
1758565ace46SCharles Keepax 
wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf * buf)1759565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
1760565ace46SCharles Keepax {
1761565ace46SCharles Keepax 	u32 next_read_index, next_write_index;
1762565ace46SCharles Keepax 	int write_index, read_index, avail;
1763565ace46SCharles Keepax 	int ret;
1764565ace46SCharles Keepax 
1765565ace46SCharles Keepax 	/* Only sync read index if we haven't already read a valid index */
1766565ace46SCharles Keepax 	if (buf->read_index < 0) {
1767565ace46SCharles Keepax 		ret = wm_adsp_buffer_read(buf,
1768565ace46SCharles Keepax 				HOST_BUFFER_FIELD(next_read_index),
1769565ace46SCharles Keepax 				&next_read_index);
1770565ace46SCharles Keepax 		if (ret < 0)
1771565ace46SCharles Keepax 			return ret;
1772565ace46SCharles Keepax 
1773565ace46SCharles Keepax 		read_index = sign_extend32(next_read_index, 23);
1774565ace46SCharles Keepax 
1775565ace46SCharles Keepax 		if (read_index < 0) {
17760d3fba3eSCharles Keepax 			compr_dbg(buf, "Avail check on unstarted stream\n");
1777565ace46SCharles Keepax 			return 0;
1778565ace46SCharles Keepax 		}
1779565ace46SCharles Keepax 
1780565ace46SCharles Keepax 		buf->read_index = read_index;
1781565ace46SCharles Keepax 	}
1782565ace46SCharles Keepax 
1783565ace46SCharles Keepax 	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
1784565ace46SCharles Keepax 			&next_write_index);
1785565ace46SCharles Keepax 	if (ret < 0)
1786565ace46SCharles Keepax 		return ret;
1787565ace46SCharles Keepax 
1788565ace46SCharles Keepax 	write_index = sign_extend32(next_write_index, 23);
1789565ace46SCharles Keepax 
1790565ace46SCharles Keepax 	avail = write_index - buf->read_index;
1791565ace46SCharles Keepax 	if (avail < 0)
1792565ace46SCharles Keepax 		avail += wm_adsp_buffer_size(buf);
1793565ace46SCharles Keepax 
17940d3fba3eSCharles Keepax 	compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
17955beb8eeaSSimon Trimmer 		  buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE);
1796565ace46SCharles Keepax 
1797565ace46SCharles Keepax 	buf->avail = avail;
1798565ace46SCharles Keepax 
1799565ace46SCharles Keepax 	return 0;
1800565ace46SCharles Keepax }
1801565ace46SCharles Keepax 
wm_adsp_compr_handle_irq(struct wm_adsp * dsp)1802565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
1803565ace46SCharles Keepax {
1804612047f0SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1805612047f0SCharles Keepax 	struct wm_adsp_compr *compr;
1806565ace46SCharles Keepax 	int ret = 0;
1807565ace46SCharles Keepax 
1808e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1809565ace46SCharles Keepax 
18104f2d4eabSStuart Henderson 	if (list_empty(&dsp->buffer_list)) {
1811565ace46SCharles Keepax 		ret = -ENODEV;
1812565ace46SCharles Keepax 		goto out;
1813565ace46SCharles Keepax 	}
18140d3fba3eSCharles Keepax 
1815565ace46SCharles Keepax 	adsp_dbg(dsp, "Handling buffer IRQ\n");
1816565ace46SCharles Keepax 
18174f2d4eabSStuart Henderson 	list_for_each_entry(buf, &dsp->buffer_list, list) {
18184f2d4eabSStuart Henderson 		compr = buf->compr;
18194f2d4eabSStuart Henderson 
18209771b18aSCharles Keepax 		ret = wm_adsp_buffer_get_error(buf);
18219771b18aSCharles Keepax 		if (ret < 0)
18225847609eSCharles Keepax 			goto out_notify; /* Wake poll to report error */
1823565ace46SCharles Keepax 
1824565ace46SCharles Keepax 		ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
1825565ace46SCharles Keepax 					  &buf->irq_count);
1826565ace46SCharles Keepax 		if (ret < 0) {
18270d3fba3eSCharles Keepax 			compr_err(buf, "Failed to get irq_count: %d\n", ret);
1828565ace46SCharles Keepax 			goto out;
1829565ace46SCharles Keepax 		}
1830565ace46SCharles Keepax 
1831565ace46SCharles Keepax 		ret = wm_adsp_buffer_update_avail(buf);
1832565ace46SCharles Keepax 		if (ret < 0) {
18330d3fba3eSCharles Keepax 			compr_err(buf, "Error reading avail: %d\n", ret);
1834565ace46SCharles Keepax 			goto out;
1835565ace46SCharles Keepax 		}
1836565ace46SCharles Keepax 
183720b7f7c5SCharles Keepax 		if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
183820b7f7c5SCharles Keepax 			ret = WM_ADSP_COMPR_VOICE_TRIGGER;
183920b7f7c5SCharles Keepax 
18405847609eSCharles Keepax out_notify:
1841c7dae7c4SCharles Keepax 		if (compr && compr->stream)
184283a40ce9SCharles Keepax 			snd_compr_fragment_elapsed(compr->stream);
18434f2d4eabSStuart Henderson 	}
184483a40ce9SCharles Keepax 
1845565ace46SCharles Keepax out:
1846e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1847565ace46SCharles Keepax 
1848565ace46SCharles Keepax 	return ret;
1849565ace46SCharles Keepax }
1850565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
1851565ace46SCharles Keepax 
wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf * buf)1852565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
1853565ace46SCharles Keepax {
1854565ace46SCharles Keepax 	if (buf->irq_count & 0x01)
1855565ace46SCharles Keepax 		return 0;
1856565ace46SCharles Keepax 
18570d3fba3eSCharles Keepax 	compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count);
1858565ace46SCharles Keepax 
1859565ace46SCharles Keepax 	buf->irq_count |= 0x01;
1860565ace46SCharles Keepax 
1861565ace46SCharles Keepax 	return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
1862565ace46SCharles Keepax 				    buf->irq_count);
1863565ace46SCharles Keepax }
1864565ace46SCharles Keepax 
wm_adsp_compr_pointer(struct snd_soc_component * component,struct snd_compr_stream * stream,struct snd_compr_tstamp * tstamp)18653a5ccf25SKuninori Morimoto int wm_adsp_compr_pointer(struct snd_soc_component *component,
18663a5ccf25SKuninori Morimoto 			  struct snd_compr_stream *stream,
1867565ace46SCharles Keepax 			  struct snd_compr_tstamp *tstamp)
1868565ace46SCharles Keepax {
1869565ace46SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1870565ace46SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1871612047f0SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1872565ace46SCharles Keepax 	int ret = 0;
1873565ace46SCharles Keepax 
18740d3fba3eSCharles Keepax 	compr_dbg(compr, "Pointer request\n");
1875565ace46SCharles Keepax 
1876e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1877565ace46SCharles Keepax 
1878612047f0SCharles Keepax 	buf = compr->buf;
1879612047f0SCharles Keepax 
1880aa612f2bSCharles Keepax 	if (dsp->fatal_error || !buf || buf->error) {
18818d280664SCharles Keepax 		snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
1882565ace46SCharles Keepax 		ret = -EIO;
1883565ace46SCharles Keepax 		goto out;
1884565ace46SCharles Keepax 	}
1885565ace46SCharles Keepax 
1886565ace46SCharles Keepax 	if (buf->avail < wm_adsp_compr_frag_words(compr)) {
1887565ace46SCharles Keepax 		ret = wm_adsp_buffer_update_avail(buf);
1888565ace46SCharles Keepax 		if (ret < 0) {
18890d3fba3eSCharles Keepax 			compr_err(compr, "Error reading avail: %d\n", ret);
1890565ace46SCharles Keepax 			goto out;
1891565ace46SCharles Keepax 		}
1892565ace46SCharles Keepax 
1893565ace46SCharles Keepax 		/*
1894565ace46SCharles Keepax 		 * If we really have less than 1 fragment available tell the
1895565ace46SCharles Keepax 		 * DSP to inform us once a whole fragment is available.
1896565ace46SCharles Keepax 		 */
1897565ace46SCharles Keepax 		if (buf->avail < wm_adsp_compr_frag_words(compr)) {
18985847609eSCharles Keepax 			ret = wm_adsp_buffer_get_error(buf);
18998d280664SCharles Keepax 			if (ret < 0) {
1900789b930aSCharles Keepax 				if (buf->error)
19018d280664SCharles Keepax 					snd_compr_stop_error(stream,
19028d280664SCharles Keepax 							SNDRV_PCM_STATE_XRUN);
19035847609eSCharles Keepax 				goto out;
19048d280664SCharles Keepax 			}
19055847609eSCharles Keepax 
1906565ace46SCharles Keepax 			ret = wm_adsp_buffer_reenable_irq(buf);
1907565ace46SCharles Keepax 			if (ret < 0) {
19080d3fba3eSCharles Keepax 				compr_err(compr, "Failed to re-enable buffer IRQ: %d\n",
1909565ace46SCharles Keepax 					  ret);
1910565ace46SCharles Keepax 				goto out;
1911565ace46SCharles Keepax 			}
1912565ace46SCharles Keepax 		}
1913565ace46SCharles Keepax 	}
1914565ace46SCharles Keepax 
1915565ace46SCharles Keepax 	tstamp->copied_total = compr->copied_total;
19165beb8eeaSSimon Trimmer 	tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE;
1917da2b3358SCharles Keepax 	tstamp->sampling_rate = compr->sample_rate;
1918565ace46SCharles Keepax 
1919565ace46SCharles Keepax out:
1920e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1921565ace46SCharles Keepax 
1922565ace46SCharles Keepax 	return ret;
1923565ace46SCharles Keepax }
1924565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
1925565ace46SCharles Keepax 
wm_adsp_buffer_capture_block(struct wm_adsp_compr * compr,int target)192683a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
192783a40ce9SCharles Keepax {
192883a40ce9SCharles Keepax 	struct wm_adsp_compr_buf *buf = compr->buf;
192983a40ce9SCharles Keepax 	unsigned int adsp_addr;
193083a40ce9SCharles Keepax 	int mem_type, nwords, max_read;
1931cc7d6ce9SCharles Keepax 	int i, ret;
193283a40ce9SCharles Keepax 
193383a40ce9SCharles Keepax 	/* Calculate read parameters */
193483a40ce9SCharles Keepax 	for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
193583a40ce9SCharles Keepax 		if (buf->read_index < buf->regions[i].cumulative_size)
193683a40ce9SCharles Keepax 			break;
193783a40ce9SCharles Keepax 
193883a40ce9SCharles Keepax 	if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
193983a40ce9SCharles Keepax 		return -EINVAL;
194083a40ce9SCharles Keepax 
194183a40ce9SCharles Keepax 	mem_type = buf->regions[i].mem_type;
194283a40ce9SCharles Keepax 	adsp_addr = buf->regions[i].base_addr +
194383a40ce9SCharles Keepax 		    (buf->read_index - buf->regions[i].offset);
194483a40ce9SCharles Keepax 
194583a40ce9SCharles Keepax 	max_read = wm_adsp_compr_frag_words(compr);
194683a40ce9SCharles Keepax 	nwords = buf->regions[i].cumulative_size - buf->read_index;
194783a40ce9SCharles Keepax 
194883a40ce9SCharles Keepax 	if (nwords > target)
194983a40ce9SCharles Keepax 		nwords = target;
195083a40ce9SCharles Keepax 	if (nwords > buf->avail)
195183a40ce9SCharles Keepax 		nwords = buf->avail;
195283a40ce9SCharles Keepax 	if (nwords > max_read)
195383a40ce9SCharles Keepax 		nwords = max_read;
195483a40ce9SCharles Keepax 	if (!nwords)
195583a40ce9SCharles Keepax 		return 0;
195683a40ce9SCharles Keepax 
195783a40ce9SCharles Keepax 	/* Read data from DSP */
1958e1468202SSimon Trimmer 	ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr,
1959a0b653e8SRichard Fitzgerald 					 nwords, (__be32 *)compr->raw_buf);
196083a40ce9SCharles Keepax 	if (ret < 0)
196183a40ce9SCharles Keepax 		return ret;
196283a40ce9SCharles Keepax 
19635beb8eeaSSimon Trimmer 	cs_dsp_remove_padding(compr->raw_buf, nwords);
196483a40ce9SCharles Keepax 
196583a40ce9SCharles Keepax 	/* update read index to account for words read */
196683a40ce9SCharles Keepax 	buf->read_index += nwords;
196783a40ce9SCharles Keepax 	if (buf->read_index == wm_adsp_buffer_size(buf))
196883a40ce9SCharles Keepax 		buf->read_index = 0;
196983a40ce9SCharles Keepax 
197083a40ce9SCharles Keepax 	ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
197183a40ce9SCharles Keepax 				   buf->read_index);
197283a40ce9SCharles Keepax 	if (ret < 0)
197383a40ce9SCharles Keepax 		return ret;
197483a40ce9SCharles Keepax 
197583a40ce9SCharles Keepax 	/* update avail to account for words read */
197683a40ce9SCharles Keepax 	buf->avail -= nwords;
197783a40ce9SCharles Keepax 
197883a40ce9SCharles Keepax 	return nwords;
197983a40ce9SCharles Keepax }
198083a40ce9SCharles Keepax 
wm_adsp_compr_read(struct wm_adsp_compr * compr,char __user * buf,size_t count)198183a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
198283a40ce9SCharles Keepax 			      char __user *buf, size_t count)
198383a40ce9SCharles Keepax {
1984aa612f2bSCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
198583a40ce9SCharles Keepax 	int ntotal = 0;
198683a40ce9SCharles Keepax 	int nwords, nbytes;
198783a40ce9SCharles Keepax 
19880d3fba3eSCharles Keepax 	compr_dbg(compr, "Requested read of %zu bytes\n", count);
198983a40ce9SCharles Keepax 
1990aa612f2bSCharles Keepax 	if (dsp->fatal_error || !compr->buf || compr->buf->error) {
19918d280664SCharles Keepax 		snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
199283a40ce9SCharles Keepax 		return -EIO;
19938d280664SCharles Keepax 	}
199483a40ce9SCharles Keepax 
19955beb8eeaSSimon Trimmer 	count /= CS_DSP_DATA_WORD_SIZE;
199683a40ce9SCharles Keepax 
199783a40ce9SCharles Keepax 	do {
199883a40ce9SCharles Keepax 		nwords = wm_adsp_buffer_capture_block(compr, count);
199983a40ce9SCharles Keepax 		if (nwords < 0) {
20000d3fba3eSCharles Keepax 			compr_err(compr, "Failed to capture block: %d\n",
20010d3fba3eSCharles Keepax 				  nwords);
200283a40ce9SCharles Keepax 			return nwords;
200383a40ce9SCharles Keepax 		}
200483a40ce9SCharles Keepax 
20055beb8eeaSSimon Trimmer 		nbytes = nwords * CS_DSP_DATA_WORD_SIZE;
200683a40ce9SCharles Keepax 
20070d3fba3eSCharles Keepax 		compr_dbg(compr, "Read %d bytes\n", nbytes);
200883a40ce9SCharles Keepax 
200983a40ce9SCharles Keepax 		if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
20100d3fba3eSCharles Keepax 			compr_err(compr, "Failed to copy data to user: %d, %d\n",
201183a40ce9SCharles Keepax 				  ntotal, nbytes);
201283a40ce9SCharles Keepax 			return -EFAULT;
201383a40ce9SCharles Keepax 		}
201483a40ce9SCharles Keepax 
201583a40ce9SCharles Keepax 		count -= nwords;
201683a40ce9SCharles Keepax 		ntotal += nbytes;
201783a40ce9SCharles Keepax 	} while (nwords > 0 && count > 0);
201883a40ce9SCharles Keepax 
201983a40ce9SCharles Keepax 	compr->copied_total += ntotal;
202083a40ce9SCharles Keepax 
202183a40ce9SCharles Keepax 	return ntotal;
202283a40ce9SCharles Keepax }
202383a40ce9SCharles Keepax 
wm_adsp_compr_copy(struct snd_soc_component * component,struct snd_compr_stream * stream,char __user * buf,size_t count)20243a5ccf25SKuninori Morimoto int wm_adsp_compr_copy(struct snd_soc_component *component,
20253a5ccf25SKuninori Morimoto 		       struct snd_compr_stream *stream, char __user *buf,
202683a40ce9SCharles Keepax 		       size_t count)
202783a40ce9SCharles Keepax {
202883a40ce9SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
202983a40ce9SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
203083a40ce9SCharles Keepax 	int ret;
203183a40ce9SCharles Keepax 
2032e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
203383a40ce9SCharles Keepax 
203483a40ce9SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
203583a40ce9SCharles Keepax 		ret = wm_adsp_compr_read(compr, buf, count);
203683a40ce9SCharles Keepax 	else
203783a40ce9SCharles Keepax 		ret = -ENOTSUPP;
203883a40ce9SCharles Keepax 
2039e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
204083a40ce9SCharles Keepax 
204183a40ce9SCharles Keepax 	return ret;
204283a40ce9SCharles Keepax }
204383a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
204483a40ce9SCharles Keepax 
wm_adsp_fatal_error(struct cs_dsp * cs_dsp)2045e1468202SSimon Trimmer static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp)
2046a2bcbc1bSCharles Keepax {
2047e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
2048a2bcbc1bSCharles Keepax 	struct wm_adsp_compr *compr;
2049a2bcbc1bSCharles Keepax 
2050a2bcbc1bSCharles Keepax 	dsp->fatal_error = true;
2051a2bcbc1bSCharles Keepax 
2052a2bcbc1bSCharles Keepax 	list_for_each_entry(compr, &dsp->compr_list, list) {
2053aa612f2bSCharles Keepax 		if (compr->stream)
2054a2bcbc1bSCharles Keepax 			snd_compr_fragment_elapsed(compr->stream);
2055a2bcbc1bSCharles Keepax 	}
2056a2bcbc1bSCharles Keepax }
2057a2bcbc1bSCharles Keepax 
wm_adsp2_bus_error(int irq,void * data)205825ca837bSSimon Trimmer irqreturn_t wm_adsp2_bus_error(int irq, void *data)
205925ca837bSSimon Trimmer {
206025ca837bSSimon Trimmer 	struct wm_adsp *dsp = (struct wm_adsp *)data;
206125ca837bSSimon Trimmer 
2062e1468202SSimon Trimmer 	cs_dsp_adsp2_bus_error(&dsp->cs_dsp);
2063a2225a6dSCharles Keepax 
206451a2c944SMayuresh Kulkarni 	return IRQ_HANDLED;
206551a2c944SMayuresh Kulkarni }
206651a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
206751a2c944SMayuresh Kulkarni 
wm_halo_bus_error(int irq,void * data)206825ca837bSSimon Trimmer irqreturn_t wm_halo_bus_error(int irq, void *data)
206925ca837bSSimon Trimmer {
207025ca837bSSimon Trimmer 	struct wm_adsp *dsp = (struct wm_adsp *)data;
207125ca837bSSimon Trimmer 
2072e1468202SSimon Trimmer 	cs_dsp_halo_bus_error(&dsp->cs_dsp);
20732ae58138SRichard Fitzgerald 
20742ae58138SRichard Fitzgerald 	return IRQ_HANDLED;
20752ae58138SRichard Fitzgerald }
20762ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error);
20772ae58138SRichard Fitzgerald 
wm_halo_wdt_expire(int irq,void * data)207825ca837bSSimon Trimmer irqreturn_t wm_halo_wdt_expire(int irq, void *data)
207925ca837bSSimon Trimmer {
208025ca837bSSimon Trimmer 	struct wm_adsp *dsp = data;
208125ca837bSSimon Trimmer 
2082e1468202SSimon Trimmer 	cs_dsp_halo_wdt_expire(&dsp->cs_dsp);
20838bc144f9SStuart Henderson 
20848bc144f9SStuart Henderson 	return IRQ_HANDLED;
20858bc144f9SStuart Henderson }
20868bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);
20878bc144f9SStuart Henderson 
20882dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp1_client_ops = {
20892dd04464SSimon Trimmer 	.control_add = wm_adsp_control_add,
20902dd04464SSimon Trimmer 	.control_remove = wm_adsp_control_remove,
20912dd04464SSimon Trimmer };
20922dd04464SSimon Trimmer 
20932dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp2_client_ops = {
20942dd04464SSimon Trimmer 	.control_add = wm_adsp_control_add,
20952dd04464SSimon Trimmer 	.control_remove = wm_adsp_control_remove,
2096fe071308SRichard Fitzgerald 	.pre_run = wm_adsp_pre_run,
20972dd04464SSimon Trimmer 	.post_run = wm_adsp_event_post_run,
20982dd04464SSimon Trimmer 	.post_stop = wm_adsp_event_post_stop,
20992dd04464SSimon Trimmer 	.watchdog_expired = wm_adsp_fatal_error,
21002dd04464SSimon Trimmer };
21012dd04464SSimon Trimmer 
21020a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2");
2103e57d904aSRichard Fitzgerald MODULE_IMPORT_NS(FW_CS_DSP);
2104