xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision 353bb6a5)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22159ad93SMark Brown /*
32159ad93SMark Brown  * wm_adsp.c  --  Wolfson ADSP support
42159ad93SMark Brown  *
52159ad93SMark Brown  * Copyright 2012 Wolfson Microelectronics plc
62159ad93SMark Brown  *
72159ad93SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
82159ad93SMark Brown  */
92159ad93SMark Brown 
10605391d0SRichard Fitzgerald #include <linux/ctype.h>
112159ad93SMark Brown #include <linux/module.h>
122159ad93SMark Brown #include <linux/moduleparam.h>
132159ad93SMark Brown #include <linux/init.h>
142159ad93SMark Brown #include <linux/delay.h>
152159ad93SMark Brown #include <linux/firmware.h>
16cf17c83cSMark Brown #include <linux/list.h>
172159ad93SMark Brown #include <linux/pm.h>
182159ad93SMark Brown #include <linux/pm_runtime.h>
192159ad93SMark Brown #include <linux/regmap.h>
20973838a0SMark Brown #include <linux/regulator/consumer.h>
212159ad93SMark Brown #include <linux/slab.h>
226ab2b7b4SDimitris Papastamos #include <linux/workqueue.h>
23f9f55e31SRichard Fitzgerald #include <linux/debugfs.h>
242159ad93SMark Brown #include <sound/core.h>
252159ad93SMark Brown #include <sound/pcm.h>
262159ad93SMark Brown #include <sound/pcm_params.h>
272159ad93SMark Brown #include <sound/soc.h>
282159ad93SMark Brown #include <sound/jack.h>
292159ad93SMark Brown #include <sound/initval.h>
302159ad93SMark Brown #include <sound/tlv.h>
312159ad93SMark Brown 
322159ad93SMark Brown #include "wm_adsp.h"
332159ad93SMark Brown 
342159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \
35e1468202SSimon Trimmer 	dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
362159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \
37e1468202SSimon Trimmer 	dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
382159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \
39e1468202SSimon Trimmer 	dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
402159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \
41e1468202SSimon Trimmer 	dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
422159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \
43e1468202SSimon Trimmer 	dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)
442159ad93SMark Brown 
450d3fba3eSCharles Keepax #define compr_err(_obj, fmt, ...) \
460d3fba3eSCharles Keepax 	adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
470d3fba3eSCharles Keepax 		 ##__VA_ARGS__)
480d3fba3eSCharles Keepax #define compr_dbg(_obj, fmt, ...) \
490d3fba3eSCharles Keepax 	adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
500d3fba3eSCharles Keepax 		 ##__VA_ARGS__)
510d3fba3eSCharles Keepax 
529ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE               512
539ee78757SCharles Keepax 
542dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp1_client_ops;
552dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp2_client_ops;
562dd04464SSimon Trimmer 
57dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS  0
5804d1300fSCharles Keepax #define WM_ADSP_FW_HIFI     1
5904d1300fSCharles Keepax #define WM_ADSP_FW_TX       2
6004d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK   3
6104d1300fSCharles Keepax #define WM_ADSP_FW_RX       4
6204d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC   5
6304d1300fSCharles Keepax #define WM_ADSP_FW_CTRL     6
6404d1300fSCharles Keepax #define WM_ADSP_FW_ASR      7
6504d1300fSCharles Keepax #define WM_ADSP_FW_TRACE    8
6604d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9
67d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_CALI 10
68d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_DIAG 11
69d6fea46eSVlad Karpovich #define WM_ADSP_FW_MISC     12
7004d1300fSCharles Keepax 
71d6fea46eSVlad Karpovich #define WM_ADSP_NUM_FW      13
72dd84f925SMark Brown 
731023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
74dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] =  "MBC/VSS",
7504d1300fSCharles Keepax 	[WM_ADSP_FW_HIFI] =     "MasterHiFi",
76dd84f925SMark Brown 	[WM_ADSP_FW_TX] =       "Tx",
77dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =   "Tx Speaker",
7804d1300fSCharles Keepax 	[WM_ADSP_FW_RX] =       "Rx",
79dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =   "Rx ANC",
8004d1300fSCharles Keepax 	[WM_ADSP_FW_CTRL] =     "Voice Ctrl",
8104d1300fSCharles Keepax 	[WM_ADSP_FW_ASR] =      "ASR Assist",
8204d1300fSCharles Keepax 	[WM_ADSP_FW_TRACE] =    "Dbg Trace",
8304d1300fSCharles Keepax 	[WM_ADSP_FW_SPK_PROT] = "Protection",
84d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_CALI] = "Calibration",
85d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_DIAG] = "Diagnostic",
8604d1300fSCharles Keepax 	[WM_ADSP_FW_MISC] =     "Misc",
871023dbd9SMark Brown };
881023dbd9SMark Brown 
892cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr {
902cd19bdbSCharles Keepax 	__be32 sys_enable;
912cd19bdbSCharles Keepax 	__be32 fw_id;
922cd19bdbSCharles Keepax 	__be32 fw_rev;
932cd19bdbSCharles Keepax 	__be32 boot_status;
942cd19bdbSCharles Keepax 	__be32 watchdog;
952cd19bdbSCharles Keepax 	__be32 dma_buffer_size;
962cd19bdbSCharles Keepax 	__be32 rdma[6];
972cd19bdbSCharles Keepax 	__be32 wdma[8];
982cd19bdbSCharles Keepax 	__be32 build_job_name[3];
992cd19bdbSCharles Keepax 	__be32 build_job_number;
100*353bb6a5SSimon 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;
106*353bb6a5SSimon 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;
117*353bb6a5SSimon 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 */
123*353bb6a5SSimon 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) */
144*353bb6a5SSimon 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
1835beb8eeaSSimon Trimmer #define WM_ADSP_MIN_FRAGMENT_SIZE      (64 * 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 	},
29904d1300fSCharles Keepax 	[WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
300d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" },
301d6fea46eSVlad Karpovich 	[WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" },
30204d1300fSCharles Keepax 	[WM_ADSP_FW_MISC] =     { .file = "misc" },
3031023dbd9SMark Brown };
3041023dbd9SMark Brown 
3056ab2b7b4SDimitris Papastamos struct wm_coeff_ctl {
3066ab2b7b4SDimitris Papastamos 	const char *name;
3070700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
3089ee78757SCharles Keepax 	struct soc_bytes_ext bytes_ext;
309df6c505cSSimon Trimmer 	struct work_struct work;
3106ab2b7b4SDimitris Papastamos };
3116ab2b7b4SDimitris Papastamos 
3120a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
3131023dbd9SMark Brown 		   struct snd_ctl_elem_value *ucontrol)
3141023dbd9SMark Brown {
3150fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
3161023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
3170fe1daa6SKuninori Morimoto 	struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
3181023dbd9SMark Brown 
31915c66570STakashi Iwai 	ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
3201023dbd9SMark Brown 
3211023dbd9SMark Brown 	return 0;
3221023dbd9SMark Brown }
3230a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get);
3241023dbd9SMark Brown 
3250a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
3261023dbd9SMark Brown 		   struct snd_ctl_elem_value *ucontrol)
3271023dbd9SMark Brown {
3280fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
3291023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
3300fe1daa6SKuninori Morimoto 	struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
331d27c5e15SCharles Keepax 	int ret = 0;
3321023dbd9SMark Brown 
33315c66570STakashi Iwai 	if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
3341023dbd9SMark Brown 		return 0;
3351023dbd9SMark Brown 
33615c66570STakashi Iwai 	if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
3371023dbd9SMark Brown 		return -EINVAL;
3381023dbd9SMark Brown 
339e1468202SSimon Trimmer 	mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock);
3401023dbd9SMark Brown 
341e1468202SSimon Trimmer 	if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list))
342d27c5e15SCharles Keepax 		ret = -EBUSY;
343d27c5e15SCharles Keepax 	else
34415c66570STakashi Iwai 		dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
3451023dbd9SMark Brown 
346e1468202SSimon Trimmer 	mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock);
347d27c5e15SCharles Keepax 
348d27c5e15SCharles Keepax 	return ret;
3491023dbd9SMark Brown }
3500a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put);
3511023dbd9SMark Brown 
3520a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = {
3531023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3541023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3551023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3561023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
357e1ea1879SRichard Fitzgerald 	SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
358e1ea1879SRichard Fitzgerald 	SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
359e1ea1879SRichard Fitzgerald 	SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
3601023dbd9SMark Brown };
3610a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
3622159ad93SMark Brown 
3639ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
3649ee78757SCharles Keepax {
3659ee78757SCharles Keepax 	return container_of(ext, struct wm_coeff_ctl, bytes_ext);
3669ee78757SCharles Keepax }
3679ee78757SCharles Keepax 
3687585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl,
3696ab2b7b4SDimitris Papastamos 			 struct snd_ctl_elem_info *uinfo)
3706ab2b7b4SDimitris Papastamos {
3719ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
3729ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
3739ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
3740700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
3756ab2b7b4SDimitris Papastamos 
3760700bc2fSSimon Trimmer 	switch (cs_ctl->type) {
377a23ebba8SRichard Fitzgerald 	case WMFW_CTL_TYPE_ACKED:
378a23ebba8SRichard Fitzgerald 		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3795beb8eeaSSimon Trimmer 		uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE;
3805beb8eeaSSimon Trimmer 		uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE;
381a23ebba8SRichard Fitzgerald 		uinfo->value.integer.step = 1;
382a23ebba8SRichard Fitzgerald 		uinfo->count = 1;
383a23ebba8SRichard Fitzgerald 		break;
384a23ebba8SRichard Fitzgerald 	default:
3856ab2b7b4SDimitris Papastamos 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
3860700bc2fSSimon Trimmer 		uinfo->count = cs_ctl->len;
387a23ebba8SRichard Fitzgerald 		break;
388a23ebba8SRichard Fitzgerald 	}
389a23ebba8SRichard Fitzgerald 
3906ab2b7b4SDimitris Papastamos 	return 0;
3916ab2b7b4SDimitris Papastamos }
3926ab2b7b4SDimitris Papastamos 
3937585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl,
3946ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
3956ab2b7b4SDimitris Papastamos {
3969ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
3979ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
3989ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
3990700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4006ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
401168d10e7SCharles Keepax 	int ret = 0;
402168d10e7SCharles Keepax 
4030700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
404f444da38SCharles Keepax 	ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
4050700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
406168d10e7SCharles Keepax 
407168d10e7SCharles Keepax 	return ret;
4086ab2b7b4SDimitris Papastamos }
4096ab2b7b4SDimitris Papastamos 
4109ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
4119ee78757SCharles Keepax 			    const unsigned int __user *bytes, unsigned int size)
4129ee78757SCharles Keepax {
4139ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4149ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4159ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4160700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4179ee78757SCharles Keepax 	int ret = 0;
4189ee78757SCharles Keepax 
4190700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
4209ee78757SCharles Keepax 
4210700bc2fSSimon Trimmer 	if (copy_from_user(cs_ctl->cache, bytes, size))
4229ee78757SCharles Keepax 		ret = -EFAULT;
42373ecf1a6SCharles Keepax 	else
424f444da38SCharles Keepax 		ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size);
4259ee78757SCharles Keepax 
4260700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
4279ee78757SCharles Keepax 
4289ee78757SCharles Keepax 	return ret;
4299ee78757SCharles Keepax }
4309ee78757SCharles Keepax 
431a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
432a23ebba8SRichard Fitzgerald 			      struct snd_ctl_elem_value *ucontrol)
433a23ebba8SRichard Fitzgerald {
434a23ebba8SRichard Fitzgerald 	struct soc_bytes_ext *bytes_ext =
435a23ebba8SRichard Fitzgerald 		(struct soc_bytes_ext *)kctl->private_value;
436a23ebba8SRichard Fitzgerald 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4370700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
438a23ebba8SRichard Fitzgerald 	unsigned int val = ucontrol->value.integer.value[0];
439a23ebba8SRichard Fitzgerald 	int ret;
440a23ebba8SRichard Fitzgerald 
441a23ebba8SRichard Fitzgerald 	if (val == 0)
442a23ebba8SRichard Fitzgerald 		return 0;	/* 0 means no event */
443a23ebba8SRichard Fitzgerald 
4440700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
445a23ebba8SRichard Fitzgerald 
446edb1d6d7SSimon Trimmer 	if (cs_ctl->enabled)
4470700bc2fSSimon Trimmer 		ret = cs_dsp_coeff_write_acked_control(cs_ctl, val);
448a23ebba8SRichard Fitzgerald 	else
449a23ebba8SRichard Fitzgerald 		ret = -EPERM;
450a23ebba8SRichard Fitzgerald 
4510700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
452a23ebba8SRichard Fitzgerald 
453a23ebba8SRichard Fitzgerald 	return ret;
454a23ebba8SRichard Fitzgerald }
455a23ebba8SRichard Fitzgerald 
4567585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl,
4576ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4586ab2b7b4SDimitris Papastamos {
4599ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4609ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4619ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4620700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4636ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
46473ecf1a6SCharles Keepax 	int ret;
465168d10e7SCharles Keepax 
4660700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
467f444da38SCharles Keepax 	ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
4680700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
46926c22a19SCharles Keepax 
470168d10e7SCharles Keepax 	return ret;
4716ab2b7b4SDimitris Papastamos }
4726ab2b7b4SDimitris Papastamos 
4739ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
4749ee78757SCharles Keepax 			    unsigned int __user *bytes, unsigned int size)
4759ee78757SCharles Keepax {
4769ee78757SCharles Keepax 	struct soc_bytes_ext *bytes_ext =
4779ee78757SCharles Keepax 		(struct soc_bytes_ext *)kctl->private_value;
4789ee78757SCharles Keepax 	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
4790700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
4809ee78757SCharles Keepax 	int ret = 0;
4819ee78757SCharles Keepax 
4820700bc2fSSimon Trimmer 	mutex_lock(&cs_ctl->dsp->pwr_lock);
4839ee78757SCharles Keepax 
484f444da38SCharles Keepax 	ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size);
4859ee78757SCharles Keepax 
4860700bc2fSSimon Trimmer 	if (!ret && copy_to_user(bytes, cs_ctl->cache, size))
4879ee78757SCharles Keepax 		ret = -EFAULT;
4889ee78757SCharles Keepax 
4890700bc2fSSimon Trimmer 	mutex_unlock(&cs_ctl->dsp->pwr_lock);
4909ee78757SCharles Keepax 
4919ee78757SCharles Keepax 	return ret;
4929ee78757SCharles Keepax }
4939ee78757SCharles Keepax 
494a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
495a23ebba8SRichard Fitzgerald 			      struct snd_ctl_elem_value *ucontrol)
496a23ebba8SRichard Fitzgerald {
497a23ebba8SRichard Fitzgerald 	/*
498a23ebba8SRichard Fitzgerald 	 * Although it's not useful to read an acked control, we must satisfy
499a23ebba8SRichard Fitzgerald 	 * user-side assumptions that all controls are readable and that a
500a23ebba8SRichard Fitzgerald 	 * write of the same value should be filtered out (it's valid to send
501a23ebba8SRichard Fitzgerald 	 * the same event number again to the firmware). We therefore return 0,
502a23ebba8SRichard Fitzgerald 	 * meaning "no event" so valid event numbers will always be a change
503a23ebba8SRichard Fitzgerald 	 */
504a23ebba8SRichard Fitzgerald 	ucontrol->value.integer.value[0] = 0;
505a23ebba8SRichard Fitzgerald 
506a23ebba8SRichard Fitzgerald 	return 0;
507a23ebba8SRichard Fitzgerald }
508a23ebba8SRichard Fitzgerald 
5099ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
5109ee78757SCharles Keepax {
5119ee78757SCharles Keepax 	unsigned int out, rd, wr, vol;
5129ee78757SCharles Keepax 
5139ee78757SCharles Keepax 	if (len > ADSP_MAX_STD_CTRL_SIZE) {
5149ee78757SCharles Keepax 		rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
5159ee78757SCharles Keepax 		wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
5169ee78757SCharles Keepax 		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
5179ee78757SCharles Keepax 
5189ee78757SCharles Keepax 		out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
5199ee78757SCharles Keepax 	} else {
5209ee78757SCharles Keepax 		rd = SNDRV_CTL_ELEM_ACCESS_READ;
5219ee78757SCharles Keepax 		wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
5229ee78757SCharles Keepax 		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
5239ee78757SCharles Keepax 
5249ee78757SCharles Keepax 		out = 0;
5259ee78757SCharles Keepax 	}
5269ee78757SCharles Keepax 
5279ee78757SCharles Keepax 	if (in) {
5289ee78757SCharles Keepax 		out |= rd;
5299ee78757SCharles Keepax 		if (in & WMFW_CTL_FLAG_WRITEABLE)
5309ee78757SCharles Keepax 			out |= wr;
5319ee78757SCharles Keepax 		if (in & WMFW_CTL_FLAG_VOLATILE)
5329ee78757SCharles Keepax 			out |= vol;
5339ee78757SCharles Keepax 	} else {
5349ee78757SCharles Keepax 		out |= rd | wr | vol;
5359ee78757SCharles Keepax 	}
5369ee78757SCharles Keepax 
5379ee78757SCharles Keepax 	return out;
5389ee78757SCharles Keepax }
5399ee78757SCharles Keepax 
54056717d72SCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work)
5416ab2b7b4SDimitris Papastamos {
54256717d72SCharles Keepax 	struct wm_coeff_ctl *ctl = container_of(work,
54356717d72SCharles Keepax 						struct wm_coeff_ctl,
54456717d72SCharles Keepax 						work);
5450700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
54656717d72SCharles Keepax 	struct wm_adsp *dsp = container_of(cs_ctl->dsp,
54756717d72SCharles Keepax 					   struct wm_adsp,
54856717d72SCharles Keepax 					   cs_dsp);
5496ab2b7b4SDimitris Papastamos 	struct snd_kcontrol_new *kcontrol;
5506ab2b7b4SDimitris Papastamos 
5516ab2b7b4SDimitris Papastamos 	kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
5526ab2b7b4SDimitris Papastamos 	if (!kcontrol)
55356717d72SCharles Keepax 		return;
5546ab2b7b4SDimitris Papastamos 
5556ab2b7b4SDimitris Papastamos 	kcontrol->name = ctl->name;
5566ab2b7b4SDimitris Papastamos 	kcontrol->info = wm_coeff_info;
5579ee78757SCharles Keepax 	kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
5589ee78757SCharles Keepax 	kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
5599ee78757SCharles Keepax 	kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
5600700bc2fSSimon Trimmer 	kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);
561a23ebba8SRichard Fitzgerald 
5620700bc2fSSimon Trimmer 	switch (cs_ctl->type) {
563a23ebba8SRichard Fitzgerald 	case WMFW_CTL_TYPE_ACKED:
564a23ebba8SRichard Fitzgerald 		kcontrol->get = wm_coeff_get_acked;
565a23ebba8SRichard Fitzgerald 		kcontrol->put = wm_coeff_put_acked;
566a23ebba8SRichard Fitzgerald 		break;
567a23ebba8SRichard Fitzgerald 	default:
568d7789f5bSRichard Fitzgerald 		if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
5690700bc2fSSimon Trimmer 			ctl->bytes_ext.max = cs_ctl->len;
5709ee78757SCharles Keepax 			ctl->bytes_ext.get = wm_coeff_tlv_get;
5719ee78757SCharles Keepax 			ctl->bytes_ext.put = wm_coeff_tlv_put;
572d7789f5bSRichard Fitzgerald 		} else {
573d7789f5bSRichard Fitzgerald 			kcontrol->get = wm_coeff_get;
574d7789f5bSRichard Fitzgerald 			kcontrol->put = wm_coeff_put;
575d7789f5bSRichard Fitzgerald 		}
576a23ebba8SRichard Fitzgerald 		break;
577a23ebba8SRichard Fitzgerald 	}
57826c22a19SCharles Keepax 
57956717d72SCharles Keepax 	snd_soc_add_component_controls(dsp->component, kcontrol, 1);
5806ab2b7b4SDimitris Papastamos 
5816ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
582b21acc1cSCharles Keepax }
583b21acc1cSCharles Keepax 
5840700bc2fSSimon Trimmer static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
5850700bc2fSSimon Trimmer {
586e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
587e1468202SSimon Trimmer 	struct cs_dsp *cs_dsp = &dsp->cs_dsp;
5880700bc2fSSimon Trimmer 	struct wm_coeff_ctl *ctl;
5890700bc2fSSimon Trimmer 	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
5900700bc2fSSimon Trimmer 	const char *region_name;
5910700bc2fSSimon Trimmer 	int ret;
5920700bc2fSSimon Trimmer 
5930700bc2fSSimon Trimmer 	if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
5940700bc2fSSimon Trimmer 		return 0;
5950700bc2fSSimon Trimmer 
5960700bc2fSSimon Trimmer 	region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
5970700bc2fSSimon Trimmer 	if (!region_name) {
5980700bc2fSSimon Trimmer 		adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type);
5990700bc2fSSimon Trimmer 		return -EINVAL;
6000700bc2fSSimon Trimmer 	}
6010700bc2fSSimon Trimmer 
602e1468202SSimon Trimmer 	switch (cs_dsp->fw_ver) {
6030700bc2fSSimon Trimmer 	case 0:
6040700bc2fSSimon Trimmer 	case 1:
605a6e849d0SSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
606a6e849d0SSimon Trimmer 				"%s %s %x", cs_dsp->name, region_name,
607a6e849d0SSimon Trimmer 				cs_ctl->alg_region.alg);
6080700bc2fSSimon Trimmer 		break;
6090700bc2fSSimon Trimmer 	case 2:
6100700bc2fSSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
611e1468202SSimon Trimmer 				"%s%c %.12s %x", cs_dsp->name, *region_name,
6120700bc2fSSimon Trimmer 				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
6130700bc2fSSimon Trimmer 		break;
6140700bc2fSSimon Trimmer 	default:
6150700bc2fSSimon Trimmer 		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
616e1468202SSimon Trimmer 				"%s %.12s %x", cs_dsp->name,
6170700bc2fSSimon Trimmer 				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);
6180700bc2fSSimon Trimmer 		break;
6190700bc2fSSimon Trimmer 	}
6200700bc2fSSimon Trimmer 
6210700bc2fSSimon Trimmer 	if (cs_ctl->subname) {
6220700bc2fSSimon Trimmer 		int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
6230700bc2fSSimon Trimmer 		int skip = 0;
6240700bc2fSSimon Trimmer 
6250700bc2fSSimon Trimmer 		if (dsp->component->name_prefix)
6260700bc2fSSimon Trimmer 			avail -= strlen(dsp->component->name_prefix) + 1;
6270700bc2fSSimon Trimmer 
6280700bc2fSSimon Trimmer 		/* Truncate the subname from the start if it is too long */
6290700bc2fSSimon Trimmer 		if (cs_ctl->subname_len > avail)
6300700bc2fSSimon Trimmer 			skip = cs_ctl->subname_len - avail;
6310700bc2fSSimon Trimmer 
6320700bc2fSSimon Trimmer 		snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
6330700bc2fSSimon Trimmer 			 " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
6340700bc2fSSimon Trimmer 	}
6350700bc2fSSimon Trimmer 
6360700bc2fSSimon Trimmer 	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
6370700bc2fSSimon Trimmer 	if (!ctl)
6380700bc2fSSimon Trimmer 		return -ENOMEM;
6390700bc2fSSimon Trimmer 	ctl->cs_ctl = cs_ctl;
6400700bc2fSSimon Trimmer 
6410700bc2fSSimon Trimmer 	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
6420700bc2fSSimon Trimmer 	if (!ctl->name) {
6430700bc2fSSimon Trimmer 		ret = -ENOMEM;
6440700bc2fSSimon Trimmer 		goto err_ctl;
6450700bc2fSSimon Trimmer 	}
6460700bc2fSSimon Trimmer 
6470700bc2fSSimon Trimmer 	cs_ctl->priv = ctl;
6480700bc2fSSimon Trimmer 
6490700bc2fSSimon Trimmer 	INIT_WORK(&ctl->work, wm_adsp_ctl_work);
6500700bc2fSSimon Trimmer 	schedule_work(&ctl->work);
6510700bc2fSSimon Trimmer 
6520700bc2fSSimon Trimmer 	return 0;
6530700bc2fSSimon Trimmer 
6540700bc2fSSimon Trimmer err_ctl:
6550700bc2fSSimon Trimmer 	kfree(ctl);
6560700bc2fSSimon Trimmer 
6570700bc2fSSimon Trimmer 	return ret;
6580700bc2fSSimon Trimmer }
6590700bc2fSSimon Trimmer 
6600700bc2fSSimon Trimmer static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
6610700bc2fSSimon Trimmer {
6620700bc2fSSimon Trimmer 	struct wm_coeff_ctl *ctl = cs_ctl->priv;
6630700bc2fSSimon Trimmer 
664df6c505cSSimon Trimmer 	cancel_work_sync(&ctl->work);
665df6c505cSSimon Trimmer 
66666225e98SRichard Fitzgerald 	kfree(ctl->name);
66766225e98SRichard Fitzgerald 	kfree(ctl);
66866225e98SRichard Fitzgerald }
66966225e98SRichard Fitzgerald 
670eb65ccdbSLi Xu int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
671eb65ccdbSLi Xu 		      unsigned int alg, void *buf, size_t len)
672eb65ccdbSLi Xu {
6730700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
674eb65ccdbSLi Xu 	struct wm_coeff_ctl *ctl;
675eb65ccdbSLi Xu 	struct snd_kcontrol *kcontrol;
67620441614SAdam Brickman 	char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
677eb65ccdbSLi Xu 	int ret;
678eb65ccdbSLi Xu 
679e1468202SSimon Trimmer 	cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
6800700bc2fSSimon Trimmer 	if (!cs_ctl)
681eb65ccdbSLi Xu 		return -EINVAL;
682eb65ccdbSLi Xu 
6830700bc2fSSimon Trimmer 	ctl = cs_ctl->priv;
6840700bc2fSSimon Trimmer 
6850700bc2fSSimon Trimmer 	if (len > cs_ctl->len)
686eb65ccdbSLi Xu 		return -EINVAL;
687eb65ccdbSLi Xu 
688f444da38SCharles Keepax 	ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
68920441614SAdam Brickman 	if (ret)
69020441614SAdam Brickman 		return ret;
691eb65ccdbSLi Xu 
6920700bc2fSSimon Trimmer 	if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
69320441614SAdam Brickman 		return 0;
69420441614SAdam Brickman 
69520441614SAdam Brickman 	if (dsp->component->name_prefix)
69620441614SAdam Brickman 		snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s",
69720441614SAdam Brickman 			 dsp->component->name_prefix, ctl->name);
69820441614SAdam Brickman 	else
69920441614SAdam Brickman 		snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s",
70020441614SAdam Brickman 			 ctl->name);
70120441614SAdam Brickman 
70220441614SAdam Brickman 	kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name);
70320441614SAdam Brickman 	if (!kcontrol) {
70420441614SAdam Brickman 		adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name);
70520441614SAdam Brickman 		return -EINVAL;
70620441614SAdam Brickman 	}
70720441614SAdam Brickman 
708eb65ccdbSLi Xu 	snd_ctl_notify(dsp->component->card->snd_card,
709eb65ccdbSLi Xu 		       SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id);
710eb65ccdbSLi Xu 
711492df5b0SPierre-Louis Bossart 	return 0;
712eb65ccdbSLi Xu }
713eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
714eb65ccdbSLi Xu 
715eb65ccdbSLi Xu int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
716eb65ccdbSLi Xu 		     unsigned int alg, void *buf, size_t len)
717eb65ccdbSLi Xu {
7180700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
719eb65ccdbSLi Xu 
720e1468202SSimon Trimmer 	cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);
7210700bc2fSSimon Trimmer 	if (!cs_ctl)
722eb65ccdbSLi Xu 		return -EINVAL;
723eb65ccdbSLi Xu 
7240700bc2fSSimon Trimmer 	if (len > cs_ctl->len)
725eb65ccdbSLi Xu 		return -EINVAL;
726eb65ccdbSLi Xu 
727f444da38SCharles Keepax 	return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len);
728eb65ccdbSLi Xu }
729eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
730eb65ccdbSLi Xu 
731f6bc909eSSimon Trimmer static void wm_adsp_release_firmware_files(struct wm_adsp *dsp,
732f6bc909eSSimon Trimmer 					   const struct firmware *wmfw_firmware,
733f6bc909eSSimon Trimmer 					   char *wmfw_filename,
734f6bc909eSSimon Trimmer 					   const struct firmware *coeff_firmware,
735f6bc909eSSimon Trimmer 					   char *coeff_filename)
7362323736dSCharles Keepax {
737f6bc909eSSimon Trimmer 	if (wmfw_firmware)
738f6bc909eSSimon Trimmer 		release_firmware(wmfw_firmware);
739f6bc909eSSimon Trimmer 	kfree(wmfw_filename);
7402323736dSCharles Keepax 
741f6bc909eSSimon Trimmer 	if (coeff_firmware)
742f6bc909eSSimon Trimmer 		release_firmware(coeff_firmware);
743f6bc909eSSimon Trimmer 	kfree(coeff_filename);
7442323736dSCharles Keepax }
7452323736dSCharles Keepax 
746f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
747f6bc909eSSimon Trimmer 					 const struct firmware **firmware,
748f6bc909eSSimon Trimmer 					 char **filename,
749f6bc909eSSimon Trimmer 					 char *suffix)
750db40517cSMark Brown {
751f6bc909eSSimon Trimmer 	struct cs_dsp *cs_dsp = &dsp->cs_dsp;
752f6bc909eSSimon Trimmer 	int ret = 0;
753db40517cSMark Brown 
754f6bc909eSSimon Trimmer 	*filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", dsp->part, dsp->fwf_name,
755f6bc909eSSimon Trimmer 			      wm_adsp_fw[dsp->fw].file, suffix);
756f6bc909eSSimon Trimmer 	if (*filename == NULL)
757605391d0SRichard Fitzgerald 		return -ENOMEM;
758f6bc909eSSimon Trimmer 
759f6bc909eSSimon Trimmer 	ret = request_firmware(firmware, *filename, cs_dsp->dev);
760f6bc909eSSimon Trimmer 	if (ret != 0) {
761f6bc909eSSimon Trimmer 		adsp_err(dsp, "Failed to request '%s'\n", *filename);
762f6bc909eSSimon Trimmer 		kfree(*filename);
763f6bc909eSSimon Trimmer 		*filename = NULL;
764605391d0SRichard Fitzgerald 	}
765605391d0SRichard Fitzgerald 
766f6bc909eSSimon Trimmer 	return ret;
767605391d0SRichard Fitzgerald }
768605391d0SRichard Fitzgerald 
769f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
770f6bc909eSSimon Trimmer 					  const struct firmware **wmfw_firmware,
771f6bc909eSSimon Trimmer 					  char **wmfw_filename,
772f6bc909eSSimon Trimmer 					  const struct firmware **coeff_firmware,
773f6bc909eSSimon Trimmer 					  char **coeff_filename)
7745e7a7a22SMark Brown {
775f6bc909eSSimon Trimmer 	int ret = 0;
776605391d0SRichard Fitzgerald 
777f6bc909eSSimon Trimmer 	ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, "wmfw");
778f6bc909eSSimon Trimmer 	if (ret != 0)
779605391d0SRichard Fitzgerald 		return ret;
780605391d0SRichard Fitzgerald 
781f6bc909eSSimon Trimmer 	wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, "bin");
782078e7183SCharles Keepax 
7835e7a7a22SMark Brown 	return 0;
7845e7a7a22SMark Brown }
785dcad34f8SRichard Fitzgerald 
786e1468202SSimon Trimmer static int wm_adsp_common_init(struct wm_adsp *dsp)
78725ca837bSSimon Trimmer {
788e1468202SSimon Trimmer 	char *p;
789e1468202SSimon Trimmer 
79025ca837bSSimon Trimmer 	INIT_LIST_HEAD(&dsp->compr_list);
79125ca837bSSimon Trimmer 	INIT_LIST_HEAD(&dsp->buffer_list);
792e1468202SSimon Trimmer 
793e1468202SSimon Trimmer 	if (!dsp->fwf_name) {
794e1468202SSimon Trimmer 		p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL);
795e1468202SSimon Trimmer 		if (!p)
796e1468202SSimon Trimmer 			return -ENOMEM;
797e1468202SSimon Trimmer 
798e1468202SSimon Trimmer 		dsp->fwf_name = p;
799e1468202SSimon Trimmer 		for (; *p != 0; ++p)
800e1468202SSimon Trimmer 			*p = tolower(*p);
80125ca837bSSimon Trimmer 	}
80225ca837bSSimon Trimmer 
803e1468202SSimon Trimmer 	return 0;
804e1468202SSimon Trimmer }
805e1468202SSimon Trimmer 
80625ca837bSSimon Trimmer int wm_adsp1_init(struct wm_adsp *dsp)
80725ca837bSSimon Trimmer {
808e1468202SSimon Trimmer 	int ret;
80925ca837bSSimon Trimmer 
8102dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp1_client_ops;
8112dd04464SSimon Trimmer 
812e1468202SSimon Trimmer 	ret = cs_dsp_adsp1_init(&dsp->cs_dsp);
813e1468202SSimon Trimmer 	if (ret)
814e1468202SSimon Trimmer 		return ret;
815e1468202SSimon Trimmer 
816e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
817dcad34f8SRichard Fitzgerald }
8185e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
8195e7a7a22SMark Brown 
820186152dfSSimon Trimmer int wm_adsp1_event(struct snd_soc_dapm_widget *w,
821186152dfSSimon Trimmer 		   struct snd_kcontrol *kcontrol,
822186152dfSSimon Trimmer 		   int event)
823186152dfSSimon Trimmer {
824186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
825186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
826186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
827186152dfSSimon Trimmer 	int ret = 0;
828a828056fSSimon Trimmer 	char *wmfw_filename = NULL;
829a828056fSSimon Trimmer 	const struct firmware *wmfw_firmware = NULL;
830a828056fSSimon Trimmer 	char *coeff_filename = NULL;
831a828056fSSimon Trimmer 	const struct firmware *coeff_firmware = NULL;
832186152dfSSimon Trimmer 
833186152dfSSimon Trimmer 	dsp->component = component;
834186152dfSSimon Trimmer 
835186152dfSSimon Trimmer 	switch (event) {
836186152dfSSimon Trimmer 	case SND_SOC_DAPM_POST_PMU:
837a828056fSSimon Trimmer 		ret = wm_adsp_request_firmware_files(dsp,
838a828056fSSimon Trimmer 						     &wmfw_firmware, &wmfw_filename,
839a828056fSSimon Trimmer 						     &coeff_firmware, &coeff_filename);
840a828056fSSimon Trimmer 		if (ret)
841a828056fSSimon Trimmer 			break;
842a828056fSSimon Trimmer 
843e1468202SSimon Trimmer 		ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp,
844a828056fSSimon Trimmer 					    wmfw_firmware, wmfw_filename,
845a828056fSSimon Trimmer 					    coeff_firmware, coeff_filename,
8462169f2f1SSimon Trimmer 					    wm_adsp_fw_text[dsp->fw]);
847a828056fSSimon Trimmer 
848a828056fSSimon Trimmer 		wm_adsp_release_firmware_files(dsp,
849a828056fSSimon Trimmer 					       wmfw_firmware, wmfw_filename,
850a828056fSSimon Trimmer 					       coeff_firmware, coeff_filename);
851186152dfSSimon Trimmer 		break;
852186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
853e1468202SSimon Trimmer 		cs_dsp_adsp1_power_down(&dsp->cs_dsp);
854186152dfSSimon Trimmer 		break;
8552159ad93SMark Brown 	default:
8562159ad93SMark Brown 		break;
8572159ad93SMark Brown 	}
8582159ad93SMark Brown 
8592159ad93SMark Brown 	return ret;
8602159ad93SMark Brown }
8612159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
8622159ad93SMark Brown 
86325ca837bSSimon Trimmer int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq)
86425ca837bSSimon Trimmer {
86525ca837bSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
86625ca837bSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
86725ca837bSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
86825ca837bSSimon Trimmer 
869e1468202SSimon Trimmer 	return cs_dsp_set_dspclk(&dsp->cs_dsp, freq);
87025ca837bSSimon Trimmer }
871b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk);
872d82d767fSCharles Keepax 
873af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
874af813a6fSCharles Keepax 			   struct snd_ctl_elem_value *ucontrol)
875af813a6fSCharles Keepax {
8760fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
877b1470d4cSAjit Pandey 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
878b1470d4cSAjit Pandey 	struct soc_mixer_control *mc =
879b1470d4cSAjit Pandey 		(struct soc_mixer_control *)kcontrol->private_value;
880b1470d4cSAjit Pandey 	struct wm_adsp *dsp = &dsps[mc->shift - 1];
881af813a6fSCharles Keepax 
882af813a6fSCharles Keepax 	ucontrol->value.integer.value[0] = dsp->preloaded;
883af813a6fSCharles Keepax 
884af813a6fSCharles Keepax 	return 0;
885af813a6fSCharles Keepax }
886af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);
887af813a6fSCharles Keepax 
888af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
889af813a6fSCharles Keepax 			   struct snd_ctl_elem_value *ucontrol)
890af813a6fSCharles Keepax {
8910fe1daa6SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
892b1470d4cSAjit Pandey 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
8930fe1daa6SKuninori Morimoto 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
894af813a6fSCharles Keepax 	struct soc_mixer_control *mc =
895af813a6fSCharles Keepax 		(struct soc_mixer_control *)kcontrol->private_value;
896b1470d4cSAjit Pandey 	struct wm_adsp *dsp = &dsps[mc->shift - 1];
897af813a6fSCharles Keepax 	char preload[32];
898af813a6fSCharles Keepax 
899ba235634SCharles Keepax 	if (dsp->preloaded == ucontrol->value.integer.value[0])
900ba235634SCharles Keepax 		return 0;
901ba235634SCharles Keepax 
902e1468202SSimon Trimmer 	snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
903af813a6fSCharles Keepax 
904ba235634SCharles Keepax 	if (ucontrol->value.integer.value[0] || dsp->toggle_preload)
90595a594d0SCharles Keepax 		snd_soc_component_force_enable_pin(component, preload);
906af813a6fSCharles Keepax 	else
90795a594d0SCharles Keepax 		snd_soc_component_disable_pin(component, preload);
908af813a6fSCharles Keepax 
909af813a6fSCharles Keepax 	snd_soc_dapm_sync(dapm);
910af813a6fSCharles Keepax 
911868e49a4SStuart Henderson 	flush_work(&dsp->boot_work);
912868e49a4SStuart Henderson 
913ba235634SCharles Keepax 	dsp->preloaded = ucontrol->value.integer.value[0];
914ba235634SCharles Keepax 
915ba235634SCharles Keepax 	if (dsp->toggle_preload) {
916ba235634SCharles Keepax 		snd_soc_component_disable_pin(component, preload);
917ba235634SCharles Keepax 		snd_soc_dapm_sync(dapm);
918ba235634SCharles Keepax 	}
919ba235634SCharles Keepax 
920af813a6fSCharles Keepax 	return 0;
921af813a6fSCharles Keepax }
922af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
923af813a6fSCharles Keepax 
924186152dfSSimon Trimmer static void wm_adsp_boot_work(struct work_struct *work)
925186152dfSSimon Trimmer {
926186152dfSSimon Trimmer 	struct wm_adsp *dsp = container_of(work,
927186152dfSSimon Trimmer 					   struct wm_adsp,
928186152dfSSimon Trimmer 					   boot_work);
929a828056fSSimon Trimmer 	int ret = 0;
930a828056fSSimon Trimmer 	char *wmfw_filename = NULL;
931a828056fSSimon Trimmer 	const struct firmware *wmfw_firmware = NULL;
932a828056fSSimon Trimmer 	char *coeff_filename = NULL;
933a828056fSSimon Trimmer 	const struct firmware *coeff_firmware = NULL;
934a828056fSSimon Trimmer 
935a828056fSSimon Trimmer 	ret = wm_adsp_request_firmware_files(dsp,
936a828056fSSimon Trimmer 					     &wmfw_firmware, &wmfw_filename,
937a828056fSSimon Trimmer 					     &coeff_firmware, &coeff_filename);
938a828056fSSimon Trimmer 	if (ret)
939a828056fSSimon Trimmer 		return;
940186152dfSSimon Trimmer 
941e1468202SSimon Trimmer 	cs_dsp_power_up(&dsp->cs_dsp,
942a828056fSSimon Trimmer 			wmfw_firmware, wmfw_filename,
943a828056fSSimon Trimmer 			coeff_firmware, coeff_filename,
9442169f2f1SSimon Trimmer 			wm_adsp_fw_text[dsp->fw]);
945a828056fSSimon Trimmer 
946a828056fSSimon Trimmer 	wm_adsp_release_firmware_files(dsp,
947a828056fSSimon Trimmer 				       wmfw_firmware, wmfw_filename,
948a828056fSSimon Trimmer 				       coeff_firmware, coeff_filename);
949186152dfSSimon Trimmer }
950186152dfSSimon Trimmer 
951186152dfSSimon Trimmer int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
952186152dfSSimon Trimmer 			struct snd_kcontrol *kcontrol, int event)
953186152dfSSimon Trimmer {
954186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
955186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
956186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
957186152dfSSimon Trimmer 
958186152dfSSimon Trimmer 	switch (event) {
959186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMU:
960186152dfSSimon Trimmer 		queue_work(system_unbound_wq, &dsp->boot_work);
961186152dfSSimon Trimmer 		break;
962186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
963e1468202SSimon Trimmer 		cs_dsp_power_down(&dsp->cs_dsp);
96457a60cc3SCharles Keepax 		break;
96512db5eddSCharles Keepax 	default:
96612db5eddSCharles Keepax 		break;
967cab27258SCharles Keepax 	}
96812db5eddSCharles Keepax 
96912db5eddSCharles Keepax 	return 0;
97012db5eddSCharles Keepax }
9714e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event);
97212db5eddSCharles Keepax 
973e1468202SSimon Trimmer static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp)
974d8a64d6aSCharles Keepax {
975e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
976e1468202SSimon Trimmer 
977186152dfSSimon Trimmer 	if (wm_adsp_fw[dsp->fw].num_caps != 0)
978186152dfSSimon Trimmer 		return wm_adsp_buffer_init(dsp);
979d8a64d6aSCharles Keepax 
980186152dfSSimon Trimmer 	return 0;
981186152dfSSimon Trimmer }
982186152dfSSimon Trimmer 
983e1468202SSimon Trimmer static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp)
984186152dfSSimon Trimmer {
985e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
986e1468202SSimon Trimmer 
987186152dfSSimon Trimmer 	if (wm_adsp_fw[dsp->fw].num_caps != 0)
988186152dfSSimon Trimmer 		wm_adsp_buffer_free(dsp);
989186152dfSSimon Trimmer 
990186152dfSSimon Trimmer 	dsp->fatal_error = false;
991186152dfSSimon Trimmer }
992186152dfSSimon Trimmer 
993186152dfSSimon Trimmer int wm_adsp_event(struct snd_soc_dapm_widget *w,
994186152dfSSimon Trimmer 		  struct snd_kcontrol *kcontrol, int event)
995186152dfSSimon Trimmer {
996186152dfSSimon Trimmer 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
997186152dfSSimon Trimmer 	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
998186152dfSSimon Trimmer 	struct wm_adsp *dsp = &dsps[w->shift];
999186152dfSSimon Trimmer 	int ret = 0;
1000186152dfSSimon Trimmer 
1001186152dfSSimon Trimmer 	switch (event) {
1002186152dfSSimon Trimmer 	case SND_SOC_DAPM_POST_PMU:
1003186152dfSSimon Trimmer 		flush_work(&dsp->boot_work);
1004e1468202SSimon Trimmer 		ret = cs_dsp_run(&dsp->cs_dsp);
1005186152dfSSimon Trimmer 		break;
1006186152dfSSimon Trimmer 	case SND_SOC_DAPM_PRE_PMD:
1007e1468202SSimon Trimmer 		cs_dsp_stop(&dsp->cs_dsp);
1008186152dfSSimon Trimmer 		break;
10092159ad93SMark Brown 	default:
10102159ad93SMark Brown 		break;
10112159ad93SMark Brown 	}
10122159ad93SMark Brown 
10132159ad93SMark Brown 	return ret;
10142159ad93SMark Brown }
10154e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event);
1016973838a0SMark Brown 
10170fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
1018f5e2ce92SRichard Fitzgerald {
1019af813a6fSCharles Keepax 	char preload[32];
1020af813a6fSCharles Keepax 
1021e1468202SSimon Trimmer 	snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
102295a594d0SCharles Keepax 	snd_soc_component_disable_pin(component, preload);
1023685f51a5SRichard Fitzgerald 
1024e1468202SSimon Trimmer 	cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);
1025f9f55e31SRichard Fitzgerald 
10260fe1daa6SKuninori Morimoto 	dsp->component = component;
1027af813a6fSCharles Keepax 
10280a047f07SRichard Fitzgerald 	return 0;
1029f5e2ce92SRichard Fitzgerald }
10300fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
1031f5e2ce92SRichard Fitzgerald 
10320fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
1033f5e2ce92SRichard Fitzgerald {
1034e1468202SSimon Trimmer 	cs_dsp_cleanup_debugfs(&dsp->cs_dsp);
1035f9f55e31SRichard Fitzgerald 
1036f5e2ce92SRichard Fitzgerald 	return 0;
1037f5e2ce92SRichard Fitzgerald }
10380fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove);
1039f5e2ce92SRichard Fitzgerald 
104025ca837bSSimon Trimmer int wm_adsp2_init(struct wm_adsp *dsp)
104125ca837bSSimon Trimmer {
1042e1468202SSimon Trimmer 	int ret;
1043e1468202SSimon Trimmer 
10444e08d50dSCharles Keepax 	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
10456ab2b7b4SDimitris Papastamos 
10466092be2dSCharles Keepax 	dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr);
10472dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
10486092be2dSCharles Keepax 
1049e1468202SSimon Trimmer 	ret = cs_dsp_adsp2_init(&dsp->cs_dsp);
1050e1468202SSimon Trimmer 	if (ret)
1051e1468202SSimon Trimmer 		return ret;
105225ca837bSSimon Trimmer 
1053e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
1054973838a0SMark Brown }
1055973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
10560a37c6efSPraveen Diwakar 
105725ca837bSSimon Trimmer int wm_halo_init(struct wm_adsp *dsp)
105825ca837bSSimon Trimmer {
1059e1468202SSimon Trimmer 	int ret;
1060e1468202SSimon Trimmer 
1061170b1e12SWen Shi 	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
1062170b1e12SWen Shi 
10636092be2dSCharles Keepax 	dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr);
10642dd04464SSimon Trimmer 	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;
10656092be2dSCharles Keepax 
1066e1468202SSimon Trimmer 	ret = cs_dsp_halo_init(&dsp->cs_dsp);
1067e1468202SSimon Trimmer 	if (ret)
1068e1468202SSimon Trimmer 		return ret;
106925ca837bSSimon Trimmer 
1070e1468202SSimon Trimmer 	return wm_adsp_common_init(dsp);
1071170b1e12SWen Shi }
1072170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init);
1073170b1e12SWen Shi 
107425ca837bSSimon Trimmer void wm_adsp2_remove(struct wm_adsp *dsp)
107525ca837bSSimon Trimmer {
1076e1468202SSimon Trimmer 	cs_dsp_remove(&dsp->cs_dsp);
107725ca837bSSimon Trimmer }
107866225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove);
107966225e98SRichard Fitzgerald 
1080edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
1081edd71350SCharles Keepax {
1082edd71350SCharles Keepax 	return compr->buf != NULL;
1083edd71350SCharles Keepax }
1084edd71350SCharles Keepax 
1085edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
1086edd71350SCharles Keepax {
10874f2d4eabSStuart Henderson 	struct wm_adsp_compr_buf *buf = NULL, *tmp;
10884f2d4eabSStuart Henderson 
1089a2bcbc1bSCharles Keepax 	if (compr->dsp->fatal_error)
1090a2bcbc1bSCharles Keepax 		return -EINVAL;
1091a2bcbc1bSCharles Keepax 
10924f2d4eabSStuart Henderson 	list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
10934f2d4eabSStuart Henderson 		if (!tmp->name || !strcmp(compr->name, tmp->name)) {
10944f2d4eabSStuart Henderson 			buf = tmp;
10954f2d4eabSStuart Henderson 			break;
10964f2d4eabSStuart Henderson 		}
10974f2d4eabSStuart Henderson 	}
10984f2d4eabSStuart Henderson 
10994f2d4eabSStuart Henderson 	if (!buf)
1100edd71350SCharles Keepax 		return -EINVAL;
1101edd71350SCharles Keepax 
11024f2d4eabSStuart Henderson 	compr->buf = buf;
1103789b930aSCharles Keepax 	buf->compr = compr;
1104edd71350SCharles Keepax 
1105edd71350SCharles Keepax 	return 0;
1106edd71350SCharles Keepax }
1107edd71350SCharles Keepax 
1108721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
1109721be3beSCharles Keepax {
1110721be3beSCharles Keepax 	if (!compr)
1111721be3beSCharles Keepax 		return;
1112721be3beSCharles Keepax 
1113721be3beSCharles Keepax 	/* Wake the poll so it can see buffer is no longer attached */
1114721be3beSCharles Keepax 	if (compr->stream)
1115721be3beSCharles Keepax 		snd_compr_fragment_elapsed(compr->stream);
1116721be3beSCharles Keepax 
1117721be3beSCharles Keepax 	if (wm_adsp_compr_attached(compr)) {
1118721be3beSCharles Keepax 		compr->buf->compr = NULL;
1119721be3beSCharles Keepax 		compr->buf = NULL;
1120721be3beSCharles Keepax 	}
1121721be3beSCharles Keepax }
1122721be3beSCharles Keepax 
1123406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
1124406abc95SCharles Keepax {
11254f2d4eabSStuart Henderson 	struct wm_adsp_compr *compr, *tmp;
11264f2d4eabSStuart Henderson 	struct snd_soc_pcm_runtime *rtd = stream->private_data;
1127406abc95SCharles Keepax 	int ret = 0;
1128406abc95SCharles Keepax 
1129e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1130406abc95SCharles Keepax 
1131406abc95SCharles Keepax 	if (wm_adsp_fw[dsp->fw].num_caps == 0) {
11320d3fba3eSCharles Keepax 		adsp_err(dsp, "%s: Firmware does not support compressed API\n",
1133b5cb8558SKuninori Morimoto 			 asoc_rtd_to_codec(rtd, 0)->name);
1134406abc95SCharles Keepax 		ret = -ENXIO;
1135406abc95SCharles Keepax 		goto out;
1136406abc95SCharles Keepax 	}
1137406abc95SCharles Keepax 
1138406abc95SCharles Keepax 	if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
11390d3fba3eSCharles Keepax 		adsp_err(dsp, "%s: Firmware does not support stream direction\n",
1140b5cb8558SKuninori Morimoto 			 asoc_rtd_to_codec(rtd, 0)->name);
1141406abc95SCharles Keepax 		ret = -EINVAL;
1142406abc95SCharles Keepax 		goto out;
1143406abc95SCharles Keepax 	}
1144406abc95SCharles Keepax 
11454f2d4eabSStuart Henderson 	list_for_each_entry(tmp, &dsp->compr_list, list) {
1146b5cb8558SKuninori Morimoto 		if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) {
11470d3fba3eSCharles Keepax 			adsp_err(dsp, "%s: Only a single stream supported per dai\n",
1148b5cb8558SKuninori Morimoto 				 asoc_rtd_to_codec(rtd, 0)->name);
114995fe9597SCharles Keepax 			ret = -EBUSY;
115095fe9597SCharles Keepax 			goto out;
115195fe9597SCharles Keepax 		}
11524f2d4eabSStuart Henderson 	}
115395fe9597SCharles Keepax 
1154406abc95SCharles Keepax 	compr = kzalloc(sizeof(*compr), GFP_KERNEL);
1155406abc95SCharles Keepax 	if (!compr) {
1156406abc95SCharles Keepax 		ret = -ENOMEM;
1157406abc95SCharles Keepax 		goto out;
1158406abc95SCharles Keepax 	}
1159406abc95SCharles Keepax 
1160406abc95SCharles Keepax 	compr->dsp = dsp;
1161406abc95SCharles Keepax 	compr->stream = stream;
1162b5cb8558SKuninori Morimoto 	compr->name = asoc_rtd_to_codec(rtd, 0)->name;
1163406abc95SCharles Keepax 
11644f2d4eabSStuart Henderson 	list_add_tail(&compr->list, &dsp->compr_list);
1165406abc95SCharles Keepax 
1166406abc95SCharles Keepax 	stream->runtime->private_data = compr;
1167406abc95SCharles Keepax 
1168406abc95SCharles Keepax out:
1169e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1170406abc95SCharles Keepax 
1171406abc95SCharles Keepax 	return ret;
1172406abc95SCharles Keepax }
1173406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
1174406abc95SCharles Keepax 
11753a5ccf25SKuninori Morimoto int wm_adsp_compr_free(struct snd_soc_component *component,
11763a5ccf25SKuninori Morimoto 		       struct snd_compr_stream *stream)
1177406abc95SCharles Keepax {
1178406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1179406abc95SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1180406abc95SCharles Keepax 
1181e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1182406abc95SCharles Keepax 
1183721be3beSCharles Keepax 	wm_adsp_compr_detach(compr);
11844f2d4eabSStuart Henderson 	list_del(&compr->list);
1185406abc95SCharles Keepax 
118683a40ce9SCharles Keepax 	kfree(compr->raw_buf);
1187406abc95SCharles Keepax 	kfree(compr);
1188406abc95SCharles Keepax 
1189e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1190406abc95SCharles Keepax 
1191406abc95SCharles Keepax 	return 0;
1192406abc95SCharles Keepax }
1193406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free);
1194406abc95SCharles Keepax 
1195406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
1196406abc95SCharles Keepax 				      struct snd_compr_params *params)
1197406abc95SCharles Keepax {
1198406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1199406abc95SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1200406abc95SCharles Keepax 	const struct wm_adsp_fw_caps *caps;
1201406abc95SCharles Keepax 	const struct snd_codec_desc *desc;
1202406abc95SCharles Keepax 	int i, j;
1203406abc95SCharles Keepax 
1204406abc95SCharles Keepax 	if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||
1205406abc95SCharles Keepax 	    params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
1206406abc95SCharles Keepax 	    params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
1207406abc95SCharles Keepax 	    params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
12085beb8eeaSSimon Trimmer 	    params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) {
12090d3fba3eSCharles Keepax 		compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n",
1210406abc95SCharles Keepax 			  params->buffer.fragment_size,
1211406abc95SCharles Keepax 			  params->buffer.fragments);
1212406abc95SCharles Keepax 
1213406abc95SCharles Keepax 		return -EINVAL;
1214406abc95SCharles Keepax 	}
1215406abc95SCharles Keepax 
1216406abc95SCharles Keepax 	for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
1217406abc95SCharles Keepax 		caps = &wm_adsp_fw[dsp->fw].caps[i];
1218406abc95SCharles Keepax 		desc = &caps->desc;
1219406abc95SCharles Keepax 
1220406abc95SCharles Keepax 		if (caps->id != params->codec.id)
1221406abc95SCharles Keepax 			continue;
1222406abc95SCharles Keepax 
1223406abc95SCharles Keepax 		if (stream->direction == SND_COMPRESS_PLAYBACK) {
1224406abc95SCharles Keepax 			if (desc->max_ch < params->codec.ch_out)
1225406abc95SCharles Keepax 				continue;
1226406abc95SCharles Keepax 		} else {
1227406abc95SCharles Keepax 			if (desc->max_ch < params->codec.ch_in)
1228406abc95SCharles Keepax 				continue;
1229406abc95SCharles Keepax 		}
1230406abc95SCharles Keepax 
1231406abc95SCharles Keepax 		if (!(desc->formats & (1 << params->codec.format)))
1232406abc95SCharles Keepax 			continue;
1233406abc95SCharles Keepax 
1234406abc95SCharles Keepax 		for (j = 0; j < desc->num_sample_rates; ++j)
1235406abc95SCharles Keepax 			if (desc->sample_rates[j] == params->codec.sample_rate)
1236406abc95SCharles Keepax 				return 0;
1237406abc95SCharles Keepax 	}
1238406abc95SCharles Keepax 
12390d3fba3eSCharles Keepax 	compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
1240406abc95SCharles Keepax 		  params->codec.id, params->codec.ch_in, params->codec.ch_out,
1241406abc95SCharles Keepax 		  params->codec.sample_rate, params->codec.format);
1242406abc95SCharles Keepax 	return -EINVAL;
1243406abc95SCharles Keepax }
1244406abc95SCharles Keepax 
1245565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
1246565ace46SCharles Keepax {
12475beb8eeaSSimon Trimmer 	return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE;
1248565ace46SCharles Keepax }
1249565ace46SCharles Keepax 
12503a5ccf25SKuninori Morimoto int wm_adsp_compr_set_params(struct snd_soc_component *component,
12513a5ccf25SKuninori Morimoto 			     struct snd_compr_stream *stream,
1252406abc95SCharles Keepax 			     struct snd_compr_params *params)
1253406abc95SCharles Keepax {
1254406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
125583a40ce9SCharles Keepax 	unsigned int size;
1256406abc95SCharles Keepax 	int ret;
1257406abc95SCharles Keepax 
1258406abc95SCharles Keepax 	ret = wm_adsp_compr_check_params(stream, params);
1259406abc95SCharles Keepax 	if (ret)
1260406abc95SCharles Keepax 		return ret;
1261406abc95SCharles Keepax 
1262406abc95SCharles Keepax 	compr->size = params->buffer;
1263406abc95SCharles Keepax 
12640d3fba3eSCharles Keepax 	compr_dbg(compr, "fragment_size=%d fragments=%d\n",
1265406abc95SCharles Keepax 		  compr->size.fragment_size, compr->size.fragments);
1266406abc95SCharles Keepax 
126783a40ce9SCharles Keepax 	size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
126883a40ce9SCharles Keepax 	compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
126983a40ce9SCharles Keepax 	if (!compr->raw_buf)
127083a40ce9SCharles Keepax 		return -ENOMEM;
127183a40ce9SCharles Keepax 
1272da2b3358SCharles Keepax 	compr->sample_rate = params->codec.sample_rate;
1273da2b3358SCharles Keepax 
1274406abc95SCharles Keepax 	return 0;
1275406abc95SCharles Keepax }
1276406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
1277406abc95SCharles Keepax 
12783a5ccf25SKuninori Morimoto int wm_adsp_compr_get_caps(struct snd_soc_component *component,
12793a5ccf25SKuninori Morimoto 			   struct snd_compr_stream *stream,
1280406abc95SCharles Keepax 			   struct snd_compr_caps *caps)
1281406abc95SCharles Keepax {
1282406abc95SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1283406abc95SCharles Keepax 	int fw = compr->dsp->fw;
1284406abc95SCharles Keepax 	int i;
1285406abc95SCharles Keepax 
1286406abc95SCharles Keepax 	if (wm_adsp_fw[fw].caps) {
1287406abc95SCharles Keepax 		for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
1288406abc95SCharles Keepax 			caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
1289406abc95SCharles Keepax 
1290406abc95SCharles Keepax 		caps->num_codecs = i;
1291406abc95SCharles Keepax 		caps->direction = wm_adsp_fw[fw].compr_direction;
1292406abc95SCharles Keepax 
1293406abc95SCharles Keepax 		caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
1294406abc95SCharles Keepax 		caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
1295406abc95SCharles Keepax 		caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;
1296406abc95SCharles Keepax 		caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;
1297406abc95SCharles Keepax 	}
1298406abc95SCharles Keepax 
1299406abc95SCharles Keepax 	return 0;
1300406abc95SCharles Keepax }
1301406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
1302406abc95SCharles Keepax 
13032cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
13042cd19bdbSCharles Keepax 				      unsigned int field_offset, u32 *data)
13052cd19bdbSCharles Keepax {
1306e1468202SSimon Trimmer 	return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
13072cd19bdbSCharles Keepax 				     buf->host_buf_ptr + field_offset, data);
13082cd19bdbSCharles Keepax }
13092cd19bdbSCharles Keepax 
13102cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
13112cd19bdbSCharles Keepax 				       unsigned int field_offset, u32 data)
13122cd19bdbSCharles Keepax {
1313e1468202SSimon Trimmer 	return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,
1314e1468202SSimon Trimmer 				      buf->host_buf_ptr + field_offset,
1315e1468202SSimon Trimmer 				      data);
13162cd19bdbSCharles Keepax }
13172cd19bdbSCharles Keepax 
13181e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
13191e38f069SCharles Keepax {
13201e38f069SCharles Keepax 	const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
13211e38f069SCharles Keepax 	struct wm_adsp_buffer_region *region;
13221e38f069SCharles Keepax 	u32 offset = 0;
13231e38f069SCharles Keepax 	int i, ret;
13241e38f069SCharles Keepax 
1325a792af69SCharles Keepax 	buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions),
1326a792af69SCharles Keepax 			       GFP_KERNEL);
1327a792af69SCharles Keepax 	if (!buf->regions)
1328a792af69SCharles Keepax 		return -ENOMEM;
1329a792af69SCharles Keepax 
13301e38f069SCharles Keepax 	for (i = 0; i < caps->num_regions; ++i) {
13311e38f069SCharles Keepax 		region = &buf->regions[i];
13321e38f069SCharles Keepax 
13331e38f069SCharles Keepax 		region->offset = offset;
13341e38f069SCharles Keepax 		region->mem_type = caps->region_defs[i].mem_type;
13351e38f069SCharles Keepax 
13361e38f069SCharles Keepax 		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
13371e38f069SCharles Keepax 					  &region->base_addr);
13381e38f069SCharles Keepax 		if (ret < 0)
13391e38f069SCharles Keepax 			return ret;
13401e38f069SCharles Keepax 
13411e38f069SCharles Keepax 		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
13421e38f069SCharles Keepax 					  &offset);
13431e38f069SCharles Keepax 		if (ret < 0)
13441e38f069SCharles Keepax 			return ret;
13451e38f069SCharles Keepax 
13461e38f069SCharles Keepax 		region->cumulative_size = offset;
13471e38f069SCharles Keepax 
13480d3fba3eSCharles Keepax 		compr_dbg(buf,
13491e38f069SCharles Keepax 			  "region=%d type=%d base=%08x off=%08x size=%08x\n",
13501e38f069SCharles Keepax 			  i, region->mem_type, region->base_addr,
13511e38f069SCharles Keepax 			  region->offset, region->cumulative_size);
13521e38f069SCharles Keepax 	}
13531e38f069SCharles Keepax 
13541e38f069SCharles Keepax 	return 0;
13551e38f069SCharles Keepax }
13561e38f069SCharles Keepax 
13571e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf)
13581e38f069SCharles Keepax {
13591e38f069SCharles Keepax 	buf->irq_count = 0xFFFFFFFF;
13601e38f069SCharles Keepax 	buf->read_index = -1;
13611e38f069SCharles Keepax 	buf->avail = 0;
13621e38f069SCharles Keepax }
13631e38f069SCharles Keepax 
1364a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)
1365a792af69SCharles Keepax {
1366a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1367a792af69SCharles Keepax 
1368a792af69SCharles Keepax 	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
1369a792af69SCharles Keepax 	if (!buf)
1370a792af69SCharles Keepax 		return NULL;
1371a792af69SCharles Keepax 
1372a792af69SCharles Keepax 	buf->dsp = dsp;
1373a792af69SCharles Keepax 
1374a792af69SCharles Keepax 	wm_adsp_buffer_clear(buf);
1375a792af69SCharles Keepax 
13764f2d4eabSStuart Henderson 	list_add_tail(&buf->list, &dsp->buffer_list);
1377a792af69SCharles Keepax 
1378a792af69SCharles Keepax 	return buf;
1379a792af69SCharles Keepax }
1380a792af69SCharles Keepax 
1381a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
13822cd19bdbSCharles Keepax {
13835beb8eeaSSimon Trimmer 	struct cs_dsp_alg_region *alg_region;
1384a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
13852cd19bdbSCharles Keepax 	u32 xmalg, addr, magic;
13862cd19bdbSCharles Keepax 	int i, ret;
13872cd19bdbSCharles Keepax 
1388e1468202SSimon Trimmer 	alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id);
13899daf4fd0SLi Xu 	if (!alg_region) {
13909daf4fd0SLi Xu 		adsp_err(dsp, "No algorithm region found\n");
13919daf4fd0SLi Xu 		return -EINVAL;
13929daf4fd0SLi Xu 	}
13939daf4fd0SLi Xu 
1394a792af69SCharles Keepax 	buf = wm_adsp_buffer_alloc(dsp);
1395a792af69SCharles Keepax 	if (!buf)
1396a792af69SCharles Keepax 		return -ENOMEM;
1397a792af69SCharles Keepax 
13986092be2dSCharles Keepax 	xmalg = dsp->sys_config_size / sizeof(__be32);
13992cd19bdbSCharles Keepax 
14002cd19bdbSCharles Keepax 	addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
1401e1468202SSimon Trimmer 	ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic);
14022cd19bdbSCharles Keepax 	if (ret < 0)
14032cd19bdbSCharles Keepax 		return ret;
14042cd19bdbSCharles Keepax 
14052cd19bdbSCharles Keepax 	if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
1406a792af69SCharles Keepax 		return -ENODEV;
14072cd19bdbSCharles Keepax 
14082cd19bdbSCharles Keepax 	addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
14092cd19bdbSCharles Keepax 	for (i = 0; i < 5; ++i) {
1410e1468202SSimon Trimmer 		ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr,
14112cd19bdbSCharles Keepax 					    &buf->host_buf_ptr);
14122cd19bdbSCharles Keepax 		if (ret < 0)
14132cd19bdbSCharles Keepax 			return ret;
14142cd19bdbSCharles Keepax 
14152cd19bdbSCharles Keepax 		if (buf->host_buf_ptr)
14162cd19bdbSCharles Keepax 			break;
14172cd19bdbSCharles Keepax 
14182cd19bdbSCharles Keepax 		usleep_range(1000, 2000);
14192cd19bdbSCharles Keepax 	}
14202cd19bdbSCharles Keepax 
14212cd19bdbSCharles Keepax 	if (!buf->host_buf_ptr)
14222cd19bdbSCharles Keepax 		return -EIO;
14232cd19bdbSCharles Keepax 
1424fb13f19dSAndrew Ford 	buf->host_buf_mem_type = WMFW_ADSP2_XM;
1425fb13f19dSAndrew Ford 
1426a792af69SCharles Keepax 	ret = wm_adsp_buffer_populate(buf);
1427a792af69SCharles Keepax 	if (ret < 0)
1428a792af69SCharles Keepax 		return ret;
1429a792af69SCharles Keepax 
14300d3fba3eSCharles Keepax 	compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr);
14312cd19bdbSCharles Keepax 
14322cd19bdbSCharles Keepax 	return 0;
14332cd19bdbSCharles Keepax }
14342cd19bdbSCharles Keepax 
14350700bc2fSSimon Trimmer static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)
1436d52ed4b0SRichard Fitzgerald {
14374f2d4eabSStuart Henderson 	struct wm_adsp_host_buf_coeff_v1 coeff_v1;
1438a792af69SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1439e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);
144004ae0859SCharles Keepax 	unsigned int version;
1441a792af69SCharles Keepax 	int ret, i;
1442d52ed4b0SRichard Fitzgerald 
1443d52ed4b0SRichard Fitzgerald 	for (i = 0; i < 5; ++i) {
1444a887f9c7SCharles Keepax 		ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1,
1445a887f9c7SCharles Keepax 					     min(cs_ctl->len, sizeof(coeff_v1)));
1446d52ed4b0SRichard Fitzgerald 		if (ret < 0)
1447d52ed4b0SRichard Fitzgerald 			return ret;
1448d52ed4b0SRichard Fitzgerald 
144904ae0859SCharles Keepax 		if (coeff_v1.host_buf_ptr)
1450d52ed4b0SRichard Fitzgerald 			break;
1451d52ed4b0SRichard Fitzgerald 
1452d52ed4b0SRichard Fitzgerald 		usleep_range(1000, 2000);
1453d52ed4b0SRichard Fitzgerald 	}
1454d52ed4b0SRichard Fitzgerald 
145504ae0859SCharles Keepax 	if (!coeff_v1.host_buf_ptr) {
14560700bc2fSSimon Trimmer 		adsp_err(dsp, "Failed to acquire host buffer\n");
1457d52ed4b0SRichard Fitzgerald 		return -EIO;
1458d52ed4b0SRichard Fitzgerald 	}
1459d52ed4b0SRichard Fitzgerald 
14600700bc2fSSimon Trimmer 	buf = wm_adsp_buffer_alloc(dsp);
14612cd19bdbSCharles Keepax 	if (!buf)
14622cd19bdbSCharles Keepax 		return -ENOMEM;
14632cd19bdbSCharles Keepax 
14640700bc2fSSimon Trimmer 	buf->host_buf_mem_type = cs_ctl->alg_region.type;
146504ae0859SCharles Keepax 	buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr);
14662cd19bdbSCharles Keepax 
14672cd19bdbSCharles Keepax 	ret = wm_adsp_buffer_populate(buf);
1468a792af69SCharles Keepax 	if (ret < 0)
1469a792af69SCharles Keepax 		return ret;
1470a792af69SCharles Keepax 
14714f2d4eabSStuart Henderson 	/*
14724f2d4eabSStuart Henderson 	 * v0 host_buffer coefficients didn't have versioning, so if the
14734f2d4eabSStuart Henderson 	 * control is one word, assume version 0.
14744f2d4eabSStuart Henderson 	 */
14750700bc2fSSimon Trimmer 	if (cs_ctl->len == 4) {
14760d3fba3eSCharles Keepax 		compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr);
1477a792af69SCharles Keepax 		return 0;
14782cd19bdbSCharles Keepax 	}
14792cd19bdbSCharles Keepax 
1480a0b653e8SRichard Fitzgerald 	version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK;
1481a0b653e8SRichard Fitzgerald 	version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;
14824f2d4eabSStuart Henderson 
1483a0b653e8SRichard Fitzgerald 	if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {
14840700bc2fSSimon Trimmer 		adsp_err(dsp,
14854f2d4eabSStuart Henderson 			 "Host buffer coeff ver %u > supported version %u\n",
1486a0b653e8SRichard Fitzgerald 			 version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);
14874f2d4eabSStuart Henderson 		return -EINVAL;
14884f2d4eabSStuart Henderson 	}
14894f2d4eabSStuart Henderson 
14905beb8eeaSSimon Trimmer 	cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name));
14914f2d4eabSStuart Henderson 
14920700bc2fSSimon Trimmer 	buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part,
14934f2d4eabSStuart Henderson 			      (char *)&coeff_v1.name);
14944f2d4eabSStuart Henderson 
14950d3fba3eSCharles Keepax 	compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n",
1496a0b653e8SRichard Fitzgerald 		  buf->host_buf_ptr, version);
14974f2d4eabSStuart Henderson 
1498a0b653e8SRichard Fitzgerald 	return version;
14994f2d4eabSStuart Henderson }
15004f2d4eabSStuart Henderson 
1501a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp)
1502a792af69SCharles Keepax {
15030700bc2fSSimon Trimmer 	struct cs_dsp_coeff_ctl *cs_ctl;
1504a792af69SCharles Keepax 	int ret;
1505a792af69SCharles Keepax 
1506e1468202SSimon Trimmer 	list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) {
15070700bc2fSSimon Trimmer 		if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
1508a792af69SCharles Keepax 			continue;
1509a792af69SCharles Keepax 
15100700bc2fSSimon Trimmer 		if (!cs_ctl->enabled)
1511a792af69SCharles Keepax 			continue;
1512a792af69SCharles Keepax 
15130700bc2fSSimon Trimmer 		ret = wm_adsp_buffer_parse_coeff(cs_ctl);
1514a792af69SCharles Keepax 		if (ret < 0) {
1515a792af69SCharles Keepax 			adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
1516a792af69SCharles Keepax 			goto error;
15174f2d4eabSStuart Henderson 		} else if (ret == 0) {
15184f2d4eabSStuart Henderson 			/* Only one buffer supported for version 0 */
1519a792af69SCharles Keepax 			return 0;
1520a792af69SCharles Keepax 		}
15214f2d4eabSStuart Henderson 	}
1522a792af69SCharles Keepax 
15234f2d4eabSStuart Henderson 	if (list_empty(&dsp->buffer_list)) {
1524a792af69SCharles Keepax 		/* Fall back to legacy support */
1525a792af69SCharles Keepax 		ret = wm_adsp_buffer_parse_legacy(dsp);
1526a792af69SCharles Keepax 		if (ret) {
1527a792af69SCharles Keepax 			adsp_err(dsp, "Failed to parse legacy: %d\n", ret);
1528a792af69SCharles Keepax 			goto error;
1529a792af69SCharles Keepax 		}
1530a792af69SCharles Keepax 	}
15312cd19bdbSCharles Keepax 
15322cd19bdbSCharles Keepax 	return 0;
15332cd19bdbSCharles Keepax 
1534a792af69SCharles Keepax error:
1535a792af69SCharles Keepax 	wm_adsp_buffer_free(dsp);
15362cd19bdbSCharles Keepax 	return ret;
15372cd19bdbSCharles Keepax }
15382cd19bdbSCharles Keepax 
15392cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp)
15402cd19bdbSCharles Keepax {
15414f2d4eabSStuart Henderson 	struct wm_adsp_compr_buf *buf, *tmp;
1542721be3beSCharles Keepax 
15434f2d4eabSStuart Henderson 	list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {
15444f2d4eabSStuart Henderson 		wm_adsp_compr_detach(buf->compr);
15452cd19bdbSCharles Keepax 
15464f2d4eabSStuart Henderson 		kfree(buf->name);
15474f2d4eabSStuart Henderson 		kfree(buf->regions);
15484f2d4eabSStuart Henderson 		list_del(&buf->list);
15494f2d4eabSStuart Henderson 		kfree(buf);
15502cd19bdbSCharles Keepax 	}
15512cd19bdbSCharles Keepax 
15522cd19bdbSCharles Keepax 	return 0;
15532cd19bdbSCharles Keepax }
15542cd19bdbSCharles Keepax 
1555f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
1556f938f348SStuart Henderson {
1557f938f348SStuart Henderson 	int ret;
1558f938f348SStuart Henderson 
1559f938f348SStuart Henderson 	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
1560f938f348SStuart Henderson 	if (ret < 0) {
156148ead31cSCharles Keepax 		compr_err(buf, "Failed to check buffer error: %d\n", ret);
1562f938f348SStuart Henderson 		return ret;
1563f938f348SStuart Henderson 	}
1564f938f348SStuart Henderson 	if (buf->error != 0) {
156548ead31cSCharles Keepax 		compr_err(buf, "Buffer error occurred: %d\n", buf->error);
1566f938f348SStuart Henderson 		return -EIO;
1567f938f348SStuart Henderson 	}
1568f938f348SStuart Henderson 
1569f938f348SStuart Henderson 	return 0;
1570f938f348SStuart Henderson }
1571f938f348SStuart Henderson 
15723a5ccf25SKuninori Morimoto int wm_adsp_compr_trigger(struct snd_soc_component *component,
15733a5ccf25SKuninori Morimoto 			  struct snd_compr_stream *stream, int cmd)
157495fe9597SCharles Keepax {
157595fe9597SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
157695fe9597SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
157795fe9597SCharles Keepax 	int ret = 0;
157895fe9597SCharles Keepax 
15790d3fba3eSCharles Keepax 	compr_dbg(compr, "Trigger: %d\n", cmd);
158095fe9597SCharles Keepax 
1581e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
158295fe9597SCharles Keepax 
158395fe9597SCharles Keepax 	switch (cmd) {
158495fe9597SCharles Keepax 	case SNDRV_PCM_TRIGGER_START:
158561fc060cSCharles Keepax 		if (!wm_adsp_compr_attached(compr)) {
158695fe9597SCharles Keepax 			ret = wm_adsp_compr_attach(compr);
158795fe9597SCharles Keepax 			if (ret < 0) {
15880d3fba3eSCharles Keepax 				compr_err(compr, "Failed to link buffer and stream: %d\n",
158995fe9597SCharles Keepax 					  ret);
159095fe9597SCharles Keepax 				break;
159195fe9597SCharles Keepax 			}
159261fc060cSCharles Keepax 		}
159361fc060cSCharles Keepax 
1594f938f348SStuart Henderson 		ret = wm_adsp_buffer_get_error(compr->buf);
1595f938f348SStuart Henderson 		if (ret < 0)
1596f938f348SStuart Henderson 			break;
1597f938f348SStuart Henderson 
1598565ace46SCharles Keepax 		/* Trigger the IRQ at one fragment of data */
1599565ace46SCharles Keepax 		ret = wm_adsp_buffer_write(compr->buf,
1600565ace46SCharles Keepax 					   HOST_BUFFER_FIELD(high_water_mark),
1601565ace46SCharles Keepax 					   wm_adsp_compr_frag_words(compr));
1602565ace46SCharles Keepax 		if (ret < 0) {
16030d3fba3eSCharles Keepax 			compr_err(compr, "Failed to set high water mark: %d\n",
1604565ace46SCharles Keepax 				  ret);
1605565ace46SCharles Keepax 			break;
1606565ace46SCharles Keepax 		}
160795fe9597SCharles Keepax 		break;
160895fe9597SCharles Keepax 	case SNDRV_PCM_TRIGGER_STOP:
160943d147beSCharles Keepax 		if (wm_adsp_compr_attached(compr))
1610639e5eb3SCharles Keepax 			wm_adsp_buffer_clear(compr->buf);
161195fe9597SCharles Keepax 		break;
161295fe9597SCharles Keepax 	default:
161395fe9597SCharles Keepax 		ret = -EINVAL;
161495fe9597SCharles Keepax 		break;
161595fe9597SCharles Keepax 	}
161695fe9597SCharles Keepax 
1617e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
161895fe9597SCharles Keepax 
161995fe9597SCharles Keepax 	return ret;
162095fe9597SCharles Keepax }
162195fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
162295fe9597SCharles Keepax 
1623565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
1624565ace46SCharles Keepax {
1625565ace46SCharles Keepax 	int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
1626565ace46SCharles Keepax 
1627565ace46SCharles Keepax 	return buf->regions[last_region].cumulative_size;
1628565ace46SCharles Keepax }
1629565ace46SCharles Keepax 
1630565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
1631565ace46SCharles Keepax {
1632565ace46SCharles Keepax 	u32 next_read_index, next_write_index;
1633565ace46SCharles Keepax 	int write_index, read_index, avail;
1634565ace46SCharles Keepax 	int ret;
1635565ace46SCharles Keepax 
1636565ace46SCharles Keepax 	/* Only sync read index if we haven't already read a valid index */
1637565ace46SCharles Keepax 	if (buf->read_index < 0) {
1638565ace46SCharles Keepax 		ret = wm_adsp_buffer_read(buf,
1639565ace46SCharles Keepax 				HOST_BUFFER_FIELD(next_read_index),
1640565ace46SCharles Keepax 				&next_read_index);
1641565ace46SCharles Keepax 		if (ret < 0)
1642565ace46SCharles Keepax 			return ret;
1643565ace46SCharles Keepax 
1644565ace46SCharles Keepax 		read_index = sign_extend32(next_read_index, 23);
1645565ace46SCharles Keepax 
1646565ace46SCharles Keepax 		if (read_index < 0) {
16470d3fba3eSCharles Keepax 			compr_dbg(buf, "Avail check on unstarted stream\n");
1648565ace46SCharles Keepax 			return 0;
1649565ace46SCharles Keepax 		}
1650565ace46SCharles Keepax 
1651565ace46SCharles Keepax 		buf->read_index = read_index;
1652565ace46SCharles Keepax 	}
1653565ace46SCharles Keepax 
1654565ace46SCharles Keepax 	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
1655565ace46SCharles Keepax 			&next_write_index);
1656565ace46SCharles Keepax 	if (ret < 0)
1657565ace46SCharles Keepax 		return ret;
1658565ace46SCharles Keepax 
1659565ace46SCharles Keepax 	write_index = sign_extend32(next_write_index, 23);
1660565ace46SCharles Keepax 
1661565ace46SCharles Keepax 	avail = write_index - buf->read_index;
1662565ace46SCharles Keepax 	if (avail < 0)
1663565ace46SCharles Keepax 		avail += wm_adsp_buffer_size(buf);
1664565ace46SCharles Keepax 
16650d3fba3eSCharles Keepax 	compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
16665beb8eeaSSimon Trimmer 		  buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE);
1667565ace46SCharles Keepax 
1668565ace46SCharles Keepax 	buf->avail = avail;
1669565ace46SCharles Keepax 
1670565ace46SCharles Keepax 	return 0;
1671565ace46SCharles Keepax }
1672565ace46SCharles Keepax 
1673565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
1674565ace46SCharles Keepax {
1675612047f0SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1676612047f0SCharles Keepax 	struct wm_adsp_compr *compr;
1677565ace46SCharles Keepax 	int ret = 0;
1678565ace46SCharles Keepax 
1679e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1680565ace46SCharles Keepax 
16814f2d4eabSStuart Henderson 	if (list_empty(&dsp->buffer_list)) {
1682565ace46SCharles Keepax 		ret = -ENODEV;
1683565ace46SCharles Keepax 		goto out;
1684565ace46SCharles Keepax 	}
16850d3fba3eSCharles Keepax 
1686565ace46SCharles Keepax 	adsp_dbg(dsp, "Handling buffer IRQ\n");
1687565ace46SCharles Keepax 
16884f2d4eabSStuart Henderson 	list_for_each_entry(buf, &dsp->buffer_list, list) {
16894f2d4eabSStuart Henderson 		compr = buf->compr;
16904f2d4eabSStuart Henderson 
16919771b18aSCharles Keepax 		ret = wm_adsp_buffer_get_error(buf);
16929771b18aSCharles Keepax 		if (ret < 0)
16935847609eSCharles Keepax 			goto out_notify; /* Wake poll to report error */
1694565ace46SCharles Keepax 
1695565ace46SCharles Keepax 		ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
1696565ace46SCharles Keepax 					  &buf->irq_count);
1697565ace46SCharles Keepax 		if (ret < 0) {
16980d3fba3eSCharles Keepax 			compr_err(buf, "Failed to get irq_count: %d\n", ret);
1699565ace46SCharles Keepax 			goto out;
1700565ace46SCharles Keepax 		}
1701565ace46SCharles Keepax 
1702565ace46SCharles Keepax 		ret = wm_adsp_buffer_update_avail(buf);
1703565ace46SCharles Keepax 		if (ret < 0) {
17040d3fba3eSCharles Keepax 			compr_err(buf, "Error reading avail: %d\n", ret);
1705565ace46SCharles Keepax 			goto out;
1706565ace46SCharles Keepax 		}
1707565ace46SCharles Keepax 
170820b7f7c5SCharles Keepax 		if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
170920b7f7c5SCharles Keepax 			ret = WM_ADSP_COMPR_VOICE_TRIGGER;
171020b7f7c5SCharles Keepax 
17115847609eSCharles Keepax out_notify:
1712c7dae7c4SCharles Keepax 		if (compr && compr->stream)
171383a40ce9SCharles Keepax 			snd_compr_fragment_elapsed(compr->stream);
17144f2d4eabSStuart Henderson 	}
171583a40ce9SCharles Keepax 
1716565ace46SCharles Keepax out:
1717e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1718565ace46SCharles Keepax 
1719565ace46SCharles Keepax 	return ret;
1720565ace46SCharles Keepax }
1721565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
1722565ace46SCharles Keepax 
1723565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
1724565ace46SCharles Keepax {
1725565ace46SCharles Keepax 	if (buf->irq_count & 0x01)
1726565ace46SCharles Keepax 		return 0;
1727565ace46SCharles Keepax 
17280d3fba3eSCharles Keepax 	compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count);
1729565ace46SCharles Keepax 
1730565ace46SCharles Keepax 	buf->irq_count |= 0x01;
1731565ace46SCharles Keepax 
1732565ace46SCharles Keepax 	return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
1733565ace46SCharles Keepax 				    buf->irq_count);
1734565ace46SCharles Keepax }
1735565ace46SCharles Keepax 
17363a5ccf25SKuninori Morimoto int wm_adsp_compr_pointer(struct snd_soc_component *component,
17373a5ccf25SKuninori Morimoto 			  struct snd_compr_stream *stream,
1738565ace46SCharles Keepax 			  struct snd_compr_tstamp *tstamp)
1739565ace46SCharles Keepax {
1740565ace46SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
1741565ace46SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
1742612047f0SCharles Keepax 	struct wm_adsp_compr_buf *buf;
1743565ace46SCharles Keepax 	int ret = 0;
1744565ace46SCharles Keepax 
17450d3fba3eSCharles Keepax 	compr_dbg(compr, "Pointer request\n");
1746565ace46SCharles Keepax 
1747e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
1748565ace46SCharles Keepax 
1749612047f0SCharles Keepax 	buf = compr->buf;
1750612047f0SCharles Keepax 
1751aa612f2bSCharles Keepax 	if (dsp->fatal_error || !buf || buf->error) {
17528d280664SCharles Keepax 		snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
1753565ace46SCharles Keepax 		ret = -EIO;
1754565ace46SCharles Keepax 		goto out;
1755565ace46SCharles Keepax 	}
1756565ace46SCharles Keepax 
1757565ace46SCharles Keepax 	if (buf->avail < wm_adsp_compr_frag_words(compr)) {
1758565ace46SCharles Keepax 		ret = wm_adsp_buffer_update_avail(buf);
1759565ace46SCharles Keepax 		if (ret < 0) {
17600d3fba3eSCharles Keepax 			compr_err(compr, "Error reading avail: %d\n", ret);
1761565ace46SCharles Keepax 			goto out;
1762565ace46SCharles Keepax 		}
1763565ace46SCharles Keepax 
1764565ace46SCharles Keepax 		/*
1765565ace46SCharles Keepax 		 * If we really have less than 1 fragment available tell the
1766565ace46SCharles Keepax 		 * DSP to inform us once a whole fragment is available.
1767565ace46SCharles Keepax 		 */
1768565ace46SCharles Keepax 		if (buf->avail < wm_adsp_compr_frag_words(compr)) {
17695847609eSCharles Keepax 			ret = wm_adsp_buffer_get_error(buf);
17708d280664SCharles Keepax 			if (ret < 0) {
1771789b930aSCharles Keepax 				if (buf->error)
17728d280664SCharles Keepax 					snd_compr_stop_error(stream,
17738d280664SCharles Keepax 							SNDRV_PCM_STATE_XRUN);
17745847609eSCharles Keepax 				goto out;
17758d280664SCharles Keepax 			}
17765847609eSCharles Keepax 
1777565ace46SCharles Keepax 			ret = wm_adsp_buffer_reenable_irq(buf);
1778565ace46SCharles Keepax 			if (ret < 0) {
17790d3fba3eSCharles Keepax 				compr_err(compr, "Failed to re-enable buffer IRQ: %d\n",
1780565ace46SCharles Keepax 					  ret);
1781565ace46SCharles Keepax 				goto out;
1782565ace46SCharles Keepax 			}
1783565ace46SCharles Keepax 		}
1784565ace46SCharles Keepax 	}
1785565ace46SCharles Keepax 
1786565ace46SCharles Keepax 	tstamp->copied_total = compr->copied_total;
17875beb8eeaSSimon Trimmer 	tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE;
1788da2b3358SCharles Keepax 	tstamp->sampling_rate = compr->sample_rate;
1789565ace46SCharles Keepax 
1790565ace46SCharles Keepax out:
1791e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
1792565ace46SCharles Keepax 
1793565ace46SCharles Keepax 	return ret;
1794565ace46SCharles Keepax }
1795565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
1796565ace46SCharles Keepax 
179783a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
179883a40ce9SCharles Keepax {
179983a40ce9SCharles Keepax 	struct wm_adsp_compr_buf *buf = compr->buf;
180083a40ce9SCharles Keepax 	unsigned int adsp_addr;
180183a40ce9SCharles Keepax 	int mem_type, nwords, max_read;
1802cc7d6ce9SCharles Keepax 	int i, ret;
180383a40ce9SCharles Keepax 
180483a40ce9SCharles Keepax 	/* Calculate read parameters */
180583a40ce9SCharles Keepax 	for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
180683a40ce9SCharles Keepax 		if (buf->read_index < buf->regions[i].cumulative_size)
180783a40ce9SCharles Keepax 			break;
180883a40ce9SCharles Keepax 
180983a40ce9SCharles Keepax 	if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
181083a40ce9SCharles Keepax 		return -EINVAL;
181183a40ce9SCharles Keepax 
181283a40ce9SCharles Keepax 	mem_type = buf->regions[i].mem_type;
181383a40ce9SCharles Keepax 	adsp_addr = buf->regions[i].base_addr +
181483a40ce9SCharles Keepax 		    (buf->read_index - buf->regions[i].offset);
181583a40ce9SCharles Keepax 
181683a40ce9SCharles Keepax 	max_read = wm_adsp_compr_frag_words(compr);
181783a40ce9SCharles Keepax 	nwords = buf->regions[i].cumulative_size - buf->read_index;
181883a40ce9SCharles Keepax 
181983a40ce9SCharles Keepax 	if (nwords > target)
182083a40ce9SCharles Keepax 		nwords = target;
182183a40ce9SCharles Keepax 	if (nwords > buf->avail)
182283a40ce9SCharles Keepax 		nwords = buf->avail;
182383a40ce9SCharles Keepax 	if (nwords > max_read)
182483a40ce9SCharles Keepax 		nwords = max_read;
182583a40ce9SCharles Keepax 	if (!nwords)
182683a40ce9SCharles Keepax 		return 0;
182783a40ce9SCharles Keepax 
182883a40ce9SCharles Keepax 	/* Read data from DSP */
1829e1468202SSimon Trimmer 	ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr,
1830a0b653e8SRichard Fitzgerald 					 nwords, (__be32 *)compr->raw_buf);
183183a40ce9SCharles Keepax 	if (ret < 0)
183283a40ce9SCharles Keepax 		return ret;
183383a40ce9SCharles Keepax 
18345beb8eeaSSimon Trimmer 	cs_dsp_remove_padding(compr->raw_buf, nwords);
183583a40ce9SCharles Keepax 
183683a40ce9SCharles Keepax 	/* update read index to account for words read */
183783a40ce9SCharles Keepax 	buf->read_index += nwords;
183883a40ce9SCharles Keepax 	if (buf->read_index == wm_adsp_buffer_size(buf))
183983a40ce9SCharles Keepax 		buf->read_index = 0;
184083a40ce9SCharles Keepax 
184183a40ce9SCharles Keepax 	ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
184283a40ce9SCharles Keepax 				   buf->read_index);
184383a40ce9SCharles Keepax 	if (ret < 0)
184483a40ce9SCharles Keepax 		return ret;
184583a40ce9SCharles Keepax 
184683a40ce9SCharles Keepax 	/* update avail to account for words read */
184783a40ce9SCharles Keepax 	buf->avail -= nwords;
184883a40ce9SCharles Keepax 
184983a40ce9SCharles Keepax 	return nwords;
185083a40ce9SCharles Keepax }
185183a40ce9SCharles Keepax 
185283a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
185383a40ce9SCharles Keepax 			      char __user *buf, size_t count)
185483a40ce9SCharles Keepax {
1855aa612f2bSCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
185683a40ce9SCharles Keepax 	int ntotal = 0;
185783a40ce9SCharles Keepax 	int nwords, nbytes;
185883a40ce9SCharles Keepax 
18590d3fba3eSCharles Keepax 	compr_dbg(compr, "Requested read of %zu bytes\n", count);
186083a40ce9SCharles Keepax 
1861aa612f2bSCharles Keepax 	if (dsp->fatal_error || !compr->buf || compr->buf->error) {
18628d280664SCharles Keepax 		snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
186383a40ce9SCharles Keepax 		return -EIO;
18648d280664SCharles Keepax 	}
186583a40ce9SCharles Keepax 
18665beb8eeaSSimon Trimmer 	count /= CS_DSP_DATA_WORD_SIZE;
186783a40ce9SCharles Keepax 
186883a40ce9SCharles Keepax 	do {
186983a40ce9SCharles Keepax 		nwords = wm_adsp_buffer_capture_block(compr, count);
187083a40ce9SCharles Keepax 		if (nwords < 0) {
18710d3fba3eSCharles Keepax 			compr_err(compr, "Failed to capture block: %d\n",
18720d3fba3eSCharles Keepax 				  nwords);
187383a40ce9SCharles Keepax 			return nwords;
187483a40ce9SCharles Keepax 		}
187583a40ce9SCharles Keepax 
18765beb8eeaSSimon Trimmer 		nbytes = nwords * CS_DSP_DATA_WORD_SIZE;
187783a40ce9SCharles Keepax 
18780d3fba3eSCharles Keepax 		compr_dbg(compr, "Read %d bytes\n", nbytes);
187983a40ce9SCharles Keepax 
188083a40ce9SCharles Keepax 		if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
18810d3fba3eSCharles Keepax 			compr_err(compr, "Failed to copy data to user: %d, %d\n",
188283a40ce9SCharles Keepax 				  ntotal, nbytes);
188383a40ce9SCharles Keepax 			return -EFAULT;
188483a40ce9SCharles Keepax 		}
188583a40ce9SCharles Keepax 
188683a40ce9SCharles Keepax 		count -= nwords;
188783a40ce9SCharles Keepax 		ntotal += nbytes;
188883a40ce9SCharles Keepax 	} while (nwords > 0 && count > 0);
188983a40ce9SCharles Keepax 
189083a40ce9SCharles Keepax 	compr->copied_total += ntotal;
189183a40ce9SCharles Keepax 
189283a40ce9SCharles Keepax 	return ntotal;
189383a40ce9SCharles Keepax }
189483a40ce9SCharles Keepax 
18953a5ccf25SKuninori Morimoto int wm_adsp_compr_copy(struct snd_soc_component *component,
18963a5ccf25SKuninori Morimoto 		       struct snd_compr_stream *stream, char __user *buf,
189783a40ce9SCharles Keepax 		       size_t count)
189883a40ce9SCharles Keepax {
189983a40ce9SCharles Keepax 	struct wm_adsp_compr *compr = stream->runtime->private_data;
190083a40ce9SCharles Keepax 	struct wm_adsp *dsp = compr->dsp;
190183a40ce9SCharles Keepax 	int ret;
190283a40ce9SCharles Keepax 
1903e1468202SSimon Trimmer 	mutex_lock(&dsp->cs_dsp.pwr_lock);
190483a40ce9SCharles Keepax 
190583a40ce9SCharles Keepax 	if (stream->direction == SND_COMPRESS_CAPTURE)
190683a40ce9SCharles Keepax 		ret = wm_adsp_compr_read(compr, buf, count);
190783a40ce9SCharles Keepax 	else
190883a40ce9SCharles Keepax 		ret = -ENOTSUPP;
190983a40ce9SCharles Keepax 
1910e1468202SSimon Trimmer 	mutex_unlock(&dsp->cs_dsp.pwr_lock);
191183a40ce9SCharles Keepax 
191283a40ce9SCharles Keepax 	return ret;
191383a40ce9SCharles Keepax }
191483a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
191583a40ce9SCharles Keepax 
1916e1468202SSimon Trimmer static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp)
1917a2bcbc1bSCharles Keepax {
1918e1468202SSimon Trimmer 	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);
1919a2bcbc1bSCharles Keepax 	struct wm_adsp_compr *compr;
1920a2bcbc1bSCharles Keepax 
1921a2bcbc1bSCharles Keepax 	dsp->fatal_error = true;
1922a2bcbc1bSCharles Keepax 
1923a2bcbc1bSCharles Keepax 	list_for_each_entry(compr, &dsp->compr_list, list) {
1924aa612f2bSCharles Keepax 		if (compr->stream)
1925a2bcbc1bSCharles Keepax 			snd_compr_fragment_elapsed(compr->stream);
1926a2bcbc1bSCharles Keepax 	}
1927a2bcbc1bSCharles Keepax }
1928a2bcbc1bSCharles Keepax 
192925ca837bSSimon Trimmer irqreturn_t wm_adsp2_bus_error(int irq, void *data)
193025ca837bSSimon Trimmer {
193125ca837bSSimon Trimmer 	struct wm_adsp *dsp = (struct wm_adsp *)data;
193225ca837bSSimon Trimmer 
1933e1468202SSimon Trimmer 	cs_dsp_adsp2_bus_error(&dsp->cs_dsp);
1934a2225a6dSCharles Keepax 
193551a2c944SMayuresh Kulkarni 	return IRQ_HANDLED;
193651a2c944SMayuresh Kulkarni }
193751a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
193851a2c944SMayuresh Kulkarni 
193925ca837bSSimon Trimmer irqreturn_t wm_halo_bus_error(int irq, void *data)
194025ca837bSSimon Trimmer {
194125ca837bSSimon Trimmer 	struct wm_adsp *dsp = (struct wm_adsp *)data;
194225ca837bSSimon Trimmer 
1943e1468202SSimon Trimmer 	cs_dsp_halo_bus_error(&dsp->cs_dsp);
19442ae58138SRichard Fitzgerald 
19452ae58138SRichard Fitzgerald 	return IRQ_HANDLED;
19462ae58138SRichard Fitzgerald }
19472ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error);
19482ae58138SRichard Fitzgerald 
194925ca837bSSimon Trimmer irqreturn_t wm_halo_wdt_expire(int irq, void *data)
195025ca837bSSimon Trimmer {
195125ca837bSSimon Trimmer 	struct wm_adsp *dsp = data;
195225ca837bSSimon Trimmer 
1953e1468202SSimon Trimmer 	cs_dsp_halo_wdt_expire(&dsp->cs_dsp);
19548bc144f9SStuart Henderson 
19558bc144f9SStuart Henderson 	return IRQ_HANDLED;
19568bc144f9SStuart Henderson }
19578bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);
19588bc144f9SStuart Henderson 
19592dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp1_client_ops = {
19602dd04464SSimon Trimmer 	.control_add = wm_adsp_control_add,
19612dd04464SSimon Trimmer 	.control_remove = wm_adsp_control_remove,
19622dd04464SSimon Trimmer };
19632dd04464SSimon Trimmer 
19642dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp2_client_ops = {
19652dd04464SSimon Trimmer 	.control_add = wm_adsp_control_add,
19662dd04464SSimon Trimmer 	.control_remove = wm_adsp_control_remove,
19672dd04464SSimon Trimmer 	.post_run = wm_adsp_event_post_run,
19682dd04464SSimon Trimmer 	.post_stop = wm_adsp_event_post_stop,
19692dd04464SSimon Trimmer 	.watchdog_expired = wm_adsp_fatal_error,
19702dd04464SSimon Trimmer };
19712dd04464SSimon Trimmer 
19720a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2");
1973