blob: 99f00f879c5e6576106df76e302a2adbafab4bab [file] [log] [blame]
Mark Brown2159ad932012-10-11 11:54:02 +09001/*
2 * wm_adsp.c -- Wolfson ADSP support
3 *
4 * Copyright 2012 Wolfson Microelectronics plc
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/firmware.h>
18#include <linux/pm.h>
19#include <linux/pm_runtime.h>
20#include <linux/regmap.h>
Mark Brown973838a2012-11-28 17:20:32 +000021#include <linux/regulator/consumer.h>
Mark Brown2159ad932012-10-11 11:54:02 +090022#include <linux/slab.h>
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/pcm_params.h>
26#include <sound/soc.h>
27#include <sound/jack.h>
28#include <sound/initval.h>
29#include <sound/tlv.h>
30
31#include <linux/mfd/arizona/registers.h>
32
33#include "wm_adsp.h"
34
35#define adsp_crit(_dsp, fmt, ...) \
36 dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
37#define adsp_err(_dsp, fmt, ...) \
38 dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
39#define adsp_warn(_dsp, fmt, ...) \
40 dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
41#define adsp_info(_dsp, fmt, ...) \
42 dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
43#define adsp_dbg(_dsp, fmt, ...) \
44 dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
45
46#define ADSP1_CONTROL_1 0x00
47#define ADSP1_CONTROL_2 0x02
48#define ADSP1_CONTROL_3 0x03
49#define ADSP1_CONTROL_4 0x04
50#define ADSP1_CONTROL_5 0x06
51#define ADSP1_CONTROL_6 0x07
52#define ADSP1_CONTROL_7 0x08
53#define ADSP1_CONTROL_8 0x09
54#define ADSP1_CONTROL_9 0x0A
55#define ADSP1_CONTROL_10 0x0B
56#define ADSP1_CONTROL_11 0x0C
57#define ADSP1_CONTROL_12 0x0D
58#define ADSP1_CONTROL_13 0x0F
59#define ADSP1_CONTROL_14 0x10
60#define ADSP1_CONTROL_15 0x11
61#define ADSP1_CONTROL_16 0x12
62#define ADSP1_CONTROL_17 0x13
63#define ADSP1_CONTROL_18 0x14
64#define ADSP1_CONTROL_19 0x16
65#define ADSP1_CONTROL_20 0x17
66#define ADSP1_CONTROL_21 0x18
67#define ADSP1_CONTROL_22 0x1A
68#define ADSP1_CONTROL_23 0x1B
69#define ADSP1_CONTROL_24 0x1C
70#define ADSP1_CONTROL_25 0x1E
71#define ADSP1_CONTROL_26 0x20
72#define ADSP1_CONTROL_27 0x21
73#define ADSP1_CONTROL_28 0x22
74#define ADSP1_CONTROL_29 0x23
75#define ADSP1_CONTROL_30 0x24
76#define ADSP1_CONTROL_31 0x26
77
78/*
79 * ADSP1 Control 19
80 */
81#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
82#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
83#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
84
85
86/*
87 * ADSP1 Control 30
88 */
89#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
90#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
91#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
92#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
93#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
94#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
95#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
96#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
97#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
98#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
99#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
100#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
101#define ADSP1_START 0x0001 /* DSP1_START */
102#define ADSP1_START_MASK 0x0001 /* DSP1_START */
103#define ADSP1_START_SHIFT 0 /* DSP1_START */
104#define ADSP1_START_WIDTH 1 /* DSP1_START */
105
Chris Rattray94e205b2013-01-18 08:43:09 +0000106/*
107 * ADSP1 Control 31
108 */
109#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
110#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
111#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
112
Mark Brown973838a2012-11-28 17:20:32 +0000113#define ADSP2_CONTROL 0
114#define ADSP2_CLOCKING 1
115#define ADSP2_STATUS1 4
Mark Brown2159ad932012-10-11 11:54:02 +0900116
117/*
118 * ADSP2 Control
119 */
120
121#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
122#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
123#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
124#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
125#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
126#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
127#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
128#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
129#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
130#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
131#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
132#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
133#define ADSP2_START 0x0001 /* DSP1_START */
134#define ADSP2_START_MASK 0x0001 /* DSP1_START */
135#define ADSP2_START_SHIFT 0 /* DSP1_START */
136#define ADSP2_START_WIDTH 1 /* DSP1_START */
137
138/*
Mark Brown973838a2012-11-28 17:20:32 +0000139 * ADSP2 clocking
140 */
141#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
142#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
143#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
144
145/*
Mark Brown2159ad932012-10-11 11:54:02 +0900146 * ADSP2 Status 1
147 */
148#define ADSP2_RAM_RDY 0x0001
149#define ADSP2_RAM_RDY_MASK 0x0001
150#define ADSP2_RAM_RDY_SHIFT 0
151#define ADSP2_RAM_RDY_WIDTH 1
152
Mark Brown36e8fe92013-01-25 17:47:48 +0800153#define WM_ADSP_NUM_FW 4
Mark Brown1023dbd2013-01-11 22:58:28 +0000154
155static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Mark Brown36e8fe92013-01-25 17:47:48 +0800156 "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC"
Mark Brown1023dbd2013-01-11 22:58:28 +0000157};
158
159static struct {
160 const char *file;
161} wm_adsp_fw[WM_ADSP_NUM_FW] = {
162 { .file = "mbc-vss" },
163 { .file = "tx" },
Mark Brown36e8fe92013-01-25 17:47:48 +0800164 { .file = "tx-spk" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000165 { .file = "rx-anc" },
166};
167
168static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
169 struct snd_ctl_elem_value *ucontrol)
170{
171 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
172 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
173 struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
174
175 ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
176
177 return 0;
178}
179
180static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
181 struct snd_ctl_elem_value *ucontrol)
182{
183 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
184 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
185 struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
186
187 if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
188 return 0;
189
190 if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
191 return -EINVAL;
192
193 if (adsp[e->shift_l].running)
194 return -EBUSY;
195
196 adsp->fw = ucontrol->value.integer.value[0];
197
198 return 0;
199}
200
201static const struct soc_enum wm_adsp_fw_enum[] = {
202 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
203 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
204 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
205 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
206};
207
208const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
209 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
210 wm_adsp_fw_get, wm_adsp_fw_put),
211 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
212 wm_adsp_fw_get, wm_adsp_fw_put),
213 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
214 wm_adsp_fw_get, wm_adsp_fw_put),
215 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
216 wm_adsp_fw_get, wm_adsp_fw_put),
217};
218EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
Mark Brown2159ad932012-10-11 11:54:02 +0900219
220static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
221 int type)
222{
223 int i;
224
225 for (i = 0; i < dsp->num_mems; i++)
226 if (dsp->mem[i].type == type)
227 return &dsp->mem[i];
228
229 return NULL;
230}
231
Mark Brown45b9ee72013-01-08 16:02:06 +0000232static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
233 unsigned int offset)
234{
235 switch (region->type) {
236 case WMFW_ADSP1_PM:
237 return region->base + (offset * 3);
238 case WMFW_ADSP1_DM:
239 return region->base + (offset * 2);
240 case WMFW_ADSP2_XM:
241 return region->base + (offset * 2);
242 case WMFW_ADSP2_YM:
243 return region->base + (offset * 2);
244 case WMFW_ADSP1_ZM:
245 return region->base + (offset * 2);
246 default:
247 WARN_ON(NULL != "Unknown memory region type");
248 return offset;
249 }
250}
251
Mark Brown2159ad932012-10-11 11:54:02 +0900252static int wm_adsp_load(struct wm_adsp *dsp)
253{
254 const struct firmware *firmware;
255 struct regmap *regmap = dsp->regmap;
256 unsigned int pos = 0;
257 const struct wmfw_header *header;
258 const struct wmfw_adsp1_sizes *adsp1_sizes;
259 const struct wmfw_adsp2_sizes *adsp2_sizes;
260 const struct wmfw_footer *footer;
261 const struct wmfw_region *region;
262 const struct wm_adsp_region *mem;
263 const char *region_name;
264 char *file, *text;
Mark Browna76fefa2013-01-07 19:03:17 +0000265 void *buf;
Mark Brown2159ad932012-10-11 11:54:02 +0900266 unsigned int reg;
267 int regions = 0;
268 int ret, offset, type, sizes;
269
270 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
271 if (file == NULL)
272 return -ENOMEM;
273
Mark Brown1023dbd2013-01-11 22:58:28 +0000274 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
275 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +0900276 file[PAGE_SIZE - 1] = '\0';
277
278 ret = request_firmware(&firmware, file, dsp->dev);
279 if (ret != 0) {
280 adsp_err(dsp, "Failed to request '%s'\n", file);
281 goto out;
282 }
283 ret = -EINVAL;
284
285 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
286 if (pos >= firmware->size) {
287 adsp_err(dsp, "%s: file too short, %zu bytes\n",
288 file, firmware->size);
289 goto out_fw;
290 }
291
292 header = (void*)&firmware->data[0];
293
294 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
295 adsp_err(dsp, "%s: invalid magic\n", file);
296 goto out_fw;
297 }
298
299 if (header->ver != 0) {
300 adsp_err(dsp, "%s: unknown file format %d\n",
301 file, header->ver);
302 goto out_fw;
303 }
304
305 if (header->core != dsp->type) {
306 adsp_err(dsp, "%s: invalid core %d != %d\n",
307 file, header->core, dsp->type);
308 goto out_fw;
309 }
310
311 switch (dsp->type) {
312 case WMFW_ADSP1:
313 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
314 adsp1_sizes = (void *)&(header[1]);
315 footer = (void *)&(adsp1_sizes[1]);
316 sizes = sizeof(*adsp1_sizes);
317
318 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
319 file, le32_to_cpu(adsp1_sizes->dm),
320 le32_to_cpu(adsp1_sizes->pm),
321 le32_to_cpu(adsp1_sizes->zm));
322 break;
323
324 case WMFW_ADSP2:
325 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
326 adsp2_sizes = (void *)&(header[1]);
327 footer = (void *)&(adsp2_sizes[1]);
328 sizes = sizeof(*adsp2_sizes);
329
330 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
331 file, le32_to_cpu(adsp2_sizes->xm),
332 le32_to_cpu(adsp2_sizes->ym),
333 le32_to_cpu(adsp2_sizes->pm),
334 le32_to_cpu(adsp2_sizes->zm));
335 break;
336
337 default:
338 BUG_ON(NULL == "Unknown DSP type");
339 goto out_fw;
340 }
341
342 if (le32_to_cpu(header->len) != sizeof(*header) +
343 sizes + sizeof(*footer)) {
344 adsp_err(dsp, "%s: unexpected header length %d\n",
345 file, le32_to_cpu(header->len));
346 goto out_fw;
347 }
348
349 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
350 le64_to_cpu(footer->timestamp));
351
352 while (pos < firmware->size &&
353 pos - firmware->size > sizeof(*region)) {
354 region = (void *)&(firmware->data[pos]);
355 region_name = "Unknown";
356 reg = 0;
357 text = NULL;
358 offset = le32_to_cpu(region->offset) & 0xffffff;
359 type = be32_to_cpu(region->type) & 0xff;
360 mem = wm_adsp_find_region(dsp, type);
361
362 switch (type) {
363 case WMFW_NAME_TEXT:
364 region_name = "Firmware name";
365 text = kzalloc(le32_to_cpu(region->len) + 1,
366 GFP_KERNEL);
367 break;
368 case WMFW_INFO_TEXT:
369 region_name = "Information";
370 text = kzalloc(le32_to_cpu(region->len) + 1,
371 GFP_KERNEL);
372 break;
373 case WMFW_ABSOLUTE:
374 region_name = "Absolute";
375 reg = offset;
376 break;
377 case WMFW_ADSP1_PM:
378 BUG_ON(!mem);
379 region_name = "PM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000380 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900381 break;
382 case WMFW_ADSP1_DM:
383 BUG_ON(!mem);
384 region_name = "DM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000385 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900386 break;
387 case WMFW_ADSP2_XM:
388 BUG_ON(!mem);
389 region_name = "XM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000390 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900391 break;
392 case WMFW_ADSP2_YM:
393 BUG_ON(!mem);
394 region_name = "YM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000395 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900396 break;
397 case WMFW_ADSP1_ZM:
398 BUG_ON(!mem);
399 region_name = "ZM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000400 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900401 break;
402 default:
403 adsp_warn(dsp,
404 "%s.%d: Unknown region type %x at %d(%x)\n",
405 file, regions, type, pos, pos);
406 break;
407 }
408
409 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
410 regions, le32_to_cpu(region->len), offset,
411 region_name);
412
413 if (text) {
414 memcpy(text, region->data, le32_to_cpu(region->len));
415 adsp_info(dsp, "%s: %s\n", file, text);
416 kfree(text);
417 }
418
419 if (reg) {
Mark Browna76fefa2013-01-07 19:03:17 +0000420 buf = kmemdup(region->data, le32_to_cpu(region->len),
Mark Brown7881fd02013-01-20 19:01:03 +0900421 GFP_KERNEL | GFP_DMA);
Mark Browna76fefa2013-01-07 19:03:17 +0000422 if (!buf) {
423 adsp_err(dsp, "Out of memory\n");
424 return -ENOMEM;
425 }
426
427 ret = regmap_raw_write(regmap, reg, buf,
Mark Brown2159ad932012-10-11 11:54:02 +0900428 le32_to_cpu(region->len));
Mark Browna76fefa2013-01-07 19:03:17 +0000429
430 kfree(buf);
431
Mark Brown2159ad932012-10-11 11:54:02 +0900432 if (ret != 0) {
433 adsp_err(dsp,
434 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
435 file, regions,
436 le32_to_cpu(region->len), offset,
437 region_name, ret);
438 goto out_fw;
439 }
440 }
441
442 pos += le32_to_cpu(region->len) + sizeof(*region);
443 regions++;
444 }
445
446 if (pos > firmware->size)
447 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
448 file, regions, pos - firmware->size);
449
450out_fw:
451 release_firmware(firmware);
452out:
453 kfree(file);
454
455 return ret;
456}
457
Mark Browndb405172012-10-26 19:30:40 +0100458static int wm_adsp_setup_algs(struct wm_adsp *dsp)
459{
460 struct regmap *regmap = dsp->regmap;
461 struct wmfw_adsp1_id_hdr adsp1_id;
462 struct wmfw_adsp2_id_hdr adsp2_id;
463 struct wmfw_adsp1_alg_hdr *adsp1_alg;
464 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000465 void *alg, *buf;
Mark Brown471f4882013-01-08 16:09:31 +0000466 struct wm_adsp_alg_region *region;
Mark Browndb405172012-10-26 19:30:40 +0100467 const struct wm_adsp_region *mem;
468 unsigned int pos, term;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000469 size_t algs, buf_size;
Mark Browndb405172012-10-26 19:30:40 +0100470 __be32 val;
471 int i, ret;
472
473 switch (dsp->type) {
474 case WMFW_ADSP1:
475 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
476 break;
477 case WMFW_ADSP2:
478 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
479 break;
480 default:
481 mem = NULL;
482 break;
483 }
484
485 if (mem == NULL) {
486 BUG_ON(mem != NULL);
487 return -EINVAL;
488 }
489
490 switch (dsp->type) {
491 case WMFW_ADSP1:
492 ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
493 sizeof(adsp1_id));
494 if (ret != 0) {
495 adsp_err(dsp, "Failed to read algorithm info: %d\n",
496 ret);
497 return ret;
498 }
499
Mark Brownd62f4bc2012-12-19 14:00:30 +0000500 buf = &adsp1_id;
501 buf_size = sizeof(adsp1_id);
502
Mark Browndb405172012-10-26 19:30:40 +0100503 algs = be32_to_cpu(adsp1_id.algs);
504 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
505 be32_to_cpu(adsp1_id.fw.id),
506 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
507 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
508 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
509 algs);
510
511 pos = sizeof(adsp1_id) / 2;
512 term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
513 break;
514
515 case WMFW_ADSP2:
516 ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
517 sizeof(adsp2_id));
518 if (ret != 0) {
519 adsp_err(dsp, "Failed to read algorithm info: %d\n",
520 ret);
521 return ret;
522 }
523
Mark Brownd62f4bc2012-12-19 14:00:30 +0000524 buf = &adsp2_id;
525 buf_size = sizeof(adsp2_id);
526
Mark Browndb405172012-10-26 19:30:40 +0100527 algs = be32_to_cpu(adsp2_id.algs);
528 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
529 be32_to_cpu(adsp2_id.fw.id),
530 (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
531 (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
532 be32_to_cpu(adsp2_id.fw.ver) & 0xff,
533 algs);
534
535 pos = sizeof(adsp2_id) / 2;
536 term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
537 break;
538
539 default:
540 BUG_ON(NULL == "Unknown DSP type");
541 return -EINVAL;
542 }
543
544 if (algs == 0) {
545 adsp_err(dsp, "No algorithms\n");
546 return -EINVAL;
547 }
548
Mark Brownd62f4bc2012-12-19 14:00:30 +0000549 if (algs > 1024) {
550 adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
551 print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
552 buf, buf_size);
553 return -EINVAL;
554 }
555
Mark Browndb405172012-10-26 19:30:40 +0100556 /* Read the terminator first to validate the length */
557 ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
558 if (ret != 0) {
559 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
560 ret);
561 return ret;
562 }
563
564 if (be32_to_cpu(val) != 0xbedead)
565 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
566 term, be32_to_cpu(val));
567
Mark Brownf2a93e22013-01-20 22:17:30 +0900568 alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +0100569 if (!alg)
570 return -ENOMEM;
571
572 ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
573 if (ret != 0) {
574 adsp_err(dsp, "Failed to read algorithm list: %d\n",
575 ret);
576 goto out;
577 }
578
579 adsp1_alg = alg;
580 adsp2_alg = alg;
581
582 for (i = 0; i < algs; i++) {
583 switch (dsp->type) {
584 case WMFW_ADSP1:
Mark Brown471f4882013-01-08 16:09:31 +0000585 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100586 i, be32_to_cpu(adsp1_alg[i].alg.id),
587 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
588 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000589 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
590 be32_to_cpu(adsp1_alg[i].dm),
591 be32_to_cpu(adsp1_alg[i].zm));
592
593 if (adsp1_alg[i].dm) {
594 region = kzalloc(sizeof(*region), GFP_KERNEL);
595 if (!region)
596 return -ENOMEM;
597 region->type = WMFW_ADSP1_DM;
598 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
599 region->base = be32_to_cpu(adsp1_alg[i].dm);
600 list_add_tail(&region->list,
601 &dsp->alg_regions);
602 }
603
604 if (adsp1_alg[i].zm) {
605 region = kzalloc(sizeof(*region), GFP_KERNEL);
606 if (!region)
607 return -ENOMEM;
608 region->type = WMFW_ADSP1_ZM;
609 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
610 region->base = be32_to_cpu(adsp1_alg[i].zm);
611 list_add_tail(&region->list,
612 &dsp->alg_regions);
613 }
Mark Browndb405172012-10-26 19:30:40 +0100614 break;
615
616 case WMFW_ADSP2:
Mark Brown471f4882013-01-08 16:09:31 +0000617 adsp_info(dsp,
618 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100619 i, be32_to_cpu(adsp2_alg[i].alg.id),
620 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
621 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000622 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
623 be32_to_cpu(adsp2_alg[i].xm),
624 be32_to_cpu(adsp2_alg[i].ym),
625 be32_to_cpu(adsp2_alg[i].zm));
626
627 if (adsp2_alg[i].xm) {
628 region = kzalloc(sizeof(*region), GFP_KERNEL);
629 if (!region)
630 return -ENOMEM;
631 region->type = WMFW_ADSP2_XM;
632 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
633 region->base = be32_to_cpu(adsp2_alg[i].xm);
634 list_add_tail(&region->list,
635 &dsp->alg_regions);
636 }
637
638 if (adsp2_alg[i].ym) {
639 region = kzalloc(sizeof(*region), GFP_KERNEL);
640 if (!region)
641 return -ENOMEM;
642 region->type = WMFW_ADSP2_YM;
643 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
644 region->base = be32_to_cpu(adsp2_alg[i].ym);
645 list_add_tail(&region->list,
646 &dsp->alg_regions);
647 }
648
649 if (adsp2_alg[i].zm) {
650 region = kzalloc(sizeof(*region), GFP_KERNEL);
651 if (!region)
652 return -ENOMEM;
653 region->type = WMFW_ADSP2_ZM;
654 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
655 region->base = be32_to_cpu(adsp2_alg[i].zm);
656 list_add_tail(&region->list,
657 &dsp->alg_regions);
658 }
Mark Browndb405172012-10-26 19:30:40 +0100659 break;
660 }
661 }
662
663out:
664 kfree(alg);
665 return ret;
666}
667
Mark Brown2159ad932012-10-11 11:54:02 +0900668static int wm_adsp_load_coeff(struct wm_adsp *dsp)
669{
670 struct regmap *regmap = dsp->regmap;
671 struct wmfw_coeff_hdr *hdr;
672 struct wmfw_coeff_item *blk;
673 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +0000674 const struct wm_adsp_region *mem;
675 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad932012-10-11 11:54:02 +0900676 const char *region_name;
677 int ret, pos, blocks, type, offset, reg;
678 char *file;
Mark Browna76fefa2013-01-07 19:03:17 +0000679 void *buf;
Mark Brown2159ad932012-10-11 11:54:02 +0900680
681 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
682 if (file == NULL)
683 return -ENOMEM;
684
Mark Brown1023dbd2013-01-11 22:58:28 +0000685 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
686 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +0900687 file[PAGE_SIZE - 1] = '\0';
688
689 ret = request_firmware(&firmware, file, dsp->dev);
690 if (ret != 0) {
691 adsp_warn(dsp, "Failed to request '%s'\n", file);
692 ret = 0;
693 goto out;
694 }
695 ret = -EINVAL;
696
697 if (sizeof(*hdr) >= firmware->size) {
698 adsp_err(dsp, "%s: file too short, %zu bytes\n",
699 file, firmware->size);
700 goto out_fw;
701 }
702
703 hdr = (void*)&firmware->data[0];
704 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
705 adsp_err(dsp, "%s: invalid magic\n", file);
706 return -EINVAL;
707 }
708
Mark Brownc7123262013-01-16 16:59:04 +0900709 switch (be32_to_cpu(hdr->rev) & 0xff) {
710 case 1:
711 break;
712 default:
713 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
714 file, be32_to_cpu(hdr->rev) & 0xff);
715 ret = -EINVAL;
716 goto out_fw;
717 }
718
Mark Brown2159ad932012-10-11 11:54:02 +0900719 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
720 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
721 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
722 le32_to_cpu(hdr->ver) & 0xff);
723
724 pos = le32_to_cpu(hdr->len);
725
726 blocks = 0;
727 while (pos < firmware->size &&
728 pos - firmware->size > sizeof(*blk)) {
729 blk = (void*)(&firmware->data[pos]);
730
Mark Brownc7123262013-01-16 16:59:04 +0900731 type = le16_to_cpu(blk->type);
732 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900733
734 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
735 file, blocks, le32_to_cpu(blk->id),
736 (le32_to_cpu(blk->ver) >> 16) & 0xff,
737 (le32_to_cpu(blk->ver) >> 8) & 0xff,
738 le32_to_cpu(blk->ver) & 0xff);
739 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
740 file, blocks, le32_to_cpu(blk->len), offset, type);
741
742 reg = 0;
743 region_name = "Unknown";
744 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +0900745 case (WMFW_NAME_TEXT << 8):
746 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad932012-10-11 11:54:02 +0900747 break;
Mark Brownc7123262013-01-16 16:59:04 +0900748 case (WMFW_ABSOLUTE << 8):
Mark Brown2159ad932012-10-11 11:54:02 +0900749 region_name = "register";
750 reg = offset;
751 break;
Mark Brown471f4882013-01-08 16:09:31 +0000752
753 case WMFW_ADSP1_DM:
754 case WMFW_ADSP1_ZM:
755 case WMFW_ADSP2_XM:
756 case WMFW_ADSP2_YM:
757 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
758 file, blocks, le32_to_cpu(blk->len),
759 type, le32_to_cpu(blk->id));
760
761 mem = wm_adsp_find_region(dsp, type);
762 if (!mem) {
763 adsp_err(dsp, "No base for region %x\n", type);
764 break;
765 }
766
767 reg = 0;
768 list_for_each_entry(alg_region,
769 &dsp->alg_regions, list) {
770 if (le32_to_cpu(blk->id) == alg_region->alg &&
771 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +0800772 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +0000773 reg = wm_adsp_region_to_reg(mem,
774 reg);
Mark Brown338c5182013-01-24 00:35:48 +0800775 reg += offset;
Mark Brown471f4882013-01-08 16:09:31 +0000776 }
777 }
778
779 if (reg == 0)
780 adsp_err(dsp, "No %x for algorithm %x\n",
781 type, le32_to_cpu(blk->id));
782 break;
783
Mark Brown2159ad932012-10-11 11:54:02 +0900784 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +0900785 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
786 file, blocks, type, pos);
Mark Brown2159ad932012-10-11 11:54:02 +0900787 break;
788 }
789
790 if (reg) {
Mark Browna76fefa2013-01-07 19:03:17 +0000791 buf = kmemdup(blk->data, le32_to_cpu(blk->len),
Mark Brown7881fd02013-01-20 19:01:03 +0900792 GFP_KERNEL | GFP_DMA);
Mark Browna76fefa2013-01-07 19:03:17 +0000793 if (!buf) {
794 adsp_err(dsp, "Out of memory\n");
795 return -ENOMEM;
796 }
797
Mark Brown20da6d52013-01-12 19:58:17 +0000798 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
799 file, blocks, le32_to_cpu(blk->len),
800 reg);
Mark Brown2159ad932012-10-11 11:54:02 +0900801 ret = regmap_raw_write(regmap, reg, blk->data,
802 le32_to_cpu(blk->len));
803 if (ret != 0) {
804 adsp_err(dsp,
805 "%s.%d: Failed to write to %x in %s\n",
806 file, blocks, reg, region_name);
807 }
Mark Browna76fefa2013-01-07 19:03:17 +0000808
809 kfree(buf);
Mark Brown2159ad932012-10-11 11:54:02 +0900810 }
811
812 pos += le32_to_cpu(blk->len) + sizeof(*blk);
813 blocks++;
814 }
815
816 if (pos > firmware->size)
817 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
818 file, blocks, pos - firmware->size);
819
820out_fw:
821 release_firmware(firmware);
822out:
823 kfree(file);
824 return 0;
825}
826
Mark Brown5e7a7a22013-01-16 10:03:56 +0900827int wm_adsp1_init(struct wm_adsp *adsp)
828{
829 INIT_LIST_HEAD(&adsp->alg_regions);
830
831 return 0;
832}
833EXPORT_SYMBOL_GPL(wm_adsp1_init);
834
Mark Brown2159ad932012-10-11 11:54:02 +0900835int wm_adsp1_event(struct snd_soc_dapm_widget *w,
836 struct snd_kcontrol *kcontrol,
837 int event)
838{
839 struct snd_soc_codec *codec = w->codec;
840 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
841 struct wm_adsp *dsp = &dsps[w->shift];
842 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +0000843 int val;
Mark Brown2159ad932012-10-11 11:54:02 +0900844
845 switch (event) {
846 case SND_SOC_DAPM_POST_PMU:
847 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
848 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
849
Chris Rattray94e205b2013-01-18 08:43:09 +0000850 /*
851 * For simplicity set the DSP clock rate to be the
852 * SYSCLK rate rather than making it configurable.
853 */
854 if(dsp->sysclk_reg) {
855 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
856 if (ret != 0) {
857 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
858 ret);
859 return ret;
860 }
861
862 val = (val & dsp->sysclk_mask)
863 >> dsp->sysclk_shift;
864
865 ret = regmap_update_bits(dsp->regmap,
866 dsp->base + ADSP1_CONTROL_31,
867 ADSP1_CLK_SEL_MASK, val);
868 if (ret != 0) {
869 adsp_err(dsp, "Failed to set clock rate: %d\n",
870 ret);
871 return ret;
872 }
873 }
874
Mark Brown2159ad932012-10-11 11:54:02 +0900875 ret = wm_adsp_load(dsp);
876 if (ret != 0)
877 goto err;
878
Mark Browndb405172012-10-26 19:30:40 +0100879 ret = wm_adsp_setup_algs(dsp);
880 if (ret != 0)
881 goto err;
882
Mark Brown2159ad932012-10-11 11:54:02 +0900883 ret = wm_adsp_load_coeff(dsp);
884 if (ret != 0)
885 goto err;
886
887 /* Start the core running */
888 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
889 ADSP1_CORE_ENA | ADSP1_START,
890 ADSP1_CORE_ENA | ADSP1_START);
891 break;
892
893 case SND_SOC_DAPM_PRE_PMD:
894 /* Halt the core */
895 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
896 ADSP1_CORE_ENA | ADSP1_START, 0);
897
898 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
899 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
900
901 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
902 ADSP1_SYS_ENA, 0);
903 break;
904
905 default:
906 break;
907 }
908
909 return 0;
910
911err:
912 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
913 ADSP1_SYS_ENA, 0);
914 return ret;
915}
916EXPORT_SYMBOL_GPL(wm_adsp1_event);
917
918static int wm_adsp2_ena(struct wm_adsp *dsp)
919{
920 unsigned int val;
921 int ret, count;
922
923 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
924 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
925 if (ret != 0)
926 return ret;
927
928 /* Wait for the RAM to start, should be near instantaneous */
929 count = 0;
930 do {
931 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
932 &val);
933 if (ret != 0)
934 return ret;
935 } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
936
937 if (!(val & ADSP2_RAM_RDY)) {
938 adsp_err(dsp, "Failed to start DSP RAM\n");
939 return -EBUSY;
940 }
941
942 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
943 adsp_info(dsp, "RAM ready after %d polls\n", count);
944
945 return 0;
946}
947
948int wm_adsp2_event(struct snd_soc_dapm_widget *w,
949 struct snd_kcontrol *kcontrol, int event)
950{
951 struct snd_soc_codec *codec = w->codec;
952 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
953 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +0000954 struct wm_adsp_alg_region *alg_region;
Mark Brown973838a2012-11-28 17:20:32 +0000955 unsigned int val;
Mark Brown2159ad932012-10-11 11:54:02 +0900956 int ret;
957
958 switch (event) {
959 case SND_SOC_DAPM_POST_PMU:
Mark Browndd49e2c2012-12-02 21:50:46 +0900960 /*
961 * For simplicity set the DSP clock rate to be the
962 * SYSCLK rate rather than making it configurable.
963 */
964 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
965 if (ret != 0) {
966 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
967 ret);
968 return ret;
969 }
970 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
971 >> ARIZONA_SYSCLK_FREQ_SHIFT;
972
973 ret = regmap_update_bits(dsp->regmap,
974 dsp->base + ADSP2_CLOCKING,
975 ADSP2_CLK_SEL_MASK, val);
976 if (ret != 0) {
977 adsp_err(dsp, "Failed to set clock rate: %d\n",
978 ret);
979 return ret;
980 }
981
Mark Brown973838a2012-11-28 17:20:32 +0000982 if (dsp->dvfs) {
983 ret = regmap_read(dsp->regmap,
984 dsp->base + ADSP2_CLOCKING, &val);
985 if (ret != 0) {
986 dev_err(dsp->dev,
987 "Failed to read clocking: %d\n", ret);
988 return ret;
989 }
990
Mark Brown25c6fdb2012-11-29 15:16:10 +0000991 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
Mark Brown973838a2012-11-28 17:20:32 +0000992 ret = regulator_enable(dsp->dvfs);
993 if (ret != 0) {
994 dev_err(dsp->dev,
995 "Failed to enable supply: %d\n",
996 ret);
997 return ret;
998 }
999
1000 ret = regulator_set_voltage(dsp->dvfs,
1001 1800000,
1002 1800000);
1003 if (ret != 0) {
1004 dev_err(dsp->dev,
1005 "Failed to raise supply: %d\n",
1006 ret);
1007 return ret;
1008 }
1009 }
1010 }
1011
Mark Brown2159ad932012-10-11 11:54:02 +09001012 ret = wm_adsp2_ena(dsp);
1013 if (ret != 0)
1014 return ret;
1015
1016 ret = wm_adsp_load(dsp);
1017 if (ret != 0)
1018 goto err;
1019
Mark Browndb405172012-10-26 19:30:40 +01001020 ret = wm_adsp_setup_algs(dsp);
1021 if (ret != 0)
1022 goto err;
1023
Mark Brown2159ad932012-10-11 11:54:02 +09001024 ret = wm_adsp_load_coeff(dsp);
1025 if (ret != 0)
1026 goto err;
1027
1028 ret = regmap_update_bits(dsp->regmap,
1029 dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001030 ADSP2_CORE_ENA | ADSP2_START,
1031 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad932012-10-11 11:54:02 +09001032 if (ret != 0)
1033 goto err;
Mark Brown1023dbd2013-01-11 22:58:28 +00001034
1035 dsp->running = true;
Mark Brown2159ad932012-10-11 11:54:02 +09001036 break;
1037
1038 case SND_SOC_DAPM_PRE_PMD:
Mark Brown1023dbd2013-01-11 22:58:28 +00001039 dsp->running = false;
1040
Mark Brown2159ad932012-10-11 11:54:02 +09001041 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001042 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1043 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00001044
1045 if (dsp->dvfs) {
1046 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1047 1800000);
1048 if (ret != 0)
1049 dev_warn(dsp->dev,
1050 "Failed to lower supply: %d\n",
1051 ret);
1052
1053 ret = regulator_disable(dsp->dvfs);
1054 if (ret != 0)
1055 dev_err(dsp->dev,
1056 "Failed to enable supply: %d\n",
1057 ret);
1058 }
Mark Brown471f4882013-01-08 16:09:31 +00001059
1060 while (!list_empty(&dsp->alg_regions)) {
1061 alg_region = list_first_entry(&dsp->alg_regions,
1062 struct wm_adsp_alg_region,
1063 list);
1064 list_del(&alg_region->list);
1065 kfree(alg_region);
1066 }
Mark Brown2159ad932012-10-11 11:54:02 +09001067 break;
1068
1069 default:
1070 break;
1071 }
1072
1073 return 0;
1074err:
1075 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001076 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad932012-10-11 11:54:02 +09001077 return ret;
1078}
1079EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00001080
1081int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1082{
1083 int ret;
1084
Mark Brown10a2b662012-12-02 21:37:00 +09001085 /*
1086 * Disable the DSP memory by default when in reset for a small
1087 * power saving.
1088 */
1089 ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
1090 ADSP2_MEM_ENA, 0);
1091 if (ret != 0) {
1092 adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
1093 return ret;
1094 }
1095
Mark Brown471f4882013-01-08 16:09:31 +00001096 INIT_LIST_HEAD(&adsp->alg_regions);
1097
Mark Brown973838a2012-11-28 17:20:32 +00001098 if (dvfs) {
1099 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1100 if (IS_ERR(adsp->dvfs)) {
1101 ret = PTR_ERR(adsp->dvfs);
1102 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1103 return ret;
1104 }
1105
1106 ret = regulator_enable(adsp->dvfs);
1107 if (ret != 0) {
1108 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1109 ret);
1110 return ret;
1111 }
1112
1113 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1114 if (ret != 0) {
1115 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1116 ret);
1117 return ret;
1118 }
1119
1120 ret = regulator_disable(adsp->dvfs);
1121 if (ret != 0) {
1122 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1123 ret);
1124 return ret;
1125 }
1126 }
1127
1128 return 0;
1129}
1130EXPORT_SYMBOL_GPL(wm_adsp2_init);