]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - sound/soc/codecs/wm_adsp.c
ASoC: wm_adsp: Clean up low level control read/write functions
[mirror_ubuntu-artful-kernel.git] / sound / soc / codecs / wm_adsp.c
CommitLineData
2159ad93
MB
1/*
2 * wm_adsp.c -- Wolfson ADSP support
3 *
4 * Copyright 2012 Wolfson Microelectronics plc
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/firmware.h>
cf17c83c 18#include <linux/list.h>
2159ad93
MB
19#include <linux/pm.h>
20#include <linux/pm_runtime.h>
21#include <linux/regmap.h>
973838a0 22#include <linux/regulator/consumer.h>
2159ad93 23#include <linux/slab.h>
cdcd7f72 24#include <linux/vmalloc.h>
6ab2b7b4 25#include <linux/workqueue.h>
2159ad93
MB
26#include <sound/core.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/jack.h>
31#include <sound/initval.h>
32#include <sound/tlv.h>
33
34#include <linux/mfd/arizona/registers.h>
35
dc91428a 36#include "arizona.h"
2159ad93
MB
37#include "wm_adsp.h"
38
39#define adsp_crit(_dsp, fmt, ...) \
40 dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
41#define adsp_err(_dsp, fmt, ...) \
42 dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
43#define adsp_warn(_dsp, fmt, ...) \
44 dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
45#define adsp_info(_dsp, fmt, ...) \
46 dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
47#define adsp_dbg(_dsp, fmt, ...) \
48 dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
49
50#define ADSP1_CONTROL_1 0x00
51#define ADSP1_CONTROL_2 0x02
52#define ADSP1_CONTROL_3 0x03
53#define ADSP1_CONTROL_4 0x04
54#define ADSP1_CONTROL_5 0x06
55#define ADSP1_CONTROL_6 0x07
56#define ADSP1_CONTROL_7 0x08
57#define ADSP1_CONTROL_8 0x09
58#define ADSP1_CONTROL_9 0x0A
59#define ADSP1_CONTROL_10 0x0B
60#define ADSP1_CONTROL_11 0x0C
61#define ADSP1_CONTROL_12 0x0D
62#define ADSP1_CONTROL_13 0x0F
63#define ADSP1_CONTROL_14 0x10
64#define ADSP1_CONTROL_15 0x11
65#define ADSP1_CONTROL_16 0x12
66#define ADSP1_CONTROL_17 0x13
67#define ADSP1_CONTROL_18 0x14
68#define ADSP1_CONTROL_19 0x16
69#define ADSP1_CONTROL_20 0x17
70#define ADSP1_CONTROL_21 0x18
71#define ADSP1_CONTROL_22 0x1A
72#define ADSP1_CONTROL_23 0x1B
73#define ADSP1_CONTROL_24 0x1C
74#define ADSP1_CONTROL_25 0x1E
75#define ADSP1_CONTROL_26 0x20
76#define ADSP1_CONTROL_27 0x21
77#define ADSP1_CONTROL_28 0x22
78#define ADSP1_CONTROL_29 0x23
79#define ADSP1_CONTROL_30 0x24
80#define ADSP1_CONTROL_31 0x26
81
82/*
83 * ADSP1 Control 19
84 */
85#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
86#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
87#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
88
89
90/*
91 * ADSP1 Control 30
92 */
93#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
94#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
95#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
96#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
97#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
98#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
99#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
100#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
101#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
102#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
103#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
104#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
105#define ADSP1_START 0x0001 /* DSP1_START */
106#define ADSP1_START_MASK 0x0001 /* DSP1_START */
107#define ADSP1_START_SHIFT 0 /* DSP1_START */
108#define ADSP1_START_WIDTH 1 /* DSP1_START */
109
94e205bf
CR
110/*
111 * ADSP1 Control 31
112 */
113#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
114#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
115#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
116
2d30b575
MB
117#define ADSP2_CONTROL 0x0
118#define ADSP2_CLOCKING 0x1
119#define ADSP2_STATUS1 0x4
120#define ADSP2_WDMA_CONFIG_1 0x30
121#define ADSP2_WDMA_CONFIG_2 0x31
122#define ADSP2_RDMA_CONFIG_1 0x34
2159ad93
MB
123
124/*
125 * ADSP2 Control
126 */
127
128#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
129#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
130#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
131#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
132#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
133#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
134#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
135#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
136#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
137#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
138#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
139#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
140#define ADSP2_START 0x0001 /* DSP1_START */
141#define ADSP2_START_MASK 0x0001 /* DSP1_START */
142#define ADSP2_START_SHIFT 0 /* DSP1_START */
143#define ADSP2_START_WIDTH 1 /* DSP1_START */
144
973838a0
MB
145/*
146 * ADSP2 clocking
147 */
148#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
149#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
150#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
151
2159ad93
MB
152/*
153 * ADSP2 Status 1
154 */
155#define ADSP2_RAM_RDY 0x0001
156#define ADSP2_RAM_RDY_MASK 0x0001
157#define ADSP2_RAM_RDY_SHIFT 0
158#define ADSP2_RAM_RDY_WIDTH 1
159
cf17c83c
MB
160struct wm_adsp_buf {
161 struct list_head list;
162 void *buf;
163};
164
165static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
166 struct list_head *list)
167{
168 struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
169
170 if (buf == NULL)
171 return NULL;
172
cdcd7f72 173 buf->buf = vmalloc(len);
cf17c83c 174 if (!buf->buf) {
cdcd7f72 175 vfree(buf);
cf17c83c
MB
176 return NULL;
177 }
cdcd7f72 178 memcpy(buf->buf, src, len);
cf17c83c
MB
179
180 if (list)
181 list_add_tail(&buf->list, list);
182
183 return buf;
184}
185
186static void wm_adsp_buf_free(struct list_head *list)
187{
188 while (!list_empty(list)) {
189 struct wm_adsp_buf *buf = list_first_entry(list,
190 struct wm_adsp_buf,
191 list);
192 list_del(&buf->list);
cdcd7f72 193 vfree(buf->buf);
cf17c83c
MB
194 kfree(buf);
195 }
196}
197
36e8fe99 198#define WM_ADSP_NUM_FW 4
1023dbd9 199
dd84f925
MB
200#define WM_ADSP_FW_MBC_VSS 0
201#define WM_ADSP_FW_TX 1
202#define WM_ADSP_FW_TX_SPK 2
203#define WM_ADSP_FW_RX_ANC 3
204
1023dbd9 205static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
dd84f925
MB
206 [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
207 [WM_ADSP_FW_TX] = "Tx",
208 [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
209 [WM_ADSP_FW_RX_ANC] = "Rx ANC",
1023dbd9
MB
210};
211
212static struct {
213 const char *file;
214} wm_adsp_fw[WM_ADSP_NUM_FW] = {
dd84f925
MB
215 [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
216 [WM_ADSP_FW_TX] = { .file = "tx" },
217 [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
218 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
1023dbd9
MB
219};
220
6ab2b7b4
DP
221struct wm_coeff_ctl_ops {
222 int (*xget)(struct snd_kcontrol *kcontrol,
223 struct snd_ctl_elem_value *ucontrol);
224 int (*xput)(struct snd_kcontrol *kcontrol,
225 struct snd_ctl_elem_value *ucontrol);
226 int (*xinfo)(struct snd_kcontrol *kcontrol,
227 struct snd_ctl_elem_info *uinfo);
228};
229
6ab2b7b4
DP
230struct wm_coeff_ctl {
231 const char *name;
3809f001 232 struct wm_adsp_alg_region alg_region;
6ab2b7b4 233 struct wm_coeff_ctl_ops ops;
3809f001 234 struct wm_adsp *dsp;
6ab2b7b4
DP
235 void *private;
236 unsigned int enabled:1;
237 struct list_head list;
238 void *cache;
239 size_t len;
0c2e3f34 240 unsigned int set:1;
6ab2b7b4
DP
241 struct snd_kcontrol *kcontrol;
242};
243
1023dbd9
MB
244static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
245 struct snd_ctl_elem_value *ucontrol)
246{
ea53bf77 247 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
1023dbd9 248 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
3809f001 249 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
1023dbd9 250
3809f001 251 ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
1023dbd9
MB
252
253 return 0;
254}
255
256static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
257 struct snd_ctl_elem_value *ucontrol)
258{
ea53bf77 259 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
1023dbd9 260 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
3809f001 261 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
1023dbd9 262
3809f001 263 if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
1023dbd9
MB
264 return 0;
265
266 if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
267 return -EINVAL;
268
3809f001 269 if (dsp[e->shift_l].running)
1023dbd9
MB
270 return -EBUSY;
271
3809f001 272 dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
1023dbd9
MB
273
274 return 0;
275}
276
277static const struct soc_enum wm_adsp_fw_enum[] = {
278 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
279 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
280 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
281 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
282};
283
b6ed61cf 284const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
1023dbd9
MB
285 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
286 wm_adsp_fw_get, wm_adsp_fw_put),
287 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
288 wm_adsp_fw_get, wm_adsp_fw_put),
289 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
290 wm_adsp_fw_get, wm_adsp_fw_put),
b6ed61cf
MB
291};
292EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
293
294#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
295static const struct soc_enum wm_adsp2_rate_enum[] = {
dc91428a
MB
296 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
297 ARIZONA_DSP1_RATE_SHIFT, 0xf,
298 ARIZONA_RATE_ENUM_SIZE,
299 arizona_rate_text, arizona_rate_val),
300 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
301 ARIZONA_DSP1_RATE_SHIFT, 0xf,
302 ARIZONA_RATE_ENUM_SIZE,
303 arizona_rate_text, arizona_rate_val),
304 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
305 ARIZONA_DSP1_RATE_SHIFT, 0xf,
306 ARIZONA_RATE_ENUM_SIZE,
307 arizona_rate_text, arizona_rate_val),
5be9c5b4 308 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
dc91428a
MB
309 ARIZONA_DSP1_RATE_SHIFT, 0xf,
310 ARIZONA_RATE_ENUM_SIZE,
311 arizona_rate_text, arizona_rate_val),
312};
313
b6ed61cf 314const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
1023dbd9
MB
315 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
316 wm_adsp_fw_get, wm_adsp_fw_put),
b6ed61cf 317 SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
1023dbd9
MB
318 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
319 wm_adsp_fw_get, wm_adsp_fw_put),
b6ed61cf 320 SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
1023dbd9
MB
321 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
322 wm_adsp_fw_get, wm_adsp_fw_put),
b6ed61cf 323 SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
1023dbd9
MB
324 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
325 wm_adsp_fw_get, wm_adsp_fw_put),
b6ed61cf 326 SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
1023dbd9 327};
b6ed61cf
MB
328EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
329#endif
2159ad93
MB
330
331static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
332 int type)
333{
334 int i;
335
336 for (i = 0; i < dsp->num_mems; i++)
337 if (dsp->mem[i].type == type)
338 return &dsp->mem[i];
339
340 return NULL;
341}
342
3809f001 343static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
45b9ee72
MB
344 unsigned int offset)
345{
3809f001 346 if (WARN_ON(!mem))
6c452bda 347 return offset;
3809f001 348 switch (mem->type) {
45b9ee72 349 case WMFW_ADSP1_PM:
3809f001 350 return mem->base + (offset * 3);
45b9ee72 351 case WMFW_ADSP1_DM:
3809f001 352 return mem->base + (offset * 2);
45b9ee72 353 case WMFW_ADSP2_XM:
3809f001 354 return mem->base + (offset * 2);
45b9ee72 355 case WMFW_ADSP2_YM:
3809f001 356 return mem->base + (offset * 2);
45b9ee72 357 case WMFW_ADSP1_ZM:
3809f001 358 return mem->base + (offset * 2);
45b9ee72 359 default:
6c452bda 360 WARN(1, "Unknown memory region type");
45b9ee72
MB
361 return offset;
362 }
363}
364
6ab2b7b4
DP
365static int wm_coeff_info(struct snd_kcontrol *kcontrol,
366 struct snd_ctl_elem_info *uinfo)
367{
368 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
369
370 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
371 uinfo->count = ctl->len;
372 return 0;
373}
374
c9f8dd71 375static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
6ab2b7b4
DP
376 const void *buf, size_t len)
377{
3809f001 378 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
6ab2b7b4 379 const struct wm_adsp_region *mem;
3809f001 380 struct wm_adsp *dsp = ctl->dsp;
6ab2b7b4
DP
381 void *scratch;
382 int ret;
383 unsigned int reg;
384
3809f001 385 mem = wm_adsp_find_region(dsp, alg_region->type);
6ab2b7b4 386 if (!mem) {
3809f001
CK
387 adsp_err(dsp, "No base for region %x\n",
388 alg_region->type);
6ab2b7b4
DP
389 return -EINVAL;
390 }
391
3809f001 392 reg = ctl->alg_region.base;
6ab2b7b4
DP
393 reg = wm_adsp_region_to_reg(mem, reg);
394
395 scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
396 if (!scratch)
397 return -ENOMEM;
398
3809f001 399 ret = regmap_raw_write(dsp->regmap, reg, scratch,
6ab2b7b4
DP
400 ctl->len);
401 if (ret) {
3809f001 402 adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
43bc3bf6 403 ctl->len, reg, ret);
6ab2b7b4
DP
404 kfree(scratch);
405 return ret;
406 }
3809f001 407 adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
6ab2b7b4
DP
408
409 kfree(scratch);
410
411 return 0;
412}
413
414static int wm_coeff_put(struct snd_kcontrol *kcontrol,
415 struct snd_ctl_elem_value *ucontrol)
416{
417 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
418 char *p = ucontrol->value.bytes.data;
419
420 memcpy(ctl->cache, p, ctl->len);
421
65d17a9c
NO
422 ctl->set = 1;
423 if (!ctl->enabled)
6ab2b7b4 424 return 0;
6ab2b7b4 425
c9f8dd71 426 return wm_coeff_write_control(ctl, p, ctl->len);
6ab2b7b4
DP
427}
428
c9f8dd71 429static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
6ab2b7b4
DP
430 void *buf, size_t len)
431{
3809f001 432 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
6ab2b7b4 433 const struct wm_adsp_region *mem;
3809f001 434 struct wm_adsp *dsp = ctl->dsp;
6ab2b7b4
DP
435 void *scratch;
436 int ret;
437 unsigned int reg;
438
3809f001 439 mem = wm_adsp_find_region(dsp, alg_region->type);
6ab2b7b4 440 if (!mem) {
3809f001
CK
441 adsp_err(dsp, "No base for region %x\n",
442 alg_region->type);
6ab2b7b4
DP
443 return -EINVAL;
444 }
445
3809f001 446 reg = ctl->alg_region.base;
6ab2b7b4
DP
447 reg = wm_adsp_region_to_reg(mem, reg);
448
449 scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
450 if (!scratch)
451 return -ENOMEM;
452
3809f001 453 ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
6ab2b7b4 454 if (ret) {
3809f001 455 adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
43bc3bf6 456 ctl->len, reg, ret);
6ab2b7b4
DP
457 kfree(scratch);
458 return ret;
459 }
3809f001 460 adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
6ab2b7b4
DP
461
462 memcpy(buf, scratch, ctl->len);
463 kfree(scratch);
464
465 return 0;
466}
467
468static int wm_coeff_get(struct snd_kcontrol *kcontrol,
469 struct snd_ctl_elem_value *ucontrol)
470{
471 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
472 char *p = ucontrol->value.bytes.data;
473
474 memcpy(p, ctl->cache, ctl->len);
475 return 0;
476}
477
6ab2b7b4 478struct wmfw_ctl_work {
3809f001 479 struct wm_adsp *dsp;
6ab2b7b4
DP
480 struct wm_coeff_ctl *ctl;
481 struct work_struct work;
482};
483
3809f001 484static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
6ab2b7b4
DP
485{
486 struct snd_kcontrol_new *kcontrol;
487 int ret;
488
92bb4c32 489 if (!ctl || !ctl->name)
6ab2b7b4
DP
490 return -EINVAL;
491
492 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
493 if (!kcontrol)
494 return -ENOMEM;
495 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
496
497 kcontrol->name = ctl->name;
498 kcontrol->info = wm_coeff_info;
499 kcontrol->get = wm_coeff_get;
500 kcontrol->put = wm_coeff_put;
501 kcontrol->private_value = (unsigned long)ctl;
502
3809f001 503 ret = snd_soc_add_card_controls(dsp->card,
81ad93ec 504 kcontrol, 1);
6ab2b7b4
DP
505 if (ret < 0)
506 goto err_kcontrol;
507
508 kfree(kcontrol);
509
3809f001 510 ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
81ad93ec
DP
511 ctl->name);
512
3809f001
CK
513 list_add(&ctl->list, &dsp->ctl_list);
514
6ab2b7b4
DP
515 return 0;
516
517err_kcontrol:
518 kfree(kcontrol);
519 return ret;
520}
521
2159ad93
MB
522static int wm_adsp_load(struct wm_adsp *dsp)
523{
cf17c83c 524 LIST_HEAD(buf_list);
2159ad93
MB
525 const struct firmware *firmware;
526 struct regmap *regmap = dsp->regmap;
527 unsigned int pos = 0;
528 const struct wmfw_header *header;
529 const struct wmfw_adsp1_sizes *adsp1_sizes;
530 const struct wmfw_adsp2_sizes *adsp2_sizes;
531 const struct wmfw_footer *footer;
532 const struct wmfw_region *region;
533 const struct wm_adsp_region *mem;
534 const char *region_name;
535 char *file, *text;
cf17c83c 536 struct wm_adsp_buf *buf;
2159ad93
MB
537 unsigned int reg;
538 int regions = 0;
539 int ret, offset, type, sizes;
540
541 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
542 if (file == NULL)
543 return -ENOMEM;
544
1023dbd9
MB
545 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
546 wm_adsp_fw[dsp->fw].file);
2159ad93
MB
547 file[PAGE_SIZE - 1] = '\0';
548
549 ret = request_firmware(&firmware, file, dsp->dev);
550 if (ret != 0) {
551 adsp_err(dsp, "Failed to request '%s'\n", file);
552 goto out;
553 }
554 ret = -EINVAL;
555
556 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
557 if (pos >= firmware->size) {
558 adsp_err(dsp, "%s: file too short, %zu bytes\n",
559 file, firmware->size);
560 goto out_fw;
561 }
562
563 header = (void*)&firmware->data[0];
564
565 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
566 adsp_err(dsp, "%s: invalid magic\n", file);
567 goto out_fw;
568 }
569
570 if (header->ver != 0) {
571 adsp_err(dsp, "%s: unknown file format %d\n",
572 file, header->ver);
573 goto out_fw;
574 }
3626992a 575 adsp_info(dsp, "Firmware version: %d\n", header->ver);
2159ad93
MB
576
577 if (header->core != dsp->type) {
578 adsp_err(dsp, "%s: invalid core %d != %d\n",
579 file, header->core, dsp->type);
580 goto out_fw;
581 }
582
583 switch (dsp->type) {
584 case WMFW_ADSP1:
585 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
586 adsp1_sizes = (void *)&(header[1]);
587 footer = (void *)&(adsp1_sizes[1]);
588 sizes = sizeof(*adsp1_sizes);
589
590 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
591 file, le32_to_cpu(adsp1_sizes->dm),
592 le32_to_cpu(adsp1_sizes->pm),
593 le32_to_cpu(adsp1_sizes->zm));
594 break;
595
596 case WMFW_ADSP2:
597 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
598 adsp2_sizes = (void *)&(header[1]);
599 footer = (void *)&(adsp2_sizes[1]);
600 sizes = sizeof(*adsp2_sizes);
601
602 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
603 file, le32_to_cpu(adsp2_sizes->xm),
604 le32_to_cpu(adsp2_sizes->ym),
605 le32_to_cpu(adsp2_sizes->pm),
606 le32_to_cpu(adsp2_sizes->zm));
607 break;
608
609 default:
6c452bda 610 WARN(1, "Unknown DSP type");
2159ad93
MB
611 goto out_fw;
612 }
613
614 if (le32_to_cpu(header->len) != sizeof(*header) +
615 sizes + sizeof(*footer)) {
616 adsp_err(dsp, "%s: unexpected header length %d\n",
617 file, le32_to_cpu(header->len));
618 goto out_fw;
619 }
620
621 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
622 le64_to_cpu(footer->timestamp));
623
624 while (pos < firmware->size &&
625 pos - firmware->size > sizeof(*region)) {
626 region = (void *)&(firmware->data[pos]);
627 region_name = "Unknown";
628 reg = 0;
629 text = NULL;
630 offset = le32_to_cpu(region->offset) & 0xffffff;
631 type = be32_to_cpu(region->type) & 0xff;
632 mem = wm_adsp_find_region(dsp, type);
633
634 switch (type) {
635 case WMFW_NAME_TEXT:
636 region_name = "Firmware name";
637 text = kzalloc(le32_to_cpu(region->len) + 1,
638 GFP_KERNEL);
639 break;
640 case WMFW_INFO_TEXT:
641 region_name = "Information";
642 text = kzalloc(le32_to_cpu(region->len) + 1,
643 GFP_KERNEL);
644 break;
645 case WMFW_ABSOLUTE:
646 region_name = "Absolute";
647 reg = offset;
648 break;
649 case WMFW_ADSP1_PM:
2159ad93 650 region_name = "PM";
45b9ee72 651 reg = wm_adsp_region_to_reg(mem, offset);
2159ad93
MB
652 break;
653 case WMFW_ADSP1_DM:
2159ad93 654 region_name = "DM";
45b9ee72 655 reg = wm_adsp_region_to_reg(mem, offset);
2159ad93
MB
656 break;
657 case WMFW_ADSP2_XM:
2159ad93 658 region_name = "XM";
45b9ee72 659 reg = wm_adsp_region_to_reg(mem, offset);
2159ad93
MB
660 break;
661 case WMFW_ADSP2_YM:
2159ad93 662 region_name = "YM";
45b9ee72 663 reg = wm_adsp_region_to_reg(mem, offset);
2159ad93
MB
664 break;
665 case WMFW_ADSP1_ZM:
2159ad93 666 region_name = "ZM";
45b9ee72 667 reg = wm_adsp_region_to_reg(mem, offset);
2159ad93
MB
668 break;
669 default:
670 adsp_warn(dsp,
671 "%s.%d: Unknown region type %x at %d(%x)\n",
672 file, regions, type, pos, pos);
673 break;
674 }
675
676 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
677 regions, le32_to_cpu(region->len), offset,
678 region_name);
679
680 if (text) {
681 memcpy(text, region->data, le32_to_cpu(region->len));
682 adsp_info(dsp, "%s: %s\n", file, text);
683 kfree(text);
684 }
685
686 if (reg) {
cdcd7f72
CK
687 buf = wm_adsp_buf_alloc(region->data,
688 le32_to_cpu(region->len),
689 &buf_list);
690 if (!buf) {
691 adsp_err(dsp, "Out of memory\n");
692 ret = -ENOMEM;
693 goto out_fw;
694 }
c1a7898d 695
cdcd7f72
CK
696 ret = regmap_raw_write_async(regmap, reg, buf->buf,
697 le32_to_cpu(region->len));
698 if (ret != 0) {
699 adsp_err(dsp,
700 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
701 file, regions,
702 le32_to_cpu(region->len), offset,
703 region_name, ret);
704 goto out_fw;
2159ad93
MB
705 }
706 }
707
708 pos += le32_to_cpu(region->len) + sizeof(*region);
709 regions++;
710 }
cf17c83c
MB
711
712 ret = regmap_async_complete(regmap);
713 if (ret != 0) {
714 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
715 goto out_fw;
716 }
717
2159ad93
MB
718 if (pos > firmware->size)
719 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
720 file, regions, pos - firmware->size);
721
722out_fw:
cf17c83c
MB
723 regmap_async_complete(regmap);
724 wm_adsp_buf_free(&buf_list);
2159ad93
MB
725 release_firmware(firmware);
726out:
727 kfree(file);
728
729 return ret;
730}
731
3809f001 732static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
6ab2b7b4
DP
733{
734 struct wm_coeff_ctl *ctl;
735 int ret;
736
3809f001 737 list_for_each_entry(ctl, &dsp->ctl_list, list) {
0c2e3f34 738 if (!ctl->enabled || ctl->set)
6ab2b7b4 739 continue;
c9f8dd71 740 ret = wm_coeff_read_control(ctl,
6ab2b7b4
DP
741 ctl->cache,
742 ctl->len);
743 if (ret < 0)
744 return ret;
745 }
746
747 return 0;
748}
749
3809f001 750static int wm_coeff_sync_controls(struct wm_adsp *dsp)
6ab2b7b4
DP
751{
752 struct wm_coeff_ctl *ctl;
753 int ret;
754
3809f001 755 list_for_each_entry(ctl, &dsp->ctl_list, list) {
6ab2b7b4
DP
756 if (!ctl->enabled)
757 continue;
0c2e3f34 758 if (ctl->set) {
c9f8dd71 759 ret = wm_coeff_write_control(ctl,
6ab2b7b4
DP
760 ctl->cache,
761 ctl->len);
762 if (ret < 0)
763 return ret;
6ab2b7b4
DP
764 }
765 }
766
767 return 0;
768}
769
770static void wm_adsp_ctl_work(struct work_struct *work)
771{
772 struct wmfw_ctl_work *ctl_work = container_of(work,
773 struct wmfw_ctl_work,
774 work);
775
3809f001 776 wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
6ab2b7b4
DP
777 kfree(ctl_work);
778}
779
92bb4c32 780static int wm_adsp_create_control(struct wm_adsp *dsp,
6958eb2a
CK
781 const struct wm_adsp_alg_region *alg_region,
782 unsigned int len)
6ab2b7b4 783{
6ab2b7b4
DP
784 struct wm_coeff_ctl *ctl;
785 struct wmfw_ctl_work *ctl_work;
512f2bba 786 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
6ab2b7b4
DP
787 char *region_name;
788 int ret;
789
3809f001 790 switch (alg_region->type) {
6ab2b7b4
DP
791 case WMFW_ADSP1_PM:
792 region_name = "PM";
793 break;
794 case WMFW_ADSP1_DM:
795 region_name = "DM";
796 break;
797 case WMFW_ADSP2_XM:
798 region_name = "XM";
799 break;
800 case WMFW_ADSP2_YM:
801 region_name = "YM";
802 break;
803 case WMFW_ADSP1_ZM:
804 region_name = "ZM";
805 break;
806 default:
512f2bba 807 return -EINVAL;
6ab2b7b4
DP
808 }
809
0f4e918c 810 snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
3809f001 811 dsp->num, region_name, alg_region->alg);
6ab2b7b4 812
81ad93ec 813 list_for_each_entry(ctl, &dsp->ctl_list,
6ab2b7b4
DP
814 list) {
815 if (!strcmp(ctl->name, name)) {
816 if (!ctl->enabled)
817 ctl->enabled = 1;
512f2bba 818 return 0;
6ab2b7b4
DP
819 }
820 }
821
822 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
512f2bba
CK
823 if (!ctl)
824 return -ENOMEM;
3809f001 825 ctl->alg_region = *alg_region;
6ab2b7b4
DP
826 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
827 if (!ctl->name) {
828 ret = -ENOMEM;
829 goto err_ctl;
830 }
831 ctl->enabled = 1;
0c2e3f34 832 ctl->set = 0;
6ab2b7b4
DP
833 ctl->ops.xget = wm_coeff_get;
834 ctl->ops.xput = wm_coeff_put;
3809f001 835 ctl->dsp = dsp;
6ab2b7b4 836
6958eb2a
CK
837 if (len > 512) {
838 adsp_warn(dsp, "Truncating control %s from %d\n",
839 ctl->name, len);
840 len = 512;
841 }
842 ctl->len = len;
6ab2b7b4
DP
843 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
844 if (!ctl->cache) {
845 ret = -ENOMEM;
846 goto err_ctl_name;
847 }
848
849 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
850 if (!ctl_work) {
851 ret = -ENOMEM;
852 goto err_ctl_cache;
853 }
854
3809f001 855 ctl_work->dsp = dsp;
6ab2b7b4
DP
856 ctl_work->ctl = ctl;
857 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
858 schedule_work(&ctl_work->work);
859
6ab2b7b4
DP
860 return 0;
861
862err_ctl_cache:
863 kfree(ctl->cache);
864err_ctl_name:
865 kfree(ctl->name);
866err_ctl:
867 kfree(ctl);
512f2bba 868
6ab2b7b4
DP
869 return ret;
870}
871
3809f001 872static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
b618a185 873 unsigned int pos, unsigned int len)
db40517c 874{
b618a185
CK
875 void *alg;
876 int ret;
db40517c 877 __be32 val;
db40517c 878
3809f001 879 if (n_algs == 0) {
b618a185
CK
880 adsp_err(dsp, "No algorithms\n");
881 return ERR_PTR(-EINVAL);
db40517c
MB
882 }
883
3809f001
CK
884 if (n_algs > 1024) {
885 adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
b618a185
CK
886 return ERR_PTR(-EINVAL);
887 }
db40517c 888
b618a185
CK
889 /* Read the terminator first to validate the length */
890 ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
891 if (ret != 0) {
892 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
893 ret);
894 return ERR_PTR(ret);
895 }
db40517c 896
b618a185
CK
897 if (be32_to_cpu(val) != 0xbedead)
898 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
899 pos + len, be32_to_cpu(val));
d62f4bc6 900
b618a185
CK
901 alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
902 if (!alg)
903 return ERR_PTR(-ENOMEM);
db40517c 904
b618a185
CK
905 ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
906 if (ret != 0) {
907 adsp_err(dsp, "Failed to read algorithm list: %d\n",
908 ret);
909 kfree(alg);
910 return ERR_PTR(ret);
911 }
ac50009f 912
b618a185
CK
913 return alg;
914}
ac50009f 915
b618a185
CK
916static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
917{
918 struct wmfw_adsp1_id_hdr adsp1_id;
919 struct wmfw_adsp1_alg_hdr *adsp1_alg;
3809f001 920 struct wm_adsp_alg_region *alg_region;
b618a185
CK
921 const struct wm_adsp_region *mem;
922 unsigned int pos, len;
3809f001 923 size_t n_algs;
b618a185 924 int i, ret;
db40517c 925
b618a185
CK
926 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
927 if (WARN_ON(!mem))
928 return -EINVAL;
929
930 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
931 sizeof(adsp1_id));
932 if (ret != 0) {
933 adsp_err(dsp, "Failed to read algorithm info: %d\n",
934 ret);
935 return ret;
936 }
db40517c 937
3809f001 938 n_algs = be32_to_cpu(adsp1_id.n_algs);
b618a185
CK
939 dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
940 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
941 dsp->fw_id,
942 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
943 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
944 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
3809f001 945 n_algs);
b618a185 946
3809f001
CK
947 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
948 if (!alg_region)
b618a185 949 return -ENOMEM;
3809f001
CK
950 alg_region->type = WMFW_ADSP1_ZM;
951 alg_region->alg = be32_to_cpu(adsp1_id.fw.id);
952 alg_region->base = be32_to_cpu(adsp1_id.zm);
953 list_add_tail(&alg_region->list, &dsp->alg_regions);
d62f4bc6 954
3809f001
CK
955 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
956 if (!alg_region)
b618a185 957 return -ENOMEM;
3809f001
CK
958 alg_region->type = WMFW_ADSP1_DM;
959 alg_region->alg = be32_to_cpu(adsp1_id.fw.id);
960 alg_region->base = be32_to_cpu(adsp1_id.dm);
961 list_add_tail(&alg_region->list, &dsp->alg_regions);
db40517c 962
b618a185 963 pos = sizeof(adsp1_id) / 2;
3809f001 964 len = (sizeof(*adsp1_alg) * n_algs) / 2;
b618a185 965
3809f001 966 adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
b618a185
CK
967 if (IS_ERR(adsp1_alg))
968 return PTR_ERR(adsp1_alg);
969
3809f001 970 for (i = 0; i < n_algs; i++) {
b618a185
CK
971 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
972 i, be32_to_cpu(adsp1_alg[i].alg.id),
973 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
974 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
975 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
976 be32_to_cpu(adsp1_alg[i].dm),
977 be32_to_cpu(adsp1_alg[i].zm));
ac50009f 978
3809f001
CK
979 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
980 if (!alg_region) {
b618a185
CK
981 ret = -ENOMEM;
982 goto out;
983 }
3809f001
CK
984 alg_region->type = WMFW_ADSP1_DM;
985 alg_region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
986 alg_region->base = be32_to_cpu(adsp1_alg[i].dm);
3809f001
CK
987 list_add_tail(&alg_region->list, &dsp->alg_regions);
988 if (i + 1 < n_algs) {
6958eb2a
CK
989 len = be32_to_cpu(adsp1_alg[i + 1].dm);
990 len -= be32_to_cpu(adsp1_alg[i].dm);
991 len *= 4;
992 wm_adsp_create_control(dsp, alg_region, len);
b618a185
CK
993 } else {
994 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
995 be32_to_cpu(adsp1_alg[i].alg.id));
996 }
ac50009f 997
3809f001
CK
998 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
999 if (!alg_region) {
b618a185
CK
1000 ret = -ENOMEM;
1001 goto out;
1002 }
3809f001
CK
1003 alg_region->type = WMFW_ADSP1_ZM;
1004 alg_region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
1005 alg_region->base = be32_to_cpu(adsp1_alg[i].zm);
3809f001
CK
1006 list_add_tail(&alg_region->list, &dsp->alg_regions);
1007 if (i + 1 < n_algs) {
6958eb2a
CK
1008 len = be32_to_cpu(adsp1_alg[i + 1].zm);
1009 len -= be32_to_cpu(adsp1_alg[i].zm);
1010 len *= 4;
1011 wm_adsp_create_control(dsp, alg_region, len);
b618a185
CK
1012 } else {
1013 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1014 be32_to_cpu(adsp1_alg[i].alg.id));
1015 }
db40517c
MB
1016 }
1017
b618a185
CK
1018out:
1019 kfree(adsp1_alg);
1020 return ret;
1021}
db40517c 1022
b618a185
CK
1023static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
1024{
1025 struct wmfw_adsp2_id_hdr adsp2_id;
1026 struct wmfw_adsp2_alg_hdr *adsp2_alg;
3809f001 1027 struct wm_adsp_alg_region *alg_region;
b618a185
CK
1028 const struct wm_adsp_region *mem;
1029 unsigned int pos, len;
3809f001 1030 size_t n_algs;
b618a185
CK
1031 int i, ret;
1032
1033 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
1034 if (WARN_ON(!mem))
d62f4bc6 1035 return -EINVAL;
d62f4bc6 1036
b618a185
CK
1037 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
1038 sizeof(adsp2_id));
db40517c 1039 if (ret != 0) {
b618a185
CK
1040 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1041 ret);
db40517c
MB
1042 return ret;
1043 }
1044
3809f001 1045 n_algs = be32_to_cpu(adsp2_id.n_algs);
b618a185
CK
1046 dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
1047 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1048 dsp->fw_id,
1049 (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
1050 (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
1051 be32_to_cpu(adsp2_id.fw.ver) & 0xff,
3809f001 1052 n_algs);
b618a185 1053
3809f001
CK
1054 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1055 if (!alg_region)
b618a185 1056 return -ENOMEM;
3809f001
CK
1057 alg_region->type = WMFW_ADSP2_XM;
1058 alg_region->alg = be32_to_cpu(adsp2_id.fw.id);
1059 alg_region->base = be32_to_cpu(adsp2_id.xm);
1060 list_add_tail(&alg_region->list, &dsp->alg_regions);
db40517c 1061
3809f001
CK
1062 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1063 if (!alg_region)
db40517c 1064 return -ENOMEM;
3809f001
CK
1065 alg_region->type = WMFW_ADSP2_YM;
1066 alg_region->alg = be32_to_cpu(adsp2_id.fw.id);
1067 alg_region->base = be32_to_cpu(adsp2_id.ym);
1068 list_add_tail(&alg_region->list, &dsp->alg_regions);
db40517c 1069
3809f001
CK
1070 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1071 if (!alg_region)
b618a185 1072 return -ENOMEM;
3809f001
CK
1073 alg_region->type = WMFW_ADSP2_ZM;
1074 alg_region->alg = be32_to_cpu(adsp2_id.fw.id);
1075 alg_region->base = be32_to_cpu(adsp2_id.zm);
1076 list_add_tail(&alg_region->list, &dsp->alg_regions);
db40517c 1077
b618a185 1078 pos = sizeof(adsp2_id) / 2;
3809f001 1079 len = (sizeof(*adsp2_alg) * n_algs) / 2;
db40517c 1080
3809f001 1081 adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
b618a185
CK
1082 if (IS_ERR(adsp2_alg))
1083 return PTR_ERR(adsp2_alg);
471f4885 1084
3809f001 1085 for (i = 0; i < n_algs; i++) {
b618a185
CK
1086 adsp_info(dsp,
1087 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1088 i, be32_to_cpu(adsp2_alg[i].alg.id),
1089 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1090 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1091 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1092 be32_to_cpu(adsp2_alg[i].xm),
1093 be32_to_cpu(adsp2_alg[i].ym),
1094 be32_to_cpu(adsp2_alg[i].zm));
db40517c 1095
3809f001
CK
1096 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1097 if (!alg_region) {
b618a185
CK
1098 ret = -ENOMEM;
1099 goto out;
1100 }
3809f001
CK
1101 alg_region->type = WMFW_ADSP2_XM;
1102 alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
1103 alg_region->base = be32_to_cpu(adsp2_alg[i].xm);
3809f001
CK
1104 list_add_tail(&alg_region->list, &dsp->alg_regions);
1105 if (i + 1 < n_algs) {
6958eb2a
CK
1106 len = be32_to_cpu(adsp2_alg[i + 1].xm);
1107 len -= be32_to_cpu(adsp2_alg[i].xm);
1108 len *= 4;
1109 wm_adsp_create_control(dsp, alg_region, len);
b618a185
CK
1110 } else {
1111 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
1112 be32_to_cpu(adsp2_alg[i].alg.id));
1113 }
471f4885 1114
3809f001
CK
1115 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1116 if (!alg_region) {
b618a185
CK
1117 ret = -ENOMEM;
1118 goto out;
1119 }
3809f001
CK
1120 alg_region->type = WMFW_ADSP2_YM;
1121 alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
1122 alg_region->base = be32_to_cpu(adsp2_alg[i].ym);
3809f001
CK
1123 list_add_tail(&alg_region->list, &dsp->alg_regions);
1124 if (i + 1 < n_algs) {
6958eb2a
CK
1125 len = be32_to_cpu(adsp2_alg[i + 1].ym);
1126 len -= be32_to_cpu(adsp2_alg[i].ym);
1127 len *= 4;
1128 wm_adsp_create_control(dsp, alg_region, len);
b618a185
CK
1129 } else {
1130 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
1131 be32_to_cpu(adsp2_alg[i].alg.id));
1132 }
471f4885 1133
3809f001
CK
1134 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1135 if (!alg_region) {
b618a185
CK
1136 ret = -ENOMEM;
1137 goto out;
1138 }
3809f001
CK
1139 alg_region->type = WMFW_ADSP2_ZM;
1140 alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
1141 alg_region->base = be32_to_cpu(adsp2_alg[i].zm);
3809f001
CK
1142 list_add_tail(&alg_region->list, &dsp->alg_regions);
1143 if (i + 1 < n_algs) {
6958eb2a
CK
1144 len = be32_to_cpu(adsp2_alg[i + 1].zm);
1145 len -= be32_to_cpu(adsp2_alg[i].zm);
1146 len *= 4;
1147 wm_adsp_create_control(dsp, alg_region, len);
b618a185
CK
1148 } else {
1149 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1150 be32_to_cpu(adsp2_alg[i].alg.id));
db40517c
MB
1151 }
1152 }
1153
1154out:
b618a185 1155 kfree(adsp2_alg);
db40517c
MB
1156 return ret;
1157}
1158
2159ad93
MB
1159static int wm_adsp_load_coeff(struct wm_adsp *dsp)
1160{
cf17c83c 1161 LIST_HEAD(buf_list);
2159ad93
MB
1162 struct regmap *regmap = dsp->regmap;
1163 struct wmfw_coeff_hdr *hdr;
1164 struct wmfw_coeff_item *blk;
1165 const struct firmware *firmware;
471f4885
MB
1166 const struct wm_adsp_region *mem;
1167 struct wm_adsp_alg_region *alg_region;
2159ad93
MB
1168 const char *region_name;
1169 int ret, pos, blocks, type, offset, reg;
1170 char *file;
cf17c83c 1171 struct wm_adsp_buf *buf;
2159ad93
MB
1172
1173 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1174 if (file == NULL)
1175 return -ENOMEM;
1176
1023dbd9
MB
1177 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
1178 wm_adsp_fw[dsp->fw].file);
2159ad93
MB
1179 file[PAGE_SIZE - 1] = '\0';
1180
1181 ret = request_firmware(&firmware, file, dsp->dev);
1182 if (ret != 0) {
1183 adsp_warn(dsp, "Failed to request '%s'\n", file);
1184 ret = 0;
1185 goto out;
1186 }
1187 ret = -EINVAL;
1188
1189 if (sizeof(*hdr) >= firmware->size) {
1190 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1191 file, firmware->size);
1192 goto out_fw;
1193 }
1194
1195 hdr = (void*)&firmware->data[0];
1196 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
1197 adsp_err(dsp, "%s: invalid magic\n", file);
a4cdbec7 1198 goto out_fw;
2159ad93
MB
1199 }
1200
c712326d
MB
1201 switch (be32_to_cpu(hdr->rev) & 0xff) {
1202 case 1:
1203 break;
1204 default:
1205 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
1206 file, be32_to_cpu(hdr->rev) & 0xff);
1207 ret = -EINVAL;
1208 goto out_fw;
1209 }
1210
2159ad93
MB
1211 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
1212 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
1213 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
1214 le32_to_cpu(hdr->ver) & 0xff);
1215
1216 pos = le32_to_cpu(hdr->len);
1217
1218 blocks = 0;
1219 while (pos < firmware->size &&
1220 pos - firmware->size > sizeof(*blk)) {
1221 blk = (void*)(&firmware->data[pos]);
1222
c712326d
MB
1223 type = le16_to_cpu(blk->type);
1224 offset = le16_to_cpu(blk->offset);
2159ad93
MB
1225
1226 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
1227 file, blocks, le32_to_cpu(blk->id),
1228 (le32_to_cpu(blk->ver) >> 16) & 0xff,
1229 (le32_to_cpu(blk->ver) >> 8) & 0xff,
1230 le32_to_cpu(blk->ver) & 0xff);
1231 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
1232 file, blocks, le32_to_cpu(blk->len), offset, type);
1233
1234 reg = 0;
1235 region_name = "Unknown";
1236 switch (type) {
c712326d
MB
1237 case (WMFW_NAME_TEXT << 8):
1238 case (WMFW_INFO_TEXT << 8):
2159ad93 1239 break;
c712326d 1240 case (WMFW_ABSOLUTE << 8):
f395a218
MB
1241 /*
1242 * Old files may use this for global
1243 * coefficients.
1244 */
1245 if (le32_to_cpu(blk->id) == dsp->fw_id &&
1246 offset == 0) {
1247 region_name = "global coefficients";
1248 mem = wm_adsp_find_region(dsp, type);
1249 if (!mem) {
1250 adsp_err(dsp, "No ZM\n");
1251 break;
1252 }
1253 reg = wm_adsp_region_to_reg(mem, 0);
1254
1255 } else {
1256 region_name = "register";
1257 reg = offset;
1258 }
2159ad93 1259 break;
471f4885
MB
1260
1261 case WMFW_ADSP1_DM:
1262 case WMFW_ADSP1_ZM:
1263 case WMFW_ADSP2_XM:
1264 case WMFW_ADSP2_YM:
1265 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
1266 file, blocks, le32_to_cpu(blk->len),
1267 type, le32_to_cpu(blk->id));
1268
1269 mem = wm_adsp_find_region(dsp, type);
1270 if (!mem) {
1271 adsp_err(dsp, "No base for region %x\n", type);
1272 break;
1273 }
1274
1275 reg = 0;
1276 list_for_each_entry(alg_region,
1277 &dsp->alg_regions, list) {
1278 if (le32_to_cpu(blk->id) == alg_region->alg &&
1279 type == alg_region->type) {
338c5188 1280 reg = alg_region->base;
471f4885
MB
1281 reg = wm_adsp_region_to_reg(mem,
1282 reg);
338c5188 1283 reg += offset;
d733dc08 1284 break;
471f4885
MB
1285 }
1286 }
1287
1288 if (reg == 0)
1289 adsp_err(dsp, "No %x for algorithm %x\n",
1290 type, le32_to_cpu(blk->id));
1291 break;
1292
2159ad93 1293 default:
25c62f7e
MB
1294 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
1295 file, blocks, type, pos);
2159ad93
MB
1296 break;
1297 }
1298
1299 if (reg) {
cf17c83c
MB
1300 buf = wm_adsp_buf_alloc(blk->data,
1301 le32_to_cpu(blk->len),
1302 &buf_list);
a76fefab
MB
1303 if (!buf) {
1304 adsp_err(dsp, "Out of memory\n");
f4b82812
WY
1305 ret = -ENOMEM;
1306 goto out_fw;
a76fefab
MB
1307 }
1308
20da6d5a
MB
1309 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
1310 file, blocks, le32_to_cpu(blk->len),
1311 reg);
cf17c83c
MB
1312 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1313 le32_to_cpu(blk->len));
2159ad93
MB
1314 if (ret != 0) {
1315 adsp_err(dsp,
43bc3bf6
DP
1316 "%s.%d: Failed to write to %x in %s: %d\n",
1317 file, blocks, reg, region_name, ret);
2159ad93
MB
1318 }
1319 }
1320
be951017 1321 pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
2159ad93
MB
1322 blocks++;
1323 }
1324
cf17c83c
MB
1325 ret = regmap_async_complete(regmap);
1326 if (ret != 0)
1327 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1328
2159ad93
MB
1329 if (pos > firmware->size)
1330 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1331 file, blocks, pos - firmware->size);
1332
1333out_fw:
9da7a5a9 1334 regmap_async_complete(regmap);
2159ad93 1335 release_firmware(firmware);
cf17c83c 1336 wm_adsp_buf_free(&buf_list);
2159ad93
MB
1337out:
1338 kfree(file);
f4b82812 1339 return ret;
2159ad93
MB
1340}
1341
3809f001 1342int wm_adsp1_init(struct wm_adsp *dsp)
5e7a7a22 1343{
3809f001 1344 INIT_LIST_HEAD(&dsp->alg_regions);
5e7a7a22
MB
1345
1346 return 0;
1347}
1348EXPORT_SYMBOL_GPL(wm_adsp1_init);
1349
2159ad93
MB
1350int wm_adsp1_event(struct snd_soc_dapm_widget *w,
1351 struct snd_kcontrol *kcontrol,
1352 int event)
1353{
72718517 1354 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
2159ad93
MB
1355 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1356 struct wm_adsp *dsp = &dsps[w->shift];
b0101b4f 1357 struct wm_adsp_alg_region *alg_region;
6ab2b7b4 1358 struct wm_coeff_ctl *ctl;
2159ad93 1359 int ret;
94e205bf 1360 int val;
2159ad93 1361
00200107 1362 dsp->card = codec->component.card;
92bb4c32 1363
2159ad93
MB
1364 switch (event) {
1365 case SND_SOC_DAPM_POST_PMU:
1366 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1367 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
1368
94e205bf
CR
1369 /*
1370 * For simplicity set the DSP clock rate to be the
1371 * SYSCLK rate rather than making it configurable.
1372 */
1373 if(dsp->sysclk_reg) {
1374 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
1375 if (ret != 0) {
1376 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
1377 ret);
1378 return ret;
1379 }
1380
1381 val = (val & dsp->sysclk_mask)
1382 >> dsp->sysclk_shift;
1383
1384 ret = regmap_update_bits(dsp->regmap,
1385 dsp->base + ADSP1_CONTROL_31,
1386 ADSP1_CLK_SEL_MASK, val);
1387 if (ret != 0) {
1388 adsp_err(dsp, "Failed to set clock rate: %d\n",
1389 ret);
1390 return ret;
1391 }
1392 }
1393
2159ad93
MB
1394 ret = wm_adsp_load(dsp);
1395 if (ret != 0)
1396 goto err;
1397
b618a185 1398 ret = wm_adsp1_setup_algs(dsp);
db40517c
MB
1399 if (ret != 0)
1400 goto err;
1401
2159ad93
MB
1402 ret = wm_adsp_load_coeff(dsp);
1403 if (ret != 0)
1404 goto err;
1405
0c2e3f34 1406 /* Initialize caches for enabled and unset controls */
81ad93ec 1407 ret = wm_coeff_init_control_caches(dsp);
6ab2b7b4
DP
1408 if (ret != 0)
1409 goto err;
1410
0c2e3f34 1411 /* Sync set controls */
81ad93ec 1412 ret = wm_coeff_sync_controls(dsp);
6ab2b7b4
DP
1413 if (ret != 0)
1414 goto err;
1415
2159ad93
MB
1416 /* Start the core running */
1417 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1418 ADSP1_CORE_ENA | ADSP1_START,
1419 ADSP1_CORE_ENA | ADSP1_START);
1420 break;
1421
1422 case SND_SOC_DAPM_PRE_PMD:
1423 /* Halt the core */
1424 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1425 ADSP1_CORE_ENA | ADSP1_START, 0);
1426
1427 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
1428 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
1429
1430 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1431 ADSP1_SYS_ENA, 0);
6ab2b7b4 1432
81ad93ec 1433 list_for_each_entry(ctl, &dsp->ctl_list, list)
6ab2b7b4 1434 ctl->enabled = 0;
b0101b4f
DP
1435
1436 while (!list_empty(&dsp->alg_regions)) {
1437 alg_region = list_first_entry(&dsp->alg_regions,
1438 struct wm_adsp_alg_region,
1439 list);
1440 list_del(&alg_region->list);
1441 kfree(alg_region);
1442 }
2159ad93
MB
1443 break;
1444
1445 default:
1446 break;
1447 }
1448
1449 return 0;
1450
1451err:
1452 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1453 ADSP1_SYS_ENA, 0);
1454 return ret;
1455}
1456EXPORT_SYMBOL_GPL(wm_adsp1_event);
1457
1458static int wm_adsp2_ena(struct wm_adsp *dsp)
1459{
1460 unsigned int val;
1461 int ret, count;
1462
1552c325
MB
1463 ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
1464 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
2159ad93
MB
1465 if (ret != 0)
1466 return ret;
1467
1468 /* Wait for the RAM to start, should be near instantaneous */
939fd1e8 1469 for (count = 0; count < 10; ++count) {
2159ad93
MB
1470 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
1471 &val);
1472 if (ret != 0)
1473 return ret;
939fd1e8
CK
1474
1475 if (val & ADSP2_RAM_RDY)
1476 break;
1477
1478 msleep(1);
1479 }
2159ad93
MB
1480
1481 if (!(val & ADSP2_RAM_RDY)) {
1482 adsp_err(dsp, "Failed to start DSP RAM\n");
1483 return -EBUSY;
1484 }
1485
1486 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
2159ad93
MB
1487
1488 return 0;
1489}
1490
18b1a902 1491static void wm_adsp2_boot_work(struct work_struct *work)
2159ad93 1492{
d8a64d6a
CK
1493 struct wm_adsp *dsp = container_of(work,
1494 struct wm_adsp,
1495 boot_work);
2159ad93 1496 int ret;
d8a64d6a 1497 unsigned int val;
2159ad93 1498
d8a64d6a
CK
1499 /*
1500 * For simplicity set the DSP clock rate to be the
1501 * SYSCLK rate rather than making it configurable.
1502 */
1503 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1504 if (ret != 0) {
1505 adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
1506 return;
1507 }
1508 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1509 >> ARIZONA_SYSCLK_FREQ_SHIFT;
92bb4c32 1510
d8a64d6a
CK
1511 ret = regmap_update_bits_async(dsp->regmap,
1512 dsp->base + ADSP2_CLOCKING,
1513 ADSP2_CLK_SEL_MASK, val);
1514 if (ret != 0) {
1515 adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
1516 return;
1517 }
dd49e2c8 1518
d8a64d6a
CK
1519 if (dsp->dvfs) {
1520 ret = regmap_read(dsp->regmap,
1521 dsp->base + ADSP2_CLOCKING, &val);
dd49e2c8 1522 if (ret != 0) {
62c35b3b 1523 adsp_err(dsp, "Failed to read clocking: %d\n", ret);
d8a64d6a 1524 return;
dd49e2c8
MB
1525 }
1526
d8a64d6a
CK
1527 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
1528 ret = regulator_enable(dsp->dvfs);
973838a0 1529 if (ret != 0) {
62c35b3b
CK
1530 adsp_err(dsp,
1531 "Failed to enable supply: %d\n",
1532 ret);
d8a64d6a 1533 return;
973838a0
MB
1534 }
1535
d8a64d6a
CK
1536 ret = regulator_set_voltage(dsp->dvfs,
1537 1800000,
1538 1800000);
1539 if (ret != 0) {
62c35b3b
CK
1540 adsp_err(dsp,
1541 "Failed to raise supply: %d\n",
1542 ret);
d8a64d6a 1543 return;
973838a0
MB
1544 }
1545 }
d8a64d6a 1546 }
973838a0 1547
d8a64d6a
CK
1548 ret = wm_adsp2_ena(dsp);
1549 if (ret != 0)
1550 return;
2159ad93 1551
d8a64d6a
CK
1552 ret = wm_adsp_load(dsp);
1553 if (ret != 0)
1554 goto err;
2159ad93 1555
b618a185 1556 ret = wm_adsp2_setup_algs(dsp);
d8a64d6a
CK
1557 if (ret != 0)
1558 goto err;
db40517c 1559
d8a64d6a
CK
1560 ret = wm_adsp_load_coeff(dsp);
1561 if (ret != 0)
1562 goto err;
2159ad93 1563
d8a64d6a
CK
1564 /* Initialize caches for enabled and unset controls */
1565 ret = wm_coeff_init_control_caches(dsp);
1566 if (ret != 0)
1567 goto err;
6ab2b7b4 1568
d8a64d6a
CK
1569 /* Sync set controls */
1570 ret = wm_coeff_sync_controls(dsp);
1571 if (ret != 0)
1572 goto err;
1573
d8a64d6a
CK
1574 dsp->running = true;
1575
1576 return;
6ab2b7b4 1577
d8a64d6a
CK
1578err:
1579 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1580 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
1581}
1582
12db5edd
CK
1583int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
1584 struct snd_kcontrol *kcontrol, int event)
1585{
72718517 1586 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
12db5edd
CK
1587 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1588 struct wm_adsp *dsp = &dsps[w->shift];
1589
00200107 1590 dsp->card = codec->component.card;
12db5edd
CK
1591
1592 switch (event) {
1593 case SND_SOC_DAPM_PRE_PMU:
1594 queue_work(system_unbound_wq, &dsp->boot_work);
1595 break;
1596 default:
1597 break;
cab27258 1598 }
12db5edd
CK
1599
1600 return 0;
1601}
1602EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
1603
d8a64d6a
CK
1604int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1605 struct snd_kcontrol *kcontrol, int event)
1606{
72718517 1607 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
d8a64d6a
CK
1608 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1609 struct wm_adsp *dsp = &dsps[w->shift];
1610 struct wm_adsp_alg_region *alg_region;
1611 struct wm_coeff_ctl *ctl;
1612 int ret;
1613
d8a64d6a
CK
1614 switch (event) {
1615 case SND_SOC_DAPM_POST_PMU:
d8a64d6a
CK
1616 flush_work(&dsp->boot_work);
1617
1618 if (!dsp->running)
1619 return -EIO;
6ab2b7b4 1620
d8a64d6a
CK
1621 ret = regmap_update_bits(dsp->regmap,
1622 dsp->base + ADSP2_CONTROL,
00e4c3b6
CK
1623 ADSP2_CORE_ENA | ADSP2_START,
1624 ADSP2_CORE_ENA | ADSP2_START);
2159ad93
MB
1625 if (ret != 0)
1626 goto err;
1627 break;
1628
1629 case SND_SOC_DAPM_PRE_PMD:
1023dbd9
MB
1630 dsp->running = false;
1631
2159ad93 1632 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
a7f9be7e
MB
1633 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1634 ADSP2_START, 0);
973838a0 1635
2d30b575
MB
1636 /* Make sure DMAs are quiesced */
1637 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
1638 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
1639 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
1640
973838a0
MB
1641 if (dsp->dvfs) {
1642 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1643 1800000);
1644 if (ret != 0)
62c35b3b
CK
1645 adsp_warn(dsp,
1646 "Failed to lower supply: %d\n",
1647 ret);
973838a0
MB
1648
1649 ret = regulator_disable(dsp->dvfs);
1650 if (ret != 0)
62c35b3b
CK
1651 adsp_err(dsp,
1652 "Failed to enable supply: %d\n",
1653 ret);
973838a0 1654 }
471f4885 1655
81ad93ec 1656 list_for_each_entry(ctl, &dsp->ctl_list, list)
6ab2b7b4 1657 ctl->enabled = 0;
6ab2b7b4 1658
471f4885
MB
1659 while (!list_empty(&dsp->alg_regions)) {
1660 alg_region = list_first_entry(&dsp->alg_regions,
1661 struct wm_adsp_alg_region,
1662 list);
1663 list_del(&alg_region->list);
1664 kfree(alg_region);
1665 }
ddbc5efe
CK
1666
1667 adsp_dbg(dsp, "Shutdown complete\n");
2159ad93
MB
1668 break;
1669
1670 default:
1671 break;
1672 }
1673
1674 return 0;
1675err:
1676 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
a7f9be7e 1677 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
2159ad93
MB
1678 return ret;
1679}
1680EXPORT_SYMBOL_GPL(wm_adsp2_event);
973838a0 1681
3809f001 1682int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs)
973838a0
MB
1683{
1684 int ret;
1685
10a2b662
MB
1686 /*
1687 * Disable the DSP memory by default when in reset for a small
1688 * power saving.
1689 */
3809f001 1690 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
10a2b662
MB
1691 ADSP2_MEM_ENA, 0);
1692 if (ret != 0) {
3809f001 1693 adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
10a2b662
MB
1694 return ret;
1695 }
1696
3809f001
CK
1697 INIT_LIST_HEAD(&dsp->alg_regions);
1698 INIT_LIST_HEAD(&dsp->ctl_list);
1699 INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
6ab2b7b4 1700
973838a0 1701 if (dvfs) {
3809f001
CK
1702 dsp->dvfs = devm_regulator_get(dsp->dev, "DCVDD");
1703 if (IS_ERR(dsp->dvfs)) {
1704 ret = PTR_ERR(dsp->dvfs);
1705 adsp_err(dsp, "Failed to get DCVDD: %d\n", ret);
81ad93ec 1706 return ret;
973838a0
MB
1707 }
1708
3809f001 1709 ret = regulator_enable(dsp->dvfs);
973838a0 1710 if (ret != 0) {
3809f001 1711 adsp_err(dsp, "Failed to enable DCVDD: %d\n", ret);
81ad93ec 1712 return ret;
973838a0
MB
1713 }
1714
3809f001 1715 ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000);
973838a0 1716 if (ret != 0) {
3809f001 1717 adsp_err(dsp, "Failed to initialise DVFS: %d\n", ret);
81ad93ec 1718 return ret;
973838a0
MB
1719 }
1720
3809f001 1721 ret = regulator_disable(dsp->dvfs);
973838a0 1722 if (ret != 0) {
3809f001 1723 adsp_err(dsp, "Failed to disable DCVDD: %d\n", ret);
81ad93ec 1724 return ret;
973838a0
MB
1725 }
1726 }
1727
1728 return 0;
1729}
1730EXPORT_SYMBOL_GPL(wm_adsp2_init);
0a37c6ef
PD
1731
1732MODULE_LICENSE("GPL v2");