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