blob: ef33de65102842c568dbe4d7ebf688efca9a8cda [file] [log] [blame]
Mark Brown2159ad932012-10-11 11:54:02 +09001/*
2 * wm_adsp.c -- Wolfson ADSP support
3 *
4 * Copyright 2012 Wolfson Microelectronics plc
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/firmware.h>
Mark Browncf17c832013-01-30 14:37:23 +080018#include <linux/list.h>
Mark Brown2159ad932012-10-11 11:54:02 +090019#include <linux/pm.h>
20#include <linux/pm_runtime.h>
21#include <linux/regmap.h>
Mark Brown973838a2012-11-28 17:20:32 +000022#include <linux/regulator/consumer.h>
Mark Brown2159ad932012-10-11 11:54:02 +090023#include <linux/slab.h>
Charles Keepaxcdcd7f72014-11-14 15:40:45 +000024#include <linux/vmalloc.h>
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +010025#include <linux/workqueue.h>
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +010026#include <linux/debugfs.h>
Mark Brown2159ad932012-10-11 11:54:02 +090027#include <sound/core.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/soc.h>
31#include <sound/jack.h>
32#include <sound/initval.h>
33#include <sound/tlv.h>
34
Mark Brown2159ad932012-10-11 11:54:02 +090035#include "wm_adsp.h"
36
37#define adsp_crit(_dsp, fmt, ...) \
38 dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
39#define adsp_err(_dsp, fmt, ...) \
40 dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
41#define adsp_warn(_dsp, fmt, ...) \
42 dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
43#define adsp_info(_dsp, fmt, ...) \
44 dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
45#define adsp_dbg(_dsp, fmt, ...) \
46 dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
47
48#define ADSP1_CONTROL_1 0x00
49#define ADSP1_CONTROL_2 0x02
50#define ADSP1_CONTROL_3 0x03
51#define ADSP1_CONTROL_4 0x04
52#define ADSP1_CONTROL_5 0x06
53#define ADSP1_CONTROL_6 0x07
54#define ADSP1_CONTROL_7 0x08
55#define ADSP1_CONTROL_8 0x09
56#define ADSP1_CONTROL_9 0x0A
57#define ADSP1_CONTROL_10 0x0B
58#define ADSP1_CONTROL_11 0x0C
59#define ADSP1_CONTROL_12 0x0D
60#define ADSP1_CONTROL_13 0x0F
61#define ADSP1_CONTROL_14 0x10
62#define ADSP1_CONTROL_15 0x11
63#define ADSP1_CONTROL_16 0x12
64#define ADSP1_CONTROL_17 0x13
65#define ADSP1_CONTROL_18 0x14
66#define ADSP1_CONTROL_19 0x16
67#define ADSP1_CONTROL_20 0x17
68#define ADSP1_CONTROL_21 0x18
69#define ADSP1_CONTROL_22 0x1A
70#define ADSP1_CONTROL_23 0x1B
71#define ADSP1_CONTROL_24 0x1C
72#define ADSP1_CONTROL_25 0x1E
73#define ADSP1_CONTROL_26 0x20
74#define ADSP1_CONTROL_27 0x21
75#define ADSP1_CONTROL_28 0x22
76#define ADSP1_CONTROL_29 0x23
77#define ADSP1_CONTROL_30 0x24
78#define ADSP1_CONTROL_31 0x26
79
80/*
81 * ADSP1 Control 19
82 */
83#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
84#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
85#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
86
87
88/*
89 * ADSP1 Control 30
90 */
91#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
92#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
93#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
94#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
95#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
96#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
97#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
98#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
99#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
100#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
101#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
102#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
103#define ADSP1_START 0x0001 /* DSP1_START */
104#define ADSP1_START_MASK 0x0001 /* DSP1_START */
105#define ADSP1_START_SHIFT 0 /* DSP1_START */
106#define ADSP1_START_WIDTH 1 /* DSP1_START */
107
Chris Rattray94e205b2013-01-18 08:43:09 +0000108/*
109 * ADSP1 Control 31
110 */
111#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
112#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
113#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
114
Mark Brown2d30b572013-01-28 20:18:17 +0800115#define ADSP2_CONTROL 0x0
116#define ADSP2_CLOCKING 0x1
117#define ADSP2_STATUS1 0x4
118#define ADSP2_WDMA_CONFIG_1 0x30
119#define ADSP2_WDMA_CONFIG_2 0x31
120#define ADSP2_RDMA_CONFIG_1 0x34
Mark Brown2159ad932012-10-11 11:54:02 +0900121
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100122#define ADSP2_SCRATCH0 0x40
123#define ADSP2_SCRATCH1 0x41
124#define ADSP2_SCRATCH2 0x42
125#define ADSP2_SCRATCH3 0x43
126
Mark Brown2159ad932012-10-11 11:54:02 +0900127/*
128 * ADSP2 Control
129 */
130
131#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
132#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
133#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
134#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
135#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
136#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
137#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
138#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
139#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
140#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
141#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
142#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
143#define ADSP2_START 0x0001 /* DSP1_START */
144#define ADSP2_START_MASK 0x0001 /* DSP1_START */
145#define ADSP2_START_SHIFT 0 /* DSP1_START */
146#define ADSP2_START_WIDTH 1 /* DSP1_START */
147
148/*
Mark Brown973838a2012-11-28 17:20:32 +0000149 * ADSP2 clocking
150 */
151#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
152#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
153#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
154
155/*
Mark Brown2159ad932012-10-11 11:54:02 +0900156 * ADSP2 Status 1
157 */
158#define ADSP2_RAM_RDY 0x0001
159#define ADSP2_RAM_RDY_MASK 0x0001
160#define ADSP2_RAM_RDY_SHIFT 0
161#define ADSP2_RAM_RDY_WIDTH 1
162
Charles Keepax9ee78752016-05-02 13:57:36 +0100163#define ADSP_MAX_STD_CTRL_SIZE 512
164
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000165#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
166#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
167
168/*
169 * Event control messages
170 */
171#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
172
Mark Browncf17c832013-01-30 14:37:23 +0800173struct wm_adsp_buf {
174 struct list_head list;
175 void *buf;
176};
177
178static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
179 struct list_head *list)
180{
181 struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
182
183 if (buf == NULL)
184 return NULL;
185
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000186 buf->buf = vmalloc(len);
Mark Browncf17c832013-01-30 14:37:23 +0800187 if (!buf->buf) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000188 vfree(buf);
Mark Browncf17c832013-01-30 14:37:23 +0800189 return NULL;
190 }
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000191 memcpy(buf->buf, src, len);
Mark Browncf17c832013-01-30 14:37:23 +0800192
193 if (list)
194 list_add_tail(&buf->list, list);
195
196 return buf;
197}
198
199static void wm_adsp_buf_free(struct list_head *list)
200{
201 while (!list_empty(list)) {
202 struct wm_adsp_buf *buf = list_first_entry(list,
203 struct wm_adsp_buf,
204 list);
205 list_del(&buf->list);
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000206 vfree(buf->buf);
Mark Browncf17c832013-01-30 14:37:23 +0800207 kfree(buf);
208 }
209}
210
Charles Keepax04d13002015-11-26 14:01:52 +0000211#define WM_ADSP_FW_MBC_VSS 0
212#define WM_ADSP_FW_HIFI 1
213#define WM_ADSP_FW_TX 2
214#define WM_ADSP_FW_TX_SPK 3
215#define WM_ADSP_FW_RX 4
216#define WM_ADSP_FW_RX_ANC 5
217#define WM_ADSP_FW_CTRL 6
218#define WM_ADSP_FW_ASR 7
219#define WM_ADSP_FW_TRACE 8
220#define WM_ADSP_FW_SPK_PROT 9
221#define WM_ADSP_FW_MISC 10
Mark Brown1023dbd2013-01-11 22:58:28 +0000222
Charles Keepax04d13002015-11-26 14:01:52 +0000223#define WM_ADSP_NUM_FW 11
Mark Browndd84f922013-03-08 15:25:58 +0800224
Mark Brown1023dbd2013-01-11 22:58:28 +0000225static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000226 [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
227 [WM_ADSP_FW_HIFI] = "MasterHiFi",
228 [WM_ADSP_FW_TX] = "Tx",
229 [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
230 [WM_ADSP_FW_RX] = "Rx",
231 [WM_ADSP_FW_RX_ANC] = "Rx ANC",
232 [WM_ADSP_FW_CTRL] = "Voice Ctrl",
233 [WM_ADSP_FW_ASR] = "ASR Assist",
234 [WM_ADSP_FW_TRACE] = "Dbg Trace",
235 [WM_ADSP_FW_SPK_PROT] = "Protection",
236 [WM_ADSP_FW_MISC] = "Misc",
Mark Brown1023dbd2013-01-11 22:58:28 +0000237};
238
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000239struct wm_adsp_system_config_xm_hdr {
240 __be32 sys_enable;
241 __be32 fw_id;
242 __be32 fw_rev;
243 __be32 boot_status;
244 __be32 watchdog;
245 __be32 dma_buffer_size;
246 __be32 rdma[6];
247 __be32 wdma[8];
248 __be32 build_job_name[3];
249 __be32 build_job_number;
250};
251
252struct wm_adsp_alg_xm_struct {
253 __be32 magic;
254 __be32 smoothing;
255 __be32 threshold;
256 __be32 host_buf_ptr;
257 __be32 start_seq;
258 __be32 high_water_mark;
259 __be32 low_water_mark;
260 __be64 smoothed_power;
261};
262
263struct wm_adsp_buffer {
264 __be32 X_buf_base; /* XM base addr of first X area */
265 __be32 X_buf_size; /* Size of 1st X area in words */
266 __be32 X_buf_base2; /* XM base addr of 2nd X area */
267 __be32 X_buf_brk; /* Total X size in words */
268 __be32 Y_buf_base; /* YM base addr of Y area */
269 __be32 wrap; /* Total size X and Y in words */
270 __be32 high_water_mark; /* Point at which IRQ is asserted */
271 __be32 irq_count; /* bits 1-31 count IRQ assertions */
272 __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */
273 __be32 next_write_index; /* word index of next write */
274 __be32 next_read_index; /* word index of next read */
275 __be32 error; /* error if any */
276 __be32 oldest_block_index; /* word index of oldest surviving */
277 __be32 requested_rewind; /* how many blocks rewind was done */
278 __be32 reserved_space; /* internal */
279 __be32 min_free; /* min free space since stream start */
280 __be32 blocks_written[2]; /* total blocks written (64 bit) */
281 __be32 words_written[2]; /* total words written (64 bit) */
282};
283
Charles Keepax721be3b2016-05-04 17:11:56 +0100284struct wm_adsp_compr;
285
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000286struct wm_adsp_compr_buf {
287 struct wm_adsp *dsp;
Charles Keepax721be3b2016-05-04 17:11:56 +0100288 struct wm_adsp_compr *compr;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000289
290 struct wm_adsp_buffer_region *regions;
291 u32 host_buf_ptr;
Charles Keepax565ace42016-01-06 12:33:18 +0000292
293 u32 error;
294 u32 irq_count;
295 int read_index;
296 int avail;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000297};
298
Charles Keepax406abc92015-12-15 11:29:45 +0000299struct wm_adsp_compr {
300 struct wm_adsp *dsp;
Charles Keepax95fe9592015-12-15 11:29:47 +0000301 struct wm_adsp_compr_buf *buf;
Charles Keepax406abc92015-12-15 11:29:45 +0000302
303 struct snd_compr_stream *stream;
304 struct snd_compressed_buffer size;
Charles Keepax565ace42016-01-06 12:33:18 +0000305
Charles Keepax83a40ce2016-01-06 12:33:19 +0000306 u32 *raw_buf;
Charles Keepax565ace42016-01-06 12:33:18 +0000307 unsigned int copied_total;
Charles Keepaxda2b3352016-02-02 16:41:36 +0000308
309 unsigned int sample_rate;
Charles Keepax406abc92015-12-15 11:29:45 +0000310};
311
312#define WM_ADSP_DATA_WORD_SIZE 3
313
314#define WM_ADSP_MIN_FRAGMENTS 1
315#define WM_ADSP_MAX_FRAGMENTS 256
316#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE)
317#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE)
318
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000319#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7
320
321#define HOST_BUFFER_FIELD(field) \
322 (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))
323
324#define ALG_XM_FIELD(field) \
325 (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))
326
327static int wm_adsp_buffer_init(struct wm_adsp *dsp);
328static int wm_adsp_buffer_free(struct wm_adsp *dsp);
329
330struct wm_adsp_buffer_region {
331 unsigned int offset;
332 unsigned int cumulative_size;
333 unsigned int mem_type;
334 unsigned int base_addr;
335};
336
337struct wm_adsp_buffer_region_def {
338 unsigned int mem_type;
339 unsigned int base_offset;
340 unsigned int size_offset;
341};
342
Charles Keepax3a9686c2016-02-01 15:22:34 +0000343static const struct wm_adsp_buffer_region_def default_regions[] = {
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000344 {
345 .mem_type = WMFW_ADSP2_XM,
346 .base_offset = HOST_BUFFER_FIELD(X_buf_base),
347 .size_offset = HOST_BUFFER_FIELD(X_buf_size),
348 },
349 {
350 .mem_type = WMFW_ADSP2_XM,
351 .base_offset = HOST_BUFFER_FIELD(X_buf_base2),
352 .size_offset = HOST_BUFFER_FIELD(X_buf_brk),
353 },
354 {
355 .mem_type = WMFW_ADSP2_YM,
356 .base_offset = HOST_BUFFER_FIELD(Y_buf_base),
357 .size_offset = HOST_BUFFER_FIELD(wrap),
358 },
359};
360
Charles Keepax406abc92015-12-15 11:29:45 +0000361struct wm_adsp_fw_caps {
362 u32 id;
363 struct snd_codec_desc desc;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000364 int num_regions;
Charles Keepax3a9686c2016-02-01 15:22:34 +0000365 const struct wm_adsp_buffer_region_def *region_defs;
Charles Keepax406abc92015-12-15 11:29:45 +0000366};
367
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000368static const struct wm_adsp_fw_caps ctrl_caps[] = {
Charles Keepax406abc92015-12-15 11:29:45 +0000369 {
370 .id = SND_AUDIOCODEC_BESPOKE,
371 .desc = {
372 .max_ch = 1,
373 .sample_rates = { 16000 },
374 .num_sample_rates = 1,
375 .formats = SNDRV_PCM_FMTBIT_S16_LE,
376 },
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000377 .num_regions = ARRAY_SIZE(default_regions),
378 .region_defs = default_regions,
Charles Keepax406abc92015-12-15 11:29:45 +0000379 },
380};
381
Charles Keepax7ce42832016-01-21 17:52:59 +0000382static const struct wm_adsp_fw_caps trace_caps[] = {
383 {
384 .id = SND_AUDIOCODEC_BESPOKE,
385 .desc = {
386 .max_ch = 8,
387 .sample_rates = {
388 4000, 8000, 11025, 12000, 16000, 22050,
389 24000, 32000, 44100, 48000, 64000, 88200,
390 96000, 176400, 192000
391 },
392 .num_sample_rates = 15,
393 .formats = SNDRV_PCM_FMTBIT_S16_LE,
394 },
395 .num_regions = ARRAY_SIZE(default_regions),
396 .region_defs = default_regions,
Charles Keepax406abc92015-12-15 11:29:45 +0000397 },
398};
399
400static const struct {
Mark Brown1023dbd2013-01-11 22:58:28 +0000401 const char *file;
Charles Keepax406abc92015-12-15 11:29:45 +0000402 int compr_direction;
403 int num_caps;
404 const struct wm_adsp_fw_caps *caps;
Charles Keepax20b7f7c2016-05-13 16:45:17 +0100405 bool voice_trigger;
Mark Brown1023dbd2013-01-11 22:58:28 +0000406} wm_adsp_fw[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000407 [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
408 [WM_ADSP_FW_HIFI] = { .file = "hifi" },
409 [WM_ADSP_FW_TX] = { .file = "tx" },
410 [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
411 [WM_ADSP_FW_RX] = { .file = "rx" },
412 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
Charles Keepax406abc92015-12-15 11:29:45 +0000413 [WM_ADSP_FW_CTRL] = {
414 .file = "ctrl",
415 .compr_direction = SND_COMPRESS_CAPTURE,
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000416 .num_caps = ARRAY_SIZE(ctrl_caps),
417 .caps = ctrl_caps,
Charles Keepax20b7f7c2016-05-13 16:45:17 +0100418 .voice_trigger = true,
Charles Keepax406abc92015-12-15 11:29:45 +0000419 },
Charles Keepax04d13002015-11-26 14:01:52 +0000420 [WM_ADSP_FW_ASR] = { .file = "asr" },
Charles Keepax7ce42832016-01-21 17:52:59 +0000421 [WM_ADSP_FW_TRACE] = {
422 .file = "trace",
423 .compr_direction = SND_COMPRESS_CAPTURE,
424 .num_caps = ARRAY_SIZE(trace_caps),
425 .caps = trace_caps,
426 },
Charles Keepax04d13002015-11-26 14:01:52 +0000427 [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
428 [WM_ADSP_FW_MISC] = { .file = "misc" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000429};
430
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100431struct wm_coeff_ctl_ops {
432 int (*xget)(struct snd_kcontrol *kcontrol,
433 struct snd_ctl_elem_value *ucontrol);
434 int (*xput)(struct snd_kcontrol *kcontrol,
435 struct snd_ctl_elem_value *ucontrol);
436 int (*xinfo)(struct snd_kcontrol *kcontrol,
437 struct snd_ctl_elem_info *uinfo);
438};
439
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100440struct wm_coeff_ctl {
441 const char *name;
Charles Keepax23237362015-04-13 13:28:02 +0100442 const char *fw_name;
Charles Keepax3809f002015-04-13 13:27:54 +0100443 struct wm_adsp_alg_region alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100444 struct wm_coeff_ctl_ops ops;
Charles Keepax3809f002015-04-13 13:27:54 +0100445 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100446 unsigned int enabled:1;
447 struct list_head list;
448 void *cache;
Charles Keepax23237362015-04-13 13:28:02 +0100449 unsigned int offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100450 size_t len;
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +0100451 unsigned int set:1;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100452 struct snd_kcontrol *kcontrol;
Charles Keepax9ee78752016-05-02 13:57:36 +0100453 struct soc_bytes_ext bytes_ext;
Charles Keepax26c22a12015-04-20 13:52:45 +0100454 unsigned int flags;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100455};
456
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +0000457static const char *wm_adsp_mem_region_name(unsigned int type)
458{
459 switch (type) {
460 case WMFW_ADSP1_PM:
461 return "PM";
462 case WMFW_ADSP1_DM:
463 return "DM";
464 case WMFW_ADSP2_XM:
465 return "XM";
466 case WMFW_ADSP2_YM:
467 return "YM";
468 case WMFW_ADSP1_ZM:
469 return "ZM";
470 default:
471 return NULL;
472 }
473}
474
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100475#ifdef CONFIG_DEBUG_FS
476static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
477{
478 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
479
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100480 kfree(dsp->wmfw_file_name);
481 dsp->wmfw_file_name = tmp;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100482}
483
484static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
485{
486 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
487
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100488 kfree(dsp->bin_file_name);
489 dsp->bin_file_name = tmp;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100490}
491
492static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
493{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100494 kfree(dsp->wmfw_file_name);
495 kfree(dsp->bin_file_name);
496 dsp->wmfw_file_name = NULL;
497 dsp->bin_file_name = NULL;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100498}
499
500static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
501 char __user *user_buf,
502 size_t count, loff_t *ppos)
503{
504 struct wm_adsp *dsp = file->private_data;
505 ssize_t ret;
506
Charles Keepax078e7182015-12-08 16:08:26 +0000507 mutex_lock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100508
Charles Keepax28823eb2016-09-20 13:52:32 +0100509 if (!dsp->wmfw_file_name || !dsp->booted)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100510 ret = 0;
511 else
512 ret = simple_read_from_buffer(user_buf, count, ppos,
513 dsp->wmfw_file_name,
514 strlen(dsp->wmfw_file_name));
515
Charles Keepax078e7182015-12-08 16:08:26 +0000516 mutex_unlock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100517 return ret;
518}
519
520static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
521 char __user *user_buf,
522 size_t count, loff_t *ppos)
523{
524 struct wm_adsp *dsp = file->private_data;
525 ssize_t ret;
526
Charles Keepax078e7182015-12-08 16:08:26 +0000527 mutex_lock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100528
Charles Keepax28823eb2016-09-20 13:52:32 +0100529 if (!dsp->bin_file_name || !dsp->booted)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100530 ret = 0;
531 else
532 ret = simple_read_from_buffer(user_buf, count, ppos,
533 dsp->bin_file_name,
534 strlen(dsp->bin_file_name));
535
Charles Keepax078e7182015-12-08 16:08:26 +0000536 mutex_unlock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100537 return ret;
538}
539
540static const struct {
541 const char *name;
542 const struct file_operations fops;
543} wm_adsp_debugfs_fops[] = {
544 {
545 .name = "wmfw_file_name",
546 .fops = {
547 .open = simple_open,
548 .read = wm_adsp_debugfs_wmfw_read,
549 },
550 },
551 {
552 .name = "bin_file_name",
553 .fops = {
554 .open = simple_open,
555 .read = wm_adsp_debugfs_bin_read,
556 },
557 },
558};
559
560static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
561 struct snd_soc_codec *codec)
562{
563 struct dentry *root = NULL;
564 char *root_name;
565 int i;
566
567 if (!codec->component.debugfs_root) {
568 adsp_err(dsp, "No codec debugfs root\n");
569 goto err;
570 }
571
572 root_name = kmalloc(PAGE_SIZE, GFP_KERNEL);
573 if (!root_name)
574 goto err;
575
576 snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num);
577 root = debugfs_create_dir(root_name, codec->component.debugfs_root);
578 kfree(root_name);
579
580 if (!root)
581 goto err;
582
Charles Keepax28823eb2016-09-20 13:52:32 +0100583 if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted))
584 goto err;
585
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100586 if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
587 goto err;
588
589 if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
590 goto err;
591
592 if (!debugfs_create_x32("fw_version", S_IRUGO, root,
593 &dsp->fw_id_version))
594 goto err;
595
596 for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
597 if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
598 S_IRUGO, root, dsp,
599 &wm_adsp_debugfs_fops[i].fops))
600 goto err;
601 }
602
603 dsp->debugfs_root = root;
604 return;
605
606err:
607 debugfs_remove_recursive(root);
608 adsp_err(dsp, "Failed to create debugfs\n");
609}
610
611static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
612{
613 wm_adsp_debugfs_clear(dsp);
614 debugfs_remove_recursive(dsp->debugfs_root);
615}
616#else
617static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
618 struct snd_soc_codec *codec)
619{
620}
621
622static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
623{
624}
625
626static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
627 const char *s)
628{
629}
630
631static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
632 const char *s)
633{
634}
635
636static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
637{
638}
639#endif
640
Mark Brown1023dbd2013-01-11 22:58:28 +0000641static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
642 struct snd_ctl_elem_value *ucontrol)
643{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100644 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000645 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100646 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Mark Brown1023dbd2013-01-11 22:58:28 +0000647
Takashi Iwai15c66572016-02-29 18:01:18 +0100648 ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
Mark Brown1023dbd2013-01-11 22:58:28 +0000649
650 return 0;
651}
652
653static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
654 struct snd_ctl_elem_value *ucontrol)
655{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100656 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000657 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100658 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000659 int ret = 0;
Mark Brown1023dbd2013-01-11 22:58:28 +0000660
Takashi Iwai15c66572016-02-29 18:01:18 +0100661 if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
Mark Brown1023dbd2013-01-11 22:58:28 +0000662 return 0;
663
Takashi Iwai15c66572016-02-29 18:01:18 +0100664 if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
Mark Brown1023dbd2013-01-11 22:58:28 +0000665 return -EINVAL;
666
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000667 mutex_lock(&dsp[e->shift_l].pwr_lock);
668
Charles Keepax28823eb2016-09-20 13:52:32 +0100669 if (dsp[e->shift_l].booted || dsp[e->shift_l].compr)
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000670 ret = -EBUSY;
671 else
Takashi Iwai15c66572016-02-29 18:01:18 +0100672 dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
Mark Brown1023dbd2013-01-11 22:58:28 +0000673
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000674 mutex_unlock(&dsp[e->shift_l].pwr_lock);
Mark Brown1023dbd2013-01-11 22:58:28 +0000675
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000676 return ret;
Mark Brown1023dbd2013-01-11 22:58:28 +0000677}
678
679static const struct soc_enum wm_adsp_fw_enum[] = {
680 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
681 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
682 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
683 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
684};
685
Richard Fitzgerald336d0442015-06-18 13:43:19 +0100686const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000687 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
688 wm_adsp_fw_get, wm_adsp_fw_put),
689 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
690 wm_adsp_fw_get, wm_adsp_fw_put),
691 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
692 wm_adsp_fw_get, wm_adsp_fw_put),
Richard Fitzgerald336d0442015-06-18 13:43:19 +0100693 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
694 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000695};
Richard Fitzgerald336d0442015-06-18 13:43:19 +0100696EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
Mark Brown2159ad932012-10-11 11:54:02 +0900697
698static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
699 int type)
700{
701 int i;
702
703 for (i = 0; i < dsp->num_mems; i++)
704 if (dsp->mem[i].type == type)
705 return &dsp->mem[i];
706
707 return NULL;
708}
709
Charles Keepax3809f002015-04-13 13:27:54 +0100710static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
Mark Brown45b9ee72013-01-08 16:02:06 +0000711 unsigned int offset)
712{
Charles Keepax3809f002015-04-13 13:27:54 +0100713 if (WARN_ON(!mem))
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100714 return offset;
Charles Keepax3809f002015-04-13 13:27:54 +0100715 switch (mem->type) {
Mark Brown45b9ee72013-01-08 16:02:06 +0000716 case WMFW_ADSP1_PM:
Charles Keepax3809f002015-04-13 13:27:54 +0100717 return mem->base + (offset * 3);
Mark Brown45b9ee72013-01-08 16:02:06 +0000718 case WMFW_ADSP1_DM:
Charles Keepax3809f002015-04-13 13:27:54 +0100719 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000720 case WMFW_ADSP2_XM:
Charles Keepax3809f002015-04-13 13:27:54 +0100721 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000722 case WMFW_ADSP2_YM:
Charles Keepax3809f002015-04-13 13:27:54 +0100723 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000724 case WMFW_ADSP1_ZM:
Charles Keepax3809f002015-04-13 13:27:54 +0100725 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000726 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100727 WARN(1, "Unknown memory region type");
Mark Brown45b9ee72013-01-08 16:02:06 +0000728 return offset;
729 }
730}
731
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100732static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
733{
734 u16 scratch[4];
735 int ret;
736
737 ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0,
738 scratch, sizeof(scratch));
739 if (ret) {
740 adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
741 return;
742 }
743
744 adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
745 be16_to_cpu(scratch[0]),
746 be16_to_cpu(scratch[1]),
747 be16_to_cpu(scratch[2]),
748 be16_to_cpu(scratch[3]));
749}
750
Charles Keepax9ee78752016-05-02 13:57:36 +0100751static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
752{
753 return container_of(ext, struct wm_coeff_ctl, bytes_ext);
754}
755
Charles Keepax7585a5b2015-12-08 16:08:25 +0000756static int wm_coeff_info(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100757 struct snd_ctl_elem_info *uinfo)
758{
Charles Keepax9ee78752016-05-02 13:57:36 +0100759 struct soc_bytes_ext *bytes_ext =
760 (struct soc_bytes_ext *)kctl->private_value;
761 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100762
763 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
764 uinfo->count = ctl->len;
765 return 0;
766}
767
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000768static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
769 unsigned int event_id)
770{
771 struct wm_adsp *dsp = ctl->dsp;
772 u32 val = cpu_to_be32(event_id);
773 unsigned int reg;
774 int i, ret;
775
776 ret = wm_coeff_base_reg(ctl, &reg);
777 if (ret)
778 return ret;
779
780 adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
781 event_id, ctl->alg_region.alg,
782 wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
783
784 ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
785 if (ret) {
786 adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
787 return ret;
788 }
789
790 /*
791 * Poll for ack, we initially poll at ~1ms intervals for firmwares
792 * that respond quickly, then go to ~10ms polls. A firmware is unlikely
793 * to ack instantly so we do the first 1ms delay before reading the
794 * control to avoid a pointless bus transaction
795 */
796 for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
797 switch (i) {
798 case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
799 usleep_range(1000, 2000);
800 i++;
801 break;
802 default:
803 usleep_range(10000, 20000);
804 i += 10;
805 break;
806 }
807
808 ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
809 if (ret) {
810 adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
811 return ret;
812 }
813
814 if (val == 0) {
815 adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
816 return 0;
817 }
818 }
819
820 adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
821 reg, ctl->alg_region.alg,
822 wm_adsp_mem_region_name(ctl->alg_region.type),
823 ctl->offset);
824
825 return -ETIMEDOUT;
826}
827
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100828static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100829 const void *buf, size_t len)
830{
Charles Keepax3809f002015-04-13 13:27:54 +0100831 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100832 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100833 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100834 void *scratch;
835 int ret;
836 unsigned int reg;
837
Charles Keepax3809f002015-04-13 13:27:54 +0100838 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100839 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100840 adsp_err(dsp, "No base for region %x\n",
841 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100842 return -EINVAL;
843 }
844
Charles Keepax23237362015-04-13 13:28:02 +0100845 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100846 reg = wm_adsp_region_to_reg(mem, reg);
847
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000848 scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100849 if (!scratch)
850 return -ENOMEM;
851
Charles Keepax3809f002015-04-13 13:27:54 +0100852 ret = regmap_raw_write(dsp->regmap, reg, scratch,
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000853 len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100854 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100855 adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000856 len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100857 kfree(scratch);
858 return ret;
859 }
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000860 adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100861
862 kfree(scratch);
863
864 return 0;
865}
866
Charles Keepax7585a5b2015-12-08 16:08:25 +0000867static int wm_coeff_put(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100868 struct snd_ctl_elem_value *ucontrol)
869{
Charles Keepax9ee78752016-05-02 13:57:36 +0100870 struct soc_bytes_ext *bytes_ext =
871 (struct soc_bytes_ext *)kctl->private_value;
872 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100873 char *p = ucontrol->value.bytes.data;
Charles Keepax168d10e2015-12-08 16:08:27 +0000874 int ret = 0;
875
876 mutex_lock(&ctl->dsp->pwr_lock);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100877
878 memcpy(ctl->cache, p, ctl->len);
879
Nikesh Oswal65d17a92015-02-16 15:25:48 +0000880 ctl->set = 1;
Charles Keepaxcef45772016-09-20 13:52:33 +0100881 if (ctl->enabled && ctl->dsp->running)
Charles Keepax168d10e2015-12-08 16:08:27 +0000882 ret = wm_coeff_write_control(ctl, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100883
Charles Keepax168d10e2015-12-08 16:08:27 +0000884 mutex_unlock(&ctl->dsp->pwr_lock);
885
886 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100887}
888
Charles Keepax9ee78752016-05-02 13:57:36 +0100889static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
890 const unsigned int __user *bytes, unsigned int size)
891{
892 struct soc_bytes_ext *bytes_ext =
893 (struct soc_bytes_ext *)kctl->private_value;
894 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
895 int ret = 0;
896
897 mutex_lock(&ctl->dsp->pwr_lock);
898
899 if (copy_from_user(ctl->cache, bytes, size)) {
900 ret = -EFAULT;
901 } else {
902 ctl->set = 1;
Charles Keepaxcef45772016-09-20 13:52:33 +0100903 if (ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +0100904 ret = wm_coeff_write_control(ctl, ctl->cache, size);
905 }
906
907 mutex_unlock(&ctl->dsp->pwr_lock);
908
909 return ret;
910}
911
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100912static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100913 void *buf, size_t len)
914{
Charles Keepax3809f002015-04-13 13:27:54 +0100915 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100916 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100917 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100918 void *scratch;
919 int ret;
920 unsigned int reg;
921
Charles Keepax3809f002015-04-13 13:27:54 +0100922 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100923 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100924 adsp_err(dsp, "No base for region %x\n",
925 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100926 return -EINVAL;
927 }
928
Charles Keepax23237362015-04-13 13:28:02 +0100929 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100930 reg = wm_adsp_region_to_reg(mem, reg);
931
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000932 scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100933 if (!scratch)
934 return -ENOMEM;
935
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000936 ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100937 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100938 adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
Charles Keepax5602a642016-03-10 10:46:07 +0000939 len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100940 kfree(scratch);
941 return ret;
942 }
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000943 adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100944
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000945 memcpy(buf, scratch, len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100946 kfree(scratch);
947
948 return 0;
949}
950
Charles Keepax7585a5b2015-12-08 16:08:25 +0000951static int wm_coeff_get(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100952 struct snd_ctl_elem_value *ucontrol)
953{
Charles Keepax9ee78752016-05-02 13:57:36 +0100954 struct soc_bytes_ext *bytes_ext =
955 (struct soc_bytes_ext *)kctl->private_value;
956 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100957 char *p = ucontrol->value.bytes.data;
Charles Keepax168d10e2015-12-08 16:08:27 +0000958 int ret = 0;
959
960 mutex_lock(&ctl->dsp->pwr_lock);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100961
Charles Keepax26c22a12015-04-20 13:52:45 +0100962 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
Charles Keepaxcef45772016-09-20 13:52:33 +0100963 if (ctl->enabled && ctl->dsp->running)
Charles Keepax168d10e2015-12-08 16:08:27 +0000964 ret = wm_coeff_read_control(ctl, p, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +0100965 else
Charles Keepax168d10e2015-12-08 16:08:27 +0000966 ret = -EPERM;
967 } else {
Charles Keepaxcef45772016-09-20 13:52:33 +0100968 if (!ctl->flags && ctl->enabled && ctl->dsp->running)
Charles Keepaxbc1765d2015-12-17 10:05:59 +0000969 ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
970
Charles Keepax168d10e2015-12-08 16:08:27 +0000971 memcpy(p, ctl->cache, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +0100972 }
973
Charles Keepax168d10e2015-12-08 16:08:27 +0000974 mutex_unlock(&ctl->dsp->pwr_lock);
Charles Keepax26c22a12015-04-20 13:52:45 +0100975
Charles Keepax168d10e2015-12-08 16:08:27 +0000976 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100977}
978
Charles Keepax9ee78752016-05-02 13:57:36 +0100979static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
980 unsigned int __user *bytes, unsigned int size)
981{
982 struct soc_bytes_ext *bytes_ext =
983 (struct soc_bytes_ext *)kctl->private_value;
984 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
985 int ret = 0;
986
987 mutex_lock(&ctl->dsp->pwr_lock);
988
989 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
Charles Keepaxcef45772016-09-20 13:52:33 +0100990 if (ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +0100991 ret = wm_coeff_read_control(ctl, ctl->cache, size);
992 else
993 ret = -EPERM;
994 } else {
Charles Keepaxcef45772016-09-20 13:52:33 +0100995 if (!ctl->flags && ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +0100996 ret = wm_coeff_read_control(ctl, ctl->cache, size);
997 }
998
999 if (!ret && copy_to_user(bytes, ctl->cache, size))
1000 ret = -EFAULT;
1001
1002 mutex_unlock(&ctl->dsp->pwr_lock);
1003
1004 return ret;
1005}
1006
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001007struct wmfw_ctl_work {
Charles Keepax3809f002015-04-13 13:27:54 +01001008 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001009 struct wm_coeff_ctl *ctl;
1010 struct work_struct work;
1011};
1012
Charles Keepax9ee78752016-05-02 13:57:36 +01001013static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
1014{
1015 unsigned int out, rd, wr, vol;
1016
1017 if (len > ADSP_MAX_STD_CTRL_SIZE) {
1018 rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
1019 wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
1020 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
1021
1022 out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
1023 } else {
1024 rd = SNDRV_CTL_ELEM_ACCESS_READ;
1025 wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
1026 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
1027
1028 out = 0;
1029 }
1030
1031 if (in) {
1032 if (in & WMFW_CTL_FLAG_READABLE)
1033 out |= rd;
1034 if (in & WMFW_CTL_FLAG_WRITEABLE)
1035 out |= wr;
1036 if (in & WMFW_CTL_FLAG_VOLATILE)
1037 out |= vol;
1038 } else {
1039 out |= rd | wr | vol;
1040 }
1041
1042 return out;
1043}
1044
Charles Keepax3809f002015-04-13 13:27:54 +01001045static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001046{
1047 struct snd_kcontrol_new *kcontrol;
1048 int ret;
1049
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01001050 if (!ctl || !ctl->name)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001051 return -EINVAL;
1052
1053 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
1054 if (!kcontrol)
1055 return -ENOMEM;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001056
1057 kcontrol->name = ctl->name;
1058 kcontrol->info = wm_coeff_info;
1059 kcontrol->get = wm_coeff_get;
1060 kcontrol->put = wm_coeff_put;
Charles Keepax9ee78752016-05-02 13:57:36 +01001061 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1062 kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
1063 kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001064
Charles Keepax9ee78752016-05-02 13:57:36 +01001065 ctl->bytes_ext.max = ctl->len;
1066 ctl->bytes_ext.get = wm_coeff_tlv_get;
1067 ctl->bytes_ext.put = wm_coeff_tlv_put;
1068
1069 kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +01001070
Charles Keepax7d00cd92016-02-19 14:44:43 +00001071 ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001072 if (ret < 0)
1073 goto err_kcontrol;
1074
1075 kfree(kcontrol);
1076
Charles Keepax7d00cd92016-02-19 14:44:43 +00001077 ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001078
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001079 return 0;
1080
1081err_kcontrol:
1082 kfree(kcontrol);
1083 return ret;
1084}
1085
Charles Keepaxb21acc12015-04-13 13:28:01 +01001086static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
1087{
1088 struct wm_coeff_ctl *ctl;
1089 int ret;
1090
1091 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1092 if (!ctl->enabled || ctl->set)
1093 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +01001094 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
1095 continue;
1096
Charles Keepax7d00cd92016-02-19 14:44:43 +00001097 ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
Charles Keepaxb21acc12015-04-13 13:28:01 +01001098 if (ret < 0)
1099 return ret;
1100 }
1101
1102 return 0;
1103}
1104
1105static int wm_coeff_sync_controls(struct wm_adsp *dsp)
1106{
1107 struct wm_coeff_ctl *ctl;
1108 int ret;
1109
1110 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1111 if (!ctl->enabled)
1112 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +01001113 if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00001114 ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len);
Charles Keepaxb21acc12015-04-13 13:28:01 +01001115 if (ret < 0)
1116 return ret;
1117 }
1118 }
1119
1120 return 0;
1121}
1122
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001123static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
1124 unsigned int event)
1125{
1126 struct wm_coeff_ctl *ctl;
1127 int ret;
1128
1129 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1130 if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
1131 continue;
1132
1133 ret = wm_coeff_write_acked_control(ctl, event);
1134 if (ret)
1135 adsp_warn(dsp,
1136 "Failed to send 0x%x event to alg 0x%x (%d)\n",
1137 event, ctl->alg_region.alg, ret);
1138 }
1139}
1140
Charles Keepaxb21acc12015-04-13 13:28:01 +01001141static void wm_adsp_ctl_work(struct work_struct *work)
1142{
1143 struct wmfw_ctl_work *ctl_work = container_of(work,
1144 struct wmfw_ctl_work,
1145 work);
1146
1147 wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
1148 kfree(ctl_work);
1149}
1150
Richard Fitzgerald66225e92016-04-27 14:58:27 +01001151static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
1152{
1153 kfree(ctl->cache);
1154 kfree(ctl->name);
1155 kfree(ctl);
1156}
1157
Charles Keepaxb21acc12015-04-13 13:28:01 +01001158static int wm_adsp_create_control(struct wm_adsp *dsp,
1159 const struct wm_adsp_alg_region *alg_region,
Charles Keepax23237362015-04-13 13:28:02 +01001160 unsigned int offset, unsigned int len,
Charles Keepax26c22a12015-04-20 13:52:45 +01001161 const char *subname, unsigned int subname_len,
1162 unsigned int flags)
Charles Keepaxb21acc12015-04-13 13:28:01 +01001163{
1164 struct wm_coeff_ctl *ctl;
1165 struct wmfw_ctl_work *ctl_work;
1166 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001167 const char *region_name;
Charles Keepaxb21acc12015-04-13 13:28:01 +01001168 int ret;
1169
Charles Keepax26c22a12015-04-20 13:52:45 +01001170 if (flags & WMFW_CTL_FLAG_SYS)
1171 return 0;
1172
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001173 region_name = wm_adsp_mem_region_name(alg_region->type);
1174 if (!region_name) {
Charles Keepax23237362015-04-13 13:28:02 +01001175 adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
Charles Keepaxb21acc12015-04-13 13:28:01 +01001176 return -EINVAL;
1177 }
1178
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001179 switch (dsp->fw_ver) {
1180 case 0:
1181 case 1:
1182 snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
1183 dsp->num, region_name, alg_region->alg);
1184 break;
1185 default:
1186 ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
1187 "DSP%d%c %.12s %x", dsp->num, *region_name,
1188 wm_adsp_fw_text[dsp->fw], alg_region->alg);
1189
1190 /* Truncate the subname from the start if it is too long */
1191 if (subname) {
1192 int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
1193 int skip = 0;
1194
1195 if (subname_len > avail)
1196 skip = subname_len - avail;
1197
1198 snprintf(name + ret,
1199 SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
1200 subname_len - skip, subname + skip);
1201 }
1202 break;
1203 }
Charles Keepaxb21acc12015-04-13 13:28:01 +01001204
Charles Keepax7585a5b2015-12-08 16:08:25 +00001205 list_for_each_entry(ctl, &dsp->ctl_list, list) {
Charles Keepaxb21acc12015-04-13 13:28:01 +01001206 if (!strcmp(ctl->name, name)) {
1207 if (!ctl->enabled)
1208 ctl->enabled = 1;
1209 return 0;
1210 }
1211 }
1212
1213 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
1214 if (!ctl)
1215 return -ENOMEM;
Charles Keepax23237362015-04-13 13:28:02 +01001216 ctl->fw_name = wm_adsp_fw_text[dsp->fw];
Charles Keepaxb21acc12015-04-13 13:28:01 +01001217 ctl->alg_region = *alg_region;
1218 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
1219 if (!ctl->name) {
1220 ret = -ENOMEM;
1221 goto err_ctl;
1222 }
1223 ctl->enabled = 1;
1224 ctl->set = 0;
1225 ctl->ops.xget = wm_coeff_get;
1226 ctl->ops.xput = wm_coeff_put;
1227 ctl->dsp = dsp;
1228
Charles Keepax26c22a12015-04-20 13:52:45 +01001229 ctl->flags = flags;
Charles Keepax23237362015-04-13 13:28:02 +01001230 ctl->offset = offset;
Charles Keepaxb21acc12015-04-13 13:28:01 +01001231 ctl->len = len;
1232 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
1233 if (!ctl->cache) {
1234 ret = -ENOMEM;
1235 goto err_ctl_name;
1236 }
1237
Charles Keepax23237362015-04-13 13:28:02 +01001238 list_add(&ctl->list, &dsp->ctl_list);
1239
Charles Keepaxb21acc12015-04-13 13:28:01 +01001240 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
1241 if (!ctl_work) {
1242 ret = -ENOMEM;
1243 goto err_ctl_cache;
1244 }
1245
1246 ctl_work->dsp = dsp;
1247 ctl_work->ctl = ctl;
1248 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
1249 schedule_work(&ctl_work->work);
1250
1251 return 0;
1252
1253err_ctl_cache:
1254 kfree(ctl->cache);
1255err_ctl_name:
1256 kfree(ctl->name);
1257err_ctl:
1258 kfree(ctl);
1259
1260 return ret;
1261}
1262
Charles Keepax23237362015-04-13 13:28:02 +01001263struct wm_coeff_parsed_alg {
1264 int id;
1265 const u8 *name;
1266 int name_len;
1267 int ncoeff;
1268};
1269
1270struct wm_coeff_parsed_coeff {
1271 int offset;
1272 int mem_type;
1273 const u8 *name;
1274 int name_len;
1275 int ctl_type;
1276 int flags;
1277 int len;
1278};
1279
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001280static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
1281{
1282 int length;
1283
1284 switch (bytes) {
1285 case 1:
1286 length = **pos;
1287 break;
1288 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +01001289 length = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001290 break;
1291 default:
1292 return 0;
1293 }
1294
1295 if (str)
1296 *str = *pos + bytes;
1297
1298 *pos += ((length + bytes) + 3) & ~0x03;
1299
1300 return length;
1301}
1302
1303static int wm_coeff_parse_int(int bytes, const u8 **pos)
1304{
1305 int val = 0;
1306
1307 switch (bytes) {
1308 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +01001309 val = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001310 break;
1311 case 4:
Charles Keepax8299ee82015-04-20 13:52:44 +01001312 val = le32_to_cpu(*((__le32 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001313 break;
1314 default:
1315 break;
1316 }
1317
1318 *pos += bytes;
1319
1320 return val;
1321}
1322
Charles Keepax23237362015-04-13 13:28:02 +01001323static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
1324 struct wm_coeff_parsed_alg *blk)
1325{
1326 const struct wmfw_adsp_alg_data *raw;
1327
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001328 switch (dsp->fw_ver) {
1329 case 0:
1330 case 1:
1331 raw = (const struct wmfw_adsp_alg_data *)*data;
1332 *data = raw->data;
Charles Keepax23237362015-04-13 13:28:02 +01001333
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001334 blk->id = le32_to_cpu(raw->id);
1335 blk->name = raw->name;
1336 blk->name_len = strlen(raw->name);
1337 blk->ncoeff = le32_to_cpu(raw->ncoeff);
1338 break;
1339 default:
1340 blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
1341 blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
1342 &blk->name);
1343 wm_coeff_parse_string(sizeof(u16), data, NULL);
1344 blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
1345 break;
1346 }
Charles Keepax23237362015-04-13 13:28:02 +01001347
1348 adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
1349 adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
1350 adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
1351}
1352
1353static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
1354 struct wm_coeff_parsed_coeff *blk)
1355{
1356 const struct wmfw_adsp_coeff_data *raw;
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001357 const u8 *tmp;
1358 int length;
Charles Keepax23237362015-04-13 13:28:02 +01001359
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001360 switch (dsp->fw_ver) {
1361 case 0:
1362 case 1:
1363 raw = (const struct wmfw_adsp_coeff_data *)*data;
1364 *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
Charles Keepax23237362015-04-13 13:28:02 +01001365
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001366 blk->offset = le16_to_cpu(raw->hdr.offset);
1367 blk->mem_type = le16_to_cpu(raw->hdr.type);
1368 blk->name = raw->name;
1369 blk->name_len = strlen(raw->name);
1370 blk->ctl_type = le16_to_cpu(raw->ctl_type);
1371 blk->flags = le16_to_cpu(raw->flags);
1372 blk->len = le32_to_cpu(raw->len);
1373 break;
1374 default:
1375 tmp = *data;
1376 blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
1377 blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
1378 length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
1379 blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
1380 &blk->name);
1381 wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
1382 wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
1383 blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
1384 blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
1385 blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
1386
1387 *data = *data + sizeof(raw->hdr) + length;
1388 break;
1389 }
Charles Keepax23237362015-04-13 13:28:02 +01001390
1391 adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
1392 adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
1393 adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
1394 adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
1395 adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
1396 adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
1397}
1398
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001399static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
1400 const struct wm_coeff_parsed_coeff *coeff_blk,
1401 unsigned int f_required,
1402 unsigned int f_illegal)
1403{
1404 if ((coeff_blk->flags & f_illegal) ||
1405 ((coeff_blk->flags & f_required) != f_required)) {
1406 adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
1407 coeff_blk->flags, coeff_blk->ctl_type);
1408 return -EINVAL;
1409 }
1410
1411 return 0;
1412}
1413
Charles Keepax23237362015-04-13 13:28:02 +01001414static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
1415 const struct wmfw_region *region)
1416{
1417 struct wm_adsp_alg_region alg_region = {};
1418 struct wm_coeff_parsed_alg alg_blk;
1419 struct wm_coeff_parsed_coeff coeff_blk;
1420 const u8 *data = region->data;
1421 int i, ret;
1422
1423 wm_coeff_parse_alg(dsp, &data, &alg_blk);
1424 for (i = 0; i < alg_blk.ncoeff; i++) {
1425 wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
1426
1427 switch (coeff_blk.ctl_type) {
1428 case SNDRV_CTL_ELEM_TYPE_BYTES:
1429 break;
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001430 case WMFW_CTL_TYPE_HOSTEVENT:
1431 ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
1432 WMFW_CTL_FLAG_SYS |
1433 WMFW_CTL_FLAG_VOLATILE |
1434 WMFW_CTL_FLAG_WRITEABLE |
1435 WMFW_CTL_FLAG_READABLE,
1436 0);
1437 if (ret)
1438 return -EINVAL;
1439 break;
Charles Keepax23237362015-04-13 13:28:02 +01001440 default:
1441 adsp_err(dsp, "Unknown control type: %d\n",
1442 coeff_blk.ctl_type);
1443 return -EINVAL;
1444 }
1445
1446 alg_region.type = coeff_blk.mem_type;
1447 alg_region.alg = alg_blk.id;
1448
1449 ret = wm_adsp_create_control(dsp, &alg_region,
1450 coeff_blk.offset,
1451 coeff_blk.len,
1452 coeff_blk.name,
Charles Keepax26c22a12015-04-20 13:52:45 +01001453 coeff_blk.name_len,
1454 coeff_blk.flags);
Charles Keepax23237362015-04-13 13:28:02 +01001455 if (ret < 0)
1456 adsp_err(dsp, "Failed to create control: %.*s, %d\n",
1457 coeff_blk.name_len, coeff_blk.name, ret);
1458 }
1459
1460 return 0;
1461}
1462
Mark Brown2159ad932012-10-11 11:54:02 +09001463static int wm_adsp_load(struct wm_adsp *dsp)
1464{
Mark Browncf17c832013-01-30 14:37:23 +08001465 LIST_HEAD(buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09001466 const struct firmware *firmware;
1467 struct regmap *regmap = dsp->regmap;
1468 unsigned int pos = 0;
1469 const struct wmfw_header *header;
1470 const struct wmfw_adsp1_sizes *adsp1_sizes;
1471 const struct wmfw_adsp2_sizes *adsp2_sizes;
1472 const struct wmfw_footer *footer;
1473 const struct wmfw_region *region;
1474 const struct wm_adsp_region *mem;
1475 const char *region_name;
1476 char *file, *text;
Mark Browncf17c832013-01-30 14:37:23 +08001477 struct wm_adsp_buf *buf;
Mark Brown2159ad932012-10-11 11:54:02 +09001478 unsigned int reg;
1479 int regions = 0;
1480 int ret, offset, type, sizes;
1481
1482 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1483 if (file == NULL)
1484 return -ENOMEM;
1485
Mark Brown1023dbd2013-01-11 22:58:28 +00001486 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
1487 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +09001488 file[PAGE_SIZE - 1] = '\0';
1489
1490 ret = request_firmware(&firmware, file, dsp->dev);
1491 if (ret != 0) {
1492 adsp_err(dsp, "Failed to request '%s'\n", file);
1493 goto out;
1494 }
1495 ret = -EINVAL;
1496
1497 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1498 if (pos >= firmware->size) {
1499 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1500 file, firmware->size);
1501 goto out_fw;
1502 }
1503
Charles Keepax7585a5b2015-12-08 16:08:25 +00001504 header = (void *)&firmware->data[0];
Mark Brown2159ad932012-10-11 11:54:02 +09001505
1506 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
1507 adsp_err(dsp, "%s: invalid magic\n", file);
1508 goto out_fw;
1509 }
1510
Charles Keepax23237362015-04-13 13:28:02 +01001511 switch (header->ver) {
1512 case 0:
Charles Keepaxc61e59f2015-04-13 13:28:05 +01001513 adsp_warn(dsp, "%s: Depreciated file format %d\n",
1514 file, header->ver);
1515 break;
Charles Keepax23237362015-04-13 13:28:02 +01001516 case 1:
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001517 case 2:
Charles Keepax23237362015-04-13 13:28:02 +01001518 break;
1519 default:
Mark Brown2159ad932012-10-11 11:54:02 +09001520 adsp_err(dsp, "%s: unknown file format %d\n",
1521 file, header->ver);
1522 goto out_fw;
1523 }
Charles Keepax23237362015-04-13 13:28:02 +01001524
Dimitris Papastamos36269922013-11-01 15:56:57 +00001525 adsp_info(dsp, "Firmware version: %d\n", header->ver);
Charles Keepax23237362015-04-13 13:28:02 +01001526 dsp->fw_ver = header->ver;
Mark Brown2159ad932012-10-11 11:54:02 +09001527
1528 if (header->core != dsp->type) {
1529 adsp_err(dsp, "%s: invalid core %d != %d\n",
1530 file, header->core, dsp->type);
1531 goto out_fw;
1532 }
1533
1534 switch (dsp->type) {
1535 case WMFW_ADSP1:
1536 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1537 adsp1_sizes = (void *)&(header[1]);
1538 footer = (void *)&(adsp1_sizes[1]);
1539 sizes = sizeof(*adsp1_sizes);
1540
1541 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
1542 file, le32_to_cpu(adsp1_sizes->dm),
1543 le32_to_cpu(adsp1_sizes->pm),
1544 le32_to_cpu(adsp1_sizes->zm));
1545 break;
1546
1547 case WMFW_ADSP2:
1548 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
1549 adsp2_sizes = (void *)&(header[1]);
1550 footer = (void *)&(adsp2_sizes[1]);
1551 sizes = sizeof(*adsp2_sizes);
1552
1553 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
1554 file, le32_to_cpu(adsp2_sizes->xm),
1555 le32_to_cpu(adsp2_sizes->ym),
1556 le32_to_cpu(adsp2_sizes->pm),
1557 le32_to_cpu(adsp2_sizes->zm));
1558 break;
1559
1560 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +01001561 WARN(1, "Unknown DSP type");
Mark Brown2159ad932012-10-11 11:54:02 +09001562 goto out_fw;
1563 }
1564
1565 if (le32_to_cpu(header->len) != sizeof(*header) +
1566 sizes + sizeof(*footer)) {
1567 adsp_err(dsp, "%s: unexpected header length %d\n",
1568 file, le32_to_cpu(header->len));
1569 goto out_fw;
1570 }
1571
1572 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
1573 le64_to_cpu(footer->timestamp));
1574
1575 while (pos < firmware->size &&
1576 pos - firmware->size > sizeof(*region)) {
1577 region = (void *)&(firmware->data[pos]);
1578 region_name = "Unknown";
1579 reg = 0;
1580 text = NULL;
1581 offset = le32_to_cpu(region->offset) & 0xffffff;
1582 type = be32_to_cpu(region->type) & 0xff;
1583 mem = wm_adsp_find_region(dsp, type);
Charles Keepax7585a5b2015-12-08 16:08:25 +00001584
Mark Brown2159ad932012-10-11 11:54:02 +09001585 switch (type) {
1586 case WMFW_NAME_TEXT:
1587 region_name = "Firmware name";
1588 text = kzalloc(le32_to_cpu(region->len) + 1,
1589 GFP_KERNEL);
1590 break;
Charles Keepax23237362015-04-13 13:28:02 +01001591 case WMFW_ALGORITHM_DATA:
1592 region_name = "Algorithm";
1593 ret = wm_adsp_parse_coeff(dsp, region);
1594 if (ret != 0)
1595 goto out_fw;
1596 break;
Mark Brown2159ad932012-10-11 11:54:02 +09001597 case WMFW_INFO_TEXT:
1598 region_name = "Information";
1599 text = kzalloc(le32_to_cpu(region->len) + 1,
1600 GFP_KERNEL);
1601 break;
1602 case WMFW_ABSOLUTE:
1603 region_name = "Absolute";
1604 reg = offset;
1605 break;
1606 case WMFW_ADSP1_PM:
Mark Brown2159ad932012-10-11 11:54:02 +09001607 case WMFW_ADSP1_DM:
Mark Brown2159ad932012-10-11 11:54:02 +09001608 case WMFW_ADSP2_XM:
Mark Brown2159ad932012-10-11 11:54:02 +09001609 case WMFW_ADSP2_YM:
Mark Brown2159ad932012-10-11 11:54:02 +09001610 case WMFW_ADSP1_ZM:
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001611 region_name = wm_adsp_mem_region_name(type);
Mark Brown45b9ee72013-01-08 16:02:06 +00001612 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +09001613 break;
1614 default:
1615 adsp_warn(dsp,
1616 "%s.%d: Unknown region type %x at %d(%x)\n",
1617 file, regions, type, pos, pos);
1618 break;
1619 }
1620
1621 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
1622 regions, le32_to_cpu(region->len), offset,
1623 region_name);
1624
1625 if (text) {
1626 memcpy(text, region->data, le32_to_cpu(region->len));
1627 adsp_info(dsp, "%s: %s\n", file, text);
1628 kfree(text);
1629 }
1630
1631 if (reg) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001632 buf = wm_adsp_buf_alloc(region->data,
1633 le32_to_cpu(region->len),
1634 &buf_list);
1635 if (!buf) {
1636 adsp_err(dsp, "Out of memory\n");
1637 ret = -ENOMEM;
1638 goto out_fw;
1639 }
Mark Browna76fefa2013-01-07 19:03:17 +00001640
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001641 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1642 le32_to_cpu(region->len));
1643 if (ret != 0) {
1644 adsp_err(dsp,
1645 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
1646 file, regions,
1647 le32_to_cpu(region->len), offset,
1648 region_name, ret);
1649 goto out_fw;
Mark Brown2159ad932012-10-11 11:54:02 +09001650 }
1651 }
1652
1653 pos += le32_to_cpu(region->len) + sizeof(*region);
1654 regions++;
1655 }
Mark Browncf17c832013-01-30 14:37:23 +08001656
1657 ret = regmap_async_complete(regmap);
1658 if (ret != 0) {
1659 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1660 goto out_fw;
1661 }
1662
Mark Brown2159ad932012-10-11 11:54:02 +09001663 if (pos > firmware->size)
1664 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1665 file, regions, pos - firmware->size);
1666
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001667 wm_adsp_debugfs_save_wmfwname(dsp, file);
1668
Mark Brown2159ad932012-10-11 11:54:02 +09001669out_fw:
Mark Browncf17c832013-01-30 14:37:23 +08001670 regmap_async_complete(regmap);
1671 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09001672 release_firmware(firmware);
1673out:
1674 kfree(file);
1675
1676 return ret;
1677}
1678
Charles Keepax23237362015-04-13 13:28:02 +01001679static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
1680 const struct wm_adsp_alg_region *alg_region)
1681{
1682 struct wm_coeff_ctl *ctl;
1683
1684 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1685 if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
1686 alg_region->alg == ctl->alg_region.alg &&
1687 alg_region->type == ctl->alg_region.type) {
1688 ctl->alg_region.base = alg_region->base;
1689 }
1690 }
1691}
1692
Charles Keepax3809f002015-04-13 13:27:54 +01001693static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
Charles Keepaxb618a1852015-04-13 13:27:53 +01001694 unsigned int pos, unsigned int len)
Mark Browndb405172012-10-26 19:30:40 +01001695{
Charles Keepaxb618a1852015-04-13 13:27:53 +01001696 void *alg;
1697 int ret;
Mark Browndb405172012-10-26 19:30:40 +01001698 __be32 val;
Mark Browndb405172012-10-26 19:30:40 +01001699
Charles Keepax3809f002015-04-13 13:27:54 +01001700 if (n_algs == 0) {
Mark Browndb405172012-10-26 19:30:40 +01001701 adsp_err(dsp, "No algorithms\n");
Charles Keepaxb618a1852015-04-13 13:27:53 +01001702 return ERR_PTR(-EINVAL);
Mark Browndb405172012-10-26 19:30:40 +01001703 }
1704
Charles Keepax3809f002015-04-13 13:27:54 +01001705 if (n_algs > 1024) {
1706 adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001707 return ERR_PTR(-EINVAL);
Mark Brownd62f4bc2012-12-19 14:00:30 +00001708 }
1709
Mark Browndb405172012-10-26 19:30:40 +01001710 /* Read the terminator first to validate the length */
Charles Keepaxb618a1852015-04-13 13:27:53 +01001711 ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
Mark Browndb405172012-10-26 19:30:40 +01001712 if (ret != 0) {
1713 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1714 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001715 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001716 }
1717
1718 if (be32_to_cpu(val) != 0xbedead)
1719 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
Charles Keepaxb618a1852015-04-13 13:27:53 +01001720 pos + len, be32_to_cpu(val));
Mark Browndb405172012-10-26 19:30:40 +01001721
Charles Keepaxb618a1852015-04-13 13:27:53 +01001722 alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +01001723 if (!alg)
Charles Keepaxb618a1852015-04-13 13:27:53 +01001724 return ERR_PTR(-ENOMEM);
Mark Browndb405172012-10-26 19:30:40 +01001725
Charles Keepaxb618a1852015-04-13 13:27:53 +01001726 ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
Mark Browndb405172012-10-26 19:30:40 +01001727 if (ret != 0) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00001728 adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001729 kfree(alg);
1730 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001731 }
1732
Charles Keepaxb618a1852015-04-13 13:27:53 +01001733 return alg;
1734}
1735
Charles Keepax14197092015-12-15 11:29:43 +00001736static struct wm_adsp_alg_region *
1737 wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id)
1738{
1739 struct wm_adsp_alg_region *alg_region;
1740
1741 list_for_each_entry(alg_region, &dsp->alg_regions, list) {
1742 if (id == alg_region->alg && type == alg_region->type)
1743 return alg_region;
1744 }
1745
1746 return NULL;
1747}
1748
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001749static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
1750 int type, __be32 id,
1751 __be32 base)
1752{
1753 struct wm_adsp_alg_region *alg_region;
1754
1755 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1756 if (!alg_region)
1757 return ERR_PTR(-ENOMEM);
1758
1759 alg_region->type = type;
1760 alg_region->alg = be32_to_cpu(id);
1761 alg_region->base = be32_to_cpu(base);
1762
1763 list_add_tail(&alg_region->list, &dsp->alg_regions);
1764
Charles Keepax23237362015-04-13 13:28:02 +01001765 if (dsp->fw_ver > 0)
1766 wm_adsp_ctl_fixup_base(dsp, alg_region);
1767
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001768 return alg_region;
1769}
1770
Richard Fitzgerald56574d52016-04-27 14:58:29 +01001771static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
1772{
1773 struct wm_adsp_alg_region *alg_region;
1774
1775 while (!list_empty(&dsp->alg_regions)) {
1776 alg_region = list_first_entry(&dsp->alg_regions,
1777 struct wm_adsp_alg_region,
1778 list);
1779 list_del(&alg_region->list);
1780 kfree(alg_region);
1781 }
1782}
1783
Charles Keepaxb618a1852015-04-13 13:27:53 +01001784static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
1785{
1786 struct wmfw_adsp1_id_hdr adsp1_id;
1787 struct wmfw_adsp1_alg_hdr *adsp1_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001788 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001789 const struct wm_adsp_region *mem;
1790 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001791 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001792 int i, ret;
1793
1794 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
1795 if (WARN_ON(!mem))
1796 return -EINVAL;
1797
1798 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
1799 sizeof(adsp1_id));
1800 if (ret != 0) {
1801 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1802 ret);
1803 return ret;
1804 }
1805
Charles Keepax3809f002015-04-13 13:27:54 +01001806 n_algs = be32_to_cpu(adsp1_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001807 dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
1808 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1809 dsp->fw_id,
1810 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
1811 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
1812 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001813 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001814
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001815 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1816 adsp1_id.fw.id, adsp1_id.zm);
1817 if (IS_ERR(alg_region))
1818 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001819
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001820 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1821 adsp1_id.fw.id, adsp1_id.dm);
1822 if (IS_ERR(alg_region))
1823 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001824
1825 pos = sizeof(adsp1_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001826 len = (sizeof(*adsp1_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001827
Charles Keepax3809f002015-04-13 13:27:54 +01001828 adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001829 if (IS_ERR(adsp1_alg))
1830 return PTR_ERR(adsp1_alg);
Mark Browndb405172012-10-26 19:30:40 +01001831
Charles Keepax3809f002015-04-13 13:27:54 +01001832 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001833 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
1834 i, be32_to_cpu(adsp1_alg[i].alg.id),
1835 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
1836 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
1837 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
1838 be32_to_cpu(adsp1_alg[i].dm),
1839 be32_to_cpu(adsp1_alg[i].zm));
Mark Brown471f4882013-01-08 16:09:31 +00001840
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001841 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1842 adsp1_alg[i].alg.id,
1843 adsp1_alg[i].dm);
1844 if (IS_ERR(alg_region)) {
1845 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001846 goto out;
1847 }
Charles Keepax23237362015-04-13 13:28:02 +01001848 if (dsp->fw_ver == 0) {
1849 if (i + 1 < n_algs) {
1850 len = be32_to_cpu(adsp1_alg[i + 1].dm);
1851 len -= be32_to_cpu(adsp1_alg[i].dm);
1852 len *= 4;
1853 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001854 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001855 } else {
1856 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
1857 be32_to_cpu(adsp1_alg[i].alg.id));
1858 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001859 }
Mark Brown471f4882013-01-08 16:09:31 +00001860
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001861 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1862 adsp1_alg[i].alg.id,
1863 adsp1_alg[i].zm);
1864 if (IS_ERR(alg_region)) {
1865 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001866 goto out;
1867 }
Charles Keepax23237362015-04-13 13:28:02 +01001868 if (dsp->fw_ver == 0) {
1869 if (i + 1 < n_algs) {
1870 len = be32_to_cpu(adsp1_alg[i + 1].zm);
1871 len -= be32_to_cpu(adsp1_alg[i].zm);
1872 len *= 4;
1873 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001874 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001875 } else {
1876 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1877 be32_to_cpu(adsp1_alg[i].alg.id));
1878 }
Mark Browndb405172012-10-26 19:30:40 +01001879 }
1880 }
1881
1882out:
Charles Keepaxb618a1852015-04-13 13:27:53 +01001883 kfree(adsp1_alg);
1884 return ret;
1885}
1886
1887static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
1888{
1889 struct wmfw_adsp2_id_hdr adsp2_id;
1890 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001891 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001892 const struct wm_adsp_region *mem;
1893 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001894 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001895 int i, ret;
1896
1897 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
1898 if (WARN_ON(!mem))
1899 return -EINVAL;
1900
1901 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
1902 sizeof(adsp2_id));
1903 if (ret != 0) {
1904 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1905 ret);
1906 return ret;
1907 }
1908
Charles Keepax3809f002015-04-13 13:27:54 +01001909 n_algs = be32_to_cpu(adsp2_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001910 dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001911 dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001912 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1913 dsp->fw_id,
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001914 (dsp->fw_id_version & 0xff0000) >> 16,
1915 (dsp->fw_id_version & 0xff00) >> 8,
1916 dsp->fw_id_version & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001917 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001918
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001919 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1920 adsp2_id.fw.id, adsp2_id.xm);
1921 if (IS_ERR(alg_region))
1922 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001923
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001924 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1925 adsp2_id.fw.id, adsp2_id.ym);
1926 if (IS_ERR(alg_region))
1927 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001928
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001929 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1930 adsp2_id.fw.id, adsp2_id.zm);
1931 if (IS_ERR(alg_region))
1932 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001933
1934 pos = sizeof(adsp2_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001935 len = (sizeof(*adsp2_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001936
Charles Keepax3809f002015-04-13 13:27:54 +01001937 adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001938 if (IS_ERR(adsp2_alg))
1939 return PTR_ERR(adsp2_alg);
1940
Charles Keepax3809f002015-04-13 13:27:54 +01001941 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001942 adsp_info(dsp,
1943 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1944 i, be32_to_cpu(adsp2_alg[i].alg.id),
1945 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1946 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1947 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1948 be32_to_cpu(adsp2_alg[i].xm),
1949 be32_to_cpu(adsp2_alg[i].ym),
1950 be32_to_cpu(adsp2_alg[i].zm));
1951
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001952 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1953 adsp2_alg[i].alg.id,
1954 adsp2_alg[i].xm);
1955 if (IS_ERR(alg_region)) {
1956 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001957 goto out;
1958 }
Charles Keepax23237362015-04-13 13:28:02 +01001959 if (dsp->fw_ver == 0) {
1960 if (i + 1 < n_algs) {
1961 len = be32_to_cpu(adsp2_alg[i + 1].xm);
1962 len -= be32_to_cpu(adsp2_alg[i].xm);
1963 len *= 4;
1964 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001965 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001966 } else {
1967 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
1968 be32_to_cpu(adsp2_alg[i].alg.id));
1969 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001970 }
1971
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001972 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1973 adsp2_alg[i].alg.id,
1974 adsp2_alg[i].ym);
1975 if (IS_ERR(alg_region)) {
1976 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001977 goto out;
1978 }
Charles Keepax23237362015-04-13 13:28:02 +01001979 if (dsp->fw_ver == 0) {
1980 if (i + 1 < n_algs) {
1981 len = be32_to_cpu(adsp2_alg[i + 1].ym);
1982 len -= be32_to_cpu(adsp2_alg[i].ym);
1983 len *= 4;
1984 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001985 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001986 } else {
1987 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
1988 be32_to_cpu(adsp2_alg[i].alg.id));
1989 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001990 }
1991
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001992 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1993 adsp2_alg[i].alg.id,
1994 adsp2_alg[i].zm);
1995 if (IS_ERR(alg_region)) {
1996 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001997 goto out;
1998 }
Charles Keepax23237362015-04-13 13:28:02 +01001999 if (dsp->fw_ver == 0) {
2000 if (i + 1 < n_algs) {
2001 len = be32_to_cpu(adsp2_alg[i + 1].zm);
2002 len -= be32_to_cpu(adsp2_alg[i].zm);
2003 len *= 4;
2004 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01002005 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01002006 } else {
2007 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
2008 be32_to_cpu(adsp2_alg[i].alg.id));
2009 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002010 }
2011 }
2012
2013out:
2014 kfree(adsp2_alg);
Mark Browndb405172012-10-26 19:30:40 +01002015 return ret;
2016}
2017
Mark Brown2159ad932012-10-11 11:54:02 +09002018static int wm_adsp_load_coeff(struct wm_adsp *dsp)
2019{
Mark Browncf17c832013-01-30 14:37:23 +08002020 LIST_HEAD(buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09002021 struct regmap *regmap = dsp->regmap;
2022 struct wmfw_coeff_hdr *hdr;
2023 struct wmfw_coeff_item *blk;
2024 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +00002025 const struct wm_adsp_region *mem;
2026 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad932012-10-11 11:54:02 +09002027 const char *region_name;
2028 int ret, pos, blocks, type, offset, reg;
2029 char *file;
Mark Browncf17c832013-01-30 14:37:23 +08002030 struct wm_adsp_buf *buf;
Mark Brown2159ad932012-10-11 11:54:02 +09002031
2032 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
2033 if (file == NULL)
2034 return -ENOMEM;
2035
Mark Brown1023dbd2013-01-11 22:58:28 +00002036 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
2037 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +09002038 file[PAGE_SIZE - 1] = '\0';
2039
2040 ret = request_firmware(&firmware, file, dsp->dev);
2041 if (ret != 0) {
2042 adsp_warn(dsp, "Failed to request '%s'\n", file);
2043 ret = 0;
2044 goto out;
2045 }
2046 ret = -EINVAL;
2047
2048 if (sizeof(*hdr) >= firmware->size) {
2049 adsp_err(dsp, "%s: file too short, %zu bytes\n",
2050 file, firmware->size);
2051 goto out_fw;
2052 }
2053
Charles Keepax7585a5b2015-12-08 16:08:25 +00002054 hdr = (void *)&firmware->data[0];
Mark Brown2159ad932012-10-11 11:54:02 +09002055 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
2056 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +00002057 goto out_fw;
Mark Brown2159ad932012-10-11 11:54:02 +09002058 }
2059
Mark Brownc7123262013-01-16 16:59:04 +09002060 switch (be32_to_cpu(hdr->rev) & 0xff) {
2061 case 1:
2062 break;
2063 default:
2064 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
2065 file, be32_to_cpu(hdr->rev) & 0xff);
2066 ret = -EINVAL;
2067 goto out_fw;
2068 }
2069
Mark Brown2159ad932012-10-11 11:54:02 +09002070 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
2071 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
2072 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
2073 le32_to_cpu(hdr->ver) & 0xff);
2074
2075 pos = le32_to_cpu(hdr->len);
2076
2077 blocks = 0;
2078 while (pos < firmware->size &&
2079 pos - firmware->size > sizeof(*blk)) {
Charles Keepax7585a5b2015-12-08 16:08:25 +00002080 blk = (void *)(&firmware->data[pos]);
Mark Brown2159ad932012-10-11 11:54:02 +09002081
Mark Brownc7123262013-01-16 16:59:04 +09002082 type = le16_to_cpu(blk->type);
2083 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad932012-10-11 11:54:02 +09002084
2085 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
2086 file, blocks, le32_to_cpu(blk->id),
2087 (le32_to_cpu(blk->ver) >> 16) & 0xff,
2088 (le32_to_cpu(blk->ver) >> 8) & 0xff,
2089 le32_to_cpu(blk->ver) & 0xff);
2090 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
2091 file, blocks, le32_to_cpu(blk->len), offset, type);
2092
2093 reg = 0;
2094 region_name = "Unknown";
2095 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +09002096 case (WMFW_NAME_TEXT << 8):
2097 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad932012-10-11 11:54:02 +09002098 break;
Mark Brownc7123262013-01-16 16:59:04 +09002099 case (WMFW_ABSOLUTE << 8):
Mark Brownf395a212013-03-05 22:39:54 +08002100 /*
2101 * Old files may use this for global
2102 * coefficients.
2103 */
2104 if (le32_to_cpu(blk->id) == dsp->fw_id &&
2105 offset == 0) {
2106 region_name = "global coefficients";
2107 mem = wm_adsp_find_region(dsp, type);
2108 if (!mem) {
2109 adsp_err(dsp, "No ZM\n");
2110 break;
2111 }
2112 reg = wm_adsp_region_to_reg(mem, 0);
2113
2114 } else {
2115 region_name = "register";
2116 reg = offset;
2117 }
Mark Brown2159ad932012-10-11 11:54:02 +09002118 break;
Mark Brown471f4882013-01-08 16:09:31 +00002119
2120 case WMFW_ADSP1_DM:
2121 case WMFW_ADSP1_ZM:
2122 case WMFW_ADSP2_XM:
2123 case WMFW_ADSP2_YM:
2124 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
2125 file, blocks, le32_to_cpu(blk->len),
2126 type, le32_to_cpu(blk->id));
2127
2128 mem = wm_adsp_find_region(dsp, type);
2129 if (!mem) {
2130 adsp_err(dsp, "No base for region %x\n", type);
2131 break;
2132 }
2133
Charles Keepax14197092015-12-15 11:29:43 +00002134 alg_region = wm_adsp_find_alg_region(dsp, type,
2135 le32_to_cpu(blk->id));
2136 if (alg_region) {
2137 reg = alg_region->base;
2138 reg = wm_adsp_region_to_reg(mem, reg);
2139 reg += offset;
2140 } else {
Mark Brown471f4882013-01-08 16:09:31 +00002141 adsp_err(dsp, "No %x for algorithm %x\n",
2142 type, le32_to_cpu(blk->id));
Charles Keepax14197092015-12-15 11:29:43 +00002143 }
Mark Brown471f4882013-01-08 16:09:31 +00002144 break;
2145
Mark Brown2159ad932012-10-11 11:54:02 +09002146 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +09002147 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
2148 file, blocks, type, pos);
Mark Brown2159ad932012-10-11 11:54:02 +09002149 break;
2150 }
2151
2152 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +08002153 buf = wm_adsp_buf_alloc(blk->data,
2154 le32_to_cpu(blk->len),
2155 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +00002156 if (!buf) {
2157 adsp_err(dsp, "Out of memory\n");
Wei Yongjunf4b82812013-03-12 00:23:15 +08002158 ret = -ENOMEM;
2159 goto out_fw;
Mark Browna76fefa2013-01-07 19:03:17 +00002160 }
2161
Mark Brown20da6d52013-01-12 19:58:17 +00002162 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
2163 file, blocks, le32_to_cpu(blk->len),
2164 reg);
Mark Browncf17c832013-01-30 14:37:23 +08002165 ret = regmap_raw_write_async(regmap, reg, buf->buf,
2166 le32_to_cpu(blk->len));
Mark Brown2159ad932012-10-11 11:54:02 +09002167 if (ret != 0) {
2168 adsp_err(dsp,
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +00002169 "%s.%d: Failed to write to %x in %s: %d\n",
2170 file, blocks, reg, region_name, ret);
Mark Brown2159ad932012-10-11 11:54:02 +09002171 }
2172 }
2173
Charles Keepaxbe951012015-02-16 15:25:49 +00002174 pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
Mark Brown2159ad932012-10-11 11:54:02 +09002175 blocks++;
2176 }
2177
Mark Browncf17c832013-01-30 14:37:23 +08002178 ret = regmap_async_complete(regmap);
2179 if (ret != 0)
2180 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
2181
Mark Brown2159ad932012-10-11 11:54:02 +09002182 if (pos > firmware->size)
2183 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
2184 file, blocks, pos - firmware->size);
2185
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002186 wm_adsp_debugfs_save_binname(dsp, file);
2187
Mark Brown2159ad932012-10-11 11:54:02 +09002188out_fw:
Charles Keepax9da7a5a2014-11-17 10:48:21 +00002189 regmap_async_complete(regmap);
Mark Brown2159ad932012-10-11 11:54:02 +09002190 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +08002191 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09002192out:
2193 kfree(file);
Wei Yongjunf4b82812013-03-12 00:23:15 +08002194 return ret;
Mark Brown2159ad932012-10-11 11:54:02 +09002195}
2196
Charles Keepax3809f002015-04-13 13:27:54 +01002197int wm_adsp1_init(struct wm_adsp *dsp)
Mark Brown5e7a7a22013-01-16 10:03:56 +09002198{
Charles Keepax3809f002015-04-13 13:27:54 +01002199 INIT_LIST_HEAD(&dsp->alg_regions);
Mark Brown5e7a7a22013-01-16 10:03:56 +09002200
Charles Keepax078e7182015-12-08 16:08:26 +00002201 mutex_init(&dsp->pwr_lock);
2202
Mark Brown5e7a7a22013-01-16 10:03:56 +09002203 return 0;
2204}
2205EXPORT_SYMBOL_GPL(wm_adsp1_init);
2206
Mark Brown2159ad932012-10-11 11:54:02 +09002207int wm_adsp1_event(struct snd_soc_dapm_widget *w,
2208 struct snd_kcontrol *kcontrol,
2209 int event)
2210{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01002211 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad932012-10-11 11:54:02 +09002212 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
2213 struct wm_adsp *dsp = &dsps[w->shift];
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002214 struct wm_coeff_ctl *ctl;
Mark Brown2159ad932012-10-11 11:54:02 +09002215 int ret;
Charles Keepax7585a5b2015-12-08 16:08:25 +00002216 unsigned int val;
Mark Brown2159ad932012-10-11 11:54:02 +09002217
Lars-Peter Clausen00200102014-07-17 22:01:07 +02002218 dsp->card = codec->component.card;
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01002219
Charles Keepax078e7182015-12-08 16:08:26 +00002220 mutex_lock(&dsp->pwr_lock);
2221
Mark Brown2159ad932012-10-11 11:54:02 +09002222 switch (event) {
2223 case SND_SOC_DAPM_POST_PMU:
2224 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2225 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
2226
Chris Rattray94e205b2013-01-18 08:43:09 +00002227 /*
2228 * For simplicity set the DSP clock rate to be the
2229 * SYSCLK rate rather than making it configurable.
2230 */
Charles Keepax7585a5b2015-12-08 16:08:25 +00002231 if (dsp->sysclk_reg) {
Chris Rattray94e205b2013-01-18 08:43:09 +00002232 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
2233 if (ret != 0) {
2234 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
2235 ret);
Charles Keepax078e7182015-12-08 16:08:26 +00002236 goto err_mutex;
Chris Rattray94e205b2013-01-18 08:43:09 +00002237 }
2238
Charles Keepax7d00cd92016-02-19 14:44:43 +00002239 val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
Chris Rattray94e205b2013-01-18 08:43:09 +00002240
2241 ret = regmap_update_bits(dsp->regmap,
2242 dsp->base + ADSP1_CONTROL_31,
2243 ADSP1_CLK_SEL_MASK, val);
2244 if (ret != 0) {
2245 adsp_err(dsp, "Failed to set clock rate: %d\n",
2246 ret);
Charles Keepax078e7182015-12-08 16:08:26 +00002247 goto err_mutex;
Chris Rattray94e205b2013-01-18 08:43:09 +00002248 }
2249 }
2250
Mark Brown2159ad932012-10-11 11:54:02 +09002251 ret = wm_adsp_load(dsp);
2252 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002253 goto err_ena;
Mark Brown2159ad932012-10-11 11:54:02 +09002254
Charles Keepaxb618a1852015-04-13 13:27:53 +01002255 ret = wm_adsp1_setup_algs(dsp);
Mark Browndb405172012-10-26 19:30:40 +01002256 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002257 goto err_ena;
Mark Browndb405172012-10-26 19:30:40 +01002258
Mark Brown2159ad932012-10-11 11:54:02 +09002259 ret = wm_adsp_load_coeff(dsp);
2260 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002261 goto err_ena;
Mark Brown2159ad932012-10-11 11:54:02 +09002262
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01002263 /* Initialize caches for enabled and unset controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002264 ret = wm_coeff_init_control_caches(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002265 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002266 goto err_ena;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002267
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01002268 /* Sync set controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002269 ret = wm_coeff_sync_controls(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002270 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002271 goto err_ena;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002272
Charles Keepax28823eb2016-09-20 13:52:32 +01002273 dsp->booted = true;
2274
Mark Brown2159ad932012-10-11 11:54:02 +09002275 /* Start the core running */
2276 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2277 ADSP1_CORE_ENA | ADSP1_START,
2278 ADSP1_CORE_ENA | ADSP1_START);
Charles Keepax28823eb2016-09-20 13:52:32 +01002279
2280 dsp->running = true;
Mark Brown2159ad932012-10-11 11:54:02 +09002281 break;
2282
2283 case SND_SOC_DAPM_PRE_PMD:
Charles Keepax28823eb2016-09-20 13:52:32 +01002284 dsp->running = false;
2285 dsp->booted = false;
2286
Mark Brown2159ad932012-10-11 11:54:02 +09002287 /* Halt the core */
2288 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2289 ADSP1_CORE_ENA | ADSP1_START, 0);
2290
2291 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
2292 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
2293
2294 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2295 ADSP1_SYS_ENA, 0);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002296
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002297 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002298 ctl->enabled = 0;
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00002299
Richard Fitzgerald56574d52016-04-27 14:58:29 +01002300
2301 wm_adsp_free_alg_regions(dsp);
Mark Brown2159ad932012-10-11 11:54:02 +09002302 break;
2303
2304 default:
2305 break;
2306 }
2307
Charles Keepax078e7182015-12-08 16:08:26 +00002308 mutex_unlock(&dsp->pwr_lock);
2309
Mark Brown2159ad932012-10-11 11:54:02 +09002310 return 0;
2311
Charles Keepax078e7182015-12-08 16:08:26 +00002312err_ena:
Mark Brown2159ad932012-10-11 11:54:02 +09002313 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2314 ADSP1_SYS_ENA, 0);
Charles Keepax078e7182015-12-08 16:08:26 +00002315err_mutex:
2316 mutex_unlock(&dsp->pwr_lock);
2317
Mark Brown2159ad932012-10-11 11:54:02 +09002318 return ret;
2319}
2320EXPORT_SYMBOL_GPL(wm_adsp1_event);
2321
2322static int wm_adsp2_ena(struct wm_adsp *dsp)
2323{
2324 unsigned int val;
2325 int ret, count;
2326
Mark Brown1552c322013-11-28 18:11:38 +00002327 ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
2328 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
Mark Brown2159ad932012-10-11 11:54:02 +09002329 if (ret != 0)
2330 return ret;
2331
2332 /* Wait for the RAM to start, should be near instantaneous */
Charles Keepax939fd1e2013-12-18 09:25:49 +00002333 for (count = 0; count < 10; ++count) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00002334 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
Mark Brown2159ad932012-10-11 11:54:02 +09002335 if (ret != 0)
2336 return ret;
Charles Keepax939fd1e2013-12-18 09:25:49 +00002337
2338 if (val & ADSP2_RAM_RDY)
2339 break;
2340
Charles Keepax1fa96f32016-09-26 10:15:22 +01002341 usleep_range(250, 500);
Charles Keepax939fd1e2013-12-18 09:25:49 +00002342 }
Mark Brown2159ad932012-10-11 11:54:02 +09002343
2344 if (!(val & ADSP2_RAM_RDY)) {
2345 adsp_err(dsp, "Failed to start DSP RAM\n");
2346 return -EBUSY;
2347 }
2348
2349 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
Mark Brown2159ad932012-10-11 11:54:02 +09002350
2351 return 0;
2352}
2353
Charles Keepax18b1a902014-01-09 09:06:54 +00002354static void wm_adsp2_boot_work(struct work_struct *work)
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002355{
2356 struct wm_adsp *dsp = container_of(work,
2357 struct wm_adsp,
2358 boot_work);
2359 int ret;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002360
Charles Keepax078e7182015-12-08 16:08:26 +00002361 mutex_lock(&dsp->pwr_lock);
2362
Charles Keepax90d19ba2016-09-26 10:15:23 +01002363 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2364 ADSP2_MEM_ENA, ADSP2_MEM_ENA);
2365 if (ret != 0)
2366 goto err_mutex;
2367
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002368 ret = wm_adsp2_ena(dsp);
2369 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002370 goto err_mutex;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002371
2372 ret = wm_adsp_load(dsp);
2373 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002374 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002375
Charles Keepaxb618a1852015-04-13 13:27:53 +01002376 ret = wm_adsp2_setup_algs(dsp);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002377 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002378 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002379
2380 ret = wm_adsp_load_coeff(dsp);
2381 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002382 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002383
2384 /* Initialize caches for enabled and unset controls */
2385 ret = wm_coeff_init_control_caches(dsp);
2386 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002387 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002388
Charles Keepax28823eb2016-09-20 13:52:32 +01002389 dsp->booted = true;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002390
Charles Keepax90d19ba2016-09-26 10:15:23 +01002391 /* Turn DSP back off until we are ready to run */
2392 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2393 ADSP2_SYS_ENA, 0);
2394 if (ret != 0)
2395 goto err_ena;
2396
Charles Keepax078e7182015-12-08 16:08:26 +00002397 mutex_unlock(&dsp->pwr_lock);
2398
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002399 return;
2400
Charles Keepax078e7182015-12-08 16:08:26 +00002401err_ena:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002402 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2403 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Charles Keepax078e7182015-12-08 16:08:26 +00002404err_mutex:
2405 mutex_unlock(&dsp->pwr_lock);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002406}
2407
Charles Keepaxd82d7672016-01-21 17:53:02 +00002408static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
2409{
2410 int ret;
2411
2412 ret = regmap_update_bits_async(dsp->regmap,
2413 dsp->base + ADSP2_CLOCKING,
2414 ADSP2_CLK_SEL_MASK,
2415 freq << ADSP2_CLK_SEL_SHIFT);
2416 if (ret != 0)
2417 adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
2418}
2419
Charles Keepax12db5ed2014-01-08 17:42:19 +00002420int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
Charles Keepaxd82d7672016-01-21 17:53:02 +00002421 struct snd_kcontrol *kcontrol, int event,
2422 unsigned int freq)
Charles Keepax12db5ed2014-01-08 17:42:19 +00002423{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01002424 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Charles Keepax12db5ed2014-01-08 17:42:19 +00002425 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
2426 struct wm_adsp *dsp = &dsps[w->shift];
Charles Keepax57a60cc2016-09-26 10:15:24 +01002427 struct wm_coeff_ctl *ctl;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002428
Lars-Peter Clausen00200102014-07-17 22:01:07 +02002429 dsp->card = codec->component.card;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002430
2431 switch (event) {
2432 case SND_SOC_DAPM_PRE_PMU:
Charles Keepaxd82d7672016-01-21 17:53:02 +00002433 wm_adsp2_set_dspclk(dsp, freq);
Charles Keepax12db5ed2014-01-08 17:42:19 +00002434 queue_work(system_unbound_wq, &dsp->boot_work);
2435 break;
Charles Keepax57a60cc2016-09-26 10:15:24 +01002436 case SND_SOC_DAPM_PRE_PMD:
2437 wm_adsp_debugfs_clear(dsp);
2438
2439 dsp->fw_id = 0;
2440 dsp->fw_id_version = 0;
2441
2442 dsp->booted = false;
2443
2444 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2445 ADSP2_MEM_ENA, 0);
2446
2447 list_for_each_entry(ctl, &dsp->ctl_list, list)
2448 ctl->enabled = 0;
2449
2450 wm_adsp_free_alg_regions(dsp);
2451
2452 adsp_dbg(dsp, "Shutdown complete\n");
2453 break;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002454 default:
2455 break;
Charles Keepaxcab272582014-04-17 13:42:54 +01002456 }
Charles Keepax12db5ed2014-01-08 17:42:19 +00002457
2458 return 0;
2459}
2460EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
2461
Mark Brown2159ad932012-10-11 11:54:02 +09002462int wm_adsp2_event(struct snd_soc_dapm_widget *w,
2463 struct snd_kcontrol *kcontrol, int event)
2464{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01002465 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad932012-10-11 11:54:02 +09002466 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
2467 struct wm_adsp *dsp = &dsps[w->shift];
2468 int ret;
2469
2470 switch (event) {
2471 case SND_SOC_DAPM_POST_PMU:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002472 flush_work(&dsp->boot_work);
Mark Browndd49e2c2012-12-02 21:50:46 +09002473
Charles Keepax28823eb2016-09-20 13:52:32 +01002474 if (!dsp->booted)
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002475 return -EIO;
Mark Browndd49e2c2012-12-02 21:50:46 +09002476
Charles Keepax90d19ba2016-09-26 10:15:23 +01002477 ret = wm_adsp2_ena(dsp);
2478 if (ret != 0)
2479 goto err;
2480
Charles Keepaxcef45772016-09-20 13:52:33 +01002481 /* Sync set controls */
2482 ret = wm_coeff_sync_controls(dsp);
2483 if (ret != 0)
2484 goto err;
2485
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002486 ret = regmap_update_bits(dsp->regmap,
2487 dsp->base + ADSP2_CONTROL,
Charles Keepax00e4c3b2014-11-18 16:25:27 +00002488 ADSP2_CORE_ENA | ADSP2_START,
2489 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad932012-10-11 11:54:02 +09002490 if (ret != 0)
2491 goto err;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002492
Charles Keepax28823eb2016-09-20 13:52:32 +01002493 dsp->running = true;
2494
Charles Keepax612047f2016-03-28 14:29:22 +01002495 mutex_lock(&dsp->pwr_lock);
2496
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002497 if (wm_adsp_fw[dsp->fw].num_caps != 0)
2498 ret = wm_adsp_buffer_init(dsp);
2499
Charles Keepax612047f2016-03-28 14:29:22 +01002500 mutex_unlock(&dsp->pwr_lock);
2501
Mark Brown2159ad932012-10-11 11:54:02 +09002502 break;
2503
2504 case SND_SOC_DAPM_PRE_PMD:
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00002505 /* Tell the firmware to cleanup */
2506 wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
2507
Richard Fitzgerald10337b02015-05-29 10:23:07 +01002508 /* Log firmware state, it can be useful for analysis */
2509 wm_adsp2_show_fw_status(dsp);
2510
Charles Keepax078e7182015-12-08 16:08:26 +00002511 mutex_lock(&dsp->pwr_lock);
2512
Mark Brown1023dbd2013-01-11 22:58:28 +00002513 dsp->running = false;
2514
Mark Brown2159ad932012-10-11 11:54:02 +09002515 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Charles Keepax57a60cc2016-09-26 10:15:24 +01002516 ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00002517
Mark Brown2d30b572013-01-28 20:18:17 +08002518 /* Make sure DMAs are quiesced */
Simon Trimmer6facd2d2016-06-22 15:31:03 +01002519 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
Mark Brown2d30b572013-01-28 20:18:17 +08002520 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
2521 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
Simon Trimmer6facd2d2016-06-22 15:31:03 +01002522
2523 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2524 ADSP2_SYS_ENA, 0);
Mark Brown2d30b572013-01-28 20:18:17 +08002525
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002526 if (wm_adsp_fw[dsp->fw].num_caps != 0)
2527 wm_adsp_buffer_free(dsp);
2528
Charles Keepax078e7182015-12-08 16:08:26 +00002529 mutex_unlock(&dsp->pwr_lock);
2530
Charles Keepax57a60cc2016-09-26 10:15:24 +01002531 adsp_dbg(dsp, "Execution stopped\n");
Mark Brown2159ad932012-10-11 11:54:02 +09002532 break;
2533
2534 default:
2535 break;
2536 }
2537
2538 return 0;
2539err:
2540 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00002541 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad932012-10-11 11:54:02 +09002542 return ret;
2543}
2544EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00002545
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002546int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
2547{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002548 wm_adsp2_init_debugfs(dsp, codec);
2549
Richard Fitzgerald218e5082015-06-11 11:32:31 +01002550 return snd_soc_add_codec_controls(codec,
Richard Fitzgerald336d0442015-06-18 13:43:19 +01002551 &wm_adsp_fw_controls[dsp->num - 1],
2552 1);
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002553}
2554EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe);
2555
2556int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec)
2557{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002558 wm_adsp2_cleanup_debugfs(dsp);
2559
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002560 return 0;
2561}
2562EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove);
2563
Richard Fitzgerald81ac58b2015-06-02 11:53:34 +01002564int wm_adsp2_init(struct wm_adsp *dsp)
Mark Brown973838a2012-11-28 17:20:32 +00002565{
2566 int ret;
2567
Mark Brown10a2b662012-12-02 21:37:00 +09002568 /*
2569 * Disable the DSP memory by default when in reset for a small
2570 * power saving.
2571 */
Charles Keepax3809f002015-04-13 13:27:54 +01002572 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Brown10a2b662012-12-02 21:37:00 +09002573 ADSP2_MEM_ENA, 0);
2574 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01002575 adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
Mark Brown10a2b662012-12-02 21:37:00 +09002576 return ret;
2577 }
2578
Charles Keepax3809f002015-04-13 13:27:54 +01002579 INIT_LIST_HEAD(&dsp->alg_regions);
2580 INIT_LIST_HEAD(&dsp->ctl_list);
2581 INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002582
Charles Keepax078e7182015-12-08 16:08:26 +00002583 mutex_init(&dsp->pwr_lock);
2584
Mark Brown973838a2012-11-28 17:20:32 +00002585 return 0;
2586}
2587EXPORT_SYMBOL_GPL(wm_adsp2_init);
Praveen Diwakar0a37c6ef2014-07-04 11:17:41 +05302588
Richard Fitzgerald66225e92016-04-27 14:58:27 +01002589void wm_adsp2_remove(struct wm_adsp *dsp)
2590{
2591 struct wm_coeff_ctl *ctl;
2592
2593 while (!list_empty(&dsp->ctl_list)) {
2594 ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
2595 list);
2596 list_del(&ctl->list);
2597 wm_adsp_free_ctl_blk(ctl);
2598 }
2599}
2600EXPORT_SYMBOL_GPL(wm_adsp2_remove);
2601
Charles Keepaxedd71352016-05-04 17:11:55 +01002602static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
2603{
2604 return compr->buf != NULL;
2605}
2606
2607static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
2608{
2609 /*
2610 * Note this will be more complex once each DSP can support multiple
2611 * streams
2612 */
2613 if (!compr->dsp->buffer)
2614 return -EINVAL;
2615
2616 compr->buf = compr->dsp->buffer;
Charles Keepax721be3b2016-05-04 17:11:56 +01002617 compr->buf->compr = compr;
Charles Keepaxedd71352016-05-04 17:11:55 +01002618
2619 return 0;
2620}
2621
Charles Keepax721be3b2016-05-04 17:11:56 +01002622static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
2623{
2624 if (!compr)
2625 return;
2626
2627 /* Wake the poll so it can see buffer is no longer attached */
2628 if (compr->stream)
2629 snd_compr_fragment_elapsed(compr->stream);
2630
2631 if (wm_adsp_compr_attached(compr)) {
2632 compr->buf->compr = NULL;
2633 compr->buf = NULL;
2634 }
2635}
2636
Charles Keepax406abc92015-12-15 11:29:45 +00002637int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
2638{
2639 struct wm_adsp_compr *compr;
2640 int ret = 0;
2641
2642 mutex_lock(&dsp->pwr_lock);
2643
2644 if (wm_adsp_fw[dsp->fw].num_caps == 0) {
2645 adsp_err(dsp, "Firmware does not support compressed API\n");
2646 ret = -ENXIO;
2647 goto out;
2648 }
2649
2650 if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
2651 adsp_err(dsp, "Firmware does not support stream direction\n");
2652 ret = -EINVAL;
2653 goto out;
2654 }
2655
Charles Keepax95fe9592015-12-15 11:29:47 +00002656 if (dsp->compr) {
2657 /* It is expect this limitation will be removed in future */
2658 adsp_err(dsp, "Only a single stream supported per DSP\n");
2659 ret = -EBUSY;
2660 goto out;
2661 }
2662
Charles Keepax406abc92015-12-15 11:29:45 +00002663 compr = kzalloc(sizeof(*compr), GFP_KERNEL);
2664 if (!compr) {
2665 ret = -ENOMEM;
2666 goto out;
2667 }
2668
2669 compr->dsp = dsp;
2670 compr->stream = stream;
2671
2672 dsp->compr = compr;
2673
2674 stream->runtime->private_data = compr;
2675
2676out:
2677 mutex_unlock(&dsp->pwr_lock);
2678
2679 return ret;
2680}
2681EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
2682
2683int wm_adsp_compr_free(struct snd_compr_stream *stream)
2684{
2685 struct wm_adsp_compr *compr = stream->runtime->private_data;
2686 struct wm_adsp *dsp = compr->dsp;
2687
2688 mutex_lock(&dsp->pwr_lock);
2689
Charles Keepax721be3b2016-05-04 17:11:56 +01002690 wm_adsp_compr_detach(compr);
Charles Keepax406abc92015-12-15 11:29:45 +00002691 dsp->compr = NULL;
2692
Charles Keepax83a40ce2016-01-06 12:33:19 +00002693 kfree(compr->raw_buf);
Charles Keepax406abc92015-12-15 11:29:45 +00002694 kfree(compr);
2695
2696 mutex_unlock(&dsp->pwr_lock);
2697
2698 return 0;
2699}
2700EXPORT_SYMBOL_GPL(wm_adsp_compr_free);
2701
2702static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
2703 struct snd_compr_params *params)
2704{
2705 struct wm_adsp_compr *compr = stream->runtime->private_data;
2706 struct wm_adsp *dsp = compr->dsp;
2707 const struct wm_adsp_fw_caps *caps;
2708 const struct snd_codec_desc *desc;
2709 int i, j;
2710
2711 if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||
2712 params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
2713 params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
2714 params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
2715 params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) {
2716 adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n",
2717 params->buffer.fragment_size,
2718 params->buffer.fragments);
2719
2720 return -EINVAL;
2721 }
2722
2723 for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
2724 caps = &wm_adsp_fw[dsp->fw].caps[i];
2725 desc = &caps->desc;
2726
2727 if (caps->id != params->codec.id)
2728 continue;
2729
2730 if (stream->direction == SND_COMPRESS_PLAYBACK) {
2731 if (desc->max_ch < params->codec.ch_out)
2732 continue;
2733 } else {
2734 if (desc->max_ch < params->codec.ch_in)
2735 continue;
2736 }
2737
2738 if (!(desc->formats & (1 << params->codec.format)))
2739 continue;
2740
2741 for (j = 0; j < desc->num_sample_rates; ++j)
2742 if (desc->sample_rates[j] == params->codec.sample_rate)
2743 return 0;
2744 }
2745
2746 adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
2747 params->codec.id, params->codec.ch_in, params->codec.ch_out,
2748 params->codec.sample_rate, params->codec.format);
2749 return -EINVAL;
2750}
2751
Charles Keepax565ace42016-01-06 12:33:18 +00002752static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
2753{
2754 return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
2755}
2756
Charles Keepax406abc92015-12-15 11:29:45 +00002757int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
2758 struct snd_compr_params *params)
2759{
2760 struct wm_adsp_compr *compr = stream->runtime->private_data;
Charles Keepax83a40ce2016-01-06 12:33:19 +00002761 unsigned int size;
Charles Keepax406abc92015-12-15 11:29:45 +00002762 int ret;
2763
2764 ret = wm_adsp_compr_check_params(stream, params);
2765 if (ret)
2766 return ret;
2767
2768 compr->size = params->buffer;
2769
2770 adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n",
2771 compr->size.fragment_size, compr->size.fragments);
2772
Charles Keepax83a40ce2016-01-06 12:33:19 +00002773 size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
2774 compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
2775 if (!compr->raw_buf)
2776 return -ENOMEM;
2777
Charles Keepaxda2b3352016-02-02 16:41:36 +00002778 compr->sample_rate = params->codec.sample_rate;
2779
Charles Keepax406abc92015-12-15 11:29:45 +00002780 return 0;
2781}
2782EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
2783
2784int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
2785 struct snd_compr_caps *caps)
2786{
2787 struct wm_adsp_compr *compr = stream->runtime->private_data;
2788 int fw = compr->dsp->fw;
2789 int i;
2790
2791 if (wm_adsp_fw[fw].caps) {
2792 for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
2793 caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
2794
2795 caps->num_codecs = i;
2796 caps->direction = wm_adsp_fw[fw].compr_direction;
2797
2798 caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
2799 caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
2800 caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;
2801 caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;
2802 }
2803
2804 return 0;
2805}
2806EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
2807
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002808static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
2809 unsigned int mem_addr,
2810 unsigned int num_words, u32 *data)
2811{
2812 struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
2813 unsigned int i, reg;
2814 int ret;
2815
2816 if (!mem)
2817 return -EINVAL;
2818
2819 reg = wm_adsp_region_to_reg(mem, mem_addr);
2820
2821 ret = regmap_raw_read(dsp->regmap, reg, data,
2822 sizeof(*data) * num_words);
2823 if (ret < 0)
2824 return ret;
2825
2826 for (i = 0; i < num_words; ++i)
2827 data[i] = be32_to_cpu(data[i]) & 0x00ffffffu;
2828
2829 return 0;
2830}
2831
2832static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type,
2833 unsigned int mem_addr, u32 *data)
2834{
2835 return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data);
2836}
2837
2838static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
2839 unsigned int mem_addr, u32 data)
2840{
2841 struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
2842 unsigned int reg;
2843
2844 if (!mem)
2845 return -EINVAL;
2846
2847 reg = wm_adsp_region_to_reg(mem, mem_addr);
2848
2849 data = cpu_to_be32(data & 0x00ffffffu);
2850
2851 return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data));
2852}
2853
2854static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
2855 unsigned int field_offset, u32 *data)
2856{
2857 return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM,
2858 buf->host_buf_ptr + field_offset, data);
2859}
2860
2861static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
2862 unsigned int field_offset, u32 data)
2863{
2864 return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM,
2865 buf->host_buf_ptr + field_offset, data);
2866}
2867
2868static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
2869{
2870 struct wm_adsp_alg_region *alg_region;
2871 struct wm_adsp *dsp = buf->dsp;
2872 u32 xmalg, addr, magic;
2873 int i, ret;
2874
2875 alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
2876 xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
2877
2878 addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
2879 ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
2880 if (ret < 0)
2881 return ret;
2882
2883 if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
2884 return -EINVAL;
2885
2886 addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
2887 for (i = 0; i < 5; ++i) {
2888 ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
2889 &buf->host_buf_ptr);
2890 if (ret < 0)
2891 return ret;
2892
2893 if (buf->host_buf_ptr)
2894 break;
2895
2896 usleep_range(1000, 2000);
2897 }
2898
2899 if (!buf->host_buf_ptr)
2900 return -EIO;
2901
2902 adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
2903
2904 return 0;
2905}
2906
2907static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
2908{
2909 const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
2910 struct wm_adsp_buffer_region *region;
2911 u32 offset = 0;
2912 int i, ret;
2913
2914 for (i = 0; i < caps->num_regions; ++i) {
2915 region = &buf->regions[i];
2916
2917 region->offset = offset;
2918 region->mem_type = caps->region_defs[i].mem_type;
2919
2920 ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
2921 &region->base_addr);
2922 if (ret < 0)
2923 return ret;
2924
2925 ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
2926 &offset);
2927 if (ret < 0)
2928 return ret;
2929
2930 region->cumulative_size = offset;
2931
2932 adsp_dbg(buf->dsp,
2933 "region=%d type=%d base=%04x off=%04x size=%04x\n",
2934 i, region->mem_type, region->base_addr,
2935 region->offset, region->cumulative_size);
2936 }
2937
2938 return 0;
2939}
2940
2941static int wm_adsp_buffer_init(struct wm_adsp *dsp)
2942{
2943 struct wm_adsp_compr_buf *buf;
2944 int ret;
2945
2946 buf = kzalloc(sizeof(*buf), GFP_KERNEL);
2947 if (!buf)
2948 return -ENOMEM;
2949
2950 buf->dsp = dsp;
Charles Keepax565ace42016-01-06 12:33:18 +00002951 buf->read_index = -1;
2952 buf->irq_count = 0xFFFFFFFF;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002953
2954 ret = wm_adsp_buffer_locate(buf);
2955 if (ret < 0) {
2956 adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret);
2957 goto err_buffer;
2958 }
2959
2960 buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions,
2961 sizeof(*buf->regions), GFP_KERNEL);
2962 if (!buf->regions) {
2963 ret = -ENOMEM;
2964 goto err_buffer;
2965 }
2966
2967 ret = wm_adsp_buffer_populate(buf);
2968 if (ret < 0) {
2969 adsp_err(dsp, "Failed to populate host buffer: %d\n", ret);
2970 goto err_regions;
2971 }
2972
2973 dsp->buffer = buf;
2974
2975 return 0;
2976
2977err_regions:
2978 kfree(buf->regions);
2979err_buffer:
2980 kfree(buf);
2981 return ret;
2982}
2983
2984static int wm_adsp_buffer_free(struct wm_adsp *dsp)
2985{
2986 if (dsp->buffer) {
Charles Keepax721be3b2016-05-04 17:11:56 +01002987 wm_adsp_compr_detach(dsp->buffer->compr);
2988
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002989 kfree(dsp->buffer->regions);
2990 kfree(dsp->buffer);
2991
2992 dsp->buffer = NULL;
2993 }
2994
2995 return 0;
2996}
2997
Charles Keepax95fe9592015-12-15 11:29:47 +00002998int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
2999{
3000 struct wm_adsp_compr *compr = stream->runtime->private_data;
3001 struct wm_adsp *dsp = compr->dsp;
3002 int ret = 0;
3003
3004 adsp_dbg(dsp, "Trigger: %d\n", cmd);
3005
3006 mutex_lock(&dsp->pwr_lock);
3007
3008 switch (cmd) {
3009 case SNDRV_PCM_TRIGGER_START:
3010 if (wm_adsp_compr_attached(compr))
3011 break;
3012
3013 ret = wm_adsp_compr_attach(compr);
3014 if (ret < 0) {
3015 adsp_err(dsp, "Failed to link buffer and stream: %d\n",
3016 ret);
3017 break;
3018 }
Charles Keepax565ace42016-01-06 12:33:18 +00003019
3020 /* Trigger the IRQ at one fragment of data */
3021 ret = wm_adsp_buffer_write(compr->buf,
3022 HOST_BUFFER_FIELD(high_water_mark),
3023 wm_adsp_compr_frag_words(compr));
3024 if (ret < 0) {
3025 adsp_err(dsp, "Failed to set high water mark: %d\n",
3026 ret);
3027 break;
3028 }
Charles Keepax95fe9592015-12-15 11:29:47 +00003029 break;
3030 case SNDRV_PCM_TRIGGER_STOP:
3031 break;
3032 default:
3033 ret = -EINVAL;
3034 break;
3035 }
3036
3037 mutex_unlock(&dsp->pwr_lock);
3038
3039 return ret;
3040}
3041EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
3042
Charles Keepax565ace42016-01-06 12:33:18 +00003043static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
3044{
3045 int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
3046
3047 return buf->regions[last_region].cumulative_size;
3048}
3049
3050static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
3051{
3052 u32 next_read_index, next_write_index;
3053 int write_index, read_index, avail;
3054 int ret;
3055
3056 /* Only sync read index if we haven't already read a valid index */
3057 if (buf->read_index < 0) {
3058 ret = wm_adsp_buffer_read(buf,
3059 HOST_BUFFER_FIELD(next_read_index),
3060 &next_read_index);
3061 if (ret < 0)
3062 return ret;
3063
3064 read_index = sign_extend32(next_read_index, 23);
3065
3066 if (read_index < 0) {
3067 adsp_dbg(buf->dsp, "Avail check on unstarted stream\n");
3068 return 0;
3069 }
3070
3071 buf->read_index = read_index;
3072 }
3073
3074 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
3075 &next_write_index);
3076 if (ret < 0)
3077 return ret;
3078
3079 write_index = sign_extend32(next_write_index, 23);
3080
3081 avail = write_index - buf->read_index;
3082 if (avail < 0)
3083 avail += wm_adsp_buffer_size(buf);
3084
3085 adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
Charles Keepax33d740e2016-03-28 14:29:21 +01003086 buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
Charles Keepax565ace42016-01-06 12:33:18 +00003087
3088 buf->avail = avail;
3089
3090 return 0;
3091}
3092
Charles Keepax9771b182016-04-06 11:21:53 +01003093static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
3094{
3095 int ret;
3096
3097 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
3098 if (ret < 0) {
3099 adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret);
3100 return ret;
3101 }
3102 if (buf->error != 0) {
3103 adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error);
3104 return -EIO;
3105 }
3106
3107 return 0;
3108}
3109
Charles Keepax565ace42016-01-06 12:33:18 +00003110int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
3111{
Charles Keepax612047f2016-03-28 14:29:22 +01003112 struct wm_adsp_compr_buf *buf;
3113 struct wm_adsp_compr *compr;
Charles Keepax565ace42016-01-06 12:33:18 +00003114 int ret = 0;
3115
3116 mutex_lock(&dsp->pwr_lock);
3117
Charles Keepax612047f2016-03-28 14:29:22 +01003118 buf = dsp->buffer;
3119 compr = dsp->compr;
3120
Charles Keepax565ace42016-01-06 12:33:18 +00003121 if (!buf) {
Charles Keepax565ace42016-01-06 12:33:18 +00003122 ret = -ENODEV;
3123 goto out;
3124 }
3125
3126 adsp_dbg(dsp, "Handling buffer IRQ\n");
3127
Charles Keepax9771b182016-04-06 11:21:53 +01003128 ret = wm_adsp_buffer_get_error(buf);
3129 if (ret < 0)
Charles Keepax58476092016-04-06 11:21:54 +01003130 goto out_notify; /* Wake poll to report error */
Charles Keepax565ace42016-01-06 12:33:18 +00003131
3132 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
3133 &buf->irq_count);
3134 if (ret < 0) {
3135 adsp_err(dsp, "Failed to get irq_count: %d\n", ret);
3136 goto out;
3137 }
3138
3139 ret = wm_adsp_buffer_update_avail(buf);
3140 if (ret < 0) {
3141 adsp_err(dsp, "Error reading avail: %d\n", ret);
3142 goto out;
3143 }
3144
Charles Keepax20b7f7c2016-05-13 16:45:17 +01003145 if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
3146 ret = WM_ADSP_COMPR_VOICE_TRIGGER;
3147
Charles Keepax58476092016-04-06 11:21:54 +01003148out_notify:
Charles Keepaxc7dae7c2016-02-19 14:44:41 +00003149 if (compr && compr->stream)
Charles Keepax83a40ce2016-01-06 12:33:19 +00003150 snd_compr_fragment_elapsed(compr->stream);
3151
Charles Keepax565ace42016-01-06 12:33:18 +00003152out:
3153 mutex_unlock(&dsp->pwr_lock);
3154
3155 return ret;
3156}
3157EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
3158
3159static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
3160{
3161 if (buf->irq_count & 0x01)
3162 return 0;
3163
3164 adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n",
3165 buf->irq_count);
3166
3167 buf->irq_count |= 0x01;
3168
3169 return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
3170 buf->irq_count);
3171}
3172
3173int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
3174 struct snd_compr_tstamp *tstamp)
3175{
3176 struct wm_adsp_compr *compr = stream->runtime->private_data;
Charles Keepax565ace42016-01-06 12:33:18 +00003177 struct wm_adsp *dsp = compr->dsp;
Charles Keepax612047f2016-03-28 14:29:22 +01003178 struct wm_adsp_compr_buf *buf;
Charles Keepax565ace42016-01-06 12:33:18 +00003179 int ret = 0;
3180
3181 adsp_dbg(dsp, "Pointer request\n");
3182
3183 mutex_lock(&dsp->pwr_lock);
3184
Charles Keepax612047f2016-03-28 14:29:22 +01003185 buf = compr->buf;
3186
Charles Keepax28ee3d72016-06-13 14:17:12 +01003187 if (!compr->buf || compr->buf->error) {
Charles Keepax8d280662016-06-13 14:17:11 +01003188 snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
Charles Keepax565ace42016-01-06 12:33:18 +00003189 ret = -EIO;
3190 goto out;
3191 }
3192
3193 if (buf->avail < wm_adsp_compr_frag_words(compr)) {
3194 ret = wm_adsp_buffer_update_avail(buf);
3195 if (ret < 0) {
3196 adsp_err(dsp, "Error reading avail: %d\n", ret);
3197 goto out;
3198 }
3199
3200 /*
3201 * If we really have less than 1 fragment available tell the
3202 * DSP to inform us once a whole fragment is available.
3203 */
3204 if (buf->avail < wm_adsp_compr_frag_words(compr)) {
Charles Keepax58476092016-04-06 11:21:54 +01003205 ret = wm_adsp_buffer_get_error(buf);
Charles Keepax8d280662016-06-13 14:17:11 +01003206 if (ret < 0) {
3207 if (compr->buf->error)
3208 snd_compr_stop_error(stream,
3209 SNDRV_PCM_STATE_XRUN);
Charles Keepax58476092016-04-06 11:21:54 +01003210 goto out;
Charles Keepax8d280662016-06-13 14:17:11 +01003211 }
Charles Keepax58476092016-04-06 11:21:54 +01003212
Charles Keepax565ace42016-01-06 12:33:18 +00003213 ret = wm_adsp_buffer_reenable_irq(buf);
3214 if (ret < 0) {
3215 adsp_err(dsp,
3216 "Failed to re-enable buffer IRQ: %d\n",
3217 ret);
3218 goto out;
3219 }
3220 }
3221 }
3222
3223 tstamp->copied_total = compr->copied_total;
3224 tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
Charles Keepaxda2b3352016-02-02 16:41:36 +00003225 tstamp->sampling_rate = compr->sample_rate;
Charles Keepax565ace42016-01-06 12:33:18 +00003226
3227out:
3228 mutex_unlock(&dsp->pwr_lock);
3229
3230 return ret;
3231}
3232EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
3233
Charles Keepax83a40ce2016-01-06 12:33:19 +00003234static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
3235{
3236 struct wm_adsp_compr_buf *buf = compr->buf;
3237 u8 *pack_in = (u8 *)compr->raw_buf;
3238 u8 *pack_out = (u8 *)compr->raw_buf;
3239 unsigned int adsp_addr;
3240 int mem_type, nwords, max_read;
3241 int i, j, ret;
3242
3243 /* Calculate read parameters */
3244 for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
3245 if (buf->read_index < buf->regions[i].cumulative_size)
3246 break;
3247
3248 if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
3249 return -EINVAL;
3250
3251 mem_type = buf->regions[i].mem_type;
3252 adsp_addr = buf->regions[i].base_addr +
3253 (buf->read_index - buf->regions[i].offset);
3254
3255 max_read = wm_adsp_compr_frag_words(compr);
3256 nwords = buf->regions[i].cumulative_size - buf->read_index;
3257
3258 if (nwords > target)
3259 nwords = target;
3260 if (nwords > buf->avail)
3261 nwords = buf->avail;
3262 if (nwords > max_read)
3263 nwords = max_read;
3264 if (!nwords)
3265 return 0;
3266
3267 /* Read data from DSP */
3268 ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr,
3269 nwords, compr->raw_buf);
3270 if (ret < 0)
3271 return ret;
3272
3273 /* Remove the padding bytes from the data read from the DSP */
3274 for (i = 0; i < nwords; i++) {
3275 for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++)
3276 *pack_out++ = *pack_in++;
3277
3278 pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE;
3279 }
3280
3281 /* update read index to account for words read */
3282 buf->read_index += nwords;
3283 if (buf->read_index == wm_adsp_buffer_size(buf))
3284 buf->read_index = 0;
3285
3286 ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
3287 buf->read_index);
3288 if (ret < 0)
3289 return ret;
3290
3291 /* update avail to account for words read */
3292 buf->avail -= nwords;
3293
3294 return nwords;
3295}
3296
3297static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
3298 char __user *buf, size_t count)
3299{
3300 struct wm_adsp *dsp = compr->dsp;
3301 int ntotal = 0;
3302 int nwords, nbytes;
3303
3304 adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
3305
Charles Keepax28ee3d72016-06-13 14:17:12 +01003306 if (!compr->buf || compr->buf->error) {
Charles Keepax8d280662016-06-13 14:17:11 +01003307 snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003308 return -EIO;
Charles Keepax8d280662016-06-13 14:17:11 +01003309 }
Charles Keepax83a40ce2016-01-06 12:33:19 +00003310
3311 count /= WM_ADSP_DATA_WORD_SIZE;
3312
3313 do {
3314 nwords = wm_adsp_buffer_capture_block(compr, count);
3315 if (nwords < 0) {
3316 adsp_err(dsp, "Failed to capture block: %d\n", nwords);
3317 return nwords;
3318 }
3319
3320 nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
3321
3322 adsp_dbg(dsp, "Read %d bytes\n", nbytes);
3323
3324 if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
3325 adsp_err(dsp, "Failed to copy data to user: %d, %d\n",
3326 ntotal, nbytes);
3327 return -EFAULT;
3328 }
3329
3330 count -= nwords;
3331 ntotal += nbytes;
3332 } while (nwords > 0 && count > 0);
3333
3334 compr->copied_total += ntotal;
3335
3336 return ntotal;
3337}
3338
3339int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
3340 size_t count)
3341{
3342 struct wm_adsp_compr *compr = stream->runtime->private_data;
3343 struct wm_adsp *dsp = compr->dsp;
3344 int ret;
3345
3346 mutex_lock(&dsp->pwr_lock);
3347
3348 if (stream->direction == SND_COMPRESS_CAPTURE)
3349 ret = wm_adsp_compr_read(compr, buf, count);
3350 else
3351 ret = -ENOTSUPP;
3352
3353 mutex_unlock(&dsp->pwr_lock);
3354
3355 return ret;
3356}
3357EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
3358
Praveen Diwakar0a37c6ef2014-07-04 11:17:41 +05303359MODULE_LICENSE("GPL v2");