blob: 66501b8dc46fb17e962f7bf1b69923da7bdae1a6 [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
49#define ADSP1_CONTROL_1 0x00
50#define ADSP1_CONTROL_2 0x02
51#define ADSP1_CONTROL_3 0x03
52#define ADSP1_CONTROL_4 0x04
53#define ADSP1_CONTROL_5 0x06
54#define ADSP1_CONTROL_6 0x07
55#define ADSP1_CONTROL_7 0x08
56#define ADSP1_CONTROL_8 0x09
57#define ADSP1_CONTROL_9 0x0A
58#define ADSP1_CONTROL_10 0x0B
59#define ADSP1_CONTROL_11 0x0C
60#define ADSP1_CONTROL_12 0x0D
61#define ADSP1_CONTROL_13 0x0F
62#define ADSP1_CONTROL_14 0x10
63#define ADSP1_CONTROL_15 0x11
64#define ADSP1_CONTROL_16 0x12
65#define ADSP1_CONTROL_17 0x13
66#define ADSP1_CONTROL_18 0x14
67#define ADSP1_CONTROL_19 0x16
68#define ADSP1_CONTROL_20 0x17
69#define ADSP1_CONTROL_21 0x18
70#define ADSP1_CONTROL_22 0x1A
71#define ADSP1_CONTROL_23 0x1B
72#define ADSP1_CONTROL_24 0x1C
73#define ADSP1_CONTROL_25 0x1E
74#define ADSP1_CONTROL_26 0x20
75#define ADSP1_CONTROL_27 0x21
76#define ADSP1_CONTROL_28 0x22
77#define ADSP1_CONTROL_29 0x23
78#define ADSP1_CONTROL_30 0x24
79#define ADSP1_CONTROL_31 0x26
80
81/*
82 * ADSP1 Control 19
83 */
84#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
85#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
86#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
87
88
89/*
90 * ADSP1 Control 30
91 */
92#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
93#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
94#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
95#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
96#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
97#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
98#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
99#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
100#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
101#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
102#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
103#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
104#define ADSP1_START 0x0001 /* DSP1_START */
105#define ADSP1_START_MASK 0x0001 /* DSP1_START */
106#define ADSP1_START_SHIFT 0 /* DSP1_START */
107#define ADSP1_START_WIDTH 1 /* DSP1_START */
108
Chris Rattray94e205b2013-01-18 08:43:09 +0000109/*
110 * ADSP1 Control 31
111 */
112#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
113#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
114#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
115
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100116#define ADSP2_CONTROL 0x0
117#define ADSP2_CLOCKING 0x1
118#define ADSP2V2_CLOCKING 0x2
119#define ADSP2_STATUS1 0x4
120#define ADSP2_WDMA_CONFIG_1 0x30
121#define ADSP2_WDMA_CONFIG_2 0x31
122#define ADSP2V2_WDMA_CONFIG_2 0x32
123#define ADSP2_RDMA_CONFIG_1 0x34
Mark Brown2159ad932012-10-11 11:54:02 +0900124
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100125#define ADSP2_SCRATCH0 0x40
126#define ADSP2_SCRATCH1 0x41
127#define ADSP2_SCRATCH2 0x42
128#define ADSP2_SCRATCH3 0x43
129
130#define ADSP2V2_SCRATCH0_1 0x40
131#define ADSP2V2_SCRATCH2_3 0x42
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100132
Mark Brown2159ad932012-10-11 11:54:02 +0900133/*
134 * ADSP2 Control
135 */
136
137#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
138#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
139#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
140#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
141#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
142#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
143#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
144#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
145#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
146#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
147#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
148#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
149#define ADSP2_START 0x0001 /* DSP1_START */
150#define ADSP2_START_MASK 0x0001 /* DSP1_START */
151#define ADSP2_START_SHIFT 0 /* DSP1_START */
152#define ADSP2_START_WIDTH 1 /* DSP1_START */
153
154/*
Mark Brown973838a2012-11-28 17:20:32 +0000155 * ADSP2 clocking
156 */
157#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
158#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
159#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
160
161/*
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100162 * ADSP2V2 clocking
163 */
164#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */
165#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */
166#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
167
168#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */
169#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */
170#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */
171
172/*
Mark Brown2159ad932012-10-11 11:54:02 +0900173 * ADSP2 Status 1
174 */
175#define ADSP2_RAM_RDY 0x0001
176#define ADSP2_RAM_RDY_MASK 0x0001
177#define ADSP2_RAM_RDY_SHIFT 0
178#define ADSP2_RAM_RDY_WIDTH 1
179
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +0100180/*
181 * ADSP2 Lock support
182 */
183#define ADSP2_LOCK_CODE_0 0x5555
184#define ADSP2_LOCK_CODE_1 0xAAAA
185
186#define ADSP2_WATCHDOG 0x0A
187#define ADSP2_BUS_ERR_ADDR 0x52
188#define ADSP2_REGION_LOCK_STATUS 0x64
189#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66
190#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68
191#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A
192#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C
193#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E
194#define ADSP2_LOCK_REGION_CTRL 0x7A
195#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C
196
197#define ADSP2_REGION_LOCK_ERR_MASK 0x8000
198#define ADSP2_SLAVE_ERR_MASK 0x4000
199#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
200#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002
201#define ADSP2_CTRL_ERR_EINT 0x0001
202
203#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF
204#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF
205#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000
206#define ADSP2_PMEM_ERR_ADDR_SHIFT 16
207#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD
208
209#define ADSP2_LOCK_REGION_SHIFT 16
210
Charles Keepax9ee78752016-05-02 13:57:36 +0100211#define ADSP_MAX_STD_CTRL_SIZE 512
212
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000213#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
214#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +0000215#define WM_ADSP_ACKED_CTL_MIN_VALUE 0
216#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000217
218/*
219 * Event control messages
220 */
221#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
222
Mark Browncf17c832013-01-30 14:37:23 +0800223struct wm_adsp_buf {
224 struct list_head list;
225 void *buf;
226};
227
228static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
229 struct list_head *list)
230{
231 struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
232
233 if (buf == NULL)
234 return NULL;
235
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000236 buf->buf = vmalloc(len);
Mark Browncf17c832013-01-30 14:37:23 +0800237 if (!buf->buf) {
Richard Fitzgerald4d41c742016-12-09 09:57:41 +0000238 kfree(buf);
Mark Browncf17c832013-01-30 14:37:23 +0800239 return NULL;
240 }
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000241 memcpy(buf->buf, src, len);
Mark Browncf17c832013-01-30 14:37:23 +0800242
243 if (list)
244 list_add_tail(&buf->list, list);
245
246 return buf;
247}
248
249static void wm_adsp_buf_free(struct list_head *list)
250{
251 while (!list_empty(list)) {
252 struct wm_adsp_buf *buf = list_first_entry(list,
253 struct wm_adsp_buf,
254 list);
255 list_del(&buf->list);
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000256 vfree(buf->buf);
Mark Browncf17c832013-01-30 14:37:23 +0800257 kfree(buf);
258 }
259}
260
Charles Keepax04d13002015-11-26 14:01:52 +0000261#define WM_ADSP_FW_MBC_VSS 0
262#define WM_ADSP_FW_HIFI 1
263#define WM_ADSP_FW_TX 2
264#define WM_ADSP_FW_TX_SPK 3
265#define WM_ADSP_FW_RX 4
266#define WM_ADSP_FW_RX_ANC 5
267#define WM_ADSP_FW_CTRL 6
268#define WM_ADSP_FW_ASR 7
269#define WM_ADSP_FW_TRACE 8
270#define WM_ADSP_FW_SPK_PROT 9
271#define WM_ADSP_FW_MISC 10
Mark Brown1023dbd2013-01-11 22:58:28 +0000272
Charles Keepax04d13002015-11-26 14:01:52 +0000273#define WM_ADSP_NUM_FW 11
Mark Browndd84f922013-03-08 15:25:58 +0800274
Mark Brown1023dbd2013-01-11 22:58:28 +0000275static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000276 [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
277 [WM_ADSP_FW_HIFI] = "MasterHiFi",
278 [WM_ADSP_FW_TX] = "Tx",
279 [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
280 [WM_ADSP_FW_RX] = "Rx",
281 [WM_ADSP_FW_RX_ANC] = "Rx ANC",
282 [WM_ADSP_FW_CTRL] = "Voice Ctrl",
283 [WM_ADSP_FW_ASR] = "ASR Assist",
284 [WM_ADSP_FW_TRACE] = "Dbg Trace",
285 [WM_ADSP_FW_SPK_PROT] = "Protection",
286 [WM_ADSP_FW_MISC] = "Misc",
Mark Brown1023dbd2013-01-11 22:58:28 +0000287};
288
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000289struct wm_adsp_system_config_xm_hdr {
290 __be32 sys_enable;
291 __be32 fw_id;
292 __be32 fw_rev;
293 __be32 boot_status;
294 __be32 watchdog;
295 __be32 dma_buffer_size;
296 __be32 rdma[6];
297 __be32 wdma[8];
298 __be32 build_job_name[3];
299 __be32 build_job_number;
300};
301
302struct wm_adsp_alg_xm_struct {
303 __be32 magic;
304 __be32 smoothing;
305 __be32 threshold;
306 __be32 host_buf_ptr;
307 __be32 start_seq;
308 __be32 high_water_mark;
309 __be32 low_water_mark;
310 __be64 smoothed_power;
311};
312
313struct wm_adsp_buffer {
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100314 __be32 buf1_base; /* Base addr of first buffer area */
315 __be32 buf1_size; /* Size of buf1 area in DSP words */
316 __be32 buf2_base; /* Base addr of 2nd buffer area */
317 __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */
318 __be32 buf3_base; /* Base addr of buf3 area */
319 __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000320 __be32 high_water_mark; /* Point at which IRQ is asserted */
321 __be32 irq_count; /* bits 1-31 count IRQ assertions */
322 __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */
323 __be32 next_write_index; /* word index of next write */
324 __be32 next_read_index; /* word index of next read */
325 __be32 error; /* error if any */
326 __be32 oldest_block_index; /* word index of oldest surviving */
327 __be32 requested_rewind; /* how many blocks rewind was done */
328 __be32 reserved_space; /* internal */
329 __be32 min_free; /* min free space since stream start */
330 __be32 blocks_written[2]; /* total blocks written (64 bit) */
331 __be32 words_written[2]; /* total words written (64 bit) */
332};
333
Charles Keepax721be3b2016-05-04 17:11:56 +0100334struct wm_adsp_compr;
335
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000336struct wm_adsp_compr_buf {
337 struct wm_adsp *dsp;
Charles Keepax721be3b2016-05-04 17:11:56 +0100338 struct wm_adsp_compr *compr;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000339
340 struct wm_adsp_buffer_region *regions;
341 u32 host_buf_ptr;
Charles Keepax565ace42016-01-06 12:33:18 +0000342
343 u32 error;
344 u32 irq_count;
345 int read_index;
346 int avail;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000347};
348
Charles Keepax406abc92015-12-15 11:29:45 +0000349struct wm_adsp_compr {
350 struct wm_adsp *dsp;
Charles Keepax95fe9592015-12-15 11:29:47 +0000351 struct wm_adsp_compr_buf *buf;
Charles Keepax406abc92015-12-15 11:29:45 +0000352
353 struct snd_compr_stream *stream;
354 struct snd_compressed_buffer size;
Charles Keepax565ace42016-01-06 12:33:18 +0000355
Charles Keepax83a40ce2016-01-06 12:33:19 +0000356 u32 *raw_buf;
Charles Keepax565ace42016-01-06 12:33:18 +0000357 unsigned int copied_total;
Charles Keepaxda2b3352016-02-02 16:41:36 +0000358
359 unsigned int sample_rate;
Charles Keepax406abc92015-12-15 11:29:45 +0000360};
361
362#define WM_ADSP_DATA_WORD_SIZE 3
363
364#define WM_ADSP_MIN_FRAGMENTS 1
365#define WM_ADSP_MAX_FRAGMENTS 256
366#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE)
367#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE)
368
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000369#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7
370
371#define HOST_BUFFER_FIELD(field) \
372 (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))
373
374#define ALG_XM_FIELD(field) \
375 (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))
376
377static int wm_adsp_buffer_init(struct wm_adsp *dsp);
378static int wm_adsp_buffer_free(struct wm_adsp *dsp);
379
380struct wm_adsp_buffer_region {
381 unsigned int offset;
382 unsigned int cumulative_size;
383 unsigned int mem_type;
384 unsigned int base_addr;
385};
386
387struct wm_adsp_buffer_region_def {
388 unsigned int mem_type;
389 unsigned int base_offset;
390 unsigned int size_offset;
391};
392
Charles Keepax3a9686c2016-02-01 15:22:34 +0000393static const struct wm_adsp_buffer_region_def default_regions[] = {
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000394 {
395 .mem_type = WMFW_ADSP2_XM,
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100396 .base_offset = HOST_BUFFER_FIELD(buf1_base),
397 .size_offset = HOST_BUFFER_FIELD(buf1_size),
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000398 },
399 {
400 .mem_type = WMFW_ADSP2_XM,
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100401 .base_offset = HOST_BUFFER_FIELD(buf2_base),
402 .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size),
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000403 },
404 {
405 .mem_type = WMFW_ADSP2_YM,
Richard Fitzgerald2a2aefa2018-10-19 13:25:15 +0100406 .base_offset = HOST_BUFFER_FIELD(buf3_base),
407 .size_offset = HOST_BUFFER_FIELD(buf_total_size),
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000408 },
409};
410
Charles Keepax406abc92015-12-15 11:29:45 +0000411struct wm_adsp_fw_caps {
412 u32 id;
413 struct snd_codec_desc desc;
Charles Keepax2cd19bd2015-12-15 11:29:46 +0000414 int num_regions;
Charles Keepax3a9686c2016-02-01 15:22:34 +0000415 const struct wm_adsp_buffer_region_def *region_defs;
Charles Keepax406abc92015-12-15 11:29:45 +0000416};
417
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000418static const struct wm_adsp_fw_caps ctrl_caps[] = {
Charles Keepax406abc92015-12-15 11:29:45 +0000419 {
420 .id = SND_AUDIOCODEC_BESPOKE,
421 .desc = {
Richard Fitzgerald3bbc2702018-07-19 11:50:38 +0100422 .max_ch = 8,
Charles Keepax406abc92015-12-15 11:29:45 +0000423 .sample_rates = { 16000 },
424 .num_sample_rates = 1,
425 .formats = SNDRV_PCM_FMTBIT_S16_LE,
426 },
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000427 .num_regions = ARRAY_SIZE(default_regions),
428 .region_defs = default_regions,
Charles Keepax406abc92015-12-15 11:29:45 +0000429 },
430};
431
Charles Keepax7ce42832016-01-21 17:52:59 +0000432static const struct wm_adsp_fw_caps trace_caps[] = {
433 {
434 .id = SND_AUDIOCODEC_BESPOKE,
435 .desc = {
436 .max_ch = 8,
437 .sample_rates = {
438 4000, 8000, 11025, 12000, 16000, 22050,
439 24000, 32000, 44100, 48000, 64000, 88200,
440 96000, 176400, 192000
441 },
442 .num_sample_rates = 15,
443 .formats = SNDRV_PCM_FMTBIT_S16_LE,
444 },
445 .num_regions = ARRAY_SIZE(default_regions),
446 .region_defs = default_regions,
Charles Keepax406abc92015-12-15 11:29:45 +0000447 },
448};
449
450static const struct {
Mark Brown1023dbd2013-01-11 22:58:28 +0000451 const char *file;
Charles Keepax406abc92015-12-15 11:29:45 +0000452 int compr_direction;
453 int num_caps;
454 const struct wm_adsp_fw_caps *caps;
Charles Keepax20b7f7c2016-05-13 16:45:17 +0100455 bool voice_trigger;
Mark Brown1023dbd2013-01-11 22:58:28 +0000456} wm_adsp_fw[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000457 [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
458 [WM_ADSP_FW_HIFI] = { .file = "hifi" },
459 [WM_ADSP_FW_TX] = { .file = "tx" },
460 [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
461 [WM_ADSP_FW_RX] = { .file = "rx" },
462 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
Charles Keepax406abc92015-12-15 11:29:45 +0000463 [WM_ADSP_FW_CTRL] = {
464 .file = "ctrl",
465 .compr_direction = SND_COMPRESS_CAPTURE,
Charles Keepaxe6d00f32016-01-21 17:52:58 +0000466 .num_caps = ARRAY_SIZE(ctrl_caps),
467 .caps = ctrl_caps,
Charles Keepax20b7f7c2016-05-13 16:45:17 +0100468 .voice_trigger = true,
Charles Keepax406abc92015-12-15 11:29:45 +0000469 },
Charles Keepax04d13002015-11-26 14:01:52 +0000470 [WM_ADSP_FW_ASR] = { .file = "asr" },
Charles Keepax7ce42832016-01-21 17:52:59 +0000471 [WM_ADSP_FW_TRACE] = {
472 .file = "trace",
473 .compr_direction = SND_COMPRESS_CAPTURE,
474 .num_caps = ARRAY_SIZE(trace_caps),
475 .caps = trace_caps,
476 },
Charles Keepax04d13002015-11-26 14:01:52 +0000477 [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
478 [WM_ADSP_FW_MISC] = { .file = "misc" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000479};
480
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100481struct wm_coeff_ctl_ops {
482 int (*xget)(struct snd_kcontrol *kcontrol,
483 struct snd_ctl_elem_value *ucontrol);
484 int (*xput)(struct snd_kcontrol *kcontrol,
485 struct snd_ctl_elem_value *ucontrol);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100486};
487
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100488struct wm_coeff_ctl {
489 const char *name;
Charles Keepax23237362015-04-13 13:28:02 +0100490 const char *fw_name;
Charles Keepax3809f002015-04-13 13:27:54 +0100491 struct wm_adsp_alg_region alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100492 struct wm_coeff_ctl_ops ops;
Charles Keepax3809f002015-04-13 13:27:54 +0100493 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100494 unsigned int enabled:1;
495 struct list_head list;
496 void *cache;
Charles Keepax23237362015-04-13 13:28:02 +0100497 unsigned int offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100498 size_t len;
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +0100499 unsigned int set:1;
Charles Keepax9ee78752016-05-02 13:57:36 +0100500 struct soc_bytes_ext bytes_ext;
Charles Keepax26c22a12015-04-20 13:52:45 +0100501 unsigned int flags;
Stuart Henderson8eb084d2016-11-09 17:14:16 +0000502 unsigned int type;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100503};
504
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +0000505static const char *wm_adsp_mem_region_name(unsigned int type)
506{
507 switch (type) {
508 case WMFW_ADSP1_PM:
509 return "PM";
510 case WMFW_ADSP1_DM:
511 return "DM";
512 case WMFW_ADSP2_XM:
513 return "XM";
514 case WMFW_ADSP2_YM:
515 return "YM";
516 case WMFW_ADSP1_ZM:
517 return "ZM";
518 default:
519 return NULL;
520 }
521}
522
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100523#ifdef CONFIG_DEBUG_FS
524static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
525{
526 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
527
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100528 kfree(dsp->wmfw_file_name);
529 dsp->wmfw_file_name = tmp;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100530}
531
532static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
533{
534 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
535
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100536 kfree(dsp->bin_file_name);
537 dsp->bin_file_name = tmp;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100538}
539
540static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
541{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100542 kfree(dsp->wmfw_file_name);
543 kfree(dsp->bin_file_name);
544 dsp->wmfw_file_name = NULL;
545 dsp->bin_file_name = NULL;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100546}
547
548static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
549 char __user *user_buf,
550 size_t count, loff_t *ppos)
551{
552 struct wm_adsp *dsp = file->private_data;
553 ssize_t ret;
554
Charles Keepax078e7182015-12-08 16:08:26 +0000555 mutex_lock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100556
Charles Keepax28823eb2016-09-20 13:52:32 +0100557 if (!dsp->wmfw_file_name || !dsp->booted)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100558 ret = 0;
559 else
560 ret = simple_read_from_buffer(user_buf, count, ppos,
561 dsp->wmfw_file_name,
562 strlen(dsp->wmfw_file_name));
563
Charles Keepax078e7182015-12-08 16:08:26 +0000564 mutex_unlock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100565 return ret;
566}
567
568static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
569 char __user *user_buf,
570 size_t count, loff_t *ppos)
571{
572 struct wm_adsp *dsp = file->private_data;
573 ssize_t ret;
574
Charles Keepax078e7182015-12-08 16:08:26 +0000575 mutex_lock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100576
Charles Keepax28823eb2016-09-20 13:52:32 +0100577 if (!dsp->bin_file_name || !dsp->booted)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100578 ret = 0;
579 else
580 ret = simple_read_from_buffer(user_buf, count, ppos,
581 dsp->bin_file_name,
582 strlen(dsp->bin_file_name));
583
Charles Keepax078e7182015-12-08 16:08:26 +0000584 mutex_unlock(&dsp->pwr_lock);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100585 return ret;
586}
587
588static const struct {
589 const char *name;
590 const struct file_operations fops;
591} wm_adsp_debugfs_fops[] = {
592 {
593 .name = "wmfw_file_name",
594 .fops = {
595 .open = simple_open,
596 .read = wm_adsp_debugfs_wmfw_read,
597 },
598 },
599 {
600 .name = "bin_file_name",
601 .fops = {
602 .open = simple_open,
603 .read = wm_adsp_debugfs_bin_read,
604 },
605 },
606};
607
608static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000609 struct snd_soc_component *component)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100610{
611 struct dentry *root = NULL;
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100612 int i;
613
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000614 if (!component->debugfs_root) {
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100615 adsp_err(dsp, "No codec debugfs root\n");
616 goto err;
617 }
618
Richard Fitzgerald605391d2018-08-08 17:13:39 +0100619 root = debugfs_create_dir(dsp->name, component->debugfs_root);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100620
621 if (!root)
622 goto err;
623
Joe Perches6a73cf42018-05-23 12:20:59 -0700624 if (!debugfs_create_bool("booted", 0444, root, &dsp->booted))
Charles Keepax28823eb2016-09-20 13:52:32 +0100625 goto err;
626
Joe Perches6a73cf42018-05-23 12:20:59 -0700627 if (!debugfs_create_bool("running", 0444, root, &dsp->running))
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100628 goto err;
629
Joe Perches6a73cf42018-05-23 12:20:59 -0700630 if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id))
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100631 goto err;
632
Joe Perches6a73cf42018-05-23 12:20:59 -0700633 if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version))
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100634 goto err;
635
636 for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
637 if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
Joe Perches6a73cf42018-05-23 12:20:59 -0700638 0444, root, dsp,
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100639 &wm_adsp_debugfs_fops[i].fops))
640 goto err;
641 }
642
643 dsp->debugfs_root = root;
644 return;
645
646err:
647 debugfs_remove_recursive(root);
648 adsp_err(dsp, "Failed to create debugfs\n");
649}
650
651static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
652{
653 wm_adsp_debugfs_clear(dsp);
654 debugfs_remove_recursive(dsp->debugfs_root);
655}
656#else
657static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000658 struct snd_soc_component *component)
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100659{
660}
661
662static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
663{
664}
665
666static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
667 const char *s)
668{
669}
670
671static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
672 const char *s)
673{
674}
675
676static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
677{
678}
679#endif
680
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100681int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
682 struct snd_ctl_elem_value *ucontrol)
Mark Brown1023dbd2013-01-11 22:58:28 +0000683{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000684 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000685 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000686 struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
Mark Brown1023dbd2013-01-11 22:58:28 +0000687
Takashi Iwai15c66572016-02-29 18:01:18 +0100688 ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
Mark Brown1023dbd2013-01-11 22:58:28 +0000689
690 return 0;
691}
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100692EXPORT_SYMBOL_GPL(wm_adsp_fw_get);
Mark Brown1023dbd2013-01-11 22:58:28 +0000693
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100694int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
695 struct snd_ctl_elem_value *ucontrol)
Mark Brown1023dbd2013-01-11 22:58:28 +0000696{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000697 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000698 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +0000699 struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000700 int ret = 0;
Mark Brown1023dbd2013-01-11 22:58:28 +0000701
Takashi Iwai15c66572016-02-29 18:01:18 +0100702 if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
Mark Brown1023dbd2013-01-11 22:58:28 +0000703 return 0;
704
Takashi Iwai15c66572016-02-29 18:01:18 +0100705 if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
Mark Brown1023dbd2013-01-11 22:58:28 +0000706 return -EINVAL;
707
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000708 mutex_lock(&dsp[e->shift_l].pwr_lock);
709
Charles Keepax28823eb2016-09-20 13:52:32 +0100710 if (dsp[e->shift_l].booted || dsp[e->shift_l].compr)
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000711 ret = -EBUSY;
712 else
Takashi Iwai15c66572016-02-29 18:01:18 +0100713 dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
Mark Brown1023dbd2013-01-11 22:58:28 +0000714
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000715 mutex_unlock(&dsp[e->shift_l].pwr_lock);
Mark Brown1023dbd2013-01-11 22:58:28 +0000716
Charles Keepaxd27c5e12015-12-08 16:08:28 +0000717 return ret;
Mark Brown1023dbd2013-01-11 22:58:28 +0000718}
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100719EXPORT_SYMBOL_GPL(wm_adsp_fw_put);
Mark Brown1023dbd2013-01-11 22:58:28 +0000720
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100721const struct soc_enum wm_adsp_fw_enum[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000722 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
723 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
724 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
725 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100726 SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
727 SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
728 SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
Mark Brown1023dbd2013-01-11 22:58:28 +0000729};
Richard Fitzgerald0a047f02018-08-08 17:13:38 +0100730EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
Mark Brown2159ad932012-10-11 11:54:02 +0900731
732static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
733 int type)
734{
735 int i;
736
737 for (i = 0; i < dsp->num_mems; i++)
738 if (dsp->mem[i].type == type)
739 return &dsp->mem[i];
740
741 return NULL;
742}
743
Charles Keepax3809f002015-04-13 13:27:54 +0100744static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
Mark Brown45b9ee72013-01-08 16:02:06 +0000745 unsigned int offset)
746{
Charles Keepax3809f002015-04-13 13:27:54 +0100747 if (WARN_ON(!mem))
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100748 return offset;
Charles Keepax3809f002015-04-13 13:27:54 +0100749 switch (mem->type) {
Mark Brown45b9ee72013-01-08 16:02:06 +0000750 case WMFW_ADSP1_PM:
Charles Keepax3809f002015-04-13 13:27:54 +0100751 return mem->base + (offset * 3);
Mark Brown45b9ee72013-01-08 16:02:06 +0000752 case WMFW_ADSP1_DM:
Charles Keepax3809f002015-04-13 13:27:54 +0100753 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000754 case WMFW_ADSP2_XM:
Charles Keepax3809f002015-04-13 13:27:54 +0100755 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000756 case WMFW_ADSP2_YM:
Charles Keepax3809f002015-04-13 13:27:54 +0100757 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000758 case WMFW_ADSP1_ZM:
Charles Keepax3809f002015-04-13 13:27:54 +0100759 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000760 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100761 WARN(1, "Unknown memory region type");
Mark Brown45b9ee72013-01-08 16:02:06 +0000762 return offset;
763 }
764}
765
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100766static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
767{
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000768 unsigned int scratch[4];
769 unsigned int addr = dsp->base + ADSP2_SCRATCH0;
770 unsigned int i;
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100771 int ret;
772
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000773 for (i = 0; i < ARRAY_SIZE(scratch); ++i) {
774 ret = regmap_read(dsp->regmap, addr + i, &scratch[i]);
775 if (ret) {
776 adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret);
777 return;
778 }
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100779 }
780
781 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 +0000782 scratch[0], scratch[1], scratch[2], scratch[3]);
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100783}
784
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100785static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
786{
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000787 unsigned int scratch[2];
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100788 int ret;
789
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000790 ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
791 &scratch[0]);
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100792 if (ret) {
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000793 adsp_err(dsp, "Failed to read SCRATCH0_1: %d\n", ret);
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100794 return;
795 }
796
Richard Fitzgerald20e00db2018-11-12 13:36:38 +0000797 ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH2_3,
798 &scratch[1]);
799 if (ret) {
800 adsp_err(dsp, "Failed to read SCRATCH2_3: %d\n", ret);
801 return;
802 }
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +0100803
804 adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
805 scratch[0] & 0xFFFF,
806 scratch[0] >> 16,
807 scratch[1] & 0xFFFF,
808 scratch[1] >> 16);
809}
810
Charles Keepax9ee78752016-05-02 13:57:36 +0100811static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
812{
813 return container_of(ext, struct wm_coeff_ctl, bytes_ext);
814}
815
Richard Fitzgeraldb396ebc2016-11-09 17:14:14 +0000816static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
817{
818 const struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
819 struct wm_adsp *dsp = ctl->dsp;
820 const struct wm_adsp_region *mem;
821
822 mem = wm_adsp_find_region(dsp, alg_region->type);
823 if (!mem) {
824 adsp_err(dsp, "No base for region %x\n",
825 alg_region->type);
826 return -EINVAL;
827 }
828
829 *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset);
830
831 return 0;
832}
833
Charles Keepax7585a5b2015-12-08 16:08:25 +0000834static int wm_coeff_info(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100835 struct snd_ctl_elem_info *uinfo)
836{
Charles Keepax9ee78752016-05-02 13:57:36 +0100837 struct soc_bytes_ext *bytes_ext =
838 (struct soc_bytes_ext *)kctl->private_value;
839 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100840
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +0000841 switch (ctl->type) {
842 case WMFW_CTL_TYPE_ACKED:
843 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
844 uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE;
845 uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE;
846 uinfo->value.integer.step = 1;
847 uinfo->count = 1;
848 break;
849 default:
850 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
851 uinfo->count = ctl->len;
852 break;
853 }
854
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100855 return 0;
856}
857
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +0000858static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
859 unsigned int event_id)
860{
861 struct wm_adsp *dsp = ctl->dsp;
862 u32 val = cpu_to_be32(event_id);
863 unsigned int reg;
864 int i, ret;
865
866 ret = wm_coeff_base_reg(ctl, &reg);
867 if (ret)
868 return ret;
869
870 adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
871 event_id, ctl->alg_region.alg,
872 wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
873
874 ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
875 if (ret) {
876 adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
877 return ret;
878 }
879
880 /*
881 * Poll for ack, we initially poll at ~1ms intervals for firmwares
882 * that respond quickly, then go to ~10ms polls. A firmware is unlikely
883 * to ack instantly so we do the first 1ms delay before reading the
884 * control to avoid a pointless bus transaction
885 */
886 for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
887 switch (i) {
888 case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
889 usleep_range(1000, 2000);
890 i++;
891 break;
892 default:
893 usleep_range(10000, 20000);
894 i += 10;
895 break;
896 }
897
898 ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
899 if (ret) {
900 adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
901 return ret;
902 }
903
904 if (val == 0) {
905 adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
906 return 0;
907 }
908 }
909
910 adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
911 reg, ctl->alg_region.alg,
912 wm_adsp_mem_region_name(ctl->alg_region.type),
913 ctl->offset);
914
915 return -ETIMEDOUT;
916}
917
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100918static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100919 const void *buf, size_t len)
920{
Charles Keepax3809f002015-04-13 13:27:54 +0100921 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100922 void *scratch;
923 int ret;
924 unsigned int reg;
925
Richard Fitzgeraldb396ebc2016-11-09 17:14:14 +0000926 ret = wm_coeff_base_reg(ctl, &reg);
927 if (ret)
928 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100929
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000930 scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100931 if (!scratch)
932 return -ENOMEM;
933
Charles Keepax3809f002015-04-13 13:27:54 +0100934 ret = regmap_raw_write(dsp->regmap, reg, scratch,
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000935 len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100936 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100937 adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000938 len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100939 kfree(scratch);
940 return ret;
941 }
Charles Keepax4f8ea6d2016-02-19 14:44:44 +0000942 adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100943
944 kfree(scratch);
945
946 return 0;
947}
948
Charles Keepax7585a5b2015-12-08 16:08:25 +0000949static int wm_coeff_put(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100950 struct snd_ctl_elem_value *ucontrol)
951{
Charles Keepax9ee78752016-05-02 13:57:36 +0100952 struct soc_bytes_ext *bytes_ext =
953 (struct soc_bytes_ext *)kctl->private_value;
954 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100955 char *p = ucontrol->value.bytes.data;
Charles Keepax168d10e2015-12-08 16:08:27 +0000956 int ret = 0;
957
958 mutex_lock(&ctl->dsp->pwr_lock);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100959
Charles Keepax67430a32017-03-06 16:54:33 +0000960 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
961 ret = -EPERM;
962 else
963 memcpy(ctl->cache, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100964
Nikesh Oswal65d17a92015-02-16 15:25:48 +0000965 ctl->set = 1;
Charles Keepaxcef45772016-09-20 13:52:33 +0100966 if (ctl->enabled && ctl->dsp->running)
Charles Keepax168d10e2015-12-08 16:08:27 +0000967 ret = wm_coeff_write_control(ctl, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100968
Charles Keepax168d10e2015-12-08 16:08:27 +0000969 mutex_unlock(&ctl->dsp->pwr_lock);
970
971 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100972}
973
Charles Keepax9ee78752016-05-02 13:57:36 +0100974static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
975 const unsigned int __user *bytes, unsigned int size)
976{
977 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);
980 int ret = 0;
981
982 mutex_lock(&ctl->dsp->pwr_lock);
983
984 if (copy_from_user(ctl->cache, bytes, size)) {
985 ret = -EFAULT;
986 } else {
987 ctl->set = 1;
Charles Keepaxcef45772016-09-20 13:52:33 +0100988 if (ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +0100989 ret = wm_coeff_write_control(ctl, ctl->cache, size);
Charles Keepax67430a32017-03-06 16:54:33 +0000990 else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
991 ret = -EPERM;
Charles Keepax9ee78752016-05-02 13:57:36 +0100992 }
993
994 mutex_unlock(&ctl->dsp->pwr_lock);
995
996 return ret;
997}
998
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +0000999static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
1000 struct snd_ctl_elem_value *ucontrol)
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 unsigned int val = ucontrol->value.integer.value[0];
1006 int ret;
1007
1008 if (val == 0)
1009 return 0; /* 0 means no event */
1010
1011 mutex_lock(&ctl->dsp->pwr_lock);
1012
Charles Keepax7b4af792017-03-06 16:54:34 +00001013 if (ctl->enabled && ctl->dsp->running)
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001014 ret = wm_coeff_write_acked_control(ctl, val);
1015 else
1016 ret = -EPERM;
1017
1018 mutex_unlock(&ctl->dsp->pwr_lock);
1019
1020 return ret;
1021}
1022
Charles Keepaxc9f8dd72015-04-13 13:27:58 +01001023static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001024 void *buf, size_t len)
1025{
Charles Keepax3809f002015-04-13 13:27:54 +01001026 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001027 void *scratch;
1028 int ret;
1029 unsigned int reg;
1030
Richard Fitzgeraldb396ebc2016-11-09 17:14:14 +00001031 ret = wm_coeff_base_reg(ctl, &reg);
1032 if (ret)
1033 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001034
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001035 scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001036 if (!scratch)
1037 return -ENOMEM;
1038
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001039 ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001040 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +01001041 adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
Charles Keepax5602a642016-03-10 10:46:07 +00001042 len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001043 kfree(scratch);
1044 return ret;
1045 }
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001046 adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001047
Charles Keepax4f8ea6d2016-02-19 14:44:44 +00001048 memcpy(buf, scratch, len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001049 kfree(scratch);
1050
1051 return 0;
1052}
1053
Charles Keepax7585a5b2015-12-08 16:08:25 +00001054static int wm_coeff_get(struct snd_kcontrol *kctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001055 struct snd_ctl_elem_value *ucontrol)
1056{
Charles Keepax9ee78752016-05-02 13:57:36 +01001057 struct soc_bytes_ext *bytes_ext =
1058 (struct soc_bytes_ext *)kctl->private_value;
1059 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001060 char *p = ucontrol->value.bytes.data;
Charles Keepax168d10e2015-12-08 16:08:27 +00001061 int ret = 0;
1062
1063 mutex_lock(&ctl->dsp->pwr_lock);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001064
Charles Keepax26c22a12015-04-20 13:52:45 +01001065 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
Charles Keepaxcef45772016-09-20 13:52:33 +01001066 if (ctl->enabled && ctl->dsp->running)
Charles Keepax168d10e2015-12-08 16:08:27 +00001067 ret = wm_coeff_read_control(ctl, p, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +01001068 else
Charles Keepax168d10e2015-12-08 16:08:27 +00001069 ret = -EPERM;
1070 } else {
Charles Keepaxcef45772016-09-20 13:52:33 +01001071 if (!ctl->flags && ctl->enabled && ctl->dsp->running)
Charles Keepaxbc1765d2015-12-17 10:05:59 +00001072 ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
1073
Charles Keepax168d10e2015-12-08 16:08:27 +00001074 memcpy(p, ctl->cache, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +01001075 }
1076
Charles Keepax168d10e2015-12-08 16:08:27 +00001077 mutex_unlock(&ctl->dsp->pwr_lock);
Charles Keepax26c22a12015-04-20 13:52:45 +01001078
Charles Keepax168d10e2015-12-08 16:08:27 +00001079 return ret;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001080}
1081
Charles Keepax9ee78752016-05-02 13:57:36 +01001082static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
1083 unsigned int __user *bytes, unsigned int size)
1084{
1085 struct soc_bytes_ext *bytes_ext =
1086 (struct soc_bytes_ext *)kctl->private_value;
1087 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
1088 int ret = 0;
1089
1090 mutex_lock(&ctl->dsp->pwr_lock);
1091
1092 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
Charles Keepaxcef45772016-09-20 13:52:33 +01001093 if (ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +01001094 ret = wm_coeff_read_control(ctl, ctl->cache, size);
1095 else
1096 ret = -EPERM;
1097 } else {
Charles Keepaxcef45772016-09-20 13:52:33 +01001098 if (!ctl->flags && ctl->enabled && ctl->dsp->running)
Charles Keepax9ee78752016-05-02 13:57:36 +01001099 ret = wm_coeff_read_control(ctl, ctl->cache, size);
1100 }
1101
1102 if (!ret && copy_to_user(bytes, ctl->cache, size))
1103 ret = -EFAULT;
1104
1105 mutex_unlock(&ctl->dsp->pwr_lock);
1106
1107 return ret;
1108}
1109
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001110static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
1111 struct snd_ctl_elem_value *ucontrol)
1112{
1113 /*
1114 * Although it's not useful to read an acked control, we must satisfy
1115 * user-side assumptions that all controls are readable and that a
1116 * write of the same value should be filtered out (it's valid to send
1117 * the same event number again to the firmware). We therefore return 0,
1118 * meaning "no event" so valid event numbers will always be a change
1119 */
1120 ucontrol->value.integer.value[0] = 0;
1121
1122 return 0;
1123}
1124
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001125struct wmfw_ctl_work {
Charles Keepax3809f002015-04-13 13:27:54 +01001126 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001127 struct wm_coeff_ctl *ctl;
1128 struct work_struct work;
1129};
1130
Charles Keepax9ee78752016-05-02 13:57:36 +01001131static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
1132{
1133 unsigned int out, rd, wr, vol;
1134
1135 if (len > ADSP_MAX_STD_CTRL_SIZE) {
1136 rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
1137 wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
1138 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
1139
1140 out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
1141 } else {
1142 rd = SNDRV_CTL_ELEM_ACCESS_READ;
1143 wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
1144 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
1145
1146 out = 0;
1147 }
1148
1149 if (in) {
1150 if (in & WMFW_CTL_FLAG_READABLE)
1151 out |= rd;
1152 if (in & WMFW_CTL_FLAG_WRITEABLE)
1153 out |= wr;
1154 if (in & WMFW_CTL_FLAG_VOLATILE)
1155 out |= vol;
1156 } else {
1157 out |= rd | wr | vol;
1158 }
1159
1160 return out;
1161}
1162
Charles Keepax3809f002015-04-13 13:27:54 +01001163static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001164{
1165 struct snd_kcontrol_new *kcontrol;
1166 int ret;
1167
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01001168 if (!ctl || !ctl->name)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001169 return -EINVAL;
1170
1171 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
1172 if (!kcontrol)
1173 return -ENOMEM;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001174
1175 kcontrol->name = ctl->name;
1176 kcontrol->info = wm_coeff_info;
Charles Keepax9ee78752016-05-02 13:57:36 +01001177 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1178 kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
1179 kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
Charles Keepax9ee78752016-05-02 13:57:36 +01001180 kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +01001181
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001182 switch (ctl->type) {
1183 case WMFW_CTL_TYPE_ACKED:
1184 kcontrol->get = wm_coeff_get_acked;
1185 kcontrol->put = wm_coeff_put_acked;
1186 break;
1187 default:
Richard Fitzgeraldd7789f52018-02-28 10:31:10 +00001188 if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
1189 ctl->bytes_ext.max = ctl->len;
1190 ctl->bytes_ext.get = wm_coeff_tlv_get;
1191 ctl->bytes_ext.put = wm_coeff_tlv_put;
1192 } else {
1193 kcontrol->get = wm_coeff_get;
1194 kcontrol->put = wm_coeff_put;
1195 }
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001196 break;
1197 }
1198
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00001199 ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001200 if (ret < 0)
1201 goto err_kcontrol;
1202
1203 kfree(kcontrol);
1204
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001205 return 0;
1206
1207err_kcontrol:
1208 kfree(kcontrol);
1209 return ret;
1210}
1211
Charles Keepaxb21acc12015-04-13 13:28:01 +01001212static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
1213{
1214 struct wm_coeff_ctl *ctl;
1215 int ret;
1216
1217 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1218 if (!ctl->enabled || ctl->set)
1219 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +01001220 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
1221 continue;
1222
Richard Fitzgerald04ff40a2018-02-05 11:38:17 +00001223 /*
1224 * For readable controls populate the cache from the DSP memory.
1225 * For non-readable controls the cache was zero-filled when
1226 * created so we don't need to do anything.
1227 */
1228 if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) {
1229 ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
1230 if (ret < 0)
1231 return ret;
1232 }
Charles Keepaxb21acc12015-04-13 13:28:01 +01001233 }
1234
1235 return 0;
1236}
1237
1238static int wm_coeff_sync_controls(struct wm_adsp *dsp)
1239{
1240 struct wm_coeff_ctl *ctl;
1241 int ret;
1242
1243 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1244 if (!ctl->enabled)
1245 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +01001246 if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00001247 ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len);
Charles Keepaxb21acc12015-04-13 13:28:01 +01001248 if (ret < 0)
1249 return ret;
1250 }
1251 }
1252
1253 return 0;
1254}
1255
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001256static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
1257 unsigned int event)
1258{
1259 struct wm_coeff_ctl *ctl;
1260 int ret;
1261
1262 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1263 if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
1264 continue;
1265
Charles Keepax87aa6372016-11-21 18:00:02 +00001266 if (!ctl->enabled)
1267 continue;
1268
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001269 ret = wm_coeff_write_acked_control(ctl, event);
1270 if (ret)
1271 adsp_warn(dsp,
1272 "Failed to send 0x%x event to alg 0x%x (%d)\n",
1273 event, ctl->alg_region.alg, ret);
1274 }
1275}
1276
Charles Keepaxb21acc12015-04-13 13:28:01 +01001277static void wm_adsp_ctl_work(struct work_struct *work)
1278{
1279 struct wmfw_ctl_work *ctl_work = container_of(work,
1280 struct wmfw_ctl_work,
1281 work);
1282
1283 wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
1284 kfree(ctl_work);
1285}
1286
Richard Fitzgerald66225e92016-04-27 14:58:27 +01001287static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
1288{
1289 kfree(ctl->cache);
1290 kfree(ctl->name);
1291 kfree(ctl);
1292}
1293
Charles Keepaxb21acc12015-04-13 13:28:01 +01001294static int wm_adsp_create_control(struct wm_adsp *dsp,
1295 const struct wm_adsp_alg_region *alg_region,
Charles Keepax23237362015-04-13 13:28:02 +01001296 unsigned int offset, unsigned int len,
Charles Keepax26c22a12015-04-20 13:52:45 +01001297 const char *subname, unsigned int subname_len,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001298 unsigned int flags, unsigned int type)
Charles Keepaxb21acc12015-04-13 13:28:01 +01001299{
1300 struct wm_coeff_ctl *ctl;
1301 struct wmfw_ctl_work *ctl_work;
1302 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001303 const char *region_name;
Charles Keepaxb21acc12015-04-13 13:28:01 +01001304 int ret;
1305
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001306 region_name = wm_adsp_mem_region_name(alg_region->type);
1307 if (!region_name) {
Charles Keepax23237362015-04-13 13:28:02 +01001308 adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
Charles Keepaxb21acc12015-04-13 13:28:01 +01001309 return -EINVAL;
1310 }
1311
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001312 switch (dsp->fw_ver) {
1313 case 0:
1314 case 1:
Richard Fitzgerald605391d2018-08-08 17:13:39 +01001315 snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x",
1316 dsp->name, region_name, alg_region->alg);
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001317 break;
1318 default:
1319 ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
Richard Fitzgerald605391d2018-08-08 17:13:39 +01001320 "%s%c %.12s %x", dsp->name, *region_name,
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001321 wm_adsp_fw_text[dsp->fw], alg_region->alg);
1322
1323 /* Truncate the subname from the start if it is too long */
1324 if (subname) {
1325 int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
1326 int skip = 0;
1327
Charles Keepaxb7ede5af2018-07-19 11:50:36 +01001328 if (dsp->component->name_prefix)
1329 avail -= strlen(dsp->component->name_prefix) + 1;
1330
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001331 if (subname_len > avail)
1332 skip = subname_len - avail;
1333
1334 snprintf(name + ret,
1335 SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
1336 subname_len - skip, subname + skip);
1337 }
1338 break;
1339 }
Charles Keepaxb21acc12015-04-13 13:28:01 +01001340
Charles Keepax7585a5b2015-12-08 16:08:25 +00001341 list_for_each_entry(ctl, &dsp->ctl_list, list) {
Charles Keepaxb21acc12015-04-13 13:28:01 +01001342 if (!strcmp(ctl->name, name)) {
1343 if (!ctl->enabled)
1344 ctl->enabled = 1;
1345 return 0;
1346 }
1347 }
1348
1349 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
1350 if (!ctl)
1351 return -ENOMEM;
Charles Keepax23237362015-04-13 13:28:02 +01001352 ctl->fw_name = wm_adsp_fw_text[dsp->fw];
Charles Keepaxb21acc12015-04-13 13:28:01 +01001353 ctl->alg_region = *alg_region;
1354 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
1355 if (!ctl->name) {
1356 ret = -ENOMEM;
1357 goto err_ctl;
1358 }
1359 ctl->enabled = 1;
1360 ctl->set = 0;
1361 ctl->ops.xget = wm_coeff_get;
1362 ctl->ops.xput = wm_coeff_put;
1363 ctl->dsp = dsp;
1364
Charles Keepax26c22a12015-04-20 13:52:45 +01001365 ctl->flags = flags;
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001366 ctl->type = type;
Charles Keepax23237362015-04-13 13:28:02 +01001367 ctl->offset = offset;
Charles Keepaxb21acc12015-04-13 13:28:01 +01001368 ctl->len = len;
1369 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
1370 if (!ctl->cache) {
1371 ret = -ENOMEM;
1372 goto err_ctl_name;
1373 }
1374
Charles Keepax23237362015-04-13 13:28:02 +01001375 list_add(&ctl->list, &dsp->ctl_list);
1376
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001377 if (flags & WMFW_CTL_FLAG_SYS)
1378 return 0;
1379
Charles Keepaxb21acc12015-04-13 13:28:01 +01001380 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
1381 if (!ctl_work) {
1382 ret = -ENOMEM;
1383 goto err_ctl_cache;
1384 }
1385
1386 ctl_work->dsp = dsp;
1387 ctl_work->ctl = ctl;
1388 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
1389 schedule_work(&ctl_work->work);
1390
1391 return 0;
1392
1393err_ctl_cache:
1394 kfree(ctl->cache);
1395err_ctl_name:
1396 kfree(ctl->name);
1397err_ctl:
1398 kfree(ctl);
1399
1400 return ret;
1401}
1402
Charles Keepax23237362015-04-13 13:28:02 +01001403struct wm_coeff_parsed_alg {
1404 int id;
1405 const u8 *name;
1406 int name_len;
1407 int ncoeff;
1408};
1409
1410struct wm_coeff_parsed_coeff {
1411 int offset;
1412 int mem_type;
1413 const u8 *name;
1414 int name_len;
1415 int ctl_type;
1416 int flags;
1417 int len;
1418};
1419
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001420static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
1421{
1422 int length;
1423
1424 switch (bytes) {
1425 case 1:
1426 length = **pos;
1427 break;
1428 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +01001429 length = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001430 break;
1431 default:
1432 return 0;
1433 }
1434
1435 if (str)
1436 *str = *pos + bytes;
1437
1438 *pos += ((length + bytes) + 3) & ~0x03;
1439
1440 return length;
1441}
1442
1443static int wm_coeff_parse_int(int bytes, const u8 **pos)
1444{
1445 int val = 0;
1446
1447 switch (bytes) {
1448 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +01001449 val = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001450 break;
1451 case 4:
Charles Keepax8299ee82015-04-20 13:52:44 +01001452 val = le32_to_cpu(*((__le32 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001453 break;
1454 default:
1455 break;
1456 }
1457
1458 *pos += bytes;
1459
1460 return val;
1461}
1462
Charles Keepax23237362015-04-13 13:28:02 +01001463static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
1464 struct wm_coeff_parsed_alg *blk)
1465{
1466 const struct wmfw_adsp_alg_data *raw;
1467
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001468 switch (dsp->fw_ver) {
1469 case 0:
1470 case 1:
1471 raw = (const struct wmfw_adsp_alg_data *)*data;
1472 *data = raw->data;
Charles Keepax23237362015-04-13 13:28:02 +01001473
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001474 blk->id = le32_to_cpu(raw->id);
1475 blk->name = raw->name;
1476 blk->name_len = strlen(raw->name);
1477 blk->ncoeff = le32_to_cpu(raw->ncoeff);
1478 break;
1479 default:
1480 blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
1481 blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
1482 &blk->name);
1483 wm_coeff_parse_string(sizeof(u16), data, NULL);
1484 blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
1485 break;
1486 }
Charles Keepax23237362015-04-13 13:28:02 +01001487
1488 adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
1489 adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
1490 adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
1491}
1492
1493static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
1494 struct wm_coeff_parsed_coeff *blk)
1495{
1496 const struct wmfw_adsp_coeff_data *raw;
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001497 const u8 *tmp;
1498 int length;
Charles Keepax23237362015-04-13 13:28:02 +01001499
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001500 switch (dsp->fw_ver) {
1501 case 0:
1502 case 1:
1503 raw = (const struct wmfw_adsp_coeff_data *)*data;
1504 *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
Charles Keepax23237362015-04-13 13:28:02 +01001505
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001506 blk->offset = le16_to_cpu(raw->hdr.offset);
1507 blk->mem_type = le16_to_cpu(raw->hdr.type);
1508 blk->name = raw->name;
1509 blk->name_len = strlen(raw->name);
1510 blk->ctl_type = le16_to_cpu(raw->ctl_type);
1511 blk->flags = le16_to_cpu(raw->flags);
1512 blk->len = le32_to_cpu(raw->len);
1513 break;
1514 default:
1515 tmp = *data;
1516 blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
1517 blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
1518 length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
1519 blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
1520 &blk->name);
1521 wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
1522 wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
1523 blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
1524 blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
1525 blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
1526
1527 *data = *data + sizeof(raw->hdr) + length;
1528 break;
1529 }
Charles Keepax23237362015-04-13 13:28:02 +01001530
1531 adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
1532 adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
1533 adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
1534 adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
1535 adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
1536 adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
1537}
1538
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001539static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
1540 const struct wm_coeff_parsed_coeff *coeff_blk,
1541 unsigned int f_required,
1542 unsigned int f_illegal)
1543{
1544 if ((coeff_blk->flags & f_illegal) ||
1545 ((coeff_blk->flags & f_required) != f_required)) {
1546 adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
1547 coeff_blk->flags, coeff_blk->ctl_type);
1548 return -EINVAL;
1549 }
1550
1551 return 0;
1552}
1553
Charles Keepax23237362015-04-13 13:28:02 +01001554static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
1555 const struct wmfw_region *region)
1556{
1557 struct wm_adsp_alg_region alg_region = {};
1558 struct wm_coeff_parsed_alg alg_blk;
1559 struct wm_coeff_parsed_coeff coeff_blk;
1560 const u8 *data = region->data;
1561 int i, ret;
1562
1563 wm_coeff_parse_alg(dsp, &data, &alg_blk);
1564 for (i = 0; i < alg_blk.ncoeff; i++) {
1565 wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
1566
1567 switch (coeff_blk.ctl_type) {
1568 case SNDRV_CTL_ELEM_TYPE_BYTES:
1569 break;
Richard Fitzgeralda23ebba2016-11-09 17:14:18 +00001570 case WMFW_CTL_TYPE_ACKED:
1571 if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)
1572 continue; /* ignore */
1573
1574 ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
1575 WMFW_CTL_FLAG_VOLATILE |
1576 WMFW_CTL_FLAG_WRITEABLE |
1577 WMFW_CTL_FLAG_READABLE,
1578 0);
1579 if (ret)
1580 return -EINVAL;
1581 break;
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00001582 case WMFW_CTL_TYPE_HOSTEVENT:
1583 ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
1584 WMFW_CTL_FLAG_SYS |
1585 WMFW_CTL_FLAG_VOLATILE |
1586 WMFW_CTL_FLAG_WRITEABLE |
1587 WMFW_CTL_FLAG_READABLE,
1588 0);
1589 if (ret)
1590 return -EINVAL;
1591 break;
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01001592 case WMFW_CTL_TYPE_HOST_BUFFER:
1593 ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
1594 WMFW_CTL_FLAG_SYS |
1595 WMFW_CTL_FLAG_VOLATILE |
1596 WMFW_CTL_FLAG_READABLE,
1597 0);
1598 if (ret)
1599 return -EINVAL;
1600 break;
Charles Keepax23237362015-04-13 13:28:02 +01001601 default:
1602 adsp_err(dsp, "Unknown control type: %d\n",
1603 coeff_blk.ctl_type);
1604 return -EINVAL;
1605 }
1606
1607 alg_region.type = coeff_blk.mem_type;
1608 alg_region.alg = alg_blk.id;
1609
1610 ret = wm_adsp_create_control(dsp, &alg_region,
1611 coeff_blk.offset,
1612 coeff_blk.len,
1613 coeff_blk.name,
Charles Keepax26c22a12015-04-20 13:52:45 +01001614 coeff_blk.name_len,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00001615 coeff_blk.flags,
1616 coeff_blk.ctl_type);
Charles Keepax23237362015-04-13 13:28:02 +01001617 if (ret < 0)
1618 adsp_err(dsp, "Failed to create control: %.*s, %d\n",
1619 coeff_blk.name_len, coeff_blk.name, ret);
1620 }
1621
1622 return 0;
1623}
1624
Mark Brown2159ad932012-10-11 11:54:02 +09001625static int wm_adsp_load(struct wm_adsp *dsp)
1626{
Mark Browncf17c832013-01-30 14:37:23 +08001627 LIST_HEAD(buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09001628 const struct firmware *firmware;
1629 struct regmap *regmap = dsp->regmap;
1630 unsigned int pos = 0;
1631 const struct wmfw_header *header;
1632 const struct wmfw_adsp1_sizes *adsp1_sizes;
1633 const struct wmfw_adsp2_sizes *adsp2_sizes;
1634 const struct wmfw_footer *footer;
1635 const struct wmfw_region *region;
1636 const struct wm_adsp_region *mem;
1637 const char *region_name;
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001638 char *file, *text = NULL;
Mark Browncf17c832013-01-30 14:37:23 +08001639 struct wm_adsp_buf *buf;
Mark Brown2159ad932012-10-11 11:54:02 +09001640 unsigned int reg;
1641 int regions = 0;
1642 int ret, offset, type, sizes;
1643
1644 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1645 if (file == NULL)
1646 return -ENOMEM;
1647
Richard Fitzgerald605391d2018-08-08 17:13:39 +01001648 snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name,
Mark Brown1023dbd2013-01-11 22:58:28 +00001649 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +09001650 file[PAGE_SIZE - 1] = '\0';
1651
1652 ret = request_firmware(&firmware, file, dsp->dev);
1653 if (ret != 0) {
1654 adsp_err(dsp, "Failed to request '%s'\n", file);
1655 goto out;
1656 }
1657 ret = -EINVAL;
1658
1659 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1660 if (pos >= firmware->size) {
1661 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1662 file, firmware->size);
1663 goto out_fw;
1664 }
1665
Charles Keepax7585a5b2015-12-08 16:08:25 +00001666 header = (void *)&firmware->data[0];
Mark Brown2159ad932012-10-11 11:54:02 +09001667
1668 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
1669 adsp_err(dsp, "%s: invalid magic\n", file);
1670 goto out_fw;
1671 }
1672
Charles Keepax23237362015-04-13 13:28:02 +01001673 switch (header->ver) {
1674 case 0:
Charles Keepaxc61e59f2015-04-13 13:28:05 +01001675 adsp_warn(dsp, "%s: Depreciated file format %d\n",
1676 file, header->ver);
1677 break;
Charles Keepax23237362015-04-13 13:28:02 +01001678 case 1:
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001679 case 2:
Charles Keepax23237362015-04-13 13:28:02 +01001680 break;
1681 default:
Mark Brown2159ad932012-10-11 11:54:02 +09001682 adsp_err(dsp, "%s: unknown file format %d\n",
1683 file, header->ver);
1684 goto out_fw;
1685 }
Charles Keepax23237362015-04-13 13:28:02 +01001686
Dimitris Papastamos36269922013-11-01 15:56:57 +00001687 adsp_info(dsp, "Firmware version: %d\n", header->ver);
Charles Keepax23237362015-04-13 13:28:02 +01001688 dsp->fw_ver = header->ver;
Mark Brown2159ad932012-10-11 11:54:02 +09001689
1690 if (header->core != dsp->type) {
1691 adsp_err(dsp, "%s: invalid core %d != %d\n",
1692 file, header->core, dsp->type);
1693 goto out_fw;
1694 }
1695
1696 switch (dsp->type) {
1697 case WMFW_ADSP1:
1698 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1699 adsp1_sizes = (void *)&(header[1]);
1700 footer = (void *)&(adsp1_sizes[1]);
1701 sizes = sizeof(*adsp1_sizes);
1702
1703 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
1704 file, le32_to_cpu(adsp1_sizes->dm),
1705 le32_to_cpu(adsp1_sizes->pm),
1706 le32_to_cpu(adsp1_sizes->zm));
1707 break;
1708
1709 case WMFW_ADSP2:
1710 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
1711 adsp2_sizes = (void *)&(header[1]);
1712 footer = (void *)&(adsp2_sizes[1]);
1713 sizes = sizeof(*adsp2_sizes);
1714
1715 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
1716 file, le32_to_cpu(adsp2_sizes->xm),
1717 le32_to_cpu(adsp2_sizes->ym),
1718 le32_to_cpu(adsp2_sizes->pm),
1719 le32_to_cpu(adsp2_sizes->zm));
1720 break;
1721
1722 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +01001723 WARN(1, "Unknown DSP type");
Mark Brown2159ad932012-10-11 11:54:02 +09001724 goto out_fw;
1725 }
1726
1727 if (le32_to_cpu(header->len) != sizeof(*header) +
1728 sizes + sizeof(*footer)) {
1729 adsp_err(dsp, "%s: unexpected header length %d\n",
1730 file, le32_to_cpu(header->len));
1731 goto out_fw;
1732 }
1733
1734 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
1735 le64_to_cpu(footer->timestamp));
1736
1737 while (pos < firmware->size &&
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00001738 sizeof(*region) < firmware->size - pos) {
Mark Brown2159ad932012-10-11 11:54:02 +09001739 region = (void *)&(firmware->data[pos]);
1740 region_name = "Unknown";
1741 reg = 0;
1742 text = NULL;
1743 offset = le32_to_cpu(region->offset) & 0xffffff;
1744 type = be32_to_cpu(region->type) & 0xff;
1745 mem = wm_adsp_find_region(dsp, type);
Charles Keepax7585a5b2015-12-08 16:08:25 +00001746
Mark Brown2159ad932012-10-11 11:54:02 +09001747 switch (type) {
1748 case WMFW_NAME_TEXT:
1749 region_name = "Firmware name";
1750 text = kzalloc(le32_to_cpu(region->len) + 1,
1751 GFP_KERNEL);
1752 break;
Charles Keepax23237362015-04-13 13:28:02 +01001753 case WMFW_ALGORITHM_DATA:
1754 region_name = "Algorithm";
1755 ret = wm_adsp_parse_coeff(dsp, region);
1756 if (ret != 0)
1757 goto out_fw;
1758 break;
Mark Brown2159ad932012-10-11 11:54:02 +09001759 case WMFW_INFO_TEXT:
1760 region_name = "Information";
1761 text = kzalloc(le32_to_cpu(region->len) + 1,
1762 GFP_KERNEL);
1763 break;
1764 case WMFW_ABSOLUTE:
1765 region_name = "Absolute";
1766 reg = offset;
1767 break;
1768 case WMFW_ADSP1_PM:
Mark Brown2159ad932012-10-11 11:54:02 +09001769 case WMFW_ADSP1_DM:
Mark Brown2159ad932012-10-11 11:54:02 +09001770 case WMFW_ADSP2_XM:
Mark Brown2159ad932012-10-11 11:54:02 +09001771 case WMFW_ADSP2_YM:
Mark Brown2159ad932012-10-11 11:54:02 +09001772 case WMFW_ADSP1_ZM:
Richard Fitzgerald9ce5e6e2016-11-09 17:14:15 +00001773 region_name = wm_adsp_mem_region_name(type);
Mark Brown45b9ee72013-01-08 16:02:06 +00001774 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +09001775 break;
1776 default:
1777 adsp_warn(dsp,
1778 "%s.%d: Unknown region type %x at %d(%x)\n",
1779 file, regions, type, pos, pos);
1780 break;
1781 }
1782
1783 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
1784 regions, le32_to_cpu(region->len), offset,
1785 region_name);
1786
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00001787 if (le32_to_cpu(region->len) >
1788 firmware->size - pos - sizeof(*region)) {
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001789 adsp_err(dsp,
1790 "%s.%d: %s region len %d bytes exceeds file length %zu\n",
1791 file, regions, region_name,
1792 le32_to_cpu(region->len), firmware->size);
1793 ret = -EINVAL;
1794 goto out_fw;
1795 }
1796
Mark Brown2159ad932012-10-11 11:54:02 +09001797 if (text) {
1798 memcpy(text, region->data, le32_to_cpu(region->len));
1799 adsp_info(dsp, "%s: %s\n", file, text);
1800 kfree(text);
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001801 text = NULL;
Mark Brown2159ad932012-10-11 11:54:02 +09001802 }
1803
1804 if (reg) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001805 buf = wm_adsp_buf_alloc(region->data,
1806 le32_to_cpu(region->len),
1807 &buf_list);
1808 if (!buf) {
1809 adsp_err(dsp, "Out of memory\n");
1810 ret = -ENOMEM;
1811 goto out_fw;
1812 }
Mark Browna76fefa2013-01-07 19:03:17 +00001813
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001814 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1815 le32_to_cpu(region->len));
1816 if (ret != 0) {
1817 adsp_err(dsp,
1818 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
1819 file, regions,
1820 le32_to_cpu(region->len), offset,
1821 region_name, ret);
1822 goto out_fw;
Mark Brown2159ad932012-10-11 11:54:02 +09001823 }
1824 }
1825
1826 pos += le32_to_cpu(region->len) + sizeof(*region);
1827 regions++;
1828 }
Mark Browncf17c832013-01-30 14:37:23 +08001829
1830 ret = regmap_async_complete(regmap);
1831 if (ret != 0) {
1832 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1833 goto out_fw;
1834 }
1835
Mark Brown2159ad932012-10-11 11:54:02 +09001836 if (pos > firmware->size)
1837 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1838 file, regions, pos - firmware->size);
1839
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001840 wm_adsp_debugfs_save_wmfwname(dsp, file);
1841
Mark Brown2159ad932012-10-11 11:54:02 +09001842out_fw:
Mark Browncf17c832013-01-30 14:37:23 +08001843 regmap_async_complete(regmap);
1844 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09001845 release_firmware(firmware);
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00001846 kfree(text);
Mark Brown2159ad932012-10-11 11:54:02 +09001847out:
1848 kfree(file);
1849
1850 return ret;
1851}
1852
Charles Keepax23237362015-04-13 13:28:02 +01001853static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
1854 const struct wm_adsp_alg_region *alg_region)
1855{
1856 struct wm_coeff_ctl *ctl;
1857
1858 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1859 if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
1860 alg_region->alg == ctl->alg_region.alg &&
1861 alg_region->type == ctl->alg_region.type) {
1862 ctl->alg_region.base = alg_region->base;
1863 }
1864 }
1865}
1866
Charles Keepax3809f002015-04-13 13:27:54 +01001867static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
Charles Keepax7f7cca02018-06-20 11:56:21 +01001868 const struct wm_adsp_region *mem,
Charles Keepaxb618a1852015-04-13 13:27:53 +01001869 unsigned int pos, unsigned int len)
Mark Browndb405172012-10-26 19:30:40 +01001870{
Charles Keepaxb618a1852015-04-13 13:27:53 +01001871 void *alg;
Charles Keepax7f7cca02018-06-20 11:56:21 +01001872 unsigned int reg;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001873 int ret;
Mark Browndb405172012-10-26 19:30:40 +01001874 __be32 val;
Mark Browndb405172012-10-26 19:30:40 +01001875
Charles Keepax3809f002015-04-13 13:27:54 +01001876 if (n_algs == 0) {
Mark Browndb405172012-10-26 19:30:40 +01001877 adsp_err(dsp, "No algorithms\n");
Charles Keepaxb618a1852015-04-13 13:27:53 +01001878 return ERR_PTR(-EINVAL);
Mark Browndb405172012-10-26 19:30:40 +01001879 }
1880
Charles Keepax3809f002015-04-13 13:27:54 +01001881 if (n_algs > 1024) {
1882 adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001883 return ERR_PTR(-EINVAL);
Mark Brownd62f4bc2012-12-19 14:00:30 +00001884 }
1885
Mark Browndb405172012-10-26 19:30:40 +01001886 /* Read the terminator first to validate the length */
Charles Keepax7f7cca02018-06-20 11:56:21 +01001887 reg = wm_adsp_region_to_reg(mem, pos + len);
1888
1889 ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
Mark Browndb405172012-10-26 19:30:40 +01001890 if (ret != 0) {
1891 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1892 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001893 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001894 }
1895
1896 if (be32_to_cpu(val) != 0xbedead)
Richard Fitzgerald503ada82017-05-26 10:47:07 +01001897 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n",
Charles Keepax7f7cca02018-06-20 11:56:21 +01001898 reg, be32_to_cpu(val));
1899
1900 /* Convert length from DSP words to bytes */
1901 len *= sizeof(u32);
Mark Browndb405172012-10-26 19:30:40 +01001902
Charles Keepax517ee742018-07-19 11:50:35 +01001903 alg = kzalloc(len, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +01001904 if (!alg)
Charles Keepaxb618a1852015-04-13 13:27:53 +01001905 return ERR_PTR(-ENOMEM);
Mark Browndb405172012-10-26 19:30:40 +01001906
Charles Keepax7f7cca02018-06-20 11:56:21 +01001907 reg = wm_adsp_region_to_reg(mem, pos);
1908
1909 ret = regmap_raw_read(dsp->regmap, reg, alg, len);
Mark Browndb405172012-10-26 19:30:40 +01001910 if (ret != 0) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00001911 adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001912 kfree(alg);
1913 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001914 }
1915
Charles Keepaxb618a1852015-04-13 13:27:53 +01001916 return alg;
1917}
1918
Charles Keepax14197092015-12-15 11:29:43 +00001919static struct wm_adsp_alg_region *
1920 wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id)
1921{
1922 struct wm_adsp_alg_region *alg_region;
1923
1924 list_for_each_entry(alg_region, &dsp->alg_regions, list) {
1925 if (id == alg_region->alg && type == alg_region->type)
1926 return alg_region;
1927 }
1928
1929 return NULL;
1930}
1931
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001932static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
1933 int type, __be32 id,
1934 __be32 base)
1935{
1936 struct wm_adsp_alg_region *alg_region;
1937
1938 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1939 if (!alg_region)
1940 return ERR_PTR(-ENOMEM);
1941
1942 alg_region->type = type;
1943 alg_region->alg = be32_to_cpu(id);
1944 alg_region->base = be32_to_cpu(base);
1945
1946 list_add_tail(&alg_region->list, &dsp->alg_regions);
1947
Charles Keepax23237362015-04-13 13:28:02 +01001948 if (dsp->fw_ver > 0)
1949 wm_adsp_ctl_fixup_base(dsp, alg_region);
1950
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001951 return alg_region;
1952}
1953
Richard Fitzgerald56574d52016-04-27 14:58:29 +01001954static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
1955{
1956 struct wm_adsp_alg_region *alg_region;
1957
1958 while (!list_empty(&dsp->alg_regions)) {
1959 alg_region = list_first_entry(&dsp->alg_regions,
1960 struct wm_adsp_alg_region,
1961 list);
1962 list_del(&alg_region->list);
1963 kfree(alg_region);
1964 }
1965}
1966
Charles Keepaxb618a1852015-04-13 13:27:53 +01001967static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
1968{
1969 struct wmfw_adsp1_id_hdr adsp1_id;
1970 struct wmfw_adsp1_alg_hdr *adsp1_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001971 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001972 const struct wm_adsp_region *mem;
1973 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001974 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001975 int i, ret;
1976
1977 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
1978 if (WARN_ON(!mem))
1979 return -EINVAL;
1980
1981 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
1982 sizeof(adsp1_id));
1983 if (ret != 0) {
1984 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1985 ret);
1986 return ret;
1987 }
1988
Charles Keepax3809f002015-04-13 13:27:54 +01001989 n_algs = be32_to_cpu(adsp1_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001990 dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
1991 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1992 dsp->fw_id,
1993 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
1994 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
1995 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001996 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001997
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001998 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1999 adsp1_id.fw.id, adsp1_id.zm);
2000 if (IS_ERR(alg_region))
2001 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002002
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002003 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
2004 adsp1_id.fw.id, adsp1_id.dm);
2005 if (IS_ERR(alg_region))
2006 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002007
Charles Keepax7f7cca02018-06-20 11:56:21 +01002008 /* Calculate offset and length in DSP words */
2009 pos = sizeof(adsp1_id) / sizeof(u32);
2010 len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002011
Charles Keepax7f7cca02018-06-20 11:56:21 +01002012 adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002013 if (IS_ERR(adsp1_alg))
2014 return PTR_ERR(adsp1_alg);
Mark Browndb405172012-10-26 19:30:40 +01002015
Charles Keepax3809f002015-04-13 13:27:54 +01002016 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01002017 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
2018 i, be32_to_cpu(adsp1_alg[i].alg.id),
2019 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
2020 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
2021 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
2022 be32_to_cpu(adsp1_alg[i].dm),
2023 be32_to_cpu(adsp1_alg[i].zm));
Mark Brown471f4882013-01-08 16:09:31 +00002024
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002025 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
2026 adsp1_alg[i].alg.id,
2027 adsp1_alg[i].dm);
2028 if (IS_ERR(alg_region)) {
2029 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002030 goto out;
2031 }
Charles Keepax23237362015-04-13 13:28:02 +01002032 if (dsp->fw_ver == 0) {
2033 if (i + 1 < n_algs) {
2034 len = be32_to_cpu(adsp1_alg[i + 1].dm);
2035 len -= be32_to_cpu(adsp1_alg[i].dm);
2036 len *= 4;
2037 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002038 len, NULL, 0, 0,
2039 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002040 } else {
2041 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
2042 be32_to_cpu(adsp1_alg[i].alg.id));
2043 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002044 }
Mark Brown471f4882013-01-08 16:09:31 +00002045
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002046 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
2047 adsp1_alg[i].alg.id,
2048 adsp1_alg[i].zm);
2049 if (IS_ERR(alg_region)) {
2050 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002051 goto out;
2052 }
Charles Keepax23237362015-04-13 13:28:02 +01002053 if (dsp->fw_ver == 0) {
2054 if (i + 1 < n_algs) {
2055 len = be32_to_cpu(adsp1_alg[i + 1].zm);
2056 len -= be32_to_cpu(adsp1_alg[i].zm);
2057 len *= 4;
2058 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002059 len, NULL, 0, 0,
2060 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002061 } else {
2062 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
2063 be32_to_cpu(adsp1_alg[i].alg.id));
2064 }
Mark Browndb405172012-10-26 19:30:40 +01002065 }
2066 }
2067
2068out:
Charles Keepaxb618a1852015-04-13 13:27:53 +01002069 kfree(adsp1_alg);
2070 return ret;
2071}
2072
2073static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
2074{
2075 struct wmfw_adsp2_id_hdr adsp2_id;
2076 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01002077 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01002078 const struct wm_adsp_region *mem;
2079 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01002080 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01002081 int i, ret;
2082
2083 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
2084 if (WARN_ON(!mem))
2085 return -EINVAL;
2086
2087 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
2088 sizeof(adsp2_id));
2089 if (ret != 0) {
2090 adsp_err(dsp, "Failed to read algorithm info: %d\n",
2091 ret);
2092 return ret;
2093 }
2094
Charles Keepax3809f002015-04-13 13:27:54 +01002095 n_algs = be32_to_cpu(adsp2_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002096 dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002097 dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002098 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
2099 dsp->fw_id,
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002100 (dsp->fw_id_version & 0xff0000) >> 16,
2101 (dsp->fw_id_version & 0xff00) >> 8,
2102 dsp->fw_id_version & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01002103 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002104
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002105 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
2106 adsp2_id.fw.id, adsp2_id.xm);
2107 if (IS_ERR(alg_region))
2108 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002109
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002110 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
2111 adsp2_id.fw.id, adsp2_id.ym);
2112 if (IS_ERR(alg_region))
2113 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002114
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002115 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
2116 adsp2_id.fw.id, adsp2_id.zm);
2117 if (IS_ERR(alg_region))
2118 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002119
Charles Keepax7f7cca02018-06-20 11:56:21 +01002120 /* Calculate offset and length in DSP words */
2121 pos = sizeof(adsp2_id) / sizeof(u32);
2122 len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002123
Charles Keepax7f7cca02018-06-20 11:56:21 +01002124 adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002125 if (IS_ERR(adsp2_alg))
2126 return PTR_ERR(adsp2_alg);
2127
Charles Keepax3809f002015-04-13 13:27:54 +01002128 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01002129 adsp_info(dsp,
2130 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
2131 i, be32_to_cpu(adsp2_alg[i].alg.id),
2132 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
2133 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
2134 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
2135 be32_to_cpu(adsp2_alg[i].xm),
2136 be32_to_cpu(adsp2_alg[i].ym),
2137 be32_to_cpu(adsp2_alg[i].zm));
2138
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002139 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
2140 adsp2_alg[i].alg.id,
2141 adsp2_alg[i].xm);
2142 if (IS_ERR(alg_region)) {
2143 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002144 goto out;
2145 }
Charles Keepax23237362015-04-13 13:28:02 +01002146 if (dsp->fw_ver == 0) {
2147 if (i + 1 < n_algs) {
2148 len = be32_to_cpu(adsp2_alg[i + 1].xm);
2149 len -= be32_to_cpu(adsp2_alg[i].xm);
2150 len *= 4;
2151 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002152 len, NULL, 0, 0,
2153 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002154 } else {
2155 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
2156 be32_to_cpu(adsp2_alg[i].alg.id));
2157 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002158 }
2159
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002160 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
2161 adsp2_alg[i].alg.id,
2162 adsp2_alg[i].ym);
2163 if (IS_ERR(alg_region)) {
2164 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002165 goto out;
2166 }
Charles Keepax23237362015-04-13 13:28:02 +01002167 if (dsp->fw_ver == 0) {
2168 if (i + 1 < n_algs) {
2169 len = be32_to_cpu(adsp2_alg[i + 1].ym);
2170 len -= be32_to_cpu(adsp2_alg[i].ym);
2171 len *= 4;
2172 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002173 len, NULL, 0, 0,
2174 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002175 } else {
2176 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
2177 be32_to_cpu(adsp2_alg[i].alg.id));
2178 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002179 }
2180
Charles Keepaxd9d20e12015-04-13 13:27:59 +01002181 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
2182 adsp2_alg[i].alg.id,
2183 adsp2_alg[i].zm);
2184 if (IS_ERR(alg_region)) {
2185 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01002186 goto out;
2187 }
Charles Keepax23237362015-04-13 13:28:02 +01002188 if (dsp->fw_ver == 0) {
2189 if (i + 1 < n_algs) {
2190 len = be32_to_cpu(adsp2_alg[i + 1].zm);
2191 len -= be32_to_cpu(adsp2_alg[i].zm);
2192 len *= 4;
2193 wm_adsp_create_control(dsp, alg_region, 0,
Stuart Henderson8eb084d2016-11-09 17:14:16 +00002194 len, NULL, 0, 0,
2195 SNDRV_CTL_ELEM_TYPE_BYTES);
Charles Keepax23237362015-04-13 13:28:02 +01002196 } else {
2197 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
2198 be32_to_cpu(adsp2_alg[i].alg.id));
2199 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01002200 }
2201 }
2202
2203out:
2204 kfree(adsp2_alg);
Mark Browndb405172012-10-26 19:30:40 +01002205 return ret;
2206}
2207
Mark Brown2159ad932012-10-11 11:54:02 +09002208static int wm_adsp_load_coeff(struct wm_adsp *dsp)
2209{
Mark Browncf17c832013-01-30 14:37:23 +08002210 LIST_HEAD(buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09002211 struct regmap *regmap = dsp->regmap;
2212 struct wmfw_coeff_hdr *hdr;
2213 struct wmfw_coeff_item *blk;
2214 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +00002215 const struct wm_adsp_region *mem;
2216 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad932012-10-11 11:54:02 +09002217 const char *region_name;
2218 int ret, pos, blocks, type, offset, reg;
2219 char *file;
Mark Browncf17c832013-01-30 14:37:23 +08002220 struct wm_adsp_buf *buf;
Mark Brown2159ad932012-10-11 11:54:02 +09002221
2222 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
2223 if (file == NULL)
2224 return -ENOMEM;
2225
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002226 snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name,
Mark Brown1023dbd2013-01-11 22:58:28 +00002227 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +09002228 file[PAGE_SIZE - 1] = '\0';
2229
2230 ret = request_firmware(&firmware, file, dsp->dev);
2231 if (ret != 0) {
2232 adsp_warn(dsp, "Failed to request '%s'\n", file);
2233 ret = 0;
2234 goto out;
2235 }
2236 ret = -EINVAL;
2237
2238 if (sizeof(*hdr) >= firmware->size) {
2239 adsp_err(dsp, "%s: file too short, %zu bytes\n",
2240 file, firmware->size);
2241 goto out_fw;
2242 }
2243
Charles Keepax7585a5b2015-12-08 16:08:25 +00002244 hdr = (void *)&firmware->data[0];
Mark Brown2159ad932012-10-11 11:54:02 +09002245 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
2246 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +00002247 goto out_fw;
Mark Brown2159ad932012-10-11 11:54:02 +09002248 }
2249
Mark Brownc7123262013-01-16 16:59:04 +09002250 switch (be32_to_cpu(hdr->rev) & 0xff) {
2251 case 1:
2252 break;
2253 default:
2254 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
2255 file, be32_to_cpu(hdr->rev) & 0xff);
2256 ret = -EINVAL;
2257 goto out_fw;
2258 }
2259
Mark Brown2159ad932012-10-11 11:54:02 +09002260 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
2261 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
2262 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
2263 le32_to_cpu(hdr->ver) & 0xff);
2264
2265 pos = le32_to_cpu(hdr->len);
2266
2267 blocks = 0;
2268 while (pos < firmware->size &&
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00002269 sizeof(*blk) < firmware->size - pos) {
Charles Keepax7585a5b2015-12-08 16:08:25 +00002270 blk = (void *)(&firmware->data[pos]);
Mark Brown2159ad932012-10-11 11:54:02 +09002271
Mark Brownc7123262013-01-16 16:59:04 +09002272 type = le16_to_cpu(blk->type);
2273 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad932012-10-11 11:54:02 +09002274
2275 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
2276 file, blocks, le32_to_cpu(blk->id),
2277 (le32_to_cpu(blk->ver) >> 16) & 0xff,
2278 (le32_to_cpu(blk->ver) >> 8) & 0xff,
2279 le32_to_cpu(blk->ver) & 0xff);
2280 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
2281 file, blocks, le32_to_cpu(blk->len), offset, type);
2282
2283 reg = 0;
2284 region_name = "Unknown";
2285 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +09002286 case (WMFW_NAME_TEXT << 8):
2287 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad932012-10-11 11:54:02 +09002288 break;
Mark Brownc7123262013-01-16 16:59:04 +09002289 case (WMFW_ABSOLUTE << 8):
Mark Brownf395a212013-03-05 22:39:54 +08002290 /*
2291 * Old files may use this for global
2292 * coefficients.
2293 */
2294 if (le32_to_cpu(blk->id) == dsp->fw_id &&
2295 offset == 0) {
2296 region_name = "global coefficients";
2297 mem = wm_adsp_find_region(dsp, type);
2298 if (!mem) {
2299 adsp_err(dsp, "No ZM\n");
2300 break;
2301 }
2302 reg = wm_adsp_region_to_reg(mem, 0);
2303
2304 } else {
2305 region_name = "register";
2306 reg = offset;
2307 }
Mark Brown2159ad932012-10-11 11:54:02 +09002308 break;
Mark Brown471f4882013-01-08 16:09:31 +00002309
2310 case WMFW_ADSP1_DM:
2311 case WMFW_ADSP1_ZM:
2312 case WMFW_ADSP2_XM:
2313 case WMFW_ADSP2_YM:
2314 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
2315 file, blocks, le32_to_cpu(blk->len),
2316 type, le32_to_cpu(blk->id));
2317
2318 mem = wm_adsp_find_region(dsp, type);
2319 if (!mem) {
2320 adsp_err(dsp, "No base for region %x\n", type);
2321 break;
2322 }
2323
Charles Keepax14197092015-12-15 11:29:43 +00002324 alg_region = wm_adsp_find_alg_region(dsp, type,
2325 le32_to_cpu(blk->id));
2326 if (alg_region) {
2327 reg = alg_region->base;
2328 reg = wm_adsp_region_to_reg(mem, reg);
2329 reg += offset;
2330 } else {
Mark Brown471f4882013-01-08 16:09:31 +00002331 adsp_err(dsp, "No %x for algorithm %x\n",
2332 type, le32_to_cpu(blk->id));
Charles Keepax14197092015-12-15 11:29:43 +00002333 }
Mark Brown471f4882013-01-08 16:09:31 +00002334 break;
2335
Mark Brown2159ad932012-10-11 11:54:02 +09002336 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +09002337 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
2338 file, blocks, type, pos);
Mark Brown2159ad932012-10-11 11:54:02 +09002339 break;
2340 }
2341
2342 if (reg) {
Ben Hutchings50dd2ea2017-12-08 16:15:20 +00002343 if (le32_to_cpu(blk->len) >
2344 firmware->size - pos - sizeof(*blk)) {
Richard Fitzgerald1cab2a82016-12-20 10:29:12 +00002345 adsp_err(dsp,
2346 "%s.%d: %s region len %d bytes exceeds file length %zu\n",
2347 file, blocks, region_name,
2348 le32_to_cpu(blk->len),
2349 firmware->size);
2350 ret = -EINVAL;
2351 goto out_fw;
2352 }
2353
Mark Browncf17c832013-01-30 14:37:23 +08002354 buf = wm_adsp_buf_alloc(blk->data,
2355 le32_to_cpu(blk->len),
2356 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +00002357 if (!buf) {
2358 adsp_err(dsp, "Out of memory\n");
Wei Yongjunf4b82812013-03-12 00:23:15 +08002359 ret = -ENOMEM;
2360 goto out_fw;
Mark Browna76fefa2013-01-07 19:03:17 +00002361 }
2362
Mark Brown20da6d52013-01-12 19:58:17 +00002363 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
2364 file, blocks, le32_to_cpu(blk->len),
2365 reg);
Mark Browncf17c832013-01-30 14:37:23 +08002366 ret = regmap_raw_write_async(regmap, reg, buf->buf,
2367 le32_to_cpu(blk->len));
Mark Brown2159ad932012-10-11 11:54:02 +09002368 if (ret != 0) {
2369 adsp_err(dsp,
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +00002370 "%s.%d: Failed to write to %x in %s: %d\n",
2371 file, blocks, reg, region_name, ret);
Mark Brown2159ad932012-10-11 11:54:02 +09002372 }
2373 }
2374
Charles Keepaxbe951012015-02-16 15:25:49 +00002375 pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
Mark Brown2159ad932012-10-11 11:54:02 +09002376 blocks++;
2377 }
2378
Mark Browncf17c832013-01-30 14:37:23 +08002379 ret = regmap_async_complete(regmap);
2380 if (ret != 0)
2381 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
2382
Mark Brown2159ad932012-10-11 11:54:02 +09002383 if (pos > firmware->size)
2384 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
2385 file, blocks, pos - firmware->size);
2386
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002387 wm_adsp_debugfs_save_binname(dsp, file);
2388
Mark Brown2159ad932012-10-11 11:54:02 +09002389out_fw:
Charles Keepax9da7a5a2014-11-17 10:48:21 +00002390 regmap_async_complete(regmap);
Mark Brown2159ad932012-10-11 11:54:02 +09002391 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +08002392 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad932012-10-11 11:54:02 +09002393out:
2394 kfree(file);
Wei Yongjunf4b82812013-03-12 00:23:15 +08002395 return ret;
Mark Brown2159ad932012-10-11 11:54:02 +09002396}
2397
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002398static int wm_adsp_create_name(struct wm_adsp *dsp)
2399{
2400 char *p;
2401
2402 if (!dsp->name) {
2403 dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d",
2404 dsp->num);
2405 if (!dsp->name)
2406 return -ENOMEM;
2407 }
2408
2409 if (!dsp->fwf_name) {
2410 p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL);
2411 if (!p)
2412 return -ENOMEM;
2413
2414 dsp->fwf_name = p;
2415 for (; *p != 0; ++p)
2416 *p = tolower(*p);
2417 }
2418
2419 return 0;
2420}
2421
Charles Keepax3809f002015-04-13 13:27:54 +01002422int wm_adsp1_init(struct wm_adsp *dsp)
Mark Brown5e7a7a22013-01-16 10:03:56 +09002423{
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002424 int ret;
2425
2426 ret = wm_adsp_create_name(dsp);
2427 if (ret)
2428 return ret;
2429
Charles Keepax3809f002015-04-13 13:27:54 +01002430 INIT_LIST_HEAD(&dsp->alg_regions);
Mark Brown5e7a7a22013-01-16 10:03:56 +09002431
Charles Keepax078e7182015-12-08 16:08:26 +00002432 mutex_init(&dsp->pwr_lock);
2433
Mark Brown5e7a7a22013-01-16 10:03:56 +09002434 return 0;
2435}
2436EXPORT_SYMBOL_GPL(wm_adsp1_init);
2437
Mark Brown2159ad932012-10-11 11:54:02 +09002438int wm_adsp1_event(struct snd_soc_dapm_widget *w,
2439 struct snd_kcontrol *kcontrol,
2440 int event)
2441{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002442 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2443 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Mark Brown2159ad932012-10-11 11:54:02 +09002444 struct wm_adsp *dsp = &dsps[w->shift];
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002445 struct wm_coeff_ctl *ctl;
Mark Brown2159ad932012-10-11 11:54:02 +09002446 int ret;
Charles Keepax7585a5b2015-12-08 16:08:25 +00002447 unsigned int val;
Mark Brown2159ad932012-10-11 11:54:02 +09002448
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002449 dsp->component = component;
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01002450
Charles Keepax078e7182015-12-08 16:08:26 +00002451 mutex_lock(&dsp->pwr_lock);
2452
Mark Brown2159ad932012-10-11 11:54:02 +09002453 switch (event) {
2454 case SND_SOC_DAPM_POST_PMU:
2455 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2456 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
2457
Chris Rattray94e205b2013-01-18 08:43:09 +00002458 /*
2459 * For simplicity set the DSP clock rate to be the
2460 * SYSCLK rate rather than making it configurable.
2461 */
Charles Keepax7585a5b2015-12-08 16:08:25 +00002462 if (dsp->sysclk_reg) {
Chris Rattray94e205b2013-01-18 08:43:09 +00002463 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
2464 if (ret != 0) {
2465 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
2466 ret);
Charles Keepax078e7182015-12-08 16:08:26 +00002467 goto err_mutex;
Chris Rattray94e205b2013-01-18 08:43:09 +00002468 }
2469
Charles Keepax7d00cd92016-02-19 14:44:43 +00002470 val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
Chris Rattray94e205b2013-01-18 08:43:09 +00002471
2472 ret = regmap_update_bits(dsp->regmap,
2473 dsp->base + ADSP1_CONTROL_31,
2474 ADSP1_CLK_SEL_MASK, val);
2475 if (ret != 0) {
2476 adsp_err(dsp, "Failed to set clock rate: %d\n",
2477 ret);
Charles Keepax078e7182015-12-08 16:08:26 +00002478 goto err_mutex;
Chris Rattray94e205b2013-01-18 08:43:09 +00002479 }
2480 }
2481
Mark Brown2159ad932012-10-11 11:54:02 +09002482 ret = wm_adsp_load(dsp);
2483 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002484 goto err_ena;
Mark Brown2159ad932012-10-11 11:54:02 +09002485
Charles Keepaxb618a1852015-04-13 13:27:53 +01002486 ret = wm_adsp1_setup_algs(dsp);
Mark Browndb405172012-10-26 19:30:40 +01002487 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002488 goto err_ena;
Mark Browndb405172012-10-26 19:30:40 +01002489
Mark Brown2159ad932012-10-11 11:54:02 +09002490 ret = wm_adsp_load_coeff(dsp);
2491 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002492 goto err_ena;
Mark Brown2159ad932012-10-11 11:54:02 +09002493
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01002494 /* Initialize caches for enabled and unset controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002495 ret = wm_coeff_init_control_caches(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002496 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002497 goto err_ena;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002498
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01002499 /* Sync set controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002500 ret = wm_coeff_sync_controls(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002501 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002502 goto err_ena;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002503
Charles Keepax28823eb2016-09-20 13:52:32 +01002504 dsp->booted = true;
2505
Mark Brown2159ad932012-10-11 11:54:02 +09002506 /* Start the core running */
2507 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2508 ADSP1_CORE_ENA | ADSP1_START,
2509 ADSP1_CORE_ENA | ADSP1_START);
Charles Keepax28823eb2016-09-20 13:52:32 +01002510
2511 dsp->running = true;
Mark Brown2159ad932012-10-11 11:54:02 +09002512 break;
2513
2514 case SND_SOC_DAPM_PRE_PMD:
Charles Keepax28823eb2016-09-20 13:52:32 +01002515 dsp->running = false;
2516 dsp->booted = false;
2517
Mark Brown2159ad932012-10-11 11:54:02 +09002518 /* Halt the core */
2519 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2520 ADSP1_CORE_ENA | ADSP1_START, 0);
2521
2522 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
2523 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
2524
2525 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2526 ADSP1_SYS_ENA, 0);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002527
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002528 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002529 ctl->enabled = 0;
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00002530
Richard Fitzgerald56574d52016-04-27 14:58:29 +01002531
2532 wm_adsp_free_alg_regions(dsp);
Mark Brown2159ad932012-10-11 11:54:02 +09002533 break;
2534
2535 default:
2536 break;
2537 }
2538
Charles Keepax078e7182015-12-08 16:08:26 +00002539 mutex_unlock(&dsp->pwr_lock);
2540
Mark Brown2159ad932012-10-11 11:54:02 +09002541 return 0;
2542
Charles Keepax078e7182015-12-08 16:08:26 +00002543err_ena:
Mark Brown2159ad932012-10-11 11:54:02 +09002544 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
2545 ADSP1_SYS_ENA, 0);
Charles Keepax078e7182015-12-08 16:08:26 +00002546err_mutex:
2547 mutex_unlock(&dsp->pwr_lock);
2548
Mark Brown2159ad932012-10-11 11:54:02 +09002549 return ret;
2550}
2551EXPORT_SYMBOL_GPL(wm_adsp1_event);
2552
2553static int wm_adsp2_ena(struct wm_adsp *dsp)
2554{
2555 unsigned int val;
2556 int ret, count;
2557
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002558 switch (dsp->rev) {
2559 case 0:
2560 ret = regmap_update_bits_async(dsp->regmap,
2561 dsp->base + ADSP2_CONTROL,
2562 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
2563 if (ret != 0)
2564 return ret;
2565 break;
2566 default:
2567 break;
2568 }
Mark Brown2159ad932012-10-11 11:54:02 +09002569
2570 /* Wait for the RAM to start, should be near instantaneous */
Charles Keepax939fd1e2013-12-18 09:25:49 +00002571 for (count = 0; count < 10; ++count) {
Charles Keepax7d00cd92016-02-19 14:44:43 +00002572 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
Mark Brown2159ad932012-10-11 11:54:02 +09002573 if (ret != 0)
2574 return ret;
Charles Keepax939fd1e2013-12-18 09:25:49 +00002575
2576 if (val & ADSP2_RAM_RDY)
2577 break;
2578
Charles Keepax1fa96f32016-09-26 10:15:22 +01002579 usleep_range(250, 500);
Charles Keepax939fd1e2013-12-18 09:25:49 +00002580 }
Mark Brown2159ad932012-10-11 11:54:02 +09002581
2582 if (!(val & ADSP2_RAM_RDY)) {
2583 adsp_err(dsp, "Failed to start DSP RAM\n");
2584 return -EBUSY;
2585 }
2586
2587 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
Mark Brown2159ad932012-10-11 11:54:02 +09002588
2589 return 0;
2590}
2591
Charles Keepax18b1a902014-01-09 09:06:54 +00002592static void wm_adsp2_boot_work(struct work_struct *work)
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002593{
2594 struct wm_adsp *dsp = container_of(work,
2595 struct wm_adsp,
2596 boot_work);
2597 int ret;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002598
Charles Keepax078e7182015-12-08 16:08:26 +00002599 mutex_lock(&dsp->pwr_lock);
2600
Charles Keepax90d19ba2016-09-26 10:15:23 +01002601 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2602 ADSP2_MEM_ENA, ADSP2_MEM_ENA);
2603 if (ret != 0)
2604 goto err_mutex;
2605
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002606 ret = wm_adsp2_ena(dsp);
2607 if (ret != 0)
Charles Keepaxd589d8b2017-01-24 11:44:01 +00002608 goto err_mem;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002609
2610 ret = wm_adsp_load(dsp);
2611 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002612 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002613
Charles Keepaxb618a1852015-04-13 13:27:53 +01002614 ret = wm_adsp2_setup_algs(dsp);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002615 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002616 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002617
2618 ret = wm_adsp_load_coeff(dsp);
2619 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002620 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002621
2622 /* Initialize caches for enabled and unset controls */
2623 ret = wm_coeff_init_control_caches(dsp);
2624 if (ret != 0)
Charles Keepax078e7182015-12-08 16:08:26 +00002625 goto err_ena;
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002626
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002627 switch (dsp->rev) {
2628 case 0:
2629 /* Turn DSP back off until we are ready to run */
2630 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2631 ADSP2_SYS_ENA, 0);
2632 if (ret != 0)
2633 goto err_ena;
2634 break;
2635 default:
2636 break;
2637 }
Charles Keepax90d19ba2016-09-26 10:15:23 +01002638
Charles Keepaxe7799742017-01-24 11:44:00 +00002639 dsp->booted = true;
2640
Charles Keepax078e7182015-12-08 16:08:26 +00002641 mutex_unlock(&dsp->pwr_lock);
2642
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002643 return;
2644
Charles Keepax078e7182015-12-08 16:08:26 +00002645err_ena:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002646 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2647 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Charles Keepaxd589d8b2017-01-24 11:44:01 +00002648err_mem:
2649 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2650 ADSP2_MEM_ENA, 0);
Charles Keepax078e7182015-12-08 16:08:26 +00002651err_mutex:
2652 mutex_unlock(&dsp->pwr_lock);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002653}
2654
Charles Keepaxd82d7672016-01-21 17:53:02 +00002655static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
2656{
2657 int ret;
2658
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002659 switch (dsp->rev) {
2660 case 0:
2661 ret = regmap_update_bits_async(dsp->regmap,
2662 dsp->base + ADSP2_CLOCKING,
2663 ADSP2_CLK_SEL_MASK,
2664 freq << ADSP2_CLK_SEL_SHIFT);
2665 if (ret) {
2666 adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
2667 return;
2668 }
2669 break;
2670 default:
2671 /* clock is handled by parent codec driver */
2672 break;
2673 }
Charles Keepaxd82d7672016-01-21 17:53:02 +00002674}
2675
Charles Keepaxaf813a62017-01-06 14:24:41 +00002676int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
2677 struct snd_ctl_elem_value *ucontrol)
2678{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002679 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Ajit Pandeyb1470d42018-08-07 18:30:42 +01002680 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
2681 struct soc_mixer_control *mc =
2682 (struct soc_mixer_control *)kcontrol->private_value;
2683 struct wm_adsp *dsp = &dsps[mc->shift - 1];
Charles Keepaxaf813a62017-01-06 14:24:41 +00002684
2685 ucontrol->value.integer.value[0] = dsp->preloaded;
2686
2687 return 0;
2688}
2689EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);
2690
2691int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
2692 struct snd_ctl_elem_value *ucontrol)
2693{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002694 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
Ajit Pandeyb1470d42018-08-07 18:30:42 +01002695 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002696 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002697 struct soc_mixer_control *mc =
2698 (struct soc_mixer_control *)kcontrol->private_value;
Ajit Pandeyb1470d42018-08-07 18:30:42 +01002699 struct wm_adsp *dsp = &dsps[mc->shift - 1];
Charles Keepaxaf813a62017-01-06 14:24:41 +00002700 char preload[32];
2701
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002702 snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002703
2704 dsp->preloaded = ucontrol->value.integer.value[0];
2705
2706 if (ucontrol->value.integer.value[0])
Charles Keepax95a594d2018-04-24 16:53:09 +01002707 snd_soc_component_force_enable_pin(component, preload);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002708 else
Charles Keepax95a594d2018-04-24 16:53:09 +01002709 snd_soc_component_disable_pin(component, preload);
Charles Keepaxaf813a62017-01-06 14:24:41 +00002710
2711 snd_soc_dapm_sync(dapm);
2712
Stuart Henderson868e49a2018-07-19 11:50:37 +01002713 flush_work(&dsp->boot_work);
2714
Charles Keepaxaf813a62017-01-06 14:24:41 +00002715 return 0;
2716}
2717EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
2718
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01002719static void wm_adsp_stop_watchdog(struct wm_adsp *dsp)
2720{
2721 switch (dsp->rev) {
2722 case 0:
2723 case 1:
2724 return;
2725 default:
2726 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
2727 ADSP2_WDT_ENA_MASK, 0);
2728 }
2729}
2730
Charles Keepax12db5ed2014-01-08 17:42:19 +00002731int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
Charles Keepaxd82d7672016-01-21 17:53:02 +00002732 struct snd_kcontrol *kcontrol, int event,
2733 unsigned int freq)
Charles Keepax12db5ed2014-01-08 17:42:19 +00002734{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002735 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2736 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Charles Keepax12db5ed2014-01-08 17:42:19 +00002737 struct wm_adsp *dsp = &dsps[w->shift];
Charles Keepax57a60cc2016-09-26 10:15:24 +01002738 struct wm_coeff_ctl *ctl;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002739
Charles Keepax12db5ed2014-01-08 17:42:19 +00002740 switch (event) {
2741 case SND_SOC_DAPM_PRE_PMU:
Charles Keepaxd82d7672016-01-21 17:53:02 +00002742 wm_adsp2_set_dspclk(dsp, freq);
Charles Keepax12db5ed2014-01-08 17:42:19 +00002743 queue_work(system_unbound_wq, &dsp->boot_work);
2744 break;
Charles Keepax57a60cc2016-09-26 10:15:24 +01002745 case SND_SOC_DAPM_PRE_PMD:
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002746 mutex_lock(&dsp->pwr_lock);
2747
Charles Keepax57a60cc2016-09-26 10:15:24 +01002748 wm_adsp_debugfs_clear(dsp);
2749
2750 dsp->fw_id = 0;
2751 dsp->fw_id_version = 0;
2752
2753 dsp->booted = false;
2754
2755 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2756 ADSP2_MEM_ENA, 0);
2757
2758 list_for_each_entry(ctl, &dsp->ctl_list, list)
2759 ctl->enabled = 0;
2760
2761 wm_adsp_free_alg_regions(dsp);
2762
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002763 mutex_unlock(&dsp->pwr_lock);
2764
Charles Keepax57a60cc2016-09-26 10:15:24 +01002765 adsp_dbg(dsp, "Shutdown complete\n");
2766 break;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002767 default:
2768 break;
Charles Keepaxcab272582014-04-17 13:42:54 +01002769 }
Charles Keepax12db5ed2014-01-08 17:42:19 +00002770
2771 return 0;
2772}
2773EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
2774
Mark Brown2159ad932012-10-11 11:54:02 +09002775int wm_adsp2_event(struct snd_soc_dapm_widget *w,
2776 struct snd_kcontrol *kcontrol, int event)
2777{
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002778 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2779 struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
Mark Brown2159ad932012-10-11 11:54:02 +09002780 struct wm_adsp *dsp = &dsps[w->shift];
2781 int ret;
2782
2783 switch (event) {
2784 case SND_SOC_DAPM_POST_PMU:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002785 flush_work(&dsp->boot_work);
Mark Browndd49e2c2012-12-02 21:50:46 +09002786
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002787 mutex_lock(&dsp->pwr_lock);
2788
2789 if (!dsp->booted) {
2790 ret = -EIO;
2791 goto err;
2792 }
Mark Browndd49e2c2012-12-02 21:50:46 +09002793
Charles Keepax90d19ba2016-09-26 10:15:23 +01002794 ret = wm_adsp2_ena(dsp);
2795 if (ret != 0)
2796 goto err;
2797
Charles Keepaxcef45772016-09-20 13:52:33 +01002798 /* Sync set controls */
2799 ret = wm_coeff_sync_controls(dsp);
2800 if (ret != 0)
2801 goto err;
2802
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01002803 wm_adsp2_lock(dsp, dsp->lock_regions);
2804
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002805 ret = regmap_update_bits(dsp->regmap,
2806 dsp->base + ADSP2_CONTROL,
Charles Keepax00e4c3b2014-11-18 16:25:27 +00002807 ADSP2_CORE_ENA | ADSP2_START,
2808 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad932012-10-11 11:54:02 +09002809 if (ret != 0)
2810 goto err;
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002811
Charles Keepax48c2c992016-11-22 15:38:34 +00002812 if (wm_adsp_fw[dsp->fw].num_caps != 0) {
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002813 ret = wm_adsp_buffer_init(dsp);
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002814 if (ret < 0)
Charles Keepax48c2c992016-11-22 15:38:34 +00002815 goto err;
Charles Keepax48c2c992016-11-22 15:38:34 +00002816 }
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002817
Charles Keepaxe7799742017-01-24 11:44:00 +00002818 dsp->running = true;
2819
Charles Keepax612047f2016-03-28 14:29:22 +01002820 mutex_unlock(&dsp->pwr_lock);
2821
Mark Brown2159ad932012-10-11 11:54:02 +09002822 break;
2823
2824 case SND_SOC_DAPM_PRE_PMD:
Richard Fitzgeraldf4f0c4c2016-11-09 17:14:17 +00002825 /* Tell the firmware to cleanup */
2826 wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
2827
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01002828 wm_adsp_stop_watchdog(dsp);
2829
Richard Fitzgerald10337b02015-05-29 10:23:07 +01002830 /* Log firmware state, it can be useful for analysis */
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002831 switch (dsp->rev) {
2832 case 0:
2833 wm_adsp2_show_fw_status(dsp);
2834 break;
2835 default:
2836 wm_adsp2v2_show_fw_status(dsp);
2837 break;
2838 }
Richard Fitzgerald10337b02015-05-29 10:23:07 +01002839
Charles Keepax078e7182015-12-08 16:08:26 +00002840 mutex_lock(&dsp->pwr_lock);
2841
Mark Brown1023dbd2013-01-11 22:58:28 +00002842 dsp->running = false;
2843
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002844 regmap_update_bits(dsp->regmap,
2845 dsp->base + ADSP2_CONTROL,
Charles Keepax57a60cc2016-09-26 10:15:24 +01002846 ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00002847
Mark Brown2d30b572013-01-28 20:18:17 +08002848 /* Make sure DMAs are quiesced */
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002849 switch (dsp->rev) {
2850 case 0:
2851 regmap_write(dsp->regmap,
2852 dsp->base + ADSP2_RDMA_CONFIG_1, 0);
2853 regmap_write(dsp->regmap,
2854 dsp->base + ADSP2_WDMA_CONFIG_1, 0);
2855 regmap_write(dsp->regmap,
2856 dsp->base + ADSP2_WDMA_CONFIG_2, 0);
Simon Trimmer6facd2d2016-06-22 15:31:03 +01002857
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002858 regmap_update_bits(dsp->regmap,
2859 dsp->base + ADSP2_CONTROL,
2860 ADSP2_SYS_ENA, 0);
2861 break;
2862 default:
2863 regmap_write(dsp->regmap,
2864 dsp->base + ADSP2_RDMA_CONFIG_1, 0);
2865 regmap_write(dsp->regmap,
2866 dsp->base + ADSP2_WDMA_CONFIG_1, 0);
2867 regmap_write(dsp->regmap,
2868 dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
2869 break;
2870 }
Mark Brown2d30b572013-01-28 20:18:17 +08002871
Charles Keepax2cd19bd2015-12-15 11:29:46 +00002872 if (wm_adsp_fw[dsp->fw].num_caps != 0)
2873 wm_adsp_buffer_free(dsp);
2874
Charles Keepax078e7182015-12-08 16:08:26 +00002875 mutex_unlock(&dsp->pwr_lock);
2876
Charles Keepax57a60cc2016-09-26 10:15:24 +01002877 adsp_dbg(dsp, "Execution stopped\n");
Mark Brown2159ad932012-10-11 11:54:02 +09002878 break;
2879
2880 default:
2881 break;
2882 }
2883
2884 return 0;
2885err:
2886 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00002887 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Charles Keepaxbb24ee42017-01-24 11:43:59 +00002888 mutex_unlock(&dsp->pwr_lock);
Mark Brown2159ad932012-10-11 11:54:02 +09002889 return ret;
2890}
2891EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00002892
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002893int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002894{
Charles Keepaxaf813a62017-01-06 14:24:41 +00002895 char preload[32];
2896
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002897 snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name);
Charles Keepax95a594d2018-04-24 16:53:09 +01002898 snd_soc_component_disable_pin(component, preload);
Richard Fitzgerald685f51a2016-11-22 16:58:57 +00002899
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002900 wm_adsp2_init_debugfs(dsp, component);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002901
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002902 dsp->component = component;
Charles Keepaxaf813a62017-01-06 14:24:41 +00002903
Richard Fitzgerald0a047f02018-08-08 17:13:38 +01002904 return 0;
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002905}
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002906EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002907
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002908int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002909{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002910 wm_adsp2_cleanup_debugfs(dsp);
2911
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002912 return 0;
2913}
Kuninori Morimoto0fe1daa2018-02-13 02:03:12 +00002914EXPORT_SYMBOL_GPL(wm_adsp2_component_remove);
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002915
Richard Fitzgerald81ac58b2015-06-02 11:53:34 +01002916int wm_adsp2_init(struct wm_adsp *dsp)
Mark Brown973838a2012-11-28 17:20:32 +00002917{
2918 int ret;
2919
Richard Fitzgerald605391d2018-08-08 17:13:39 +01002920 ret = wm_adsp_create_name(dsp);
2921 if (ret)
2922 return ret;
2923
Richard Fitzgeralde1ea1872017-04-05 11:07:59 +01002924 switch (dsp->rev) {
2925 case 0:
2926 /*
2927 * Disable the DSP memory by default when in reset for a small
2928 * power saving.
2929 */
2930 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2931 ADSP2_MEM_ENA, 0);
2932 if (ret) {
2933 adsp_err(dsp,
2934 "Failed to clear memory retention: %d\n", ret);
2935 return ret;
2936 }
2937 break;
2938 default:
2939 break;
Mark Brown10a2b662012-12-02 21:37:00 +09002940 }
2941
Charles Keepax3809f002015-04-13 13:27:54 +01002942 INIT_LIST_HEAD(&dsp->alg_regions);
2943 INIT_LIST_HEAD(&dsp->ctl_list);
2944 INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002945
Charles Keepax078e7182015-12-08 16:08:26 +00002946 mutex_init(&dsp->pwr_lock);
2947
Mark Brown973838a2012-11-28 17:20:32 +00002948 return 0;
2949}
2950EXPORT_SYMBOL_GPL(wm_adsp2_init);
Praveen Diwakar0a37c6ef2014-07-04 11:17:41 +05302951
Richard Fitzgerald66225e92016-04-27 14:58:27 +01002952void wm_adsp2_remove(struct wm_adsp *dsp)
2953{
2954 struct wm_coeff_ctl *ctl;
2955
2956 while (!list_empty(&dsp->ctl_list)) {
2957 ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
2958 list);
2959 list_del(&ctl->list);
2960 wm_adsp_free_ctl_blk(ctl);
2961 }
2962}
2963EXPORT_SYMBOL_GPL(wm_adsp2_remove);
2964
Charles Keepaxedd71352016-05-04 17:11:55 +01002965static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
2966{
2967 return compr->buf != NULL;
2968}
2969
2970static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
2971{
2972 /*
2973 * Note this will be more complex once each DSP can support multiple
2974 * streams
2975 */
2976 if (!compr->dsp->buffer)
2977 return -EINVAL;
2978
2979 compr->buf = compr->dsp->buffer;
Charles Keepax721be3b2016-05-04 17:11:56 +01002980 compr->buf->compr = compr;
Charles Keepaxedd71352016-05-04 17:11:55 +01002981
2982 return 0;
2983}
2984
Charles Keepax721be3b2016-05-04 17:11:56 +01002985static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
2986{
2987 if (!compr)
2988 return;
2989
2990 /* Wake the poll so it can see buffer is no longer attached */
2991 if (compr->stream)
2992 snd_compr_fragment_elapsed(compr->stream);
2993
2994 if (wm_adsp_compr_attached(compr)) {
2995 compr->buf->compr = NULL;
2996 compr->buf = NULL;
2997 }
2998}
2999
Charles Keepax406abc92015-12-15 11:29:45 +00003000int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
3001{
3002 struct wm_adsp_compr *compr;
3003 int ret = 0;
3004
3005 mutex_lock(&dsp->pwr_lock);
3006
3007 if (wm_adsp_fw[dsp->fw].num_caps == 0) {
3008 adsp_err(dsp, "Firmware does not support compressed API\n");
3009 ret = -ENXIO;
3010 goto out;
3011 }
3012
3013 if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
3014 adsp_err(dsp, "Firmware does not support stream direction\n");
3015 ret = -EINVAL;
3016 goto out;
3017 }
3018
Charles Keepax95fe9592015-12-15 11:29:47 +00003019 if (dsp->compr) {
3020 /* It is expect this limitation will be removed in future */
3021 adsp_err(dsp, "Only a single stream supported per DSP\n");
3022 ret = -EBUSY;
3023 goto out;
3024 }
3025
Charles Keepax406abc92015-12-15 11:29:45 +00003026 compr = kzalloc(sizeof(*compr), GFP_KERNEL);
3027 if (!compr) {
3028 ret = -ENOMEM;
3029 goto out;
3030 }
3031
3032 compr->dsp = dsp;
3033 compr->stream = stream;
3034
3035 dsp->compr = compr;
3036
3037 stream->runtime->private_data = compr;
3038
3039out:
3040 mutex_unlock(&dsp->pwr_lock);
3041
3042 return ret;
3043}
3044EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
3045
3046int wm_adsp_compr_free(struct snd_compr_stream *stream)
3047{
3048 struct wm_adsp_compr *compr = stream->runtime->private_data;
3049 struct wm_adsp *dsp = compr->dsp;
3050
3051 mutex_lock(&dsp->pwr_lock);
3052
Charles Keepax721be3b2016-05-04 17:11:56 +01003053 wm_adsp_compr_detach(compr);
Charles Keepax406abc92015-12-15 11:29:45 +00003054 dsp->compr = NULL;
3055
Charles Keepax83a40ce2016-01-06 12:33:19 +00003056 kfree(compr->raw_buf);
Charles Keepax406abc92015-12-15 11:29:45 +00003057 kfree(compr);
3058
3059 mutex_unlock(&dsp->pwr_lock);
3060
3061 return 0;
3062}
3063EXPORT_SYMBOL_GPL(wm_adsp_compr_free);
3064
3065static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
3066 struct snd_compr_params *params)
3067{
3068 struct wm_adsp_compr *compr = stream->runtime->private_data;
3069 struct wm_adsp *dsp = compr->dsp;
3070 const struct wm_adsp_fw_caps *caps;
3071 const struct snd_codec_desc *desc;
3072 int i, j;
3073
3074 if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||
3075 params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
3076 params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
3077 params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
3078 params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) {
3079 adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n",
3080 params->buffer.fragment_size,
3081 params->buffer.fragments);
3082
3083 return -EINVAL;
3084 }
3085
3086 for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
3087 caps = &wm_adsp_fw[dsp->fw].caps[i];
3088 desc = &caps->desc;
3089
3090 if (caps->id != params->codec.id)
3091 continue;
3092
3093 if (stream->direction == SND_COMPRESS_PLAYBACK) {
3094 if (desc->max_ch < params->codec.ch_out)
3095 continue;
3096 } else {
3097 if (desc->max_ch < params->codec.ch_in)
3098 continue;
3099 }
3100
3101 if (!(desc->formats & (1 << params->codec.format)))
3102 continue;
3103
3104 for (j = 0; j < desc->num_sample_rates; ++j)
3105 if (desc->sample_rates[j] == params->codec.sample_rate)
3106 return 0;
3107 }
3108
3109 adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
3110 params->codec.id, params->codec.ch_in, params->codec.ch_out,
3111 params->codec.sample_rate, params->codec.format);
3112 return -EINVAL;
3113}
3114
Charles Keepax565ace42016-01-06 12:33:18 +00003115static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
3116{
3117 return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
3118}
3119
Charles Keepax406abc92015-12-15 11:29:45 +00003120int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
3121 struct snd_compr_params *params)
3122{
3123 struct wm_adsp_compr *compr = stream->runtime->private_data;
Charles Keepax83a40ce2016-01-06 12:33:19 +00003124 unsigned int size;
Charles Keepax406abc92015-12-15 11:29:45 +00003125 int ret;
3126
3127 ret = wm_adsp_compr_check_params(stream, params);
3128 if (ret)
3129 return ret;
3130
3131 compr->size = params->buffer;
3132
3133 adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n",
3134 compr->size.fragment_size, compr->size.fragments);
3135
Charles Keepax83a40ce2016-01-06 12:33:19 +00003136 size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
3137 compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
3138 if (!compr->raw_buf)
3139 return -ENOMEM;
3140
Charles Keepaxda2b3352016-02-02 16:41:36 +00003141 compr->sample_rate = params->codec.sample_rate;
3142
Charles Keepax406abc92015-12-15 11:29:45 +00003143 return 0;
3144}
3145EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
3146
3147int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
3148 struct snd_compr_caps *caps)
3149{
3150 struct wm_adsp_compr *compr = stream->runtime->private_data;
3151 int fw = compr->dsp->fw;
3152 int i;
3153
3154 if (wm_adsp_fw[fw].caps) {
3155 for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
3156 caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
3157
3158 caps->num_codecs = i;
3159 caps->direction = wm_adsp_fw[fw].compr_direction;
3160
3161 caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
3162 caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
3163 caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;
3164 caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;
3165 }
3166
3167 return 0;
3168}
3169EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
3170
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003171static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
3172 unsigned int mem_addr,
3173 unsigned int num_words, u32 *data)
3174{
3175 struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
3176 unsigned int i, reg;
3177 int ret;
3178
3179 if (!mem)
3180 return -EINVAL;
3181
3182 reg = wm_adsp_region_to_reg(mem, mem_addr);
3183
3184 ret = regmap_raw_read(dsp->regmap, reg, data,
3185 sizeof(*data) * num_words);
3186 if (ret < 0)
3187 return ret;
3188
3189 for (i = 0; i < num_words; ++i)
3190 data[i] = be32_to_cpu(data[i]) & 0x00ffffffu;
3191
3192 return 0;
3193}
3194
3195static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type,
3196 unsigned int mem_addr, u32 *data)
3197{
3198 return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data);
3199}
3200
3201static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
3202 unsigned int mem_addr, u32 data)
3203{
3204 struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
3205 unsigned int reg;
3206
3207 if (!mem)
3208 return -EINVAL;
3209
3210 reg = wm_adsp_region_to_reg(mem, mem_addr);
3211
3212 data = cpu_to_be32(data & 0x00ffffffu);
3213
3214 return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data));
3215}
3216
3217static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
3218 unsigned int field_offset, u32 *data)
3219{
3220 return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM,
3221 buf->host_buf_ptr + field_offset, data);
3222}
3223
3224static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
3225 unsigned int field_offset, u32 data)
3226{
3227 return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM,
3228 buf->host_buf_ptr + field_offset, data);
3229}
3230
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003231static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf)
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003232{
3233 struct wm_adsp_alg_region *alg_region;
3234 struct wm_adsp *dsp = buf->dsp;
3235 u32 xmalg, addr, magic;
3236 int i, ret;
3237
3238 alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
3239 xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
3240
3241 addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
3242 ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
3243 if (ret < 0)
3244 return ret;
3245
3246 if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
3247 return -EINVAL;
3248
3249 addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
3250 for (i = 0; i < 5; ++i) {
3251 ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
3252 &buf->host_buf_ptr);
3253 if (ret < 0)
3254 return ret;
3255
3256 if (buf->host_buf_ptr)
3257 break;
3258
3259 usleep_range(1000, 2000);
3260 }
3261
3262 if (!buf->host_buf_ptr)
3263 return -EIO;
3264
3265 adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
3266
3267 return 0;
3268}
3269
Richard Fitzgeraldd52ed4b2018-07-19 11:50:39 +01003270static struct wm_coeff_ctl *
3271wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf)
3272{
3273 struct wm_adsp *dsp = buf->dsp;
3274 struct wm_coeff_ctl *ctl;
3275
3276 list_for_each_entry(ctl, &dsp->ctl_list, list) {
3277 if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
3278 continue;
3279
3280 if (!ctl->enabled)
3281 continue;
3282
3283 return ctl;
3284 }
3285
3286 return NULL;
3287}
3288
3289static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
3290{
3291 struct wm_adsp *dsp = buf->dsp;
3292 struct wm_coeff_ctl *ctl;
3293 unsigned int reg;
3294 u32 val;
3295 int i, ret;
3296
3297 ctl = wm_adsp_find_host_buffer_ctrl(buf);
3298 if (!ctl)
3299 return wm_adsp_legacy_host_buf_addr(buf);
3300
3301 ret = wm_coeff_base_reg(ctl, &reg);
3302 if (ret)
3303 return ret;
3304
3305 for (i = 0; i < 5; ++i) {
3306 ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
3307 if (ret < 0)
3308 return ret;
3309
3310 if (val)
3311 break;
3312
3313 usleep_range(1000, 2000);
3314 }
3315
3316 if (!val)
3317 return -EIO;
3318
3319 buf->host_buf_ptr = be32_to_cpu(val);
3320 adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
3321
3322 return 0;
3323}
3324
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003325static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
3326{
3327 const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
3328 struct wm_adsp_buffer_region *region;
3329 u32 offset = 0;
3330 int i, ret;
3331
3332 for (i = 0; i < caps->num_regions; ++i) {
3333 region = &buf->regions[i];
3334
3335 region->offset = offset;
3336 region->mem_type = caps->region_defs[i].mem_type;
3337
3338 ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
3339 &region->base_addr);
3340 if (ret < 0)
3341 return ret;
3342
3343 ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
3344 &offset);
3345 if (ret < 0)
3346 return ret;
3347
3348 region->cumulative_size = offset;
3349
3350 adsp_dbg(buf->dsp,
Richard Fitzgeralde3a360b2018-10-19 13:25:16 +01003351 "region=%d type=%d base=%08x off=%08x size=%08x\n",
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003352 i, region->mem_type, region->base_addr,
3353 region->offset, region->cumulative_size);
3354 }
3355
3356 return 0;
3357}
3358
Charles Keepax61fc0602018-02-26 10:49:47 +00003359static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf)
3360{
3361 buf->irq_count = 0xFFFFFFFF;
3362 buf->read_index = -1;
3363 buf->avail = 0;
3364}
3365
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003366static int wm_adsp_buffer_init(struct wm_adsp *dsp)
3367{
3368 struct wm_adsp_compr_buf *buf;
3369 int ret;
3370
3371 buf = kzalloc(sizeof(*buf), GFP_KERNEL);
3372 if (!buf)
3373 return -ENOMEM;
3374
3375 buf->dsp = dsp;
Charles Keepax61fc0602018-02-26 10:49:47 +00003376
3377 wm_adsp_buffer_clear(buf);
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003378
3379 ret = wm_adsp_buffer_locate(buf);
3380 if (ret < 0) {
3381 adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret);
3382 goto err_buffer;
3383 }
3384
3385 buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions,
3386 sizeof(*buf->regions), GFP_KERNEL);
3387 if (!buf->regions) {
3388 ret = -ENOMEM;
3389 goto err_buffer;
3390 }
3391
3392 ret = wm_adsp_buffer_populate(buf);
3393 if (ret < 0) {
3394 adsp_err(dsp, "Failed to populate host buffer: %d\n", ret);
3395 goto err_regions;
3396 }
3397
3398 dsp->buffer = buf;
3399
3400 return 0;
3401
3402err_regions:
3403 kfree(buf->regions);
3404err_buffer:
3405 kfree(buf);
3406 return ret;
3407}
3408
3409static int wm_adsp_buffer_free(struct wm_adsp *dsp)
3410{
3411 if (dsp->buffer) {
Charles Keepax721be3b2016-05-04 17:11:56 +01003412 wm_adsp_compr_detach(dsp->buffer->compr);
3413
Charles Keepax2cd19bd2015-12-15 11:29:46 +00003414 kfree(dsp->buffer->regions);
3415 kfree(dsp->buffer);
3416
3417 dsp->buffer = NULL;
3418 }
3419
3420 return 0;
3421}
3422
Charles Keepax95fe9592015-12-15 11:29:47 +00003423int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
3424{
3425 struct wm_adsp_compr *compr = stream->runtime->private_data;
3426 struct wm_adsp *dsp = compr->dsp;
3427 int ret = 0;
3428
3429 adsp_dbg(dsp, "Trigger: %d\n", cmd);
3430
3431 mutex_lock(&dsp->pwr_lock);
3432
3433 switch (cmd) {
3434 case SNDRV_PCM_TRIGGER_START:
Charles Keepax61fc0602018-02-26 10:49:47 +00003435 if (!wm_adsp_compr_attached(compr)) {
3436 ret = wm_adsp_compr_attach(compr);
3437 if (ret < 0) {
3438 adsp_err(dsp, "Failed to link buffer and stream: %d\n",
3439 ret);
3440 break;
3441 }
Charles Keepax95fe9592015-12-15 11:29:47 +00003442 }
Charles Keepax565ace42016-01-06 12:33:18 +00003443
Charles Keepax61fc0602018-02-26 10:49:47 +00003444 wm_adsp_buffer_clear(compr->buf);
3445
Charles Keepax565ace42016-01-06 12:33:18 +00003446 /* Trigger the IRQ at one fragment of data */
3447 ret = wm_adsp_buffer_write(compr->buf,
3448 HOST_BUFFER_FIELD(high_water_mark),
3449 wm_adsp_compr_frag_words(compr));
3450 if (ret < 0) {
3451 adsp_err(dsp, "Failed to set high water mark: %d\n",
3452 ret);
3453 break;
3454 }
Charles Keepax95fe9592015-12-15 11:29:47 +00003455 break;
3456 case SNDRV_PCM_TRIGGER_STOP:
3457 break;
3458 default:
3459 ret = -EINVAL;
3460 break;
3461 }
3462
3463 mutex_unlock(&dsp->pwr_lock);
3464
3465 return ret;
3466}
3467EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
3468
Charles Keepax565ace42016-01-06 12:33:18 +00003469static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
3470{
3471 int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
3472
3473 return buf->regions[last_region].cumulative_size;
3474}
3475
3476static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
3477{
3478 u32 next_read_index, next_write_index;
3479 int write_index, read_index, avail;
3480 int ret;
3481
3482 /* Only sync read index if we haven't already read a valid index */
3483 if (buf->read_index < 0) {
3484 ret = wm_adsp_buffer_read(buf,
3485 HOST_BUFFER_FIELD(next_read_index),
3486 &next_read_index);
3487 if (ret < 0)
3488 return ret;
3489
3490 read_index = sign_extend32(next_read_index, 23);
3491
3492 if (read_index < 0) {
3493 adsp_dbg(buf->dsp, "Avail check on unstarted stream\n");
3494 return 0;
3495 }
3496
3497 buf->read_index = read_index;
3498 }
3499
3500 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
3501 &next_write_index);
3502 if (ret < 0)
3503 return ret;
3504
3505 write_index = sign_extend32(next_write_index, 23);
3506
3507 avail = write_index - buf->read_index;
3508 if (avail < 0)
3509 avail += wm_adsp_buffer_size(buf);
3510
3511 adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
Charles Keepax33d740e2016-03-28 14:29:21 +01003512 buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
Charles Keepax565ace42016-01-06 12:33:18 +00003513
3514 buf->avail = avail;
3515
3516 return 0;
3517}
3518
Charles Keepax9771b182016-04-06 11:21:53 +01003519static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
3520{
3521 int ret;
3522
3523 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
3524 if (ret < 0) {
3525 adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret);
3526 return ret;
3527 }
3528 if (buf->error != 0) {
3529 adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error);
3530 return -EIO;
3531 }
3532
3533 return 0;
3534}
3535
Charles Keepax565ace42016-01-06 12:33:18 +00003536int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
3537{
Charles Keepax612047f2016-03-28 14:29:22 +01003538 struct wm_adsp_compr_buf *buf;
3539 struct wm_adsp_compr *compr;
Charles Keepax565ace42016-01-06 12:33:18 +00003540 int ret = 0;
3541
3542 mutex_lock(&dsp->pwr_lock);
3543
Charles Keepax612047f2016-03-28 14:29:22 +01003544 buf = dsp->buffer;
3545 compr = dsp->compr;
3546
Charles Keepax565ace42016-01-06 12:33:18 +00003547 if (!buf) {
Charles Keepax565ace42016-01-06 12:33:18 +00003548 ret = -ENODEV;
3549 goto out;
3550 }
3551
3552 adsp_dbg(dsp, "Handling buffer IRQ\n");
3553
Charles Keepax9771b182016-04-06 11:21:53 +01003554 ret = wm_adsp_buffer_get_error(buf);
3555 if (ret < 0)
Charles Keepax58476092016-04-06 11:21:54 +01003556 goto out_notify; /* Wake poll to report error */
Charles Keepax565ace42016-01-06 12:33:18 +00003557
3558 ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
3559 &buf->irq_count);
3560 if (ret < 0) {
3561 adsp_err(dsp, "Failed to get irq_count: %d\n", ret);
3562 goto out;
3563 }
3564
3565 ret = wm_adsp_buffer_update_avail(buf);
3566 if (ret < 0) {
3567 adsp_err(dsp, "Error reading avail: %d\n", ret);
3568 goto out;
3569 }
3570
Charles Keepax20b7f7c2016-05-13 16:45:17 +01003571 if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
3572 ret = WM_ADSP_COMPR_VOICE_TRIGGER;
3573
Charles Keepax58476092016-04-06 11:21:54 +01003574out_notify:
Charles Keepaxc7dae7c2016-02-19 14:44:41 +00003575 if (compr && compr->stream)
Charles Keepax83a40ce2016-01-06 12:33:19 +00003576 snd_compr_fragment_elapsed(compr->stream);
3577
Charles Keepax565ace42016-01-06 12:33:18 +00003578out:
3579 mutex_unlock(&dsp->pwr_lock);
3580
3581 return ret;
3582}
3583EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
3584
3585static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
3586{
3587 if (buf->irq_count & 0x01)
3588 return 0;
3589
3590 adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n",
3591 buf->irq_count);
3592
3593 buf->irq_count |= 0x01;
3594
3595 return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
3596 buf->irq_count);
3597}
3598
3599int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
3600 struct snd_compr_tstamp *tstamp)
3601{
3602 struct wm_adsp_compr *compr = stream->runtime->private_data;
Charles Keepax565ace42016-01-06 12:33:18 +00003603 struct wm_adsp *dsp = compr->dsp;
Charles Keepax612047f2016-03-28 14:29:22 +01003604 struct wm_adsp_compr_buf *buf;
Charles Keepax565ace42016-01-06 12:33:18 +00003605 int ret = 0;
3606
3607 adsp_dbg(dsp, "Pointer request\n");
3608
3609 mutex_lock(&dsp->pwr_lock);
3610
Charles Keepax612047f2016-03-28 14:29:22 +01003611 buf = compr->buf;
3612
Charles Keepax28ee3d72016-06-13 14:17:12 +01003613 if (!compr->buf || compr->buf->error) {
Charles Keepax8d280662016-06-13 14:17:11 +01003614 snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
Charles Keepax565ace42016-01-06 12:33:18 +00003615 ret = -EIO;
3616 goto out;
3617 }
3618
3619 if (buf->avail < wm_adsp_compr_frag_words(compr)) {
3620 ret = wm_adsp_buffer_update_avail(buf);
3621 if (ret < 0) {
3622 adsp_err(dsp, "Error reading avail: %d\n", ret);
3623 goto out;
3624 }
3625
3626 /*
3627 * If we really have less than 1 fragment available tell the
3628 * DSP to inform us once a whole fragment is available.
3629 */
3630 if (buf->avail < wm_adsp_compr_frag_words(compr)) {
Charles Keepax58476092016-04-06 11:21:54 +01003631 ret = wm_adsp_buffer_get_error(buf);
Charles Keepax8d280662016-06-13 14:17:11 +01003632 if (ret < 0) {
3633 if (compr->buf->error)
3634 snd_compr_stop_error(stream,
3635 SNDRV_PCM_STATE_XRUN);
Charles Keepax58476092016-04-06 11:21:54 +01003636 goto out;
Charles Keepax8d280662016-06-13 14:17:11 +01003637 }
Charles Keepax58476092016-04-06 11:21:54 +01003638
Charles Keepax565ace42016-01-06 12:33:18 +00003639 ret = wm_adsp_buffer_reenable_irq(buf);
3640 if (ret < 0) {
3641 adsp_err(dsp,
3642 "Failed to re-enable buffer IRQ: %d\n",
3643 ret);
3644 goto out;
3645 }
3646 }
3647 }
3648
3649 tstamp->copied_total = compr->copied_total;
3650 tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
Charles Keepaxda2b3352016-02-02 16:41:36 +00003651 tstamp->sampling_rate = compr->sample_rate;
Charles Keepax565ace42016-01-06 12:33:18 +00003652
3653out:
3654 mutex_unlock(&dsp->pwr_lock);
3655
3656 return ret;
3657}
3658EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
3659
Charles Keepax83a40ce2016-01-06 12:33:19 +00003660static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
3661{
3662 struct wm_adsp_compr_buf *buf = compr->buf;
3663 u8 *pack_in = (u8 *)compr->raw_buf;
3664 u8 *pack_out = (u8 *)compr->raw_buf;
3665 unsigned int adsp_addr;
3666 int mem_type, nwords, max_read;
3667 int i, j, ret;
3668
3669 /* Calculate read parameters */
3670 for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
3671 if (buf->read_index < buf->regions[i].cumulative_size)
3672 break;
3673
3674 if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
3675 return -EINVAL;
3676
3677 mem_type = buf->regions[i].mem_type;
3678 adsp_addr = buf->regions[i].base_addr +
3679 (buf->read_index - buf->regions[i].offset);
3680
3681 max_read = wm_adsp_compr_frag_words(compr);
3682 nwords = buf->regions[i].cumulative_size - buf->read_index;
3683
3684 if (nwords > target)
3685 nwords = target;
3686 if (nwords > buf->avail)
3687 nwords = buf->avail;
3688 if (nwords > max_read)
3689 nwords = max_read;
3690 if (!nwords)
3691 return 0;
3692
3693 /* Read data from DSP */
3694 ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr,
3695 nwords, compr->raw_buf);
3696 if (ret < 0)
3697 return ret;
3698
3699 /* Remove the padding bytes from the data read from the DSP */
3700 for (i = 0; i < nwords; i++) {
3701 for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++)
3702 *pack_out++ = *pack_in++;
3703
3704 pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE;
3705 }
3706
3707 /* update read index to account for words read */
3708 buf->read_index += nwords;
3709 if (buf->read_index == wm_adsp_buffer_size(buf))
3710 buf->read_index = 0;
3711
3712 ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
3713 buf->read_index);
3714 if (ret < 0)
3715 return ret;
3716
3717 /* update avail to account for words read */
3718 buf->avail -= nwords;
3719
3720 return nwords;
3721}
3722
3723static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
3724 char __user *buf, size_t count)
3725{
3726 struct wm_adsp *dsp = compr->dsp;
3727 int ntotal = 0;
3728 int nwords, nbytes;
3729
3730 adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
3731
Charles Keepax28ee3d72016-06-13 14:17:12 +01003732 if (!compr->buf || compr->buf->error) {
Charles Keepax8d280662016-06-13 14:17:11 +01003733 snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
Charles Keepax83a40ce2016-01-06 12:33:19 +00003734 return -EIO;
Charles Keepax8d280662016-06-13 14:17:11 +01003735 }
Charles Keepax83a40ce2016-01-06 12:33:19 +00003736
3737 count /= WM_ADSP_DATA_WORD_SIZE;
3738
3739 do {
3740 nwords = wm_adsp_buffer_capture_block(compr, count);
3741 if (nwords < 0) {
3742 adsp_err(dsp, "Failed to capture block: %d\n", nwords);
3743 return nwords;
3744 }
3745
3746 nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
3747
3748 adsp_dbg(dsp, "Read %d bytes\n", nbytes);
3749
3750 if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
3751 adsp_err(dsp, "Failed to copy data to user: %d, %d\n",
3752 ntotal, nbytes);
3753 return -EFAULT;
3754 }
3755
3756 count -= nwords;
3757 ntotal += nbytes;
3758 } while (nwords > 0 && count > 0);
3759
3760 compr->copied_total += ntotal;
3761
3762 return ntotal;
3763}
3764
3765int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
3766 size_t count)
3767{
3768 struct wm_adsp_compr *compr = stream->runtime->private_data;
3769 struct wm_adsp *dsp = compr->dsp;
3770 int ret;
3771
3772 mutex_lock(&dsp->pwr_lock);
3773
3774 if (stream->direction == SND_COMPRESS_CAPTURE)
3775 ret = wm_adsp_compr_read(compr, buf, count);
3776 else
3777 ret = -ENOTSUPP;
3778
3779 mutex_unlock(&dsp->pwr_lock);
3780
3781 return ret;
3782}
3783EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
3784
Mayuresh Kulkarni51a2c942017-04-05 11:08:00 +01003785int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
3786{
3787 struct regmap *regmap = dsp->regmap;
3788 unsigned int code0, code1, lock_reg;
3789
3790 if (!(lock_regions & WM_ADSP2_REGION_ALL))
3791 return 0;
3792
3793 lock_regions &= WM_ADSP2_REGION_ALL;
3794 lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
3795
3796 while (lock_regions) {
3797 code0 = code1 = 0;
3798 if (lock_regions & BIT(0)) {
3799 code0 = ADSP2_LOCK_CODE_0;
3800 code1 = ADSP2_LOCK_CODE_1;
3801 }
3802 if (lock_regions & BIT(1)) {
3803 code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
3804 code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
3805 }
3806 regmap_write(regmap, lock_reg, code0);
3807 regmap_write(regmap, lock_reg, code1);
3808 lock_regions >>= 2;
3809 lock_reg += 2;
3810 }
3811
3812 return 0;
3813}
3814EXPORT_SYMBOL_GPL(wm_adsp2_lock);
3815
3816irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
3817{
3818 unsigned int val;
3819 struct regmap *regmap = dsp->regmap;
3820 int ret = 0;
3821
3822 ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);
3823 if (ret) {
3824 adsp_err(dsp,
3825 "Failed to read Region Lock Ctrl register: %d\n", ret);
3826 return IRQ_HANDLED;
3827 }
3828
3829 if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {
3830 adsp_err(dsp, "watchdog timeout error\n");
3831 wm_adsp_stop_watchdog(dsp);
3832 }
3833
3834 if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {
3835 if (val & ADSP2_SLAVE_ERR_MASK)
3836 adsp_err(dsp, "bus error: slave error\n");
3837 else
3838 adsp_err(dsp, "bus error: region lock error\n");
3839
3840 ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val);
3841 if (ret) {
3842 adsp_err(dsp,
3843 "Failed to read Bus Err Addr register: %d\n",
3844 ret);
3845 return IRQ_HANDLED;
3846 }
3847
3848 adsp_err(dsp, "bus error address = 0x%x\n",
3849 val & ADSP2_BUS_ERR_ADDR_MASK);
3850
3851 ret = regmap_read(regmap,
3852 dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR,
3853 &val);
3854 if (ret) {
3855 adsp_err(dsp,
3856 "Failed to read Pmem Xmem Err Addr register: %d\n",
3857 ret);
3858 return IRQ_HANDLED;
3859 }
3860
3861 adsp_err(dsp, "xmem error address = 0x%x\n",
3862 val & ADSP2_XMEM_ERR_ADDR_MASK);
3863 adsp_err(dsp, "pmem error address = 0x%x\n",
3864 (val & ADSP2_PMEM_ERR_ADDR_MASK) >>
3865 ADSP2_PMEM_ERR_ADDR_SHIFT);
3866 }
3867
3868 regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,
3869 ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);
3870
3871 return IRQ_HANDLED;
3872}
3873EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
3874
Praveen Diwakar0a37c6ef2014-07-04 11:17:41 +05303875MODULE_LICENSE("GPL v2");