blob: c19a8a041d4dd8fdbf35fb1745bce78b1409d1a5 [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
Richard Fitzgerald605391d2018-08-08 17:13:39 +010013#include <linux/ctype.h>
Mark Brown2159ad932012-10-11 11:54:02 +090014#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/firmware.h>
Mark Browncf17c832013-01-30 14:37:23 +080019#include <linux/list.h>
Mark Brown2159ad932012-10-11 11:54:02 +090020#include <linux/pm.h>
21#include <linux/pm_runtime.h>
22#include <linux/regmap.h>
Mark Brown973838a2012-11-28 17:20:32 +000023#include <linux/regulator/consumer.h>
Mark Brown2159ad932012-10-11 11:54:02 +090024#include <linux/slab.h>
Charles Keepaxcdcd7f72014-11-14 15:40:45 +000025#include <linux/vmalloc.h>
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +010026#include <linux/workqueue.h>
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +010027#include <linux/debugfs.h>
Mark Brown2159ad932012-10-11 11:54:02 +090028#include <sound/core.h>
29#include <sound/pcm.h>
30#include <sound/pcm_params.h>
31#include <sound/soc.h>
32#include <sound/jack.h>
33#include <sound/initval.h>
34#include <sound/tlv.h>
35
Mark Brown2159ad932012-10-11 11:54:02 +090036#include "wm_adsp.h"
37
38#define adsp_crit(_dsp, fmt, ...) \
Richard Fitzgerald605391d2018-08-08 17:13:39 +010039 dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
Mark Brown2159ad932012-10-11 11:54:02 +090040#define adsp_err(_dsp, fmt, ...) \
Richard Fitzgerald605391d2018-08-08 17:13:39 +010041 dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
Mark Brown2159ad932012-10-11 11:54:02 +090042#define adsp_warn(_dsp, fmt, ...) \
Richard Fitzgerald605391d2018-08-08 17:13:39 +010043 dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
Mark Brown2159ad932012-10-11 11:54:02 +090044#define adsp_info(_dsp, fmt, ...) \
Richard Fitzgerald605391d2018-08-08 17:13:39 +010045 dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
Mark Brown2159ad932012-10-11 11:54:02 +090046#define adsp_dbg(_dsp, fmt, ...) \
Richard Fitzgerald605391d2018-08-08 17:13:39 +010047 dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)
Mark Brown2159ad932012-10-11 11:54:02 +090048
Charles Keepax0d3fba32019-02-22 10:04:21 +000049#define compr_err(_obj, fmt, ...) \
50 adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
51 ##__VA_ARGS__)
52#define compr_dbg(_obj, fmt, ...) \
53 adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \
54 ##__VA_ARGS__)
55
Mark Brown2159ad932012-10-11 11:54:02 +090056#define ADSP1_CONTROL_1 0x00
57#define ADSP1_CONTROL_2 0x02
58#define ADSP1_CONTROL_3 0x03
59#define ADSP1_CONTROL_4 0x04
60#define ADSP1_CONTROL_5 0x06
61#define ADSP1_CONTROL_6 0x07
62#define ADSP1_CONTROL_7 0x08
63#define ADSP1_CONTROL_8 0x09
64#define ADSP1_CONTROL_9 0x0A
65#define ADSP1_CONTROL_10 0x0B
66#define ADSP1_CONTROL_11 0x0C
67#define ADSP1_CONTROL_12 0x0D
68#define ADSP1_CONTROL_13 0x0F
69#define ADSP1_CONTROL_14 0x10
70#define ADSP1_CONTROL_15 0x11
71#define ADSP1_CONTROL_16 0x12
72#define ADSP1_CONTROL_17 0x13
73#define ADSP1_CONTROL_18 0x14
74#define ADSP1_CONTROL_19 0x16
75#define ADSP1_CONTROL_20 0x17
76#define ADSP1_CONTROL_21 0x18
77#define ADSP1_CONTROL_22 0x1A
78#define ADSP1_CONTROL_23 0x1B
79#define ADSP1_CONTROL_24 0x1C
80#define ADSP1_CONTROL_25 0x1E
81#define ADSP1_CONTROL_26 0x20
82#define ADSP1_CONTROL_27 0x21
83#define ADSP1_CONTROL_28 0x22
84#define ADSP1_CONTROL_29 0x23
85#define ADSP1_CONTROL_30 0x24
86#define ADSP1_CONTROL_31 0x26
87
88/*
89 * ADSP1 Control 19
90 */
91#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
92#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
93#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
94
95
96/*
97 * ADSP1 Control 30
98 */
99#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
100#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
101#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
102#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
103#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
104#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
105#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
106#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
107#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
108#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
109#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
110#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
111#define ADSP1_START 0x0001 /* DSP1_START */
112#define ADSP1_START_MASK 0x0001 /* DSP1_START */
113#define ADSP1_START_SHIFT 0 /* DSP1_START */
114#define ADSP1_START_WIDTH 1 /* DSP1_START */
115
Chris Rattray94e205b2013-01-18 08:43:09 +0000116/*
117 * ADSP1 Control 31
118 */
119#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
120#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
121#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
122
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100123#define ADSP2_CONTROL 0x0
124#define ADSP2_CLOCKING 0x1
125#define ADSP2V2_CLOCKING 0x2
126#define ADSP2_STATUS1 0x4
127#define ADSP2_WDMA_CONFIG_1 0x30
128#define ADSP2_WDMA_CONFIG_2 0x31
129#define ADSP2V2_WDMA_CONFIG_2 0x32
130#define ADSP2_RDMA_CONFIG_1 0x34
Mark Brown2159ad932012-10-11 11:54:02 +0900131
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100132#define ADSP2_SCRATCH0 0x40
133#define ADSP2_SCRATCH1 0x41
134#define ADSP2_SCRATCH2 0x42
135#define ADSP2_SCRATCH3 0x43
136
137#define ADSP2V2_SCRATCH0_1 0x40
138#define ADSP2V2_SCRATCH2_3 0x42
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100139
Mark Brown2159ad932012-10-11 11:54:02 +0900140/*
141 * ADSP2 Control
142 */
143
144#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
145#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
146#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
147#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
148#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
149#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
150#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
151#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
152#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
153#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
154#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
155#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
156#define ADSP2_START 0x0001 /* DSP1_START */
157#define ADSP2_START_MASK 0x0001 /* DSP1_START */
158#define ADSP2_START_SHIFT 0 /* DSP1_START */
159#define ADSP2_START_WIDTH 1 /* DSP1_START */
160
161/*
Mark Brown973838a2012-11-28 17:20:32 +0000162 * ADSP2 clocking
163 */
164#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
165#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
166#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
167
168/*
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100169 * ADSP2V2 clocking
170 */
171#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */
172#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */
173#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
174
175#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */
176#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */
177#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */
178
179/*
Mark Brown2159ad932012-10-11 11:54:02 +0900180 * ADSP2 Status 1
181 */
182#define ADSP2_RAM_RDY 0x0001
183#define ADSP2_RAM_RDY_MASK 0x0001
184#define ADSP2_RAM_RDY_SHIFT 0
185#define ADSP2_RAM_RDY_WIDTH 1
186
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +0100187/*
188 * ADSP2 Lock support
189 */
190#define ADSP2_LOCK_CODE_0 0x5555
191#define ADSP2_LOCK_CODE_1 0xAAAA
192
193#define ADSP2_WATCHDOG 0x0A
194#define ADSP2_BUS_ERR_ADDR 0x52
195#define ADSP2_REGION_LOCK_STATUS 0x64
196#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66
197#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68
198#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A
199#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C
200#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E
201#define ADSP2_LOCK_REGION_CTRL 0x7A
202#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C
203
204#define ADSP2_REGION_LOCK_ERR_MASK 0x8000
205#define ADSP2_SLAVE_ERR_MASK 0x4000
206#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
207#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002
208#define ADSP2_CTRL_ERR_EINT 0x0001
209
210#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF
211#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF
212#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000
213#define ADSP2_PMEM_ERR_ADDR_SHIFT 16
214#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD
215
216#define ADSP2_LOCK_REGION_SHIFT 16
217
Charles Keepax9ee78752016-05-02 13:57:36 +0100218#define ADSP_MAX_STD_CTRL_SIZE 512
219
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000220#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
221#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +0000222#define WM_ADSP_ACKED_CTL_MIN_VALUE 0
223#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000224
225/*
226 * Event control messages
227 */
228#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
229
Mark Browncf17c832013-01-30 14:37:23 +0800230struct wm_adsp_buf {
231 struct list_head list;
232 void *buf;
233};
234
235static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
236 struct list_head *list)
237{
238 struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
239
240 if (buf == NULL)
241 return NULL;
242
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000243 buf->buf = vmalloc(len);
Mark Browncf17c832013-01-30 14:37:23 +0800244 if (!buf->buf) {
Richard Fitzgerald4d41c742016-12-09 09:57:41 +0000245 kfree(buf);
Mark Browncf17c832013-01-30 14:37:23 +0800246 return NULL;
247 }
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000248 memcpy(buf->buf, src, len);
Mark Browncf17c832013-01-30 14:37:23 +0800249
250 if (list)
251 list_add_tail(&buf->list, list);
252
253 return buf;
254}
255
256static void wm_adsp_buf_free(struct list_head *list)
257{
258 while (!list_empty(list)) {
259 struct wm_adsp_buf *buf = list_first_entry(list,
260 struct wm_adsp_buf,
261 list);
262 list_del(&buf->list);
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000263 vfree(buf->buf);
Mark Browncf17c832013-01-30 14:37:23 +0800264 kfree(buf);
265 }
266}
267
Charles Keepax04d13002015-11-26 14:01:52 +0000268#define WM_ADSP_FW_MBC_VSS 0
269#define WM_ADSP_FW_HIFI 1
270#define WM_ADSP_FW_TX 2
271#define WM_ADSP_FW_TX_SPK 3
272#define WM_ADSP_FW_RX 4
273#define WM_ADSP_FW_RX_ANC 5
274#define WM_ADSP_FW_CTRL 6
275#define WM_ADSP_FW_ASR 7
276#define WM_ADSP_FW_TRACE 8
277#define WM_ADSP_FW_SPK_PROT 9
278#define WM_ADSP_FW_MISC 10
Mark Brown1023dbd2013-01-11 22:58:28 +0000279
Charles Keepax04d13002015-11-26 14:01:52 +0000280#define WM_ADSP_NUM_FW 11
Mark Browndd84f922013-03-08 15:25:58 +0800281
Mark Brown1023dbd2013-01-11 22:58:28 +0000282static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000283 [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
284 [WM_ADSP_FW_HIFI] = "MasterHiFi",
285 [WM_ADSP_FW_TX] = "Tx",
286 [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
287 [WM_ADSP_FW_RX] = "Rx",
288 [WM_ADSP_FW_RX_ANC] = "Rx ANC",
289 [WM_ADSP_FW_CTRL] = "Voice Ctrl",
290 [WM_ADSP_FW_ASR] = "ASR Assist",
291 [WM_ADSP_FW_TRACE] = "Dbg Trace",
292 [WM_ADSP_FW_SPK_PROT] = "Protection",
293 [WM_ADSP_FW_MISC] = "Misc",
Mark Brown1023dbd2013-01-11 22:58:28 +0000294};
295
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000296struct wm_adsp_system_config_xm_hdr {
297 __be32 sys_enable;
298 __be32 fw_id;
299 __be32 fw_rev;
300 __be32 boot_status;
301 __be32 watchdog;
302 __be32 dma_buffer_size;
303 __be32 rdma[6];
304 __be32 wdma[8];
305 __be32 build_job_name[3];
306 __be32 build_job_number;
307};
308
309struct wm_adsp_alg_xm_struct {
310 __be32 magic;
311 __be32 smoothing;
312 __be32 threshold;
313 __be32 host_buf_ptr;
314 __be32 start_seq;
315 __be32 high_water_mark;
316 __be32 low_water_mark;
317 __be64 smoothed_power;
318};
319
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +0000320struct wm_adsp_host_buf_coeff_v1 {
321 __be32 host_buf_ptr; /* Host buffer pointer */
322 __be32 versions; /* Version numbers */
323 __be32 name[4]; /* The buffer name */
324};
325
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000326struct wm_adsp_buffer {
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100327 __be32 buf1_base; /* Base addr of first buffer area */
328 __be32 buf1_size; /* Size of buf1 area in DSP words */
329 __be32 buf2_base; /* Base addr of 2nd buffer area */
330 __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */
331 __be32 buf3_base; /* Base addr of buf3 area */
332 __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000333 __be32 high_water_mark; /* Point at which IRQ is asserted */
334 __be32 irq_count; /* bits 1-31 count IRQ assertions */
335 __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */
336 __be32 next_write_index; /* word index of next write */
337 __be32 next_read_index; /* word index of next read */
338 __be32 error; /* error if any */
339 __be32 oldest_block_index; /* word index of oldest surviving */
340 __be32 requested_rewind; /* how many blocks rewind was done */
341 __be32 reserved_space; /* internal */
342 __be32 min_free; /* min free space since stream start */
343 __be32 blocks_written[2]; /* total blocks written (64 bit) */
344 __be32 words_written[2]; /* total words written (64 bit) */
345};
346
Charles Keepax721be3b2016-05-04 17:11:56 +0100347struct wm_adsp_compr;
348
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000349struct wm_adsp_compr_buf {
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +0000350 struct list_head list;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000351 struct wm_adsp *dsp;
Charles Keepax721be3b2016-05-04 17:11:56 +0100352 struct wm_adsp_compr *compr;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000353
354 struct wm_adsp_buffer_region *regions;
355 u32 host_buf_ptr;
Charles Keepax565ace42016-01-06 12:33:18 +0000356
357 u32 error;
358 u32 irq_count;
359 int read_index;
360 int avail;
Andrew Fordfb13f192019-02-19 17:31:56 +0000361 int host_buf_mem_type;
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +0000362
363 char *name;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000364};
365
Charles Keepax406abc92015-12-15 11:29:45 +0000366struct wm_adsp_compr {
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +0000367 struct list_head list;
Charles Keepax406abc92015-12-15 11:29:45 +0000368 struct wm_adsp *dsp;
Charles Keepax95fe9592015-12-15 11:29:47 +0000369 struct wm_adsp_compr_buf *buf;
Charles Keepax406abc92015-12-15 11:29:45 +0000370
371 struct snd_compr_stream *stream;
372 struct snd_compressed_buffer size;
Charles Keepax565ace42016-01-06 12:33:18 +0000373
Charles Keepax83a40ce2016-01-06 12:33:19 +0000374 u32 *raw_buf;
Charles Keepax565ace42016-01-06 12:33:18 +0000375 unsigned int copied_total;
Charles Keepaxda2b3352016-02-02 16:41:36 +0000376
377 unsigned int sample_rate;
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +0000378
379 const char *name;
Charles Keepax406abc92015-12-15 11:29:45 +0000380};
381
382#define WM_ADSP_DATA_WORD_SIZE 3
383
384#define WM_ADSP_MIN_FRAGMENTS 1
385#define WM_ADSP_MAX_FRAGMENTS 256
386#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE)
387#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE)
388
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000389#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7
390
391#define HOST_BUFFER_FIELD(field) \
392 (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))
393
394#define ALG_XM_FIELD(field) \
395 (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))
396
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +0000397#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1
398
399#define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00
400#define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8
401
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000402static int wm_adsp_buffer_init(struct wm_adsp *dsp);
403static int wm_adsp_buffer_free(struct wm_adsp *dsp);
404
405struct wm_adsp_buffer_region {
406 unsigned int offset;
407 unsigned int cumulative_size;
408 unsigned int mem_type;
409 unsigned int base_addr;
410};
411
412struct wm_adsp_buffer_region_def {
413 unsigned int mem_type;
414 unsigned int base_offset;
415 unsigned int size_offset;
416};
417
Charles Keepax3a9686c2016-02-01 15:22:34 +0000418static const struct wm_adsp_buffer_region_def default_regions[] = {
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000419 {
420 .mem_type = WMFW_ADSP2_XM,
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100421 .base_offset = HOST_BUFFER_FIELD(buf1_base),
422 .size_offset = HOST_BUFFER_FIELD(buf1_size),
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000423 },
424 {
425 .mem_type = WMFW_ADSP2_XM,
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100426 .base_offset = HOST_BUFFER_FIELD(buf2_base),
427 .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size),
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000428 },
429 {
430 .mem_type = WMFW_ADSP2_YM,
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100431 .base_offset = HOST_BUFFER_FIELD(buf3_base),
432 .size_offset = HOST_BUFFER_FIELD(buf_total_size),
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000433 },
434};
435
Charles Keepax406abc92015-12-15 11:29:45 +0000436struct wm_adsp_fw_caps {
437 u32 id;
438 struct snd_codec_desc desc;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000439 int num_regions;
Charles Keepax3a9686c2016-02-01 15:22:34 +0000440 const struct wm_adsp_buffer_region_def *region_defs;
Charles Keepax406abc92015-12-15 11:29:45 +0000441};
442
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000443static const struct wm_adsp_fw_caps ctrl_caps[] = {
Charles Keepax406abc92015-12-15 11:29:45 +0000444 {
445 .id = SND_AUDIOCODEC_BESPOKE,
446 .desc = {
Richard Fitzgerald3bbc2702018-07-19 11:50:38 +0100447 .max_ch = 8,
Charles Keepax406abc92015-12-15 11:29:45 +0000448 .sample_rates = { 16000 },
449 .num_sample_rates = 1,
450 .formats = SNDRV_PCM_FMTBIT_S16_LE,
451 },
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000452 .num_regions = ARRAY_SIZE(default_regions),
453 .region_defs = default_regions,
Charles Keepax406abc92015-12-15 11:29:45 +0000454 },
455};
456
Charles Keepax7ce42832016-01-21 17:52:59 +0000457static const struct wm_adsp_fw_caps trace_caps[] = {
458 {
459 .id = SND_AUDIOCODEC_BESPOKE,
460 .desc = {
461 .max_ch = 8,
462 .sample_rates = {
463 4000, 8000, 11025, 12000, 16000, 22050,
464 24000, 32000, 44100, 48000, 64000, 88200,
465 96000, 176400, 192000
466 },
467 .num_sample_rates = 15,
468 .formats = SNDRV_PCM_FMTBIT_S16_LE,
469 },
470 .num_regions = ARRAY_SIZE(default_regions),
471 .region_defs = default_regions,
Charles Keepax406abc92015-12-15 11:29:45 +0000472 },
473};
474
475static const struct {
Mark Brown1023dbd2013-01-11 22:58:28 +0000476 const char *file;
Charles Keepax406abc92015-12-15 11:29:45 +0000477 int compr_direction;
478 int num_caps;
479 const struct wm_adsp_fw_caps *caps;
Charles Keepax20b7f7c2016-05-13 16:45:17 +0100480 bool voice_trigger;
Mark Brown1023dbd2013-01-11 22:58:28 +0000481} wm_adsp_fw[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000482 [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
483 [WM_ADSP_FW_HIFI] = { .file = "hifi" },
484 [WM_ADSP_FW_TX] = { .file = "tx" },
485 [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
486 [WM_ADSP_FW_RX] = { .file = "rx" },
487 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
Charles Keepax406abc92015-12-15 11:29:45 +0000488 [WM_ADSP_FW_CTRL] = {
489 .file = "ctrl",
490 .compr_direction = SND_COMPRESS_CAPTURE,
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000491 .num_caps = ARRAY_SIZE(ctrl_caps),
492 .caps = ctrl_caps,
Charles Keepax20b7f7c2016-05-13 16:45:17 +0100493 .voice_trigger = true,
Charles Keepax406abc92015-12-15 11:29:45 +0000494 },
Charles Keepax04d13002015-11-26 14:01:52 +0000495 [WM_ADSP_FW_ASR] = { .file = "asr" },
Charles Keepax7ce42832016-01-21 17:52:59 +0000496 [WM_ADSP_FW_TRACE] = {
497 .file = "trace",
498 .compr_direction = SND_COMPRESS_CAPTURE,
499 .num_caps = ARRAY_SIZE(trace_caps),
500 .caps = trace_caps,
501 },
Charles Keepax04d13002015-11-26 14:01:52 +0000502 [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
503 [WM_ADSP_FW_MISC] = { .file = "misc" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000504};
505
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100506struct wm_coeff_ctl_ops {
507 int (*xget)(struct snd_kcontrol *kcontrol,
508 struct snd_ctl_elem_value *ucontrol);
509 int (*xput)(struct snd_kcontrol *kcontrol,
510 struct snd_ctl_elem_value *ucontrol);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100511};
512
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100513struct wm_coeff_ctl {
514 const char *name;
Charles Keepax23237362015-04-13 13:28:02 +0100515 const char *fw_name;
Charles Keepax3809f002015-04-13 13:27:54 +0100516 struct wm_adsp_alg_region alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100517 struct wm_coeff_ctl_ops ops;
Charles Keepax3809f002015-04-13 13:27:54 +0100518 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100519 unsigned int enabled:1;
520 struct list_head list;
521 void *cache;
Charles Keepax23237362015-04-13 13:28:02 +0100522 unsigned int offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100523 size_t len;
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +0100524 unsigned int set:1;
Charles Keepax9ee78752016-05-02 13:57:36 +0100525 struct soc_bytes_ext bytes_ext;
Charles Keepax26c22a12015-04-20 13:52:45 +0100526 unsigned int flags;
Stuart Henderson8eb084d2016-11-09 17:14:16 +0000527 unsigned int type;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100528};
529
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +0000530static const char *wm_adsp_mem_region_name(unsigned int type)
531{
532 switch (type) {
533 case WMFW_ADSP1_PM:
534 return "PM";
535 case WMFW_ADSP1_DM:
536 return "DM";
537 case WMFW_ADSP2_XM:
538 return "XM";
539 case WMFW_ADSP2_YM:
540 return "YM";
541 case WMFW_ADSP1_ZM:
542 return "ZM";
543 default:
544 return NULL;
545 }
546}
547
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100548#ifdef CONFIG_DEBUG_FS
549static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
550{
551 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
552
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100553 kfree(dsp->wmfw_file_name);
554 dsp->wmfw_file_name = tmp;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100555}
556
557static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
558{
559 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
560
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100561 kfree(dsp->bin_file_name);
562 dsp->bin_file_name = tmp;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100563}
564
565static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
566{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100567 kfree(dsp->wmfw_file_name);
568 kfree(dsp->bin_file_name);
569 dsp->wmfw_file_name = NULL;
570 dsp->bin_file_name = NULL;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100571}
572
573static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
574 char __user *user_buf,
575 size_t count, loff_t *ppos)
576{
577 struct wm_adsp *dsp = file->private_data;
578 ssize_t ret;
579
Charles Keepax078e7182015-12-08 16:08:26 +0000580 mutex_lock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100581
Charles Keepax28823eb2016-09-20 13:52:32 +0100582 if (!dsp->wmfw_file_name || !dsp->booted)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100583 ret = 0;
584 else
585 ret = simple_read_from_buffer(user_buf, count, ppos,
586 dsp->wmfw_file_name,
587 strlen(dsp->wmfw_file_name));
588
Charles Keepax078e7182015-12-08 16:08:26 +0000589 mutex_unlock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100590 return ret;
591}
592
593static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
594 char __user *user_buf,
595 size_t count, loff_t *ppos)
596{
597 struct wm_adsp *dsp = file->private_data;
598 ssize_t ret;
599
Charles Keepax078e7182015-12-08 16:08:26 +0000600 mutex_lock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100601
Charles Keepax28823eb2016-09-20 13:52:32 +0100602 if (!dsp->bin_file_name || !dsp->booted)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100603 ret = 0;
604 else
605 ret = simple_read_from_buffer(user_buf, count, ppos,
606 dsp->bin_file_name,
607 strlen(dsp->bin_file_name));
608
Charles Keepax078e7182015-12-08 16:08:26 +0000609 mutex_unlock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100610 return ret;
611}
612
613static const struct {
614 const char *name;
615 const struct file_operations fops;
616} wm_adsp_debugfs_fops[] = {
617 {
618 .name = "wmfw_file_name",
619 .fops = {
620 .open = simple_open,
621 .read = wm_adsp_debugfs_wmfw_read,
622 },
623 },
624 {
625 .name = "bin_file_name",
626 .fops = {
627 .open = simple_open,
628 .read = wm_adsp_debugfs_bin_read,
629 },
630 },
631};
632
633static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000634 struct snd_soc_component *component)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100635{
636 struct dentry *root = NULL;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100637 int i;
638
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000639 if (!component->debugfs_root) {
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100640 adsp_err(dsp, "No codec debugfs root\n");
641 goto err;
642 }
643
Richard Fitzgerald605391d2018-08-08 17:13:39 +0100644 root = debugfs_create_dir(dsp->name, component->debugfs_root);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100645
646 if (!root)
647 goto err;
648
Joe Perches6a73cf42018-05-23 12:20:59 -0700649 if (!debugfs_create_bool("booted", 0444, root, &dsp->booted))
Charles Keepax28823eb2016-09-20 13:52:32 +0100650 goto err;
651
Joe Perches6a73cf42018-05-23 12:20:59 -0700652 if (!debugfs_create_bool("running", 0444, root, &dsp->running))
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100653 goto err;
654
Joe Perches6a73cf42018-05-23 12:20:59 -0700655 if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id))
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100656 goto err;
657
Joe Perches6a73cf42018-05-23 12:20:59 -0700658 if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version))
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100659 goto err;
660
661 for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
662 if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
Joe Perches6a73cf42018-05-23 12:20:59 -0700663 0444, root, dsp,
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100664 &wm_adsp_debugfs_fops[i].fops))
665 goto err;
666 }
667
668 dsp->debugfs_root = root;
669 return;
670
671err:
672 debugfs_remove_recursive(root);
673 adsp_err(dsp, "Failed to create debugfs\n");
674}
675
676static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
677{
678 wm_adsp_debugfs_clear(dsp);
679 debugfs_remove_recursive(dsp->debugfs_root);
680}
681#else
682static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000683 struct snd_soc_component *component)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100684{
685}
686
687static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
688{
689}
690
691static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
692 const char *s)
693{
694}
695
696static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
697 const char *s)
698{
699}
700
701static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
702{
703}
704#endif
705
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100706int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
707 struct snd_ctl_elem_value *ucontrol)
Mark Brown1023dbd2013-01-11 22:58:28 +0000708{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000709 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000710 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000711 struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
Mark Brown1023dbd2013-01-11 22:58:28 +0000712
Takashi Iwai15c66572016-02-29 18:01:18 +0100713 ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
Mark Brown1023dbd2013-01-11 22:58:28 +0000714
715 return 0;
716}
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100717EXPORT_SYMBOL_GPL(wm_adsp_fw_get);
Mark Brown1023dbd2013-01-11 22:58:28 +0000718
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100719int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
720 struct snd_ctl_elem_value *ucontrol)
Mark Brown1023dbd2013-01-11 22:58:28 +0000721{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000722 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000723 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000724 struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000725 int ret = 0;
Mark Brown1023dbd2013-01-11 22:58:28 +0000726
Takashi Iwai15c66572016-02-29 18:01:18 +0100727 if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
Mark Brown1023dbd2013-01-11 22:58:28 +0000728 return 0;
729
Takashi Iwai15c66572016-02-29 18:01:18 +0100730 if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
Mark Brown1023dbd2013-01-11 22:58:28 +0000731 return -EINVAL;
732
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000733 mutex_lock(&dsp[e->shift_l].pwr_lock);
734
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +0000735 if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list))
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000736 ret = -EBUSY;
737 else
Takashi Iwai15c66572016-02-29 18:01:18 +0100738 dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
Mark Brown1023dbd2013-01-11 22:58:28 +0000739
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000740 mutex_unlock(&dsp[e->shift_l].pwr_lock);
Mark Brown1023dbd2013-01-11 22:58:28 +0000741
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000742 return ret;
Mark Brown1023dbd2013-01-11 22:58:28 +0000743}
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100744EXPORT_SYMBOL_GPL(wm_adsp_fw_put);
Mark Brown1023dbd2013-01-11 22:58:28 +0000745
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100746const struct soc_enum wm_adsp_fw_enum[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000747 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
748 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
749 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
750 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100751 SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
752 SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
753 SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
Mark Brown1023dbd2013-01-11 22:58:28 +0000754};
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100755EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
Mark Brown2159ad932012-10-11 11:54:02 +0900756
757static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
758 int type)
759{
760 int i;
761
762 for (i = 0; i < dsp->num_mems; i++)
763 if (dsp->mem[i].type == type)
764 return &dsp->mem[i];
765
766 return NULL;
767}
768
Charles Keepax3809f002015-04-13 13:27:54 +0100769static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
Mark Brown45b9ee72013-01-08 16:02:06 +0000770 unsigned int offset)
771{
Charles Keepax3809f002015-04-13 13:27:54 +0100772 if (WARN_ON(!mem))
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100773 return offset;
Charles Keepax3809f002015-04-13 13:27:54 +0100774 switch (mem->type) {
Mark Brown45b9ee72013-01-08 16:02:06 +0000775 case WMFW_ADSP1_PM:
Charles Keepax3809f002015-04-13 13:27:54 +0100776 return mem->base + (offset * 3);
Mark Brown45b9ee72013-01-08 16:02:06 +0000777 case WMFW_ADSP1_DM:
Charles Keepax3809f002015-04-13 13:27:54 +0100778 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000779 case WMFW_ADSP2_XM:
Charles Keepax3809f002015-04-13 13:27:54 +0100780 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000781 case WMFW_ADSP2_YM:
Charles Keepax3809f002015-04-13 13:27:54 +0100782 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000783 case WMFW_ADSP1_ZM:
Charles Keepax3809f002015-04-13 13:27:54 +0100784 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000785 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100786 WARN(1, "Unknown memory region type");
Mark Brown45b9ee72013-01-08 16:02:06 +0000787 return offset;
788 }
789}
790
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100791static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
792{
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000793 unsigned int scratch[4];
794 unsigned int addr = dsp->base + ADSP2_SCRATCH0;
795 unsigned int i;
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100796 int ret;
797
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000798 for (i = 0; i < ARRAY_SIZE(scratch); ++i) {
799 ret = regmap_read(dsp->regmap, addr + i, &scratch[i]);
800 if (ret) {
801 adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret);
802 return;
803 }
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100804 }
805
806 adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000807 scratch[0], scratch[1], scratch[2], scratch[3]);
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100808}
809
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100810static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
811{
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000812 unsigned int scratch[2];
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100813 int ret;
814
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000815 ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
816 &scratch[0]);
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100817 if (ret) {
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000818 adsp_err(dsp, "Failed to read SCRATCH0_1: %d\n", ret);
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100819 return;
820 }
821
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000822 ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH2_3,
823 &scratch[1]);
824 if (ret) {
825 adsp_err(dsp, "Failed to read SCRATCH2_3: %d\n", ret);
826 return;
827 }
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100828
829 adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
830 scratch[0] & 0xFFFF,
831 scratch[0] >> 16,
832 scratch[1] & 0xFFFF,
833 scratch[1] >> 16);
834}
835
Charles Keepax9ee78752016-05-02 13:57:36 +0100836static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
837{
838 return container_of(ext, struct wm_coeff_ctl, bytes_ext);
839}
840
Richard Fitzgeraldb396ebc2016-11-09 17:14:14 +0000841static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
842{
843 const struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
844 struct wm_adsp *dsp = ctl->dsp;
845 const struct wm_adsp_region *mem;
846
847 mem = wm_adsp_find_region(dsp, alg_region->type);
848 if (!mem) {
849 adsp_err(dsp, "No base for region %x\n",
850 alg_region->type);
851 return -EINVAL;
852 }
853
854 *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset);
855
856 return 0;
857}
858
Charles Keepax7585a5b2015-12-08 16:08:25 +0000859static int wm_coeff_info(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100860 struct snd_ctl_elem_info *uinfo)
861{
Charles Keepax9ee78752016-05-02 13:57:36 +0100862 struct soc_bytes_ext *bytes_ext =
863 (struct soc_bytes_ext *)kctl->private_value;
864 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100865
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +0000866 switch (ctl->type) {
867 case WMFW_CTL_TYPE_ACKED:
868 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
869 uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE;
870 uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE;
871 uinfo->value.integer.step = 1;
872 uinfo->count = 1;
873 break;
874 default:
875 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
876 uinfo->count = ctl->len;
877 break;
878 }
879
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100880 return 0;
881}
882
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000883static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
884 unsigned int event_id)
885{
886 struct wm_adsp *dsp = ctl->dsp;
887 u32 val = cpu_to_be32(event_id);
888 unsigned int reg;
889 int i, ret;
890
891 ret = wm_coeff_base_reg(ctl, &reg);
892 if (ret)
893 return ret;
894
895 adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
896 event_id, ctl->alg_region.alg,
897 wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
898
899 ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
900 if (ret) {
901 adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
902 return ret;
903 }
904
905 /*
906 * Poll for ack, we initially poll at ~1ms intervals for firmwares
907 * that respond quickly, then go to ~10ms polls. A firmware is unlikely
908 * to ack instantly so we do the first 1ms delay before reading the
909 * control to avoid a pointless bus transaction
910 */
911 for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
912 switch (i) {
913 case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
914 usleep_range(1000, 2000);
915 i++;
916 break;
917 default:
918 usleep_range(10000, 20000);
919 i += 10;
920 break;
921 }
922
923 ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
924 if (ret) {
925 adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
926 return ret;
927 }
928
929 if (val == 0) {
930 adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
931 return 0;
932 }
933 }
934
935 adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
936 reg, ctl->alg_region.alg,
937 wm_adsp_mem_region_name(ctl->alg_region.type),
938 ctl->offset);
939
940 return -ETIMEDOUT;
941}
942
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100943static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100944 const void *buf, size_t len)
945{
Charles Keepax3809f002015-04-13 13:27:54 +0100946 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100947 void *scratch;
948 int ret;
949 unsigned int reg;
950
Richard Fitzgeraldb396ebc2016-11-09 17:14:14 +0000951 ret = wm_coeff_base_reg(ctl, &reg);
952 if (ret)
953 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100954
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000955 scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100956 if (!scratch)
957 return -ENOMEM;
958
Charles Keepax3809f002015-04-13 13:27:54 +0100959 ret = regmap_raw_write(dsp->regmap, reg, scratch,
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000960 len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100961 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100962 adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000963 len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100964 kfree(scratch);
965 return ret;
966 }
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000967 adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100968
969 kfree(scratch);
970
971 return 0;
972}
973
Charles Keepax7585a5b2015-12-08 16:08:25 +0000974static int wm_coeff_put(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100975 struct snd_ctl_elem_value *ucontrol)
976{
Charles Keepax9ee78752016-05-02 13:57:36 +0100977 struct soc_bytes_ext *bytes_ext =
978 (struct soc_bytes_ext *)kctl->private_value;
979 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100980 char *p = ucontrol->value.bytes.data;
Charles Keepax168d10e2015-12-08 16:08:27 +0000981 int ret = 0;
982
983 mutex_lock(&ctl->dsp->pwr_lock);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100984
Charles Keepax67430a32017-03-06 16:54:33 +0000985 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
986 ret = -EPERM;
987 else
988 memcpy(ctl->cache, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100989
Nikesh Oswal65d17a92015-02-16 15:25:48 +0000990 ctl->set = 1;
Charles Keepaxcef45772016-09-20 13:52:33 +0100991 if (ctl->enabled && ctl->dsp->running)
Charles Keepax168d10e2015-12-08 16:08:27 +0000992 ret = wm_coeff_write_control(ctl, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100993
Charles Keepax168d10e2015-12-08 16:08:27 +0000994 mutex_unlock(&ctl->dsp->pwr_lock);
995
996 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100997}
998
Charles Keepax9ee78752016-05-02 13:57:36 +0100999static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
1000 const unsigned int __user *bytes, unsigned int size)
1001{
1002 struct soc_bytes_ext *bytes_ext =
1003 (struct soc_bytes_ext *)kctl->private_value;
1004 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
1005 int ret = 0;
1006
1007 mutex_lock(&ctl->dsp->pwr_lock);
1008
1009 if (copy_from_user(ctl->cache, bytes, size)) {
1010 ret = -EFAULT;
1011 } else {
1012 ctl->set = 1;
Charles Keepaxcef45772016-09-20 13:52:33 +01001013 if (ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +01001014 ret = wm_coeff_write_control(ctl, ctl->cache, size);
Charles Keepax67430a32017-03-06 16:54:33 +00001015 else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
1016 ret = -EPERM;
Charles Keepax9ee78752016-05-02 13:57:36 +01001017 }
1018
1019 mutex_unlock(&ctl->dsp->pwr_lock);
1020
1021 return ret;
1022}
1023
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001024static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
1025 struct snd_ctl_elem_value *ucontrol)
1026{
1027 struct soc_bytes_ext *bytes_ext =
1028 (struct soc_bytes_ext *)kctl->private_value;
1029 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
1030 unsigned int val = ucontrol->value.integer.value[0];
1031 int ret;
1032
1033 if (val == 0)
1034 return 0; /* 0 means no event */
1035
1036 mutex_lock(&ctl->dsp->pwr_lock);
1037
Charles Keepax7b4af792017-03-06 16:54:34 +00001038 if (ctl->enabled && ctl->dsp->running)
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001039 ret = wm_coeff_write_acked_control(ctl, val);
1040 else
1041 ret = -EPERM;
1042
1043 mutex_unlock(&ctl->dsp->pwr_lock);
1044
1045 return ret;
1046}
1047
Charles Keepaxc9f8dd72015-04-13 13:27:58 +01001048static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001049 void *buf, size_t len)
1050{
Charles Keepax3809f002015-04-13 13:27:54 +01001051 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001052 void *scratch;
1053 int ret;
1054 unsigned int reg;
1055
Richard Fitzgeraldb396ebc2016-11-09 17:14:14 +00001056 ret = wm_coeff_base_reg(ctl, &reg);
1057 if (ret)
1058 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001059
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001060 scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001061 if (!scratch)
1062 return -ENOMEM;
1063
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001064 ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001065 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +01001066 adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
Charles Keepax5602a642016-03-10 10:46:07 +00001067 len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001068 kfree(scratch);
1069 return ret;
1070 }
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001071 adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001072
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001073 memcpy(buf, scratch, len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001074 kfree(scratch);
1075
1076 return 0;
1077}
1078
Charles Keepax7585a5b2015-12-08 16:08:25 +00001079static int wm_coeff_get(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001080 struct snd_ctl_elem_value *ucontrol)
1081{
Charles Keepax9ee78752016-05-02 13:57:36 +01001082 struct soc_bytes_ext *bytes_ext =
1083 (struct soc_bytes_ext *)kctl->private_value;
1084 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001085 char *p = ucontrol->value.bytes.data;
Charles Keepax168d10e2015-12-08 16:08:27 +00001086 int ret = 0;
1087
1088 mutex_lock(&ctl->dsp->pwr_lock);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001089
Charles Keepax26c22a12015-04-20 13:52:45 +01001090 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
Charles Keepaxcef45772016-09-20 13:52:33 +01001091 if (ctl->enabled && ctl->dsp->running)
Charles Keepax168d10e2015-12-08 16:08:27 +00001092 ret = wm_coeff_read_control(ctl, p, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +01001093 else
Charles Keepax168d10e2015-12-08 16:08:27 +00001094 ret = -EPERM;
1095 } else {
Charles Keepaxcef45772016-09-20 13:52:33 +01001096 if (!ctl->flags && ctl->enabled && ctl->dsp->running)
Charles Keepaxbc1765d2015-12-17 10:05:59 +00001097 ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
1098
Charles Keepax168d10e2015-12-08 16:08:27 +00001099 memcpy(p, ctl->cache, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +01001100 }
1101
Charles Keepax168d10e2015-12-08 16:08:27 +00001102 mutex_unlock(&ctl->dsp->pwr_lock);
Charles Keepax26c22a12015-04-20 13:52:45 +01001103
Charles Keepax168d10e2015-12-08 16:08:27 +00001104 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001105}
1106
Charles Keepax9ee78752016-05-02 13:57:36 +01001107static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
1108 unsigned int __user *bytes, unsigned int size)
1109{
1110 struct soc_bytes_ext *bytes_ext =
1111 (struct soc_bytes_ext *)kctl->private_value;
1112 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
1113 int ret = 0;
1114
1115 mutex_lock(&ctl->dsp->pwr_lock);
1116
1117 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
Charles Keepaxcef45772016-09-20 13:52:33 +01001118 if (ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +01001119 ret = wm_coeff_read_control(ctl, ctl->cache, size);
1120 else
1121 ret = -EPERM;
1122 } else {
Charles Keepaxcef45772016-09-20 13:52:33 +01001123 if (!ctl->flags && ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +01001124 ret = wm_coeff_read_control(ctl, ctl->cache, size);
1125 }
1126
1127 if (!ret && copy_to_user(bytes, ctl->cache, size))
1128 ret = -EFAULT;
1129
1130 mutex_unlock(&ctl->dsp->pwr_lock);
1131
1132 return ret;
1133}
1134
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001135static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
1136 struct snd_ctl_elem_value *ucontrol)
1137{
1138 /*
1139 * Although it's not useful to read an acked control, we must satisfy
1140 * user-side assumptions that all controls are readable and that a
1141 * write of the same value should be filtered out (it's valid to send
1142 * the same event number again to the firmware). We therefore return 0,
1143 * meaning "no event" so valid event numbers will always be a change
1144 */
1145 ucontrol->value.integer.value[0] = 0;
1146
1147 return 0;
1148}
1149
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001150struct wmfw_ctl_work {
Charles Keepax3809f002015-04-13 13:27:54 +01001151 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001152 struct wm_coeff_ctl *ctl;
1153 struct work_struct work;
1154};
1155
Charles Keepax9ee78752016-05-02 13:57:36 +01001156static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
1157{
1158 unsigned int out, rd, wr, vol;
1159
1160 if (len > ADSP_MAX_STD_CTRL_SIZE) {
1161 rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
1162 wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
1163 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
1164
1165 out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
1166 } else {
1167 rd = SNDRV_CTL_ELEM_ACCESS_READ;
1168 wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
1169 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
1170
1171 out = 0;
1172 }
1173
1174 if (in) {
1175 if (in & WMFW_CTL_FLAG_READABLE)
1176 out |= rd;
1177 if (in & WMFW_CTL_FLAG_WRITEABLE)
1178 out |= wr;
1179 if (in & WMFW_CTL_FLAG_VOLATILE)
1180 out |= vol;
1181 } else {
1182 out |= rd | wr | vol;
1183 }
1184
1185 return out;
1186}
1187
Charles Keepax3809f002015-04-13 13:27:54 +01001188static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001189{
1190 struct snd_kcontrol_new *kcontrol;
1191 int ret;
1192
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01001193 if (!ctl || !ctl->name)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001194 return -EINVAL;
1195
1196 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
1197 if (!kcontrol)
1198 return -ENOMEM;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001199
1200 kcontrol->name = ctl->name;
1201 kcontrol->info = wm_coeff_info;
Charles Keepax9ee78752016-05-02 13:57:36 +01001202 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1203 kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
1204 kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
Charles Keepax9ee78752016-05-02 13:57:36 +01001205 kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +01001206
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001207 switch (ctl->type) {
1208 case WMFW_CTL_TYPE_ACKED:
1209 kcontrol->get = wm_coeff_get_acked;
1210 kcontrol->put = wm_coeff_put_acked;
1211 break;
1212 default:
Richard Fitzgeraldd7789f52018-02-28 10:31:10 +00001213 if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
1214 ctl->bytes_ext.max = ctl->len;
1215 ctl->bytes_ext.get = wm_coeff_tlv_get;
1216 ctl->bytes_ext.put = wm_coeff_tlv_put;
1217 } else {
1218 kcontrol->get = wm_coeff_get;
1219 kcontrol->put = wm_coeff_put;
1220 }
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001221 break;
1222 }
1223
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00001224 ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001225 if (ret < 0)
1226 goto err_kcontrol;
1227
1228 kfree(kcontrol);
1229
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001230 return 0;
1231
1232err_kcontrol:
1233 kfree(kcontrol);
1234 return ret;
1235}
1236
Charles Keepaxb21acc12015-04-13 13:28:01 +01001237static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
1238{
1239 struct wm_coeff_ctl *ctl;
1240 int ret;
1241
1242 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1243 if (!ctl->enabled || ctl->set)
1244 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +01001245 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
1246 continue;
1247
Richard Fitzgerald04ff40a2018-02-05 11:38:17 +00001248 /*
1249 * For readable controls populate the cache from the DSP memory.
1250 * For non-readable controls the cache was zero-filled when
1251 * created so we don't need to do anything.
1252 */
1253 if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) {
1254 ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
1255 if (ret < 0)
1256 return ret;
1257 }
Charles Keepaxb21acc12015-04-13 13:28:01 +01001258 }
1259
1260 return 0;
1261}
1262
1263static int wm_coeff_sync_controls(struct wm_adsp *dsp)
1264{
1265 struct wm_coeff_ctl *ctl;
1266 int ret;
1267
1268 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1269 if (!ctl->enabled)
1270 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +01001271 if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00001272 ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len);
Charles Keepaxb21acc12015-04-13 13:28:01 +01001273 if (ret < 0)
1274 return ret;
1275 }
1276 }
1277
1278 return 0;
1279}
1280
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001281static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
1282 unsigned int event)
1283{
1284 struct wm_coeff_ctl *ctl;
1285 int ret;
1286
1287 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1288 if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
1289 continue;
1290
Charles Keepax87aa6372016-11-21 18:00:02 +00001291 if (!ctl->enabled)
1292 continue;
1293
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001294 ret = wm_coeff_write_acked_control(ctl, event);
1295 if (ret)
1296 adsp_warn(dsp,
1297 "Failed to send 0x%x event to alg 0x%x (%d)\n",
1298 event, ctl->alg_region.alg, ret);
1299 }
1300}
1301
Charles Keepaxb21acc12015-04-13 13:28:01 +01001302static void wm_adsp_ctl_work(struct work_struct *work)
1303{
1304 struct wmfw_ctl_work *ctl_work = container_of(work,
1305 struct wmfw_ctl_work,
1306 work);
1307
1308 wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
1309 kfree(ctl_work);
1310}
1311
Richard Fitzgerald66225e92016-04-27 14:58:27 +01001312static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
1313{
1314 kfree(ctl->cache);
1315 kfree(ctl->name);
1316 kfree(ctl);
1317}
1318
Charles Keepaxb21acc12015-04-13 13:28:01 +01001319static int wm_adsp_create_control(struct wm_adsp *dsp,
1320 const struct wm_adsp_alg_region *alg_region,
Charles Keepax23237362015-04-13 13:28:02 +01001321 unsigned int offset, unsigned int len,
Charles Keepax26c22a12015-04-20 13:52:45 +01001322 const char *subname, unsigned int subname_len,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001323 unsigned int flags, unsigned int type)
Charles Keepaxb21acc12015-04-13 13:28:01 +01001324{
1325 struct wm_coeff_ctl *ctl;
1326 struct wmfw_ctl_work *ctl_work;
1327 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001328 const char *region_name;
Charles Keepaxb21acc12015-04-13 13:28:01 +01001329 int ret;
1330
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001331 region_name = wm_adsp_mem_region_name(alg_region->type);
1332 if (!region_name) {
Charles Keepax23237362015-04-13 13:28:02 +01001333 adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
Charles Keepaxb21acc12015-04-13 13:28:01 +01001334 return -EINVAL;
1335 }
1336
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001337 switch (dsp->fw_ver) {
1338 case 0:
1339 case 1:
Richard Fitzgerald605391d2018-08-08 17:13:39 +01001340 snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x",
1341 dsp->name, region_name, alg_region->alg);
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001342 break;
1343 default:
1344 ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
Richard Fitzgerald605391d2018-08-08 17:13:39 +01001345 "%s%c %.12s %x", dsp->name, *region_name,
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001346 wm_adsp_fw_text[dsp->fw], alg_region->alg);
1347
1348 /* Truncate the subname from the start if it is too long */
1349 if (subname) {
1350 int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
1351 int skip = 0;
1352
Charles Keepaxb7ede5af2018-07-19 11:50:36 +01001353 if (dsp->component->name_prefix)
1354 avail -= strlen(dsp->component->name_prefix) + 1;
1355
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001356 if (subname_len > avail)
1357 skip = subname_len - avail;
1358
1359 snprintf(name + ret,
1360 SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
1361 subname_len - skip, subname + skip);
1362 }
1363 break;
1364 }
Charles Keepaxb21acc12015-04-13 13:28:01 +01001365
Charles Keepax7585a5b2015-12-08 16:08:25 +00001366 list_for_each_entry(ctl, &dsp->ctl_list, list) {
Charles Keepaxb21acc12015-04-13 13:28:01 +01001367 if (!strcmp(ctl->name, name)) {
1368 if (!ctl->enabled)
1369 ctl->enabled = 1;
1370 return 0;
1371 }
1372 }
1373
1374 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
1375 if (!ctl)
1376 return -ENOMEM;
Charles Keepax23237362015-04-13 13:28:02 +01001377 ctl->fw_name = wm_adsp_fw_text[dsp->fw];
Charles Keepaxb21acc12015-04-13 13:28:01 +01001378 ctl->alg_region = *alg_region;
1379 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
1380 if (!ctl->name) {
1381 ret = -ENOMEM;
1382 goto err_ctl;
1383 }
1384 ctl->enabled = 1;
1385 ctl->set = 0;
1386 ctl->ops.xget = wm_coeff_get;
1387 ctl->ops.xput = wm_coeff_put;
1388 ctl->dsp = dsp;
1389
Charles Keepax26c22a12015-04-20 13:52:45 +01001390 ctl->flags = flags;
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001391 ctl->type = type;
Charles Keepax23237362015-04-13 13:28:02 +01001392 ctl->offset = offset;
Charles Keepaxb21acc12015-04-13 13:28:01 +01001393 ctl->len = len;
1394 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
1395 if (!ctl->cache) {
1396 ret = -ENOMEM;
1397 goto err_ctl_name;
1398 }
1399
Charles Keepax23237362015-04-13 13:28:02 +01001400 list_add(&ctl->list, &dsp->ctl_list);
1401
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001402 if (flags & WMFW_CTL_FLAG_SYS)
1403 return 0;
1404
Charles Keepaxb21acc12015-04-13 13:28:01 +01001405 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
1406 if (!ctl_work) {
1407 ret = -ENOMEM;
1408 goto err_ctl_cache;
1409 }
1410
1411 ctl_work->dsp = dsp;
1412 ctl_work->ctl = ctl;
1413 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
1414 schedule_work(&ctl_work->work);
1415
1416 return 0;
1417
1418err_ctl_cache:
1419 kfree(ctl->cache);
1420err_ctl_name:
1421 kfree(ctl->name);
1422err_ctl:
1423 kfree(ctl);
1424
1425 return ret;
1426}
1427
Charles Keepax23237362015-04-13 13:28:02 +01001428struct wm_coeff_parsed_alg {
1429 int id;
1430 const u8 *name;
1431 int name_len;
1432 int ncoeff;
1433};
1434
1435struct wm_coeff_parsed_coeff {
1436 int offset;
1437 int mem_type;
1438 const u8 *name;
1439 int name_len;
1440 int ctl_type;
1441 int flags;
1442 int len;
1443};
1444
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001445static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
1446{
1447 int length;
1448
1449 switch (bytes) {
1450 case 1:
1451 length = **pos;
1452 break;
1453 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +01001454 length = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001455 break;
1456 default:
1457 return 0;
1458 }
1459
1460 if (str)
1461 *str = *pos + bytes;
1462
1463 *pos += ((length + bytes) + 3) & ~0x03;
1464
1465 return length;
1466}
1467
1468static int wm_coeff_parse_int(int bytes, const u8 **pos)
1469{
1470 int val = 0;
1471
1472 switch (bytes) {
1473 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +01001474 val = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001475 break;
1476 case 4:
Charles Keepax8299ee82015-04-20 13:52:44 +01001477 val = le32_to_cpu(*((__le32 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001478 break;
1479 default:
1480 break;
1481 }
1482
1483 *pos += bytes;
1484
1485 return val;
1486}
1487
Charles Keepax23237362015-04-13 13:28:02 +01001488static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
1489 struct wm_coeff_parsed_alg *blk)
1490{
1491 const struct wmfw_adsp_alg_data *raw;
1492
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001493 switch (dsp->fw_ver) {
1494 case 0:
1495 case 1:
1496 raw = (const struct wmfw_adsp_alg_data *)*data;
1497 *data = raw->data;
Charles Keepax23237362015-04-13 13:28:02 +01001498
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001499 blk->id = le32_to_cpu(raw->id);
1500 blk->name = raw->name;
1501 blk->name_len = strlen(raw->name);
1502 blk->ncoeff = le32_to_cpu(raw->ncoeff);
1503 break;
1504 default:
1505 blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
1506 blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
1507 &blk->name);
1508 wm_coeff_parse_string(sizeof(u16), data, NULL);
1509 blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
1510 break;
1511 }
Charles Keepax23237362015-04-13 13:28:02 +01001512
1513 adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
1514 adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
1515 adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
1516}
1517
1518static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
1519 struct wm_coeff_parsed_coeff *blk)
1520{
1521 const struct wmfw_adsp_coeff_data *raw;
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001522 const u8 *tmp;
1523 int length;
Charles Keepax23237362015-04-13 13:28:02 +01001524
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001525 switch (dsp->fw_ver) {
1526 case 0:
1527 case 1:
1528 raw = (const struct wmfw_adsp_coeff_data *)*data;
1529 *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
Charles Keepax23237362015-04-13 13:28:02 +01001530
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001531 blk->offset = le16_to_cpu(raw->hdr.offset);
1532 blk->mem_type = le16_to_cpu(raw->hdr.type);
1533 blk->name = raw->name;
1534 blk->name_len = strlen(raw->name);
1535 blk->ctl_type = le16_to_cpu(raw->ctl_type);
1536 blk->flags = le16_to_cpu(raw->flags);
1537 blk->len = le32_to_cpu(raw->len);
1538 break;
1539 default:
1540 tmp = *data;
1541 blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
1542 blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
1543 length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
1544 blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
1545 &blk->name);
1546 wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
1547 wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
1548 blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
1549 blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
1550 blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
1551
1552 *data = *data + sizeof(raw->hdr) + length;
1553 break;
1554 }
Charles Keepax23237362015-04-13 13:28:02 +01001555
1556 adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
1557 adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
1558 adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
1559 adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
1560 adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
1561 adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
1562}
1563
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001564static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
1565 const struct wm_coeff_parsed_coeff *coeff_blk,
1566 unsigned int f_required,
1567 unsigned int f_illegal)
1568{
1569 if ((coeff_blk->flags & f_illegal) ||
1570 ((coeff_blk->flags & f_required) != f_required)) {
1571 adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
1572 coeff_blk->flags, coeff_blk->ctl_type);
1573 return -EINVAL;
1574 }
1575
1576 return 0;
1577}
1578
Charles Keepax23237362015-04-13 13:28:02 +01001579static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
1580 const struct wmfw_region *region)
1581{
1582 struct wm_adsp_alg_region alg_region = {};
1583 struct wm_coeff_parsed_alg alg_blk;
1584 struct wm_coeff_parsed_coeff coeff_blk;
1585 const u8 *data = region->data;
1586 int i, ret;
1587
1588 wm_coeff_parse_alg(dsp, &data, &alg_blk);
1589 for (i = 0; i < alg_blk.ncoeff; i++) {
1590 wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
1591
1592 switch (coeff_blk.ctl_type) {
1593 case SNDRV_CTL_ELEM_TYPE_BYTES:
1594 break;
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001595 case WMFW_CTL_TYPE_ACKED:
1596 if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)
1597 continue; /* ignore */
1598
1599 ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
1600 WMFW_CTL_FLAG_VOLATILE |
1601 WMFW_CTL_FLAG_WRITEABLE |
1602 WMFW_CTL_FLAG_READABLE,
1603 0);
1604 if (ret)
1605 return -EINVAL;
1606 break;
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001607 case WMFW_CTL_TYPE_HOSTEVENT:
1608 ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
1609 WMFW_CTL_FLAG_SYS |
1610 WMFW_CTL_FLAG_VOLATILE |
1611 WMFW_CTL_FLAG_WRITEABLE |
1612 WMFW_CTL_FLAG_READABLE,
1613 0);
1614 if (ret)
1615 return -EINVAL;
1616 break;
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01001617 case WMFW_CTL_TYPE_HOST_BUFFER:
1618 ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
1619 WMFW_CTL_FLAG_SYS |
1620 WMFW_CTL_FLAG_VOLATILE |
1621 WMFW_CTL_FLAG_READABLE,
1622 0);
1623 if (ret)
1624 return -EINVAL;
1625 break;
Charles Keepax23237362015-04-13 13:28:02 +01001626 default:
1627 adsp_err(dsp, "Unknown control type: %d\n",
1628 coeff_blk.ctl_type);
1629 return -EINVAL;
1630 }
1631
1632 alg_region.type = coeff_blk.mem_type;
1633 alg_region.alg = alg_blk.id;
1634
1635 ret = wm_adsp_create_control(dsp, &alg_region,
1636 coeff_blk.offset,
1637 coeff_blk.len,
1638 coeff_blk.name,
Charles Keepax26c22a12015-04-20 13:52:45 +01001639 coeff_blk.name_len,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001640 coeff_blk.flags,
1641 coeff_blk.ctl_type);
Charles Keepax23237362015-04-13 13:28:02 +01001642 if (ret < 0)
1643 adsp_err(dsp, "Failed to create control: %.*s, %d\n",
1644 coeff_blk.name_len, coeff_blk.name, ret);
1645 }
1646
1647 return 0;
1648}
1649
Mark Brown2159ad932012-10-11 11:54:02 +09001650static int wm_adsp_load(struct wm_adsp *dsp)
1651{
Mark Browncf17c832013-01-30 14:37:23 +08001652 LIST_HEAD(buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09001653 const struct firmware *firmware;
1654 struct regmap *regmap = dsp->regmap;
1655 unsigned int pos = 0;
1656 const struct wmfw_header *header;
1657 const struct wmfw_adsp1_sizes *adsp1_sizes;
1658 const struct wmfw_adsp2_sizes *adsp2_sizes;
1659 const struct wmfw_footer *footer;
1660 const struct wmfw_region *region;
1661 const struct wm_adsp_region *mem;
1662 const char *region_name;
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001663 char *file, *text = NULL;
Mark Browncf17c832013-01-30 14:37:23 +08001664 struct wm_adsp_buf *buf;
Mark Brown2159ad932012-10-11 11:54:02 +09001665 unsigned int reg;
1666 int regions = 0;
1667 int ret, offset, type, sizes;
1668
1669 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1670 if (file == NULL)
1671 return -ENOMEM;
1672
Richard Fitzgerald605391d2018-08-08 17:13:39 +01001673 snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name,
Mark Brown1023dbd2013-01-11 22:58:28 +00001674 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +09001675 file[PAGE_SIZE - 1] = '\0';
1676
1677 ret = request_firmware(&firmware, file, dsp->dev);
1678 if (ret != 0) {
1679 adsp_err(dsp, "Failed to request '%s'\n", file);
1680 goto out;
1681 }
1682 ret = -EINVAL;
1683
1684 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1685 if (pos >= firmware->size) {
1686 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1687 file, firmware->size);
1688 goto out_fw;
1689 }
1690
Charles Keepax7585a5b2015-12-08 16:08:25 +00001691 header = (void *)&firmware->data[0];
Mark Brown2159ad932012-10-11 11:54:02 +09001692
1693 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
1694 adsp_err(dsp, "%s: invalid magic\n", file);
1695 goto out_fw;
1696 }
1697
Charles Keepax23237362015-04-13 13:28:02 +01001698 switch (header->ver) {
1699 case 0:
Charles Keepaxc61e59f2015-04-13 13:28:05 +01001700 adsp_warn(dsp, "%s: Depreciated file format %d\n",
1701 file, header->ver);
1702 break;
Charles Keepax23237362015-04-13 13:28:02 +01001703 case 1:
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001704 case 2:
Charles Keepax23237362015-04-13 13:28:02 +01001705 break;
1706 default:
Mark Brown2159ad932012-10-11 11:54:02 +09001707 adsp_err(dsp, "%s: unknown file format %d\n",
1708 file, header->ver);
1709 goto out_fw;
1710 }
Charles Keepax23237362015-04-13 13:28:02 +01001711
Dimitris Papastamos36269922013-11-01 15:56:57 +00001712 adsp_info(dsp, "Firmware version: %d\n", header->ver);
Charles Keepax23237362015-04-13 13:28:02 +01001713 dsp->fw_ver = header->ver;
Mark Brown2159ad932012-10-11 11:54:02 +09001714
1715 if (header->core != dsp->type) {
1716 adsp_err(dsp, "%s: invalid core %d != %d\n",
1717 file, header->core, dsp->type);
1718 goto out_fw;
1719 }
1720
1721 switch (dsp->type) {
1722 case WMFW_ADSP1:
1723 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1724 adsp1_sizes = (void *)&(header[1]);
1725 footer = (void *)&(adsp1_sizes[1]);
1726 sizes = sizeof(*adsp1_sizes);
1727
1728 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
1729 file, le32_to_cpu(adsp1_sizes->dm),
1730 le32_to_cpu(adsp1_sizes->pm),
1731 le32_to_cpu(adsp1_sizes->zm));
1732 break;
1733
1734 case WMFW_ADSP2:
1735 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
1736 adsp2_sizes = (void *)&(header[1]);
1737 footer = (void *)&(adsp2_sizes[1]);
1738 sizes = sizeof(*adsp2_sizes);
1739
1740 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
1741 file, le32_to_cpu(adsp2_sizes->xm),
1742 le32_to_cpu(adsp2_sizes->ym),
1743 le32_to_cpu(adsp2_sizes->pm),
1744 le32_to_cpu(adsp2_sizes->zm));
1745 break;
1746
1747 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +01001748 WARN(1, "Unknown DSP type");
Mark Brown2159ad932012-10-11 11:54:02 +09001749 goto out_fw;
1750 }
1751
1752 if (le32_to_cpu(header->len) != sizeof(*header) +
1753 sizes + sizeof(*footer)) {
1754 adsp_err(dsp, "%s: unexpected header length %d\n",
1755 file, le32_to_cpu(header->len));
1756 goto out_fw;
1757 }
1758
1759 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
1760 le64_to_cpu(footer->timestamp));
1761
1762 while (pos < firmware->size &&
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00001763 sizeof(*region) < firmware->size - pos) {
Mark Brown2159ad932012-10-11 11:54:02 +09001764 region = (void *)&(firmware->data[pos]);
1765 region_name = "Unknown";
1766 reg = 0;
1767 text = NULL;
1768 offset = le32_to_cpu(region->offset) & 0xffffff;
1769 type = be32_to_cpu(region->type) & 0xff;
1770 mem = wm_adsp_find_region(dsp, type);
Charles Keepax7585a5b2015-12-08 16:08:25 +00001771
Mark Brown2159ad932012-10-11 11:54:02 +09001772 switch (type) {
1773 case WMFW_NAME_TEXT:
1774 region_name = "Firmware name";
1775 text = kzalloc(le32_to_cpu(region->len) + 1,
1776 GFP_KERNEL);
1777 break;
Charles Keepax23237362015-04-13 13:28:02 +01001778 case WMFW_ALGORITHM_DATA:
1779 region_name = "Algorithm";
1780 ret = wm_adsp_parse_coeff(dsp, region);
1781 if (ret != 0)
1782 goto out_fw;
1783 break;
Mark Brown2159ad932012-10-11 11:54:02 +09001784 case WMFW_INFO_TEXT:
1785 region_name = "Information";
1786 text = kzalloc(le32_to_cpu(region->len) + 1,
1787 GFP_KERNEL);
1788 break;
1789 case WMFW_ABSOLUTE:
1790 region_name = "Absolute";
1791 reg = offset;
1792 break;
1793 case WMFW_ADSP1_PM:
Mark Brown2159ad932012-10-11 11:54:02 +09001794 case WMFW_ADSP1_DM:
Mark Brown2159ad932012-10-11 11:54:02 +09001795 case WMFW_ADSP2_XM:
Mark Brown2159ad932012-10-11 11:54:02 +09001796 case WMFW_ADSP2_YM:
Mark Brown2159ad932012-10-11 11:54:02 +09001797 case WMFW_ADSP1_ZM:
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001798 region_name = wm_adsp_mem_region_name(type);
Mark Brown45b9ee72013-01-08 16:02:06 +00001799 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +09001800 break;
1801 default:
1802 adsp_warn(dsp,
1803 "%s.%d: Unknown region type %x at %d(%x)\n",
1804 file, regions, type, pos, pos);
1805 break;
1806 }
1807
1808 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
1809 regions, le32_to_cpu(region->len), offset,
1810 region_name);
1811
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00001812 if (le32_to_cpu(region->len) >
1813 firmware->size - pos - sizeof(*region)) {
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001814 adsp_err(dsp,
1815 "%s.%d: %s region len %d bytes exceeds file length %zu\n",
1816 file, regions, region_name,
1817 le32_to_cpu(region->len), firmware->size);
1818 ret = -EINVAL;
1819 goto out_fw;
1820 }
1821
Mark Brown2159ad932012-10-11 11:54:02 +09001822 if (text) {
1823 memcpy(text, region->data, le32_to_cpu(region->len));
1824 adsp_info(dsp, "%s: %s\n", file, text);
1825 kfree(text);
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001826 text = NULL;
Mark Brown2159ad932012-10-11 11:54:02 +09001827 }
1828
1829 if (reg) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001830 buf = wm_adsp_buf_alloc(region->data,
1831 le32_to_cpu(region->len),
1832 &buf_list);
1833 if (!buf) {
1834 adsp_err(dsp, "Out of memory\n");
1835 ret = -ENOMEM;
1836 goto out_fw;
1837 }
Mark Browna76fefa2013-01-07 19:03:17 +00001838
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001839 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1840 le32_to_cpu(region->len));
1841 if (ret != 0) {
1842 adsp_err(dsp,
1843 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
1844 file, regions,
1845 le32_to_cpu(region->len), offset,
1846 region_name, ret);
1847 goto out_fw;
Mark Brown2159ad932012-10-11 11:54:02 +09001848 }
1849 }
1850
1851 pos += le32_to_cpu(region->len) + sizeof(*region);
1852 regions++;
1853 }
Mark Browncf17c832013-01-30 14:37:23 +08001854
1855 ret = regmap_async_complete(regmap);
1856 if (ret != 0) {
1857 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1858 goto out_fw;
1859 }
1860
Mark Brown2159ad932012-10-11 11:54:02 +09001861 if (pos > firmware->size)
1862 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1863 file, regions, pos - firmware->size);
1864
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001865 wm_adsp_debugfs_save_wmfwname(dsp, file);
1866
Mark Brown2159ad932012-10-11 11:54:02 +09001867out_fw:
Mark Browncf17c832013-01-30 14:37:23 +08001868 regmap_async_complete(regmap);
1869 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09001870 release_firmware(firmware);
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001871 kfree(text);
Mark Brown2159ad932012-10-11 11:54:02 +09001872out:
1873 kfree(file);
1874
1875 return ret;
1876}
1877
Charles Keepax23237362015-04-13 13:28:02 +01001878static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
1879 const struct wm_adsp_alg_region *alg_region)
1880{
1881 struct wm_coeff_ctl *ctl;
1882
1883 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1884 if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
1885 alg_region->alg == ctl->alg_region.alg &&
1886 alg_region->type == ctl->alg_region.type) {
1887 ctl->alg_region.base = alg_region->base;
1888 }
1889 }
1890}
1891
Charles Keepax3809f002015-04-13 13:27:54 +01001892static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
Charles Keepax7f7cca02018-06-20 11:56:21 +01001893 const struct wm_adsp_region *mem,
Charles Keepaxb618a1852015-04-13 13:27:53 +01001894 unsigned int pos, unsigned int len)
Mark Browndb405172012-10-26 19:30:40 +01001895{
Charles Keepaxb618a1852015-04-13 13:27:53 +01001896 void *alg;
Charles Keepax7f7cca02018-06-20 11:56:21 +01001897 unsigned int reg;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001898 int ret;
Mark Browndb405172012-10-26 19:30:40 +01001899 __be32 val;
Mark Browndb405172012-10-26 19:30:40 +01001900
Charles Keepax3809f002015-04-13 13:27:54 +01001901 if (n_algs == 0) {
Mark Browndb405172012-10-26 19:30:40 +01001902 adsp_err(dsp, "No algorithms\n");
Charles Keepaxb618a1852015-04-13 13:27:53 +01001903 return ERR_PTR(-EINVAL);
Mark Browndb405172012-10-26 19:30:40 +01001904 }
1905
Charles Keepax3809f002015-04-13 13:27:54 +01001906 if (n_algs > 1024) {
1907 adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001908 return ERR_PTR(-EINVAL);
Mark Brownd62f4bc2012-12-19 14:00:30 +00001909 }
1910
Mark Browndb405172012-10-26 19:30:40 +01001911 /* Read the terminator first to validate the length */
Charles Keepax7f7cca02018-06-20 11:56:21 +01001912 reg = wm_adsp_region_to_reg(mem, pos + len);
1913
1914 ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
Mark Browndb405172012-10-26 19:30:40 +01001915 if (ret != 0) {
1916 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1917 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001918 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001919 }
1920
1921 if (be32_to_cpu(val) != 0xbedead)
Richard Fitzgerald503ada82017-05-26 10:47:07 +01001922 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n",
Charles Keepax7f7cca02018-06-20 11:56:21 +01001923 reg, be32_to_cpu(val));
1924
1925 /* Convert length from DSP words to bytes */
1926 len *= sizeof(u32);
Mark Browndb405172012-10-26 19:30:40 +01001927
Charles Keepax517ee742018-07-19 11:50:35 +01001928 alg = kzalloc(len, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +01001929 if (!alg)
Charles Keepaxb618a1852015-04-13 13:27:53 +01001930 return ERR_PTR(-ENOMEM);
Mark Browndb405172012-10-26 19:30:40 +01001931
Charles Keepax7f7cca02018-06-20 11:56:21 +01001932 reg = wm_adsp_region_to_reg(mem, pos);
1933
1934 ret = regmap_raw_read(dsp->regmap, reg, alg, len);
Mark Browndb405172012-10-26 19:30:40 +01001935 if (ret != 0) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00001936 adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001937 kfree(alg);
1938 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001939 }
1940
Charles Keepaxb618a1852015-04-13 13:27:53 +01001941 return alg;
1942}
1943
Charles Keepax14197092015-12-15 11:29:43 +00001944static struct wm_adsp_alg_region *
1945 wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id)
1946{
1947 struct wm_adsp_alg_region *alg_region;
1948
1949 list_for_each_entry(alg_region, &dsp->alg_regions, list) {
1950 if (id == alg_region->alg && type == alg_region->type)
1951 return alg_region;
1952 }
1953
1954 return NULL;
1955}
1956
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001957static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
1958 int type, __be32 id,
1959 __be32 base)
1960{
1961 struct wm_adsp_alg_region *alg_region;
1962
1963 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1964 if (!alg_region)
1965 return ERR_PTR(-ENOMEM);
1966
1967 alg_region->type = type;
1968 alg_region->alg = be32_to_cpu(id);
1969 alg_region->base = be32_to_cpu(base);
1970
1971 list_add_tail(&alg_region->list, &dsp->alg_regions);
1972
Charles Keepax23237362015-04-13 13:28:02 +01001973 if (dsp->fw_ver > 0)
1974 wm_adsp_ctl_fixup_base(dsp, alg_region);
1975
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001976 return alg_region;
1977}
1978
Richard Fitzgerald56574d52016-04-27 14:58:29 +01001979static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
1980{
1981 struct wm_adsp_alg_region *alg_region;
1982
1983 while (!list_empty(&dsp->alg_regions)) {
1984 alg_region = list_first_entry(&dsp->alg_regions,
1985 struct wm_adsp_alg_region,
1986 list);
1987 list_del(&alg_region->list);
1988 kfree(alg_region);
1989 }
1990}
1991
Charles Keepaxb618a1852015-04-13 13:27:53 +01001992static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
1993{
1994 struct wmfw_adsp1_id_hdr adsp1_id;
1995 struct wmfw_adsp1_alg_hdr *adsp1_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001996 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001997 const struct wm_adsp_region *mem;
1998 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001999 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01002000 int i, ret;
2001
2002 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
2003 if (WARN_ON(!mem))
2004 return -EINVAL;
2005
2006 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
2007 sizeof(adsp1_id));
2008 if (ret != 0) {
2009 adsp_err(dsp, "Failed to read algorithm info: %d\n",
2010 ret);
2011 return ret;
2012 }
2013
Charles Keepax3809f002015-04-13 13:27:54 +01002014 n_algs = be32_to_cpu(adsp1_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002015 dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
2016 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
2017 dsp->fw_id,
2018 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
2019 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
2020 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01002021 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002022
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002023 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
2024 adsp1_id.fw.id, adsp1_id.zm);
2025 if (IS_ERR(alg_region))
2026 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002027
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002028 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
2029 adsp1_id.fw.id, adsp1_id.dm);
2030 if (IS_ERR(alg_region))
2031 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002032
Charles Keepax7f7cca02018-06-20 11:56:21 +01002033 /* Calculate offset and length in DSP words */
2034 pos = sizeof(adsp1_id) / sizeof(u32);
2035 len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002036
Charles Keepax7f7cca02018-06-20 11:56:21 +01002037 adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002038 if (IS_ERR(adsp1_alg))
2039 return PTR_ERR(adsp1_alg);
Mark Browndb405172012-10-26 19:30:40 +01002040
Charles Keepax3809f002015-04-13 13:27:54 +01002041 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01002042 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
2043 i, be32_to_cpu(adsp1_alg[i].alg.id),
2044 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
2045 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
2046 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
2047 be32_to_cpu(adsp1_alg[i].dm),
2048 be32_to_cpu(adsp1_alg[i].zm));
Mark Brown471f4882013-01-08 16:09:31 +00002049
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002050 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
2051 adsp1_alg[i].alg.id,
2052 adsp1_alg[i].dm);
2053 if (IS_ERR(alg_region)) {
2054 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002055 goto out;
2056 }
Charles Keepax23237362015-04-13 13:28:02 +01002057 if (dsp->fw_ver == 0) {
2058 if (i + 1 < n_algs) {
2059 len = be32_to_cpu(adsp1_alg[i + 1].dm);
2060 len -= be32_to_cpu(adsp1_alg[i].dm);
2061 len *= 4;
2062 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002063 len, NULL, 0, 0,
2064 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002065 } else {
2066 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
2067 be32_to_cpu(adsp1_alg[i].alg.id));
2068 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002069 }
Mark Brown471f4882013-01-08 16:09:31 +00002070
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002071 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
2072 adsp1_alg[i].alg.id,
2073 adsp1_alg[i].zm);
2074 if (IS_ERR(alg_region)) {
2075 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002076 goto out;
2077 }
Charles Keepax23237362015-04-13 13:28:02 +01002078 if (dsp->fw_ver == 0) {
2079 if (i + 1 < n_algs) {
2080 len = be32_to_cpu(adsp1_alg[i + 1].zm);
2081 len -= be32_to_cpu(adsp1_alg[i].zm);
2082 len *= 4;
2083 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002084 len, NULL, 0, 0,
2085 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002086 } else {
2087 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
2088 be32_to_cpu(adsp1_alg[i].alg.id));
2089 }
Mark Browndb405172012-10-26 19:30:40 +01002090 }
2091 }
2092
2093out:
Charles Keepaxb618a1852015-04-13 13:27:53 +01002094 kfree(adsp1_alg);
2095 return ret;
2096}
2097
2098static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
2099{
2100 struct wmfw_adsp2_id_hdr adsp2_id;
2101 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01002102 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01002103 const struct wm_adsp_region *mem;
2104 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01002105 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01002106 int i, ret;
2107
2108 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
2109 if (WARN_ON(!mem))
2110 return -EINVAL;
2111
2112 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
2113 sizeof(adsp2_id));
2114 if (ret != 0) {
2115 adsp_err(dsp, "Failed to read algorithm info: %d\n",
2116 ret);
2117 return ret;
2118 }
2119
Charles Keepax3809f002015-04-13 13:27:54 +01002120 n_algs = be32_to_cpu(adsp2_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002121 dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002122 dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002123 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
2124 dsp->fw_id,
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002125 (dsp->fw_id_version & 0xff0000) >> 16,
2126 (dsp->fw_id_version & 0xff00) >> 8,
2127 dsp->fw_id_version & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01002128 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002129
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002130 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
2131 adsp2_id.fw.id, adsp2_id.xm);
2132 if (IS_ERR(alg_region))
2133 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002134
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002135 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
2136 adsp2_id.fw.id, adsp2_id.ym);
2137 if (IS_ERR(alg_region))
2138 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002139
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002140 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
2141 adsp2_id.fw.id, adsp2_id.zm);
2142 if (IS_ERR(alg_region))
2143 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002144
Charles Keepax7f7cca02018-06-20 11:56:21 +01002145 /* Calculate offset and length in DSP words */
2146 pos = sizeof(adsp2_id) / sizeof(u32);
2147 len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002148
Charles Keepax7f7cca02018-06-20 11:56:21 +01002149 adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002150 if (IS_ERR(adsp2_alg))
2151 return PTR_ERR(adsp2_alg);
2152
Charles Keepax3809f002015-04-13 13:27:54 +01002153 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01002154 adsp_info(dsp,
2155 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
2156 i, be32_to_cpu(adsp2_alg[i].alg.id),
2157 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
2158 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
2159 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
2160 be32_to_cpu(adsp2_alg[i].xm),
2161 be32_to_cpu(adsp2_alg[i].ym),
2162 be32_to_cpu(adsp2_alg[i].zm));
2163
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002164 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
2165 adsp2_alg[i].alg.id,
2166 adsp2_alg[i].xm);
2167 if (IS_ERR(alg_region)) {
2168 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002169 goto out;
2170 }
Charles Keepax23237362015-04-13 13:28:02 +01002171 if (dsp->fw_ver == 0) {
2172 if (i + 1 < n_algs) {
2173 len = be32_to_cpu(adsp2_alg[i + 1].xm);
2174 len -= be32_to_cpu(adsp2_alg[i].xm);
2175 len *= 4;
2176 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002177 len, NULL, 0, 0,
2178 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002179 } else {
2180 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
2181 be32_to_cpu(adsp2_alg[i].alg.id));
2182 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002183 }
2184
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002185 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
2186 adsp2_alg[i].alg.id,
2187 adsp2_alg[i].ym);
2188 if (IS_ERR(alg_region)) {
2189 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002190 goto out;
2191 }
Charles Keepax23237362015-04-13 13:28:02 +01002192 if (dsp->fw_ver == 0) {
2193 if (i + 1 < n_algs) {
2194 len = be32_to_cpu(adsp2_alg[i + 1].ym);
2195 len -= be32_to_cpu(adsp2_alg[i].ym);
2196 len *= 4;
2197 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002198 len, NULL, 0, 0,
2199 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002200 } else {
2201 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
2202 be32_to_cpu(adsp2_alg[i].alg.id));
2203 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002204 }
2205
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002206 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
2207 adsp2_alg[i].alg.id,
2208 adsp2_alg[i].zm);
2209 if (IS_ERR(alg_region)) {
2210 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002211 goto out;
2212 }
Charles Keepax23237362015-04-13 13:28:02 +01002213 if (dsp->fw_ver == 0) {
2214 if (i + 1 < n_algs) {
2215 len = be32_to_cpu(adsp2_alg[i + 1].zm);
2216 len -= be32_to_cpu(adsp2_alg[i].zm);
2217 len *= 4;
2218 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002219 len, NULL, 0, 0,
2220 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002221 } else {
2222 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
2223 be32_to_cpu(adsp2_alg[i].alg.id));
2224 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002225 }
2226 }
2227
2228out:
2229 kfree(adsp2_alg);
Mark Browndb405172012-10-26 19:30:40 +01002230 return ret;
2231}
2232
Mark Brown2159ad932012-10-11 11:54:02 +09002233static int wm_adsp_load_coeff(struct wm_adsp *dsp)
2234{
Mark Browncf17c832013-01-30 14:37:23 +08002235 LIST_HEAD(buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09002236 struct regmap *regmap = dsp->regmap;
2237 struct wmfw_coeff_hdr *hdr;
2238 struct wmfw_coeff_item *blk;
2239 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +00002240 const struct wm_adsp_region *mem;
2241 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad932012-10-11 11:54:02 +09002242 const char *region_name;
2243 int ret, pos, blocks, type, offset, reg;
2244 char *file;
Mark Browncf17c832013-01-30 14:37:23 +08002245 struct wm_adsp_buf *buf;
Mark Brown2159ad932012-10-11 11:54:02 +09002246
2247 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
2248 if (file == NULL)
2249 return -ENOMEM;
2250
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002251 snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name,
Mark Brown1023dbd2013-01-11 22:58:28 +00002252 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +09002253 file[PAGE_SIZE - 1] = '\0';
2254
2255 ret = request_firmware(&firmware, file, dsp->dev);
2256 if (ret != 0) {
2257 adsp_warn(dsp, "Failed to request '%s'\n", file);
2258 ret = 0;
2259 goto out;
2260 }
2261 ret = -EINVAL;
2262
2263 if (sizeof(*hdr) >= firmware->size) {
2264 adsp_err(dsp, "%s: file too short, %zu bytes\n",
2265 file, firmware->size);
2266 goto out_fw;
2267 }
2268
Charles Keepax7585a5b2015-12-08 16:08:25 +00002269 hdr = (void *)&firmware->data[0];
Mark Brown2159ad932012-10-11 11:54:02 +09002270 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
2271 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +00002272 goto out_fw;
Mark Brown2159ad932012-10-11 11:54:02 +09002273 }
2274
Mark Brownc7123262013-01-16 16:59:04 +09002275 switch (be32_to_cpu(hdr->rev) & 0xff) {
2276 case 1:
2277 break;
2278 default:
2279 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
2280 file, be32_to_cpu(hdr->rev) & 0xff);
2281 ret = -EINVAL;
2282 goto out_fw;
2283 }
2284
Mark Brown2159ad932012-10-11 11:54:02 +09002285 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
2286 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
2287 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
2288 le32_to_cpu(hdr->ver) & 0xff);
2289
2290 pos = le32_to_cpu(hdr->len);
2291
2292 blocks = 0;
2293 while (pos < firmware->size &&
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00002294 sizeof(*blk) < firmware->size - pos) {
Charles Keepax7585a5b2015-12-08 16:08:25 +00002295 blk = (void *)(&firmware->data[pos]);
Mark Brown2159ad932012-10-11 11:54:02 +09002296
Mark Brownc7123262013-01-16 16:59:04 +09002297 type = le16_to_cpu(blk->type);
2298 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad932012-10-11 11:54:02 +09002299
2300 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
2301 file, blocks, le32_to_cpu(blk->id),
2302 (le32_to_cpu(blk->ver) >> 16) & 0xff,
2303 (le32_to_cpu(blk->ver) >> 8) & 0xff,
2304 le32_to_cpu(blk->ver) & 0xff);
2305 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
2306 file, blocks, le32_to_cpu(blk->len), offset, type);
2307
2308 reg = 0;
2309 region_name = "Unknown";
2310 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +09002311 case (WMFW_NAME_TEXT << 8):
2312 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad932012-10-11 11:54:02 +09002313 break;
Mark Brownc7123262013-01-16 16:59:04 +09002314 case (WMFW_ABSOLUTE << 8):
Mark Brownf395a212013-03-05 22:39:54 +08002315 /*
2316 * Old files may use this for global
2317 * coefficients.
2318 */
2319 if (le32_to_cpu(blk->id) == dsp->fw_id &&
2320 offset == 0) {
2321 region_name = "global coefficients";
2322 mem = wm_adsp_find_region(dsp, type);
2323 if (!mem) {
2324 adsp_err(dsp, "No ZM\n");
2325 break;
2326 }
2327 reg = wm_adsp_region_to_reg(mem, 0);
2328
2329 } else {
2330 region_name = "register";
2331 reg = offset;
2332 }
Mark Brown2159ad932012-10-11 11:54:02 +09002333 break;
Mark Brown471f4882013-01-08 16:09:31 +00002334
2335 case WMFW_ADSP1_DM:
2336 case WMFW_ADSP1_ZM:
2337 case WMFW_ADSP2_XM:
2338 case WMFW_ADSP2_YM:
2339 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
2340 file, blocks, le32_to_cpu(blk->len),
2341 type, le32_to_cpu(blk->id));
2342
2343 mem = wm_adsp_find_region(dsp, type);
2344 if (!mem) {
2345 adsp_err(dsp, "No base for region %x\n", type);
2346 break;
2347 }
2348
Charles Keepax14197092015-12-15 11:29:43 +00002349 alg_region = wm_adsp_find_alg_region(dsp, type,
2350 le32_to_cpu(blk->id));
2351 if (alg_region) {
2352 reg = alg_region->base;
2353 reg = wm_adsp_region_to_reg(mem, reg);
2354 reg += offset;
2355 } else {
Mark Brown471f4882013-01-08 16:09:31 +00002356 adsp_err(dsp, "No %x for algorithm %x\n",
2357 type, le32_to_cpu(blk->id));
Charles Keepax14197092015-12-15 11:29:43 +00002358 }
Mark Brown471f4882013-01-08 16:09:31 +00002359 break;
2360
Mark Brown2159ad932012-10-11 11:54:02 +09002361 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +09002362 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
2363 file, blocks, type, pos);
Mark Brown2159ad932012-10-11 11:54:02 +09002364 break;
2365 }
2366
2367 if (reg) {
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00002368 if (le32_to_cpu(blk->len) >
2369 firmware->size - pos - sizeof(*blk)) {
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00002370 adsp_err(dsp,
2371 "%s.%d: %s region len %d bytes exceeds file length %zu\n",
2372 file, blocks, region_name,
2373 le32_to_cpu(blk->len),
2374 firmware->size);
2375 ret = -EINVAL;
2376 goto out_fw;
2377 }
2378
Mark Browncf17c832013-01-30 14:37:23 +08002379 buf = wm_adsp_buf_alloc(blk->data,
2380 le32_to_cpu(blk->len),
2381 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +00002382 if (!buf) {
2383 adsp_err(dsp, "Out of memory\n");
Wei Yongjunf4b82812013-03-12 00:23:15 +08002384 ret = -ENOMEM;
2385 goto out_fw;
Mark Browna76fefa2013-01-07 19:03:17 +00002386 }
2387
Mark Brown20da6d52013-01-12 19:58:17 +00002388 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
2389 file, blocks, le32_to_cpu(blk->len),
2390 reg);
Mark Browncf17c832013-01-30 14:37:23 +08002391 ret = regmap_raw_write_async(regmap, reg, buf->buf,
2392 le32_to_cpu(blk->len));
Mark Brown2159ad932012-10-11 11:54:02 +09002393 if (ret != 0) {
2394 adsp_err(dsp,
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +00002395 "%s.%d: Failed to write to %x in %s: %d\n",
2396 file, blocks, reg, region_name, ret);
Mark Brown2159ad932012-10-11 11:54:02 +09002397 }
2398 }
2399
Charles Keepaxbe951012015-02-16 15:25:49 +00002400 pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
Mark Brown2159ad932012-10-11 11:54:02 +09002401 blocks++;
2402 }
2403
Mark Browncf17c832013-01-30 14:37:23 +08002404 ret = regmap_async_complete(regmap);
2405 if (ret != 0)
2406 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
2407
Mark Brown2159ad932012-10-11 11:54:02 +09002408 if (pos > firmware->size)
2409 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
2410 file, blocks, pos - firmware->size);
2411
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002412 wm_adsp_debugfs_save_binname(dsp, file);
2413
Mark Brown2159ad932012-10-11 11:54:02 +09002414out_fw:
Charles Keepax9da7a5a2014-11-17 10:48:21 +00002415 regmap_async_complete(regmap);
Mark Brown2159ad932012-10-11 11:54:02 +09002416 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +08002417 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09002418out:
2419 kfree(file);
Wei Yongjunf4b82812013-03-12 00:23:15 +08002420 return ret;
Mark Brown2159ad932012-10-11 11:54:02 +09002421}
2422
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002423static int wm_adsp_create_name(struct wm_adsp *dsp)
2424{
2425 char *p;
2426
2427 if (!dsp->name) {
2428 dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d",
2429 dsp->num);
2430 if (!dsp->name)
2431 return -ENOMEM;
2432 }
2433
2434 if (!dsp->fwf_name) {
2435 p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL);
2436 if (!p)
2437 return -ENOMEM;
2438
2439 dsp->fwf_name = p;
2440 for (; *p != 0; ++p)
2441 *p = tolower(*p);
2442 }
2443
2444 return 0;
2445}
2446
Richard Fitzgeralddcad34f2018-11-12 13:36:39 +00002447static int wm_adsp_common_init(struct wm_adsp *dsp)
Mark Brown5e7a7a22013-01-16 10:03:56 +09002448{
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002449 int ret;
2450
2451 ret = wm_adsp_create_name(dsp);
2452 if (ret)
2453 return ret;
2454
Charles Keepax3809f002015-04-13 13:27:54 +01002455 INIT_LIST_HEAD(&dsp->alg_regions);
Richard Fitzgeralddcad34f2018-11-12 13:36:39 +00002456 INIT_LIST_HEAD(&dsp->ctl_list);
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00002457 INIT_LIST_HEAD(&dsp->compr_list);
2458 INIT_LIST_HEAD(&dsp->buffer_list);
Mark Brown5e7a7a22013-01-16 10:03:56 +09002459
Charles Keepax078e7182015-12-08 16:08:26 +00002460 mutex_init(&dsp->pwr_lock);
2461
Mark Brown5e7a7a22013-01-16 10:03:56 +09002462 return 0;
2463}
Richard Fitzgeralddcad34f2018-11-12 13:36:39 +00002464
2465int wm_adsp1_init(struct wm_adsp *dsp)
2466{
2467 return wm_adsp_common_init(dsp);
2468}
Mark Brown5e7a7a22013-01-16 10:03:56 +09002469EXPORT_SYMBOL_GPL(wm_adsp1_init);
2470
Mark Brown2159ad932012-10-11 11:54:02 +09002471int wm_adsp1_event(struct snd_soc_dapm_widget *w,
2472 struct snd_kcontrol *kcontrol,
2473 int event)
2474{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002475 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2476 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Mark Brown2159ad932012-10-11 11:54:02 +09002477 struct wm_adsp *dsp = &dsps[w->shift];
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002478 struct wm_coeff_ctl *ctl;
Mark Brown2159ad932012-10-11 11:54:02 +09002479 int ret;
Charles Keepax7585a5b2015-12-08 16:08:25 +00002480 unsigned int val;
Mark Brown2159ad932012-10-11 11:54:02 +09002481
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002482 dsp->component = component;
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01002483
Charles Keepax078e7182015-12-08 16:08:26 +00002484 mutex_lock(&dsp->pwr_lock);
2485
Mark Brown2159ad932012-10-11 11:54:02 +09002486 switch (event) {
2487 case SND_SOC_DAPM_POST_PMU:
2488 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2489 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
2490
Chris Rattray94e205b2013-01-18 08:43:09 +00002491 /*
2492 * For simplicity set the DSP clock rate to be the
2493 * SYSCLK rate rather than making it configurable.
2494 */
Charles Keepax7585a5b2015-12-08 16:08:25 +00002495 if (dsp->sysclk_reg) {
Chris Rattray94e205b2013-01-18 08:43:09 +00002496 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
2497 if (ret != 0) {
2498 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
2499 ret);
Charles Keepax078e7182015-12-08 16:08:26 +00002500 goto err_mutex;
Chris Rattray94e205b2013-01-18 08:43:09 +00002501 }
2502
Charles Keepax7d00cd92016-02-19 14:44:43 +00002503 val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
Chris Rattray94e205b2013-01-18 08:43:09 +00002504
2505 ret = regmap_update_bits(dsp->regmap,
2506 dsp->base + ADSP1_CONTROL_31,
2507 ADSP1_CLK_SEL_MASK, val);
2508 if (ret != 0) {
2509 adsp_err(dsp, "Failed to set clock rate: %d\n",
2510 ret);
Charles Keepax078e7182015-12-08 16:08:26 +00002511 goto err_mutex;
Chris Rattray94e205b2013-01-18 08:43:09 +00002512 }
2513 }
2514
Mark Brown2159ad932012-10-11 11:54:02 +09002515 ret = wm_adsp_load(dsp);
2516 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002517 goto err_ena;
Mark Brown2159ad932012-10-11 11:54:02 +09002518
Charles Keepaxb618a1852015-04-13 13:27:53 +01002519 ret = wm_adsp1_setup_algs(dsp);
Mark Browndb405172012-10-26 19:30:40 +01002520 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002521 goto err_ena;
Mark Browndb405172012-10-26 19:30:40 +01002522
Mark Brown2159ad932012-10-11 11:54:02 +09002523 ret = wm_adsp_load_coeff(dsp);
2524 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002525 goto err_ena;
Mark Brown2159ad932012-10-11 11:54:02 +09002526
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01002527 /* Initialize caches for enabled and unset controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002528 ret = wm_coeff_init_control_caches(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002529 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002530 goto err_ena;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002531
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01002532 /* Sync set controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002533 ret = wm_coeff_sync_controls(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002534 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002535 goto err_ena;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002536
Charles Keepax28823eb2016-09-20 13:52:32 +01002537 dsp->booted = true;
2538
Mark Brown2159ad932012-10-11 11:54:02 +09002539 /* Start the core running */
2540 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2541 ADSP1_CORE_ENA | ADSP1_START,
2542 ADSP1_CORE_ENA | ADSP1_START);
Charles Keepax28823eb2016-09-20 13:52:32 +01002543
2544 dsp->running = true;
Mark Brown2159ad932012-10-11 11:54:02 +09002545 break;
2546
2547 case SND_SOC_DAPM_PRE_PMD:
Charles Keepax28823eb2016-09-20 13:52:32 +01002548 dsp->running = false;
2549 dsp->booted = false;
2550
Mark Brown2159ad932012-10-11 11:54:02 +09002551 /* Halt the core */
2552 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2553 ADSP1_CORE_ENA | ADSP1_START, 0);
2554
2555 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
2556 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
2557
2558 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2559 ADSP1_SYS_ENA, 0);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002560
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002561 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002562 ctl->enabled = 0;
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00002563
Richard Fitzgerald56574d52016-04-27 14:58:29 +01002564
2565 wm_adsp_free_alg_regions(dsp);
Mark Brown2159ad932012-10-11 11:54:02 +09002566 break;
2567
2568 default:
2569 break;
2570 }
2571
Charles Keepax078e7182015-12-08 16:08:26 +00002572 mutex_unlock(&dsp->pwr_lock);
2573
Mark Brown2159ad932012-10-11 11:54:02 +09002574 return 0;
2575
Charles Keepax078e7182015-12-08 16:08:26 +00002576err_ena:
Mark Brown2159ad932012-10-11 11:54:02 +09002577 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2578 ADSP1_SYS_ENA, 0);
Charles Keepax078e7182015-12-08 16:08:26 +00002579err_mutex:
2580 mutex_unlock(&dsp->pwr_lock);
2581
Mark Brown2159ad932012-10-11 11:54:02 +09002582 return ret;
2583}
2584EXPORT_SYMBOL_GPL(wm_adsp1_event);
2585
2586static int wm_adsp2_ena(struct wm_adsp *dsp)
2587{
2588 unsigned int val;
2589 int ret, count;
2590
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002591 switch (dsp->rev) {
2592 case 0:
2593 ret = regmap_update_bits_async(dsp->regmap,
2594 dsp->base + ADSP2_CONTROL,
2595 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
2596 if (ret != 0)
2597 return ret;
2598 break;
2599 default:
2600 break;
2601 }
Mark Brown2159ad932012-10-11 11:54:02 +09002602
2603 /* Wait for the RAM to start, should be near instantaneous */
Charles Keepax939fd1e2013-12-18 09:25:49 +00002604 for (count = 0; count < 10; ++count) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00002605 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
Mark Brown2159ad932012-10-11 11:54:02 +09002606 if (ret != 0)
2607 return ret;
Charles Keepax939fd1e2013-12-18 09:25:49 +00002608
2609 if (val & ADSP2_RAM_RDY)
2610 break;
2611
Charles Keepax1fa96f32016-09-26 10:15:22 +01002612 usleep_range(250, 500);
Charles Keepax939fd1e2013-12-18 09:25:49 +00002613 }
Mark Brown2159ad932012-10-11 11:54:02 +09002614
2615 if (!(val & ADSP2_RAM_RDY)) {
2616 adsp_err(dsp, "Failed to start DSP RAM\n");
2617 return -EBUSY;
2618 }
2619
2620 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
Mark Brown2159ad932012-10-11 11:54:02 +09002621
2622 return 0;
2623}
2624
Charles Keepax18b1a902014-01-09 09:06:54 +00002625static void wm_adsp2_boot_work(struct work_struct *work)
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002626{
2627 struct wm_adsp *dsp = container_of(work,
2628 struct wm_adsp,
2629 boot_work);
2630 int ret;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002631
Charles Keepax078e7182015-12-08 16:08:26 +00002632 mutex_lock(&dsp->pwr_lock);
2633
Charles Keepax90d19ba2016-09-26 10:15:23 +01002634 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2635 ADSP2_MEM_ENA, ADSP2_MEM_ENA);
2636 if (ret != 0)
2637 goto err_mutex;
2638
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002639 ret = wm_adsp2_ena(dsp);
2640 if (ret != 0)
Charles Keepaxd589d8b2017-01-24 11:44:01 +00002641 goto err_mem;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002642
2643 ret = wm_adsp_load(dsp);
2644 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002645 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002646
Charles Keepaxb618a1852015-04-13 13:27:53 +01002647 ret = wm_adsp2_setup_algs(dsp);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002648 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002649 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002650
2651 ret = wm_adsp_load_coeff(dsp);
2652 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002653 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002654
2655 /* Initialize caches for enabled and unset controls */
2656 ret = wm_coeff_init_control_caches(dsp);
2657 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002658 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002659
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002660 switch (dsp->rev) {
2661 case 0:
2662 /* Turn DSP back off until we are ready to run */
2663 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2664 ADSP2_SYS_ENA, 0);
2665 if (ret != 0)
2666 goto err_ena;
2667 break;
2668 default:
2669 break;
2670 }
Charles Keepax90d19ba2016-09-26 10:15:23 +01002671
Charles Keepaxe7799742017-01-24 11:44:00 +00002672 dsp->booted = true;
2673
Charles Keepax078e7182015-12-08 16:08:26 +00002674 mutex_unlock(&dsp->pwr_lock);
2675
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002676 return;
2677
Charles Keepax078e7182015-12-08 16:08:26 +00002678err_ena:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002679 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2680 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Charles Keepaxd589d8b2017-01-24 11:44:01 +00002681err_mem:
2682 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2683 ADSP2_MEM_ENA, 0);
Charles Keepax078e7182015-12-08 16:08:26 +00002684err_mutex:
2685 mutex_unlock(&dsp->pwr_lock);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002686}
2687
Charles Keepaxd82d7672016-01-21 17:53:02 +00002688static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
2689{
2690 int ret;
2691
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002692 switch (dsp->rev) {
2693 case 0:
2694 ret = regmap_update_bits_async(dsp->regmap,
2695 dsp->base + ADSP2_CLOCKING,
2696 ADSP2_CLK_SEL_MASK,
2697 freq << ADSP2_CLK_SEL_SHIFT);
2698 if (ret) {
2699 adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
2700 return;
2701 }
2702 break;
2703 default:
2704 /* clock is handled by parent codec driver */
2705 break;
2706 }
Charles Keepaxd82d7672016-01-21 17:53:02 +00002707}
2708
Charles Keepaxaf813a62017-01-06 14:24:41 +00002709int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
2710 struct snd_ctl_elem_value *ucontrol)
2711{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002712 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Ajit Pandeyb1470d42018-08-07 18:30:42 +01002713 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
2714 struct soc_mixer_control *mc =
2715 (struct soc_mixer_control *)kcontrol->private_value;
2716 struct wm_adsp *dsp = &dsps[mc->shift - 1];
Charles Keepaxaf813a62017-01-06 14:24:41 +00002717
2718 ucontrol->value.integer.value[0] = dsp->preloaded;
2719
2720 return 0;
2721}
2722EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);
2723
2724int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
2725 struct snd_ctl_elem_value *ucontrol)
2726{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002727 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Ajit Pandeyb1470d42018-08-07 18:30:42 +01002728 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002729 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002730 struct soc_mixer_control *mc =
2731 (struct soc_mixer_control *)kcontrol->private_value;
Ajit Pandeyb1470d42018-08-07 18:30:42 +01002732 struct wm_adsp *dsp = &dsps[mc->shift - 1];
Charles Keepaxaf813a62017-01-06 14:24:41 +00002733 char preload[32];
2734
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002735 snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002736
2737 dsp->preloaded = ucontrol->value.integer.value[0];
2738
2739 if (ucontrol->value.integer.value[0])
Charles Keepax95a594d2018-04-24 16:53:09 +01002740 snd_soc_component_force_enable_pin(component, preload);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002741 else
Charles Keepax95a594d2018-04-24 16:53:09 +01002742 snd_soc_component_disable_pin(component, preload);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002743
2744 snd_soc_dapm_sync(dapm);
2745
Stuart Henderson868e49a2018-07-19 11:50:37 +01002746 flush_work(&dsp->boot_work);
2747
Charles Keepaxaf813a62017-01-06 14:24:41 +00002748 return 0;
2749}
2750EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
2751
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01002752static void wm_adsp_stop_watchdog(struct wm_adsp *dsp)
2753{
2754 switch (dsp->rev) {
2755 case 0:
2756 case 1:
2757 return;
2758 default:
2759 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
2760 ADSP2_WDT_ENA_MASK, 0);
2761 }
2762}
2763
Charles Keepax12db5ed2014-01-08 17:42:19 +00002764int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
Charles Keepaxd82d7672016-01-21 17:53:02 +00002765 struct snd_kcontrol *kcontrol, int event,
2766 unsigned int freq)
Charles Keepax12db5ed2014-01-08 17:42:19 +00002767{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002768 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2769 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Charles Keepax12db5ed2014-01-08 17:42:19 +00002770 struct wm_adsp *dsp = &dsps[w->shift];
Charles Keepax57a60cc2016-09-26 10:15:24 +01002771 struct wm_coeff_ctl *ctl;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002772
Charles Keepax12db5ed2014-01-08 17:42:19 +00002773 switch (event) {
2774 case SND_SOC_DAPM_PRE_PMU:
Charles Keepaxd82d7672016-01-21 17:53:02 +00002775 wm_adsp2_set_dspclk(dsp, freq);
Charles Keepax12db5ed2014-01-08 17:42:19 +00002776 queue_work(system_unbound_wq, &dsp->boot_work);
2777 break;
Charles Keepax57a60cc2016-09-26 10:15:24 +01002778 case SND_SOC_DAPM_PRE_PMD:
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002779 mutex_lock(&dsp->pwr_lock);
2780
Charles Keepax57a60cc2016-09-26 10:15:24 +01002781 wm_adsp_debugfs_clear(dsp);
2782
2783 dsp->fw_id = 0;
2784 dsp->fw_id_version = 0;
2785
2786 dsp->booted = false;
2787
2788 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2789 ADSP2_MEM_ENA, 0);
2790
2791 list_for_each_entry(ctl, &dsp->ctl_list, list)
2792 ctl->enabled = 0;
2793
2794 wm_adsp_free_alg_regions(dsp);
2795
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002796 mutex_unlock(&dsp->pwr_lock);
2797
Charles Keepax57a60cc2016-09-26 10:15:24 +01002798 adsp_dbg(dsp, "Shutdown complete\n");
2799 break;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002800 default:
2801 break;
Charles Keepaxcab272582014-04-17 13:42:54 +01002802 }
Charles Keepax12db5ed2014-01-08 17:42:19 +00002803
2804 return 0;
2805}
2806EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
2807
Mark Brown2159ad932012-10-11 11:54:02 +09002808int wm_adsp2_event(struct snd_soc_dapm_widget *w,
2809 struct snd_kcontrol *kcontrol, int event)
2810{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002811 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2812 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Mark Brown2159ad932012-10-11 11:54:02 +09002813 struct wm_adsp *dsp = &dsps[w->shift];
2814 int ret;
2815
2816 switch (event) {
2817 case SND_SOC_DAPM_POST_PMU:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002818 flush_work(&dsp->boot_work);
Mark Browndd49e2c2012-12-02 21:50:46 +09002819
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002820 mutex_lock(&dsp->pwr_lock);
2821
2822 if (!dsp->booted) {
2823 ret = -EIO;
2824 goto err;
2825 }
Mark Browndd49e2c2012-12-02 21:50:46 +09002826
Charles Keepax90d19ba2016-09-26 10:15:23 +01002827 ret = wm_adsp2_ena(dsp);
2828 if (ret != 0)
2829 goto err;
2830
Charles Keepaxcef45772016-09-20 13:52:33 +01002831 /* Sync set controls */
2832 ret = wm_coeff_sync_controls(dsp);
2833 if (ret != 0)
2834 goto err;
2835
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01002836 wm_adsp2_lock(dsp, dsp->lock_regions);
2837
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002838 ret = regmap_update_bits(dsp->regmap,
2839 dsp->base + ADSP2_CONTROL,
Charles Keepax00e4c3b2014-11-18 16:25:27 +00002840 ADSP2_CORE_ENA | ADSP2_START,
2841 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad932012-10-11 11:54:02 +09002842 if (ret != 0)
2843 goto err;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002844
Charles Keepax48c2c992016-11-22 15:38:34 +00002845 if (wm_adsp_fw[dsp->fw].num_caps != 0) {
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002846 ret = wm_adsp_buffer_init(dsp);
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002847 if (ret < 0)
Charles Keepax48c2c992016-11-22 15:38:34 +00002848 goto err;
Charles Keepax48c2c992016-11-22 15:38:34 +00002849 }
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002850
Charles Keepaxe7799742017-01-24 11:44:00 +00002851 dsp->running = true;
2852
Charles Keepax612047f2016-03-28 14:29:22 +01002853 mutex_unlock(&dsp->pwr_lock);
2854
Mark Brown2159ad932012-10-11 11:54:02 +09002855 break;
2856
2857 case SND_SOC_DAPM_PRE_PMD:
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00002858 /* Tell the firmware to cleanup */
2859 wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
2860
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01002861 wm_adsp_stop_watchdog(dsp);
2862
Richard Fitzgerald10337b02015-05-29 10:23:07 +01002863 /* Log firmware state, it can be useful for analysis */
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002864 switch (dsp->rev) {
2865 case 0:
2866 wm_adsp2_show_fw_status(dsp);
2867 break;
2868 default:
2869 wm_adsp2v2_show_fw_status(dsp);
2870 break;
2871 }
Richard Fitzgerald10337b02015-05-29 10:23:07 +01002872
Charles Keepax078e7182015-12-08 16:08:26 +00002873 mutex_lock(&dsp->pwr_lock);
2874
Mark Brown1023dbd2013-01-11 22:58:28 +00002875 dsp->running = false;
2876
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002877 regmap_update_bits(dsp->regmap,
2878 dsp->base + ADSP2_CONTROL,
Charles Keepax57a60cc2016-09-26 10:15:24 +01002879 ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00002880
Mark Brown2d30b572013-01-28 20:18:17 +08002881 /* Make sure DMAs are quiesced */
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002882 switch (dsp->rev) {
2883 case 0:
2884 regmap_write(dsp->regmap,
2885 dsp->base + ADSP2_RDMA_CONFIG_1, 0);
2886 regmap_write(dsp->regmap,
2887 dsp->base + ADSP2_WDMA_CONFIG_1, 0);
2888 regmap_write(dsp->regmap,
2889 dsp->base + ADSP2_WDMA_CONFIG_2, 0);
Simon Trimmer6facd2d2016-06-22 15:31:03 +01002890
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002891 regmap_update_bits(dsp->regmap,
2892 dsp->base + ADSP2_CONTROL,
2893 ADSP2_SYS_ENA, 0);
2894 break;
2895 default:
2896 regmap_write(dsp->regmap,
2897 dsp->base + ADSP2_RDMA_CONFIG_1, 0);
2898 regmap_write(dsp->regmap,
2899 dsp->base + ADSP2_WDMA_CONFIG_1, 0);
2900 regmap_write(dsp->regmap,
2901 dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
2902 break;
2903 }
Mark Brown2d30b572013-01-28 20:18:17 +08002904
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002905 if (wm_adsp_fw[dsp->fw].num_caps != 0)
2906 wm_adsp_buffer_free(dsp);
2907
Charles Keepax078e7182015-12-08 16:08:26 +00002908 mutex_unlock(&dsp->pwr_lock);
2909
Charles Keepax57a60cc2016-09-26 10:15:24 +01002910 adsp_dbg(dsp, "Execution stopped\n");
Mark Brown2159ad932012-10-11 11:54:02 +09002911 break;
2912
2913 default:
2914 break;
2915 }
2916
2917 return 0;
2918err:
2919 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00002920 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002921 mutex_unlock(&dsp->pwr_lock);
Mark Brown2159ad932012-10-11 11:54:02 +09002922 return ret;
2923}
2924EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00002925
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002926int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002927{
Charles Keepaxaf813a62017-01-06 14:24:41 +00002928 char preload[32];
2929
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002930 snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name);
Charles Keepax95a594d2018-04-24 16:53:09 +01002931 snd_soc_component_disable_pin(component, preload);
Richard Fitzgerald685f51a2016-11-22 16:58:57 +00002932
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002933 wm_adsp2_init_debugfs(dsp, component);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002934
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002935 dsp->component = component;
Charles Keepaxaf813a62017-01-06 14:24:41 +00002936
Richard Fitzgerald0a047f02018-08-08 17:13:38 +01002937 return 0;
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002938}
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002939EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002940
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002941int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002942{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002943 wm_adsp2_cleanup_debugfs(dsp);
2944
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002945 return 0;
2946}
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002947EXPORT_SYMBOL_GPL(wm_adsp2_component_remove);
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002948
Richard Fitzgerald81ac58b2015-06-02 11:53:34 +01002949int wm_adsp2_init(struct wm_adsp *dsp)
Mark Brown973838a2012-11-28 17:20:32 +00002950{
2951 int ret;
2952
Richard Fitzgeralddcad34f2018-11-12 13:36:39 +00002953 ret = wm_adsp_common_init(dsp);
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002954 if (ret)
2955 return ret;
2956
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002957 switch (dsp->rev) {
2958 case 0:
2959 /*
2960 * Disable the DSP memory by default when in reset for a small
2961 * power saving.
2962 */
2963 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2964 ADSP2_MEM_ENA, 0);
2965 if (ret) {
2966 adsp_err(dsp,
2967 "Failed to clear memory retention: %d\n", ret);
2968 return ret;
2969 }
2970 break;
2971 default:
2972 break;
Mark Brown10a2b662012-12-02 21:37:00 +09002973 }
2974
Charles Keepax3809f002015-04-13 13:27:54 +01002975 INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002976
Mark Brown973838a2012-11-28 17:20:32 +00002977 return 0;
2978}
2979EXPORT_SYMBOL_GPL(wm_adsp2_init);
Praveen Diwakar0a37c6ef2014-07-04 11:17:41 +05302980
Richard Fitzgerald66225e92016-04-27 14:58:27 +01002981void wm_adsp2_remove(struct wm_adsp *dsp)
2982{
2983 struct wm_coeff_ctl *ctl;
2984
2985 while (!list_empty(&dsp->ctl_list)) {
2986 ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
2987 list);
2988 list_del(&ctl->list);
2989 wm_adsp_free_ctl_blk(ctl);
2990 }
2991}
2992EXPORT_SYMBOL_GPL(wm_adsp2_remove);
2993
Charles Keepaxedd71352016-05-04 17:11:55 +01002994static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
2995{
2996 return compr->buf != NULL;
2997}
2998
2999static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
3000{
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003001 struct wm_adsp_compr_buf *buf = NULL, *tmp;
3002
3003 list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
3004 if (!tmp->name || !strcmp(compr->name, tmp->name)) {
3005 buf = tmp;
3006 break;
3007 }
3008 }
3009
3010 if (!buf)
Charles Keepaxedd71352016-05-04 17:11:55 +01003011 return -EINVAL;
3012
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003013 compr->buf = buf;
Charles Keepax721be3b2016-05-04 17:11:56 +01003014 compr->buf->compr = compr;
Charles Keepaxedd71352016-05-04 17:11:55 +01003015
3016 return 0;
3017}
3018
Charles Keepax721be3b2016-05-04 17:11:56 +01003019static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
3020{
3021 if (!compr)
3022 return;
3023
3024 /* Wake the poll so it can see buffer is no longer attached */
3025 if (compr->stream)
3026 snd_compr_fragment_elapsed(compr->stream);
3027
3028 if (wm_adsp_compr_attached(compr)) {
3029 compr->buf->compr = NULL;
3030 compr->buf = NULL;
3031 }
3032}
3033
Charles Keepax406abc92015-12-15 11:29:45 +00003034int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
3035{
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003036 struct wm_adsp_compr *compr, *tmp;
3037 struct snd_soc_pcm_runtime *rtd = stream->private_data;
Charles Keepax406abc92015-12-15 11:29:45 +00003038 int ret = 0;
3039
3040 mutex_lock(&dsp->pwr_lock);
3041
3042 if (wm_adsp_fw[dsp->fw].num_caps == 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003043 adsp_err(dsp, "%s: Firmware does not support compressed API\n",
3044 rtd->codec_dai->name);
Charles Keepax406abc92015-12-15 11:29:45 +00003045 ret = -ENXIO;
3046 goto out;
3047 }
3048
3049 if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003050 adsp_err(dsp, "%s: Firmware does not support stream direction\n",
3051 rtd->codec_dai->name);
Charles Keepax406abc92015-12-15 11:29:45 +00003052 ret = -EINVAL;
3053 goto out;
3054 }
3055
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003056 list_for_each_entry(tmp, &dsp->compr_list, list) {
3057 if (!strcmp(tmp->name, rtd->codec_dai->name)) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003058 adsp_err(dsp, "%s: Only a single stream supported per dai\n",
3059 rtd->codec_dai->name);
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003060 ret = -EBUSY;
3061 goto out;
3062 }
Charles Keepax95fe9592015-12-15 11:29:47 +00003063 }
3064
Charles Keepax406abc92015-12-15 11:29:45 +00003065 compr = kzalloc(sizeof(*compr), GFP_KERNEL);
3066 if (!compr) {
3067 ret = -ENOMEM;
3068 goto out;
3069 }
3070
3071 compr->dsp = dsp;
3072 compr->stream = stream;
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003073 compr->name = rtd->codec_dai->name;
Charles Keepax406abc92015-12-15 11:29:45 +00003074
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003075 list_add_tail(&compr->list, &dsp->compr_list);
Charles Keepax406abc92015-12-15 11:29:45 +00003076
3077 stream->runtime->private_data = compr;
3078
3079out:
3080 mutex_unlock(&dsp->pwr_lock);
3081
3082 return ret;
3083}
3084EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
3085
3086int wm_adsp_compr_free(struct snd_compr_stream *stream)
3087{
3088 struct wm_adsp_compr *compr = stream->runtime->private_data;
3089 struct wm_adsp *dsp = compr->dsp;
3090
3091 mutex_lock(&dsp->pwr_lock);
3092
Charles Keepax721be3b2016-05-04 17:11:56 +01003093 wm_adsp_compr_detach(compr);
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003094 list_del(&compr->list);
Charles Keepax406abc92015-12-15 11:29:45 +00003095
Charles Keepax83a40ce2016-01-06 12:33:19 +00003096 kfree(compr->raw_buf);
Charles Keepax406abc92015-12-15 11:29:45 +00003097 kfree(compr);
3098
3099 mutex_unlock(&dsp->pwr_lock);
3100
3101 return 0;
3102}
3103EXPORT_SYMBOL_GPL(wm_adsp_compr_free);
3104
3105static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
3106 struct snd_compr_params *params)
3107{
3108 struct wm_adsp_compr *compr = stream->runtime->private_data;
3109 struct wm_adsp *dsp = compr->dsp;
3110 const struct wm_adsp_fw_caps *caps;
3111 const struct snd_codec_desc *desc;
3112 int i, j;
3113
3114 if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||
3115 params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
3116 params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
3117 params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
3118 params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003119 compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n",
3120 params->buffer.fragment_size,
3121 params->buffer.fragments);
Charles Keepax406abc92015-12-15 11:29:45 +00003122
3123 return -EINVAL;
3124 }
3125
3126 for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
3127 caps = &wm_adsp_fw[dsp->fw].caps[i];
3128 desc = &caps->desc;
3129
3130 if (caps->id != params->codec.id)
3131 continue;
3132
3133 if (stream->direction == SND_COMPRESS_PLAYBACK) {
3134 if (desc->max_ch < params->codec.ch_out)
3135 continue;
3136 } else {
3137 if (desc->max_ch < params->codec.ch_in)
3138 continue;
3139 }
3140
3141 if (!(desc->formats & (1 << params->codec.format)))
3142 continue;
3143
3144 for (j = 0; j < desc->num_sample_rates; ++j)
3145 if (desc->sample_rates[j] == params->codec.sample_rate)
3146 return 0;
3147 }
3148
Charles Keepax0d3fba32019-02-22 10:04:21 +00003149 compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
3150 params->codec.id, params->codec.ch_in, params->codec.ch_out,
3151 params->codec.sample_rate, params->codec.format);
Charles Keepax406abc92015-12-15 11:29:45 +00003152 return -EINVAL;
3153}
3154
Charles Keepax565ace42016-01-06 12:33:18 +00003155static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
3156{
3157 return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
3158}
3159
Charles Keepax406abc92015-12-15 11:29:45 +00003160int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
3161 struct snd_compr_params *params)
3162{
3163 struct wm_adsp_compr *compr = stream->runtime->private_data;
Charles Keepax83a40ce2016-01-06 12:33:19 +00003164 unsigned int size;
Charles Keepax406abc92015-12-15 11:29:45 +00003165 int ret;
3166
3167 ret = wm_adsp_compr_check_params(stream, params);
3168 if (ret)
3169 return ret;
3170
3171 compr->size = params->buffer;
3172
Charles Keepax0d3fba32019-02-22 10:04:21 +00003173 compr_dbg(compr, "fragment_size=%d fragments=%d\n",
3174 compr->size.fragment_size, compr->size.fragments);
Charles Keepax406abc92015-12-15 11:29:45 +00003175
Charles Keepax83a40ce2016-01-06 12:33:19 +00003176 size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
3177 compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
3178 if (!compr->raw_buf)
3179 return -ENOMEM;
3180
Charles Keepaxda2b3352016-02-02 16:41:36 +00003181 compr->sample_rate = params->codec.sample_rate;
3182
Charles Keepax406abc92015-12-15 11:29:45 +00003183 return 0;
3184}
3185EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
3186
3187int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
3188 struct snd_compr_caps *caps)
3189{
3190 struct wm_adsp_compr *compr = stream->runtime->private_data;
3191 int fw = compr->dsp->fw;
3192 int i;
3193
3194 if (wm_adsp_fw[fw].caps) {
3195 for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
3196 caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
3197
3198 caps->num_codecs = i;
3199 caps->direction = wm_adsp_fw[fw].compr_direction;
3200
3201 caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
3202 caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
3203 caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;
3204 caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;
3205 }
3206
3207 return 0;
3208}
3209EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
3210
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003211static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
3212 unsigned int mem_addr,
3213 unsigned int num_words, u32 *data)
3214{
3215 struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
3216 unsigned int i, reg;
3217 int ret;
3218
3219 if (!mem)
3220 return -EINVAL;
3221
3222 reg = wm_adsp_region_to_reg(mem, mem_addr);
3223
3224 ret = regmap_raw_read(dsp->regmap, reg, data,
3225 sizeof(*data) * num_words);
3226 if (ret < 0)
3227 return ret;
3228
3229 for (i = 0; i < num_words; ++i)
3230 data[i] = be32_to_cpu(data[i]) & 0x00ffffffu;
3231
3232 return 0;
3233}
3234
3235static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type,
3236 unsigned int mem_addr, u32 *data)
3237{
3238 return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data);
3239}
3240
3241static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
3242 unsigned int mem_addr, u32 data)
3243{
3244 struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
3245 unsigned int reg;
3246
3247 if (!mem)
3248 return -EINVAL;
3249
3250 reg = wm_adsp_region_to_reg(mem, mem_addr);
3251
3252 data = cpu_to_be32(data & 0x00ffffffu);
3253
3254 return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data));
3255}
3256
3257static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
3258 unsigned int field_offset, u32 *data)
3259{
Andrew Fordfb13f192019-02-19 17:31:56 +00003260 return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type,
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003261 buf->host_buf_ptr + field_offset, data);
3262}
3263
3264static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
3265 unsigned int field_offset, u32 data)
3266{
Andrew Fordfb13f192019-02-19 17:31:56 +00003267 return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type,
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003268 buf->host_buf_ptr + field_offset, data);
3269}
3270
Charles Keepaxcc7d6ce2019-02-22 10:04:17 +00003271static void wm_adsp_remove_padding(u32 *buf, int nwords, int data_word_size)
3272{
3273 u8 *pack_in = (u8 *)buf;
3274 u8 *pack_out = (u8 *)buf;
3275 int i, j;
3276
3277 /* Remove the padding bytes from the data read from the DSP */
3278 for (i = 0; i < nwords; i++) {
3279 for (j = 0; j < data_word_size; j++)
3280 *pack_out++ = *pack_in++;
3281
3282 pack_in += sizeof(*buf) - data_word_size;
3283 }
3284}
3285
Charles Keepax1e38f062019-02-22 10:04:18 +00003286static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
3287{
3288 const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
3289 struct wm_adsp_buffer_region *region;
3290 u32 offset = 0;
3291 int i, ret;
3292
Charles Keepaxa792af62019-02-22 10:04:19 +00003293 buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions),
3294 GFP_KERNEL);
3295 if (!buf->regions)
3296 return -ENOMEM;
3297
Charles Keepax1e38f062019-02-22 10:04:18 +00003298 for (i = 0; i < caps->num_regions; ++i) {
3299 region = &buf->regions[i];
3300
3301 region->offset = offset;
3302 region->mem_type = caps->region_defs[i].mem_type;
3303
3304 ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
3305 &region->base_addr);
3306 if (ret < 0)
3307 return ret;
3308
3309 ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
3310 &offset);
3311 if (ret < 0)
3312 return ret;
3313
3314 region->cumulative_size = offset;
3315
Charles Keepax0d3fba32019-02-22 10:04:21 +00003316 compr_dbg(buf,
3317 "region=%d type=%d base=%08x off=%08x size=%08x\n",
3318 i, region->mem_type, region->base_addr,
3319 region->offset, region->cumulative_size);
Charles Keepax1e38f062019-02-22 10:04:18 +00003320 }
3321
3322 return 0;
3323}
3324
3325static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf)
3326{
3327 buf->irq_count = 0xFFFFFFFF;
3328 buf->read_index = -1;
3329 buf->avail = 0;
3330}
3331
Charles Keepaxa792af62019-02-22 10:04:19 +00003332static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)
3333{
3334 struct wm_adsp_compr_buf *buf;
3335
3336 buf = kzalloc(sizeof(*buf), GFP_KERNEL);
3337 if (!buf)
3338 return NULL;
3339
3340 buf->dsp = dsp;
3341
3342 wm_adsp_buffer_clear(buf);
3343
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003344 list_add_tail(&buf->list, &dsp->buffer_list);
Charles Keepaxa792af62019-02-22 10:04:19 +00003345
3346 return buf;
3347}
3348
3349static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003350{
3351 struct wm_adsp_alg_region *alg_region;
Charles Keepaxa792af62019-02-22 10:04:19 +00003352 struct wm_adsp_compr_buf *buf;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003353 u32 xmalg, addr, magic;
3354 int i, ret;
3355
Charles Keepaxa792af62019-02-22 10:04:19 +00003356 buf = wm_adsp_buffer_alloc(dsp);
3357 if (!buf)
3358 return -ENOMEM;
3359
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003360 alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
3361 xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
3362
3363 addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
3364 ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
3365 if (ret < 0)
3366 return ret;
3367
3368 if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
Charles Keepaxa792af62019-02-22 10:04:19 +00003369 return -ENODEV;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003370
3371 addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
3372 for (i = 0; i < 5; ++i) {
3373 ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
3374 &buf->host_buf_ptr);
3375 if (ret < 0)
3376 return ret;
3377
3378 if (buf->host_buf_ptr)
3379 break;
3380
3381 usleep_range(1000, 2000);
3382 }
3383
3384 if (!buf->host_buf_ptr)
3385 return -EIO;
3386
Andrew Fordfb13f192019-02-19 17:31:56 +00003387 buf->host_buf_mem_type = WMFW_ADSP2_XM;
3388
Charles Keepaxa792af62019-02-22 10:04:19 +00003389 ret = wm_adsp_buffer_populate(buf);
3390 if (ret < 0)
3391 return ret;
3392
Charles Keepax0d3fba32019-02-22 10:04:21 +00003393 compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr);
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003394
3395 return 0;
3396}
3397
Charles Keepaxa792af62019-02-22 10:04:19 +00003398static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl)
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003399{
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003400 struct wm_adsp_host_buf_coeff_v1 coeff_v1;
Charles Keepaxa792af62019-02-22 10:04:19 +00003401 struct wm_adsp_compr_buf *buf;
3402 unsigned int val, reg;
3403 int ret, i;
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003404
3405 ret = wm_coeff_base_reg(ctl, &reg);
3406 if (ret)
3407 return ret;
3408
3409 for (i = 0; i < 5; ++i) {
Charles Keepaxa792af62019-02-22 10:04:19 +00003410 ret = regmap_raw_read(ctl->dsp->regmap, reg, &val, sizeof(val));
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003411 if (ret < 0)
3412 return ret;
3413
3414 if (val)
3415 break;
3416
3417 usleep_range(1000, 2000);
3418 }
3419
Charles Keepaxa792af62019-02-22 10:04:19 +00003420 if (!val) {
3421 adsp_err(ctl->dsp, "Failed to acquire host buffer\n");
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003422 return -EIO;
Charles Keepaxa792af62019-02-22 10:04:19 +00003423 }
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003424
Charles Keepaxa792af62019-02-22 10:04:19 +00003425 buf = wm_adsp_buffer_alloc(ctl->dsp);
3426 if (!buf)
3427 return -ENOMEM;
3428
3429 buf->host_buf_mem_type = ctl->alg_region.type;
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003430 buf->host_buf_ptr = be32_to_cpu(val);
Charles Keepaxa792af62019-02-22 10:04:19 +00003431
3432 ret = wm_adsp_buffer_populate(buf);
3433 if (ret < 0)
3434 return ret;
3435
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003436 /*
3437 * v0 host_buffer coefficients didn't have versioning, so if the
3438 * control is one word, assume version 0.
3439 */
3440 if (ctl->len == 4) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003441 compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr);
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003442 return 0;
3443 }
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003444
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003445 ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1,
3446 sizeof(coeff_v1));
3447 if (ret < 0)
3448 return ret;
3449
3450 coeff_v1.versions = be32_to_cpu(coeff_v1.versions);
3451 val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK;
3452 val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;
3453
3454 if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {
3455 adsp_err(ctl->dsp,
3456 "Host buffer coeff ver %u > supported version %u\n",
3457 val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);
3458 return -EINVAL;
3459 }
3460
3461 for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++)
3462 coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]);
3463
3464 wm_adsp_remove_padding((u32 *)&coeff_v1.name,
3465 ARRAY_SIZE(coeff_v1.name),
3466 WM_ADSP_DATA_WORD_SIZE);
3467
3468 buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part,
3469 (char *)&coeff_v1.name);
3470
Charles Keepax0d3fba32019-02-22 10:04:21 +00003471 compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n",
3472 buf->host_buf_ptr, val);
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003473
3474 return val;
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003475}
3476
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003477static int wm_adsp_buffer_init(struct wm_adsp *dsp)
3478{
Charles Keepaxa792af62019-02-22 10:04:19 +00003479 struct wm_coeff_ctl *ctl;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003480 int ret;
3481
Charles Keepaxa792af62019-02-22 10:04:19 +00003482 list_for_each_entry(ctl, &dsp->ctl_list, list) {
3483 if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
3484 continue;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003485
Charles Keepaxa792af62019-02-22 10:04:19 +00003486 if (!ctl->enabled)
3487 continue;
Charles Keepax61fc0602018-02-26 10:49:47 +00003488
Charles Keepaxa792af62019-02-22 10:04:19 +00003489 ret = wm_adsp_buffer_parse_coeff(ctl);
3490 if (ret < 0) {
3491 adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
3492 goto error;
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003493 } else if (ret == 0) {
3494 /* Only one buffer supported for version 0 */
3495 return 0;
Charles Keepaxa792af62019-02-22 10:04:19 +00003496 }
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003497 }
3498
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003499 if (list_empty(&dsp->buffer_list)) {
Charles Keepaxa792af62019-02-22 10:04:19 +00003500 /* Fall back to legacy support */
3501 ret = wm_adsp_buffer_parse_legacy(dsp);
3502 if (ret) {
3503 adsp_err(dsp, "Failed to parse legacy: %d\n", ret);
3504 goto error;
3505 }
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003506 }
3507
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003508 return 0;
3509
Charles Keepaxa792af62019-02-22 10:04:19 +00003510error:
3511 wm_adsp_buffer_free(dsp);
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003512 return ret;
3513}
3514
3515static int wm_adsp_buffer_free(struct wm_adsp *dsp)
3516{
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003517 struct wm_adsp_compr_buf *buf, *tmp;
Charles Keepax721be3b2016-05-04 17:11:56 +01003518
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003519 list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {
3520 if (buf->compr)
3521 wm_adsp_compr_detach(buf->compr);
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003522
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003523 kfree(buf->name);
3524 kfree(buf->regions);
3525 list_del(&buf->list);
3526 kfree(buf);
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003527 }
3528
3529 return 0;
3530}
3531
Stuart Hendersonf938f342019-02-19 17:31:57 +00003532static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
3533{
3534 int ret;
3535
3536 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
3537 if (ret < 0) {
Charles Keepax48ead312019-03-19 11:52:05 +00003538 compr_err(buf, "Failed to check buffer error: %d\n", ret);
Stuart Hendersonf938f342019-02-19 17:31:57 +00003539 return ret;
3540 }
3541 if (buf->error != 0) {
Charles Keepax48ead312019-03-19 11:52:05 +00003542 compr_err(buf, "Buffer error occurred: %d\n", buf->error);
Stuart Hendersonf938f342019-02-19 17:31:57 +00003543 return -EIO;
3544 }
3545
3546 return 0;
3547}
3548
Charles Keepax95fe9592015-12-15 11:29:47 +00003549int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
3550{
3551 struct wm_adsp_compr *compr = stream->runtime->private_data;
3552 struct wm_adsp *dsp = compr->dsp;
3553 int ret = 0;
3554
Charles Keepax0d3fba32019-02-22 10:04:21 +00003555 compr_dbg(compr, "Trigger: %d\n", cmd);
Charles Keepax95fe9592015-12-15 11:29:47 +00003556
3557 mutex_lock(&dsp->pwr_lock);
3558
3559 switch (cmd) {
3560 case SNDRV_PCM_TRIGGER_START:
Charles Keepax61fc0602018-02-26 10:49:47 +00003561 if (!wm_adsp_compr_attached(compr)) {
3562 ret = wm_adsp_compr_attach(compr);
3563 if (ret < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003564 compr_err(compr, "Failed to link buffer and stream: %d\n",
3565 ret);
Charles Keepax61fc0602018-02-26 10:49:47 +00003566 break;
3567 }
Charles Keepax95fe9592015-12-15 11:29:47 +00003568 }
Charles Keepax565ace42016-01-06 12:33:18 +00003569
Stuart Hendersonf938f342019-02-19 17:31:57 +00003570 ret = wm_adsp_buffer_get_error(compr->buf);
3571 if (ret < 0)
3572 break;
3573
Charles Keepax565ace42016-01-06 12:33:18 +00003574 /* Trigger the IRQ at one fragment of data */
3575 ret = wm_adsp_buffer_write(compr->buf,
3576 HOST_BUFFER_FIELD(high_water_mark),
3577 wm_adsp_compr_frag_words(compr));
3578 if (ret < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003579 compr_err(compr, "Failed to set high water mark: %d\n",
3580 ret);
Charles Keepax565ace42016-01-06 12:33:18 +00003581 break;
3582 }
Charles Keepax95fe9592015-12-15 11:29:47 +00003583 break;
3584 case SNDRV_PCM_TRIGGER_STOP:
Charles Keepax639e5eb2019-03-19 11:52:04 +00003585 wm_adsp_buffer_clear(compr->buf);
Charles Keepax95fe9592015-12-15 11:29:47 +00003586 break;
3587 default:
3588 ret = -EINVAL;
3589 break;
3590 }
3591
3592 mutex_unlock(&dsp->pwr_lock);
3593
3594 return ret;
3595}
3596EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
3597
Charles Keepax565ace42016-01-06 12:33:18 +00003598static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
3599{
3600 int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
3601
3602 return buf->regions[last_region].cumulative_size;
3603}
3604
3605static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
3606{
3607 u32 next_read_index, next_write_index;
3608 int write_index, read_index, avail;
3609 int ret;
3610
3611 /* Only sync read index if we haven't already read a valid index */
3612 if (buf->read_index < 0) {
3613 ret = wm_adsp_buffer_read(buf,
3614 HOST_BUFFER_FIELD(next_read_index),
3615 &next_read_index);
3616 if (ret < 0)
3617 return ret;
3618
3619 read_index = sign_extend32(next_read_index, 23);
3620
3621 if (read_index < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003622 compr_dbg(buf, "Avail check on unstarted stream\n");
Charles Keepax565ace42016-01-06 12:33:18 +00003623 return 0;
3624 }
3625
3626 buf->read_index = read_index;
3627 }
3628
3629 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
3630 &next_write_index);
3631 if (ret < 0)
3632 return ret;
3633
3634 write_index = sign_extend32(next_write_index, 23);
3635
3636 avail = write_index - buf->read_index;
3637 if (avail < 0)
3638 avail += wm_adsp_buffer_size(buf);
3639
Charles Keepax0d3fba32019-02-22 10:04:21 +00003640 compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
3641 buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
Charles Keepax565ace42016-01-06 12:33:18 +00003642
3643 buf->avail = avail;
3644
3645 return 0;
3646}
3647
3648int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
3649{
Charles Keepax612047f2016-03-28 14:29:22 +01003650 struct wm_adsp_compr_buf *buf;
3651 struct wm_adsp_compr *compr;
Charles Keepax565ace42016-01-06 12:33:18 +00003652 int ret = 0;
3653
3654 mutex_lock(&dsp->pwr_lock);
3655
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003656 if (list_empty(&dsp->buffer_list)) {
Charles Keepax565ace42016-01-06 12:33:18 +00003657 ret = -ENODEV;
3658 goto out;
3659 }
Charles Keepax0d3fba32019-02-22 10:04:21 +00003660
Charles Keepax565ace42016-01-06 12:33:18 +00003661 adsp_dbg(dsp, "Handling buffer IRQ\n");
3662
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003663 list_for_each_entry(buf, &dsp->buffer_list, list) {
3664 compr = buf->compr;
Charles Keepax565ace42016-01-06 12:33:18 +00003665
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003666 ret = wm_adsp_buffer_get_error(buf);
3667 if (ret < 0)
3668 goto out_notify; /* Wake poll to report error */
Charles Keepax565ace42016-01-06 12:33:18 +00003669
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003670 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
3671 &buf->irq_count);
3672 if (ret < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003673 compr_err(buf, "Failed to get irq_count: %d\n", ret);
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003674 goto out;
3675 }
Charles Keepax565ace42016-01-06 12:33:18 +00003676
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003677 ret = wm_adsp_buffer_update_avail(buf);
3678 if (ret < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003679 compr_err(buf, "Error reading avail: %d\n", ret);
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003680 goto out;
3681 }
3682
3683 if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
3684 ret = WM_ADSP_COMPR_VOICE_TRIGGER;
Charles Keepax20b7f7c2016-05-13 16:45:17 +01003685
Charles Keepax58476092016-04-06 11:21:54 +01003686out_notify:
Stuart Henderson4f2d4ea2019-02-22 10:04:20 +00003687 if (compr && compr->stream)
3688 snd_compr_fragment_elapsed(compr->stream);
3689 }
Charles Keepax83a40ce2016-01-06 12:33:19 +00003690
Charles Keepax565ace42016-01-06 12:33:18 +00003691out:
3692 mutex_unlock(&dsp->pwr_lock);
3693
3694 return ret;
3695}
3696EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
3697
3698static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
3699{
3700 if (buf->irq_count & 0x01)
3701 return 0;
3702
Charles Keepax0d3fba32019-02-22 10:04:21 +00003703 compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count);
Charles Keepax565ace42016-01-06 12:33:18 +00003704
3705 buf->irq_count |= 0x01;
3706
3707 return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
3708 buf->irq_count);
3709}
3710
3711int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
3712 struct snd_compr_tstamp *tstamp)
3713{
3714 struct wm_adsp_compr *compr = stream->runtime->private_data;
Charles Keepax565ace42016-01-06 12:33:18 +00003715 struct wm_adsp *dsp = compr->dsp;
Charles Keepax612047f2016-03-28 14:29:22 +01003716 struct wm_adsp_compr_buf *buf;
Charles Keepax565ace42016-01-06 12:33:18 +00003717 int ret = 0;
3718
Charles Keepax0d3fba32019-02-22 10:04:21 +00003719 compr_dbg(compr, "Pointer request\n");
Charles Keepax565ace42016-01-06 12:33:18 +00003720
3721 mutex_lock(&dsp->pwr_lock);
3722
Charles Keepax612047f2016-03-28 14:29:22 +01003723 buf = compr->buf;
3724
Charles Keepax28ee3d72016-06-13 14:17:12 +01003725 if (!compr->buf || compr->buf->error) {
Charles Keepax8d280662016-06-13 14:17:11 +01003726 snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
Charles Keepax565ace42016-01-06 12:33:18 +00003727 ret = -EIO;
3728 goto out;
3729 }
3730
3731 if (buf->avail < wm_adsp_compr_frag_words(compr)) {
3732 ret = wm_adsp_buffer_update_avail(buf);
3733 if (ret < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003734 compr_err(compr, "Error reading avail: %d\n", ret);
Charles Keepax565ace42016-01-06 12:33:18 +00003735 goto out;
3736 }
3737
3738 /*
3739 * If we really have less than 1 fragment available tell the
3740 * DSP to inform us once a whole fragment is available.
3741 */
3742 if (buf->avail < wm_adsp_compr_frag_words(compr)) {
Charles Keepax58476092016-04-06 11:21:54 +01003743 ret = wm_adsp_buffer_get_error(buf);
Charles Keepax8d280662016-06-13 14:17:11 +01003744 if (ret < 0) {
3745 if (compr->buf->error)
3746 snd_compr_stop_error(stream,
3747 SNDRV_PCM_STATE_XRUN);
Charles Keepax58476092016-04-06 11:21:54 +01003748 goto out;
Charles Keepax8d280662016-06-13 14:17:11 +01003749 }
Charles Keepax58476092016-04-06 11:21:54 +01003750
Charles Keepax565ace42016-01-06 12:33:18 +00003751 ret = wm_adsp_buffer_reenable_irq(buf);
3752 if (ret < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003753 compr_err(compr, "Failed to re-enable buffer IRQ: %d\n",
3754 ret);
Charles Keepax565ace42016-01-06 12:33:18 +00003755 goto out;
3756 }
3757 }
3758 }
3759
3760 tstamp->copied_total = compr->copied_total;
3761 tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
Charles Keepaxda2b3352016-02-02 16:41:36 +00003762 tstamp->sampling_rate = compr->sample_rate;
Charles Keepax565ace42016-01-06 12:33:18 +00003763
3764out:
3765 mutex_unlock(&dsp->pwr_lock);
3766
3767 return ret;
3768}
3769EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
3770
Charles Keepax83a40ce2016-01-06 12:33:19 +00003771static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
3772{
3773 struct wm_adsp_compr_buf *buf = compr->buf;
Charles Keepax83a40ce2016-01-06 12:33:19 +00003774 unsigned int adsp_addr;
3775 int mem_type, nwords, max_read;
Charles Keepaxcc7d6ce2019-02-22 10:04:17 +00003776 int i, ret;
Charles Keepax83a40ce2016-01-06 12:33:19 +00003777
3778 /* Calculate read parameters */
3779 for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
3780 if (buf->read_index < buf->regions[i].cumulative_size)
3781 break;
3782
3783 if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
3784 return -EINVAL;
3785
3786 mem_type = buf->regions[i].mem_type;
3787 adsp_addr = buf->regions[i].base_addr +
3788 (buf->read_index - buf->regions[i].offset);
3789
3790 max_read = wm_adsp_compr_frag_words(compr);
3791 nwords = buf->regions[i].cumulative_size - buf->read_index;
3792
3793 if (nwords > target)
3794 nwords = target;
3795 if (nwords > buf->avail)
3796 nwords = buf->avail;
3797 if (nwords > max_read)
3798 nwords = max_read;
3799 if (!nwords)
3800 return 0;
3801
3802 /* Read data from DSP */
3803 ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr,
3804 nwords, compr->raw_buf);
3805 if (ret < 0)
3806 return ret;
3807
Charles Keepaxcc7d6ce2019-02-22 10:04:17 +00003808 wm_adsp_remove_padding(compr->raw_buf, nwords, WM_ADSP_DATA_WORD_SIZE);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003809
3810 /* update read index to account for words read */
3811 buf->read_index += nwords;
3812 if (buf->read_index == wm_adsp_buffer_size(buf))
3813 buf->read_index = 0;
3814
3815 ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
3816 buf->read_index);
3817 if (ret < 0)
3818 return ret;
3819
3820 /* update avail to account for words read */
3821 buf->avail -= nwords;
3822
3823 return nwords;
3824}
3825
3826static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
3827 char __user *buf, size_t count)
3828{
Charles Keepax83a40ce2016-01-06 12:33:19 +00003829 int ntotal = 0;
3830 int nwords, nbytes;
3831
Charles Keepax0d3fba32019-02-22 10:04:21 +00003832 compr_dbg(compr, "Requested read of %zu bytes\n", count);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003833
Charles Keepax28ee3d72016-06-13 14:17:12 +01003834 if (!compr->buf || compr->buf->error) {
Charles Keepax8d280662016-06-13 14:17:11 +01003835 snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003836 return -EIO;
Charles Keepax8d280662016-06-13 14:17:11 +01003837 }
Charles Keepax83a40ce2016-01-06 12:33:19 +00003838
3839 count /= WM_ADSP_DATA_WORD_SIZE;
3840
3841 do {
3842 nwords = wm_adsp_buffer_capture_block(compr, count);
3843 if (nwords < 0) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003844 compr_err(compr, "Failed to capture block: %d\n",
3845 nwords);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003846 return nwords;
3847 }
3848
3849 nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
3850
Charles Keepax0d3fba32019-02-22 10:04:21 +00003851 compr_dbg(compr, "Read %d bytes\n", nbytes);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003852
3853 if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
Charles Keepax0d3fba32019-02-22 10:04:21 +00003854 compr_err(compr, "Failed to copy data to user: %d, %d\n",
3855 ntotal, nbytes);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003856 return -EFAULT;
3857 }
3858
3859 count -= nwords;
3860 ntotal += nbytes;
3861 } while (nwords > 0 && count > 0);
3862
3863 compr->copied_total += ntotal;
3864
3865 return ntotal;
3866}
3867
3868int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
3869 size_t count)
3870{
3871 struct wm_adsp_compr *compr = stream->runtime->private_data;
3872 struct wm_adsp *dsp = compr->dsp;
3873 int ret;
3874
3875 mutex_lock(&dsp->pwr_lock);
3876
3877 if (stream->direction == SND_COMPRESS_CAPTURE)
3878 ret = wm_adsp_compr_read(compr, buf, count);
3879 else
3880 ret = -ENOTSUPP;
3881
3882 mutex_unlock(&dsp->pwr_lock);
3883
3884 return ret;
3885}
3886EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
3887
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01003888int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
3889{
3890 struct regmap *regmap = dsp->regmap;
3891 unsigned int code0, code1, lock_reg;
3892
3893 if (!(lock_regions & WM_ADSP2_REGION_ALL))
3894 return 0;
3895
3896 lock_regions &= WM_ADSP2_REGION_ALL;
3897 lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
3898
3899 while (lock_regions) {
3900 code0 = code1 = 0;
3901 if (lock_regions & BIT(0)) {
3902 code0 = ADSP2_LOCK_CODE_0;
3903 code1 = ADSP2_LOCK_CODE_1;
3904 }
3905 if (lock_regions & BIT(1)) {
3906 code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
3907 code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
3908 }
3909 regmap_write(regmap, lock_reg, code0);
3910 regmap_write(regmap, lock_reg, code1);
3911 lock_regions >>= 2;
3912 lock_reg += 2;
3913 }
3914
3915 return 0;
3916}
3917EXPORT_SYMBOL_GPL(wm_adsp2_lock);
3918
3919irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
3920{
3921 unsigned int val;
3922 struct regmap *regmap = dsp->regmap;
3923 int ret = 0;
3924
Charles Keepaxa2225a62019-03-19 11:52:06 +00003925 mutex_lock(&dsp->pwr_lock);
3926
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01003927 ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);
3928 if (ret) {
3929 adsp_err(dsp,
3930 "Failed to read Region Lock Ctrl register: %d\n", ret);
Charles Keepaxa2225a62019-03-19 11:52:06 +00003931 goto error;
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01003932 }
3933
3934 if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {
3935 adsp_err(dsp, "watchdog timeout error\n");
3936 wm_adsp_stop_watchdog(dsp);
3937 }
3938
3939 if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {
3940 if (val & ADSP2_SLAVE_ERR_MASK)
3941 adsp_err(dsp, "bus error: slave error\n");
3942 else
3943 adsp_err(dsp, "bus error: region lock error\n");
3944
3945 ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val);
3946 if (ret) {
3947 adsp_err(dsp,
3948 "Failed to read Bus Err Addr register: %d\n",
3949 ret);
Charles Keepaxa2225a62019-03-19 11:52:06 +00003950 goto error;
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01003951 }
3952
3953 adsp_err(dsp, "bus error address = 0x%x\n",
3954 val & ADSP2_BUS_ERR_ADDR_MASK);
3955
3956 ret = regmap_read(regmap,
3957 dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR,
3958 &val);
3959 if (ret) {
3960 adsp_err(dsp,
3961 "Failed to read Pmem Xmem Err Addr register: %d\n",
3962 ret);
Charles Keepaxa2225a62019-03-19 11:52:06 +00003963 goto error;
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01003964 }
3965
3966 adsp_err(dsp, "xmem error address = 0x%x\n",
3967 val & ADSP2_XMEM_ERR_ADDR_MASK);
3968 adsp_err(dsp, "pmem error address = 0x%x\n",
3969 (val & ADSP2_PMEM_ERR_ADDR_MASK) >>
3970 ADSP2_PMEM_ERR_ADDR_SHIFT);
3971 }
3972
3973 regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,
3974 ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);
3975
Charles Keepaxa2225a62019-03-19 11:52:06 +00003976error:
3977 mutex_unlock(&dsp->pwr_lock);
3978
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01003979 return IRQ_HANDLED;
3980}
3981EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
3982
Praveen Diwakar0a37c6ef2014-07-04 11:17:41 +05303983MODULE_LICENSE("GPL v2");