blob: 888316484541d55991e1506f2c40dc062e66845e [file] [log] [blame]
Mark Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-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 Brown2159ad92012-10-11 11:54:02 +0900428 le32_to_cpu(region->len));
Mark Browna76fefa2013-01-07 19:03:17 +0000429
430 kfree(buf);
431
Mark Brown2159ad92012-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
Mark Brown74808002013-01-26 00:29:51 +0800593 region = kzalloc(sizeof(*region), GFP_KERNEL);
594 if (!region)
595 return -ENOMEM;
596 region->type = WMFW_ADSP1_DM;
597 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
598 region->base = be32_to_cpu(adsp1_alg[i].dm);
599 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000600
Mark Brown74808002013-01-26 00:29:51 +0800601 region = kzalloc(sizeof(*region), GFP_KERNEL);
602 if (!region)
603 return -ENOMEM;
604 region->type = WMFW_ADSP1_ZM;
605 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
606 region->base = be32_to_cpu(adsp1_alg[i].zm);
607 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100608 break;
609
610 case WMFW_ADSP2:
Mark Brown471f4882013-01-08 16:09:31 +0000611 adsp_info(dsp,
612 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100613 i, be32_to_cpu(adsp2_alg[i].alg.id),
614 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
615 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000616 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
617 be32_to_cpu(adsp2_alg[i].xm),
618 be32_to_cpu(adsp2_alg[i].ym),
619 be32_to_cpu(adsp2_alg[i].zm));
620
Mark Brown74808002013-01-26 00:29:51 +0800621 region = kzalloc(sizeof(*region), GFP_KERNEL);
622 if (!region)
623 return -ENOMEM;
624 region->type = WMFW_ADSP2_XM;
625 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
626 region->base = be32_to_cpu(adsp2_alg[i].xm);
627 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000628
Mark Brown74808002013-01-26 00:29:51 +0800629 region = kzalloc(sizeof(*region), GFP_KERNEL);
630 if (!region)
631 return -ENOMEM;
632 region->type = WMFW_ADSP2_YM;
633 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
634 region->base = be32_to_cpu(adsp2_alg[i].ym);
635 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000636
Mark Brown74808002013-01-26 00:29:51 +0800637 region = kzalloc(sizeof(*region), GFP_KERNEL);
638 if (!region)
639 return -ENOMEM;
640 region->type = WMFW_ADSP2_ZM;
641 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
642 region->base = be32_to_cpu(adsp2_alg[i].zm);
643 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100644 break;
645 }
646 }
647
648out:
649 kfree(alg);
650 return ret;
651}
652
Mark Brown2159ad92012-10-11 11:54:02 +0900653static int wm_adsp_load_coeff(struct wm_adsp *dsp)
654{
655 struct regmap *regmap = dsp->regmap;
656 struct wmfw_coeff_hdr *hdr;
657 struct wmfw_coeff_item *blk;
658 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +0000659 const struct wm_adsp_region *mem;
660 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad92012-10-11 11:54:02 +0900661 const char *region_name;
662 int ret, pos, blocks, type, offset, reg;
663 char *file;
Mark Browna76fefa2013-01-07 19:03:17 +0000664 void *buf;
Mark Brown2159ad92012-10-11 11:54:02 +0900665
666 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
667 if (file == NULL)
668 return -ENOMEM;
669
Mark Brown1023dbd2013-01-11 22:58:28 +0000670 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
671 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +0900672 file[PAGE_SIZE - 1] = '\0';
673
674 ret = request_firmware(&firmware, file, dsp->dev);
675 if (ret != 0) {
676 adsp_warn(dsp, "Failed to request '%s'\n", file);
677 ret = 0;
678 goto out;
679 }
680 ret = -EINVAL;
681
682 if (sizeof(*hdr) >= firmware->size) {
683 adsp_err(dsp, "%s: file too short, %zu bytes\n",
684 file, firmware->size);
685 goto out_fw;
686 }
687
688 hdr = (void*)&firmware->data[0];
689 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
690 adsp_err(dsp, "%s: invalid magic\n", file);
691 return -EINVAL;
692 }
693
Mark Brownc7123262013-01-16 16:59:04 +0900694 switch (be32_to_cpu(hdr->rev) & 0xff) {
695 case 1:
696 break;
697 default:
698 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
699 file, be32_to_cpu(hdr->rev) & 0xff);
700 ret = -EINVAL;
701 goto out_fw;
702 }
703
Mark Brown2159ad92012-10-11 11:54:02 +0900704 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
705 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
706 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
707 le32_to_cpu(hdr->ver) & 0xff);
708
709 pos = le32_to_cpu(hdr->len);
710
711 blocks = 0;
712 while (pos < firmware->size &&
713 pos - firmware->size > sizeof(*blk)) {
714 blk = (void*)(&firmware->data[pos]);
715
Mark Brownc7123262013-01-16 16:59:04 +0900716 type = le16_to_cpu(blk->type);
717 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900718
719 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
720 file, blocks, le32_to_cpu(blk->id),
721 (le32_to_cpu(blk->ver) >> 16) & 0xff,
722 (le32_to_cpu(blk->ver) >> 8) & 0xff,
723 le32_to_cpu(blk->ver) & 0xff);
724 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
725 file, blocks, le32_to_cpu(blk->len), offset, type);
726
727 reg = 0;
728 region_name = "Unknown";
729 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +0900730 case (WMFW_NAME_TEXT << 8):
731 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad92012-10-11 11:54:02 +0900732 break;
Mark Brownc7123262013-01-16 16:59:04 +0900733 case (WMFW_ABSOLUTE << 8):
Mark Brown2159ad92012-10-11 11:54:02 +0900734 region_name = "register";
735 reg = offset;
736 break;
Mark Brown471f4882013-01-08 16:09:31 +0000737
738 case WMFW_ADSP1_DM:
739 case WMFW_ADSP1_ZM:
740 case WMFW_ADSP2_XM:
741 case WMFW_ADSP2_YM:
742 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
743 file, blocks, le32_to_cpu(blk->len),
744 type, le32_to_cpu(blk->id));
745
746 mem = wm_adsp_find_region(dsp, type);
747 if (!mem) {
748 adsp_err(dsp, "No base for region %x\n", type);
749 break;
750 }
751
752 reg = 0;
753 list_for_each_entry(alg_region,
754 &dsp->alg_regions, list) {
755 if (le32_to_cpu(blk->id) == alg_region->alg &&
756 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +0800757 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +0000758 reg = wm_adsp_region_to_reg(mem,
759 reg);
Mark Brown338c5182013-01-24 00:35:48 +0800760 reg += offset;
Mark Brown471f4882013-01-08 16:09:31 +0000761 }
762 }
763
764 if (reg == 0)
765 adsp_err(dsp, "No %x for algorithm %x\n",
766 type, le32_to_cpu(blk->id));
767 break;
768
Mark Brown2159ad92012-10-11 11:54:02 +0900769 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +0900770 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
771 file, blocks, type, pos);
Mark Brown2159ad92012-10-11 11:54:02 +0900772 break;
773 }
774
775 if (reg) {
Mark Browna76fefa2013-01-07 19:03:17 +0000776 buf = kmemdup(blk->data, le32_to_cpu(blk->len),
Mark Brown7881fd02013-01-20 19:01:03 +0900777 GFP_KERNEL | GFP_DMA);
Mark Browna76fefa2013-01-07 19:03:17 +0000778 if (!buf) {
779 adsp_err(dsp, "Out of memory\n");
780 return -ENOMEM;
781 }
782
Mark Brown20da6d52013-01-12 19:58:17 +0000783 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
784 file, blocks, le32_to_cpu(blk->len),
785 reg);
Mark Brown2159ad92012-10-11 11:54:02 +0900786 ret = regmap_raw_write(regmap, reg, blk->data,
787 le32_to_cpu(blk->len));
788 if (ret != 0) {
789 adsp_err(dsp,
790 "%s.%d: Failed to write to %x in %s\n",
791 file, blocks, reg, region_name);
792 }
Mark Browna76fefa2013-01-07 19:03:17 +0000793
794 kfree(buf);
Mark Brown2159ad92012-10-11 11:54:02 +0900795 }
796
797 pos += le32_to_cpu(blk->len) + sizeof(*blk);
798 blocks++;
799 }
800
801 if (pos > firmware->size)
802 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
803 file, blocks, pos - firmware->size);
804
805out_fw:
806 release_firmware(firmware);
807out:
808 kfree(file);
809 return 0;
810}
811
Mark Brown5e7a7a22013-01-16 10:03:56 +0900812int wm_adsp1_init(struct wm_adsp *adsp)
813{
814 INIT_LIST_HEAD(&adsp->alg_regions);
815
816 return 0;
817}
818EXPORT_SYMBOL_GPL(wm_adsp1_init);
819
Mark Brown2159ad92012-10-11 11:54:02 +0900820int wm_adsp1_event(struct snd_soc_dapm_widget *w,
821 struct snd_kcontrol *kcontrol,
822 int event)
823{
824 struct snd_soc_codec *codec = w->codec;
825 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
826 struct wm_adsp *dsp = &dsps[w->shift];
827 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +0000828 int val;
Mark Brown2159ad92012-10-11 11:54:02 +0900829
830 switch (event) {
831 case SND_SOC_DAPM_POST_PMU:
832 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
833 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
834
Chris Rattray94e205b2013-01-18 08:43:09 +0000835 /*
836 * For simplicity set the DSP clock rate to be the
837 * SYSCLK rate rather than making it configurable.
838 */
839 if(dsp->sysclk_reg) {
840 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
841 if (ret != 0) {
842 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
843 ret);
844 return ret;
845 }
846
847 val = (val & dsp->sysclk_mask)
848 >> dsp->sysclk_shift;
849
850 ret = regmap_update_bits(dsp->regmap,
851 dsp->base + ADSP1_CONTROL_31,
852 ADSP1_CLK_SEL_MASK, val);
853 if (ret != 0) {
854 adsp_err(dsp, "Failed to set clock rate: %d\n",
855 ret);
856 return ret;
857 }
858 }
859
Mark Brown2159ad92012-10-11 11:54:02 +0900860 ret = wm_adsp_load(dsp);
861 if (ret != 0)
862 goto err;
863
Mark Browndb405172012-10-26 19:30:40 +0100864 ret = wm_adsp_setup_algs(dsp);
865 if (ret != 0)
866 goto err;
867
Mark Brown2159ad92012-10-11 11:54:02 +0900868 ret = wm_adsp_load_coeff(dsp);
869 if (ret != 0)
870 goto err;
871
872 /* Start the core running */
873 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
874 ADSP1_CORE_ENA | ADSP1_START,
875 ADSP1_CORE_ENA | ADSP1_START);
876 break;
877
878 case SND_SOC_DAPM_PRE_PMD:
879 /* Halt the core */
880 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
881 ADSP1_CORE_ENA | ADSP1_START, 0);
882
883 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
884 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
885
886 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
887 ADSP1_SYS_ENA, 0);
888 break;
889
890 default:
891 break;
892 }
893
894 return 0;
895
896err:
897 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
898 ADSP1_SYS_ENA, 0);
899 return ret;
900}
901EXPORT_SYMBOL_GPL(wm_adsp1_event);
902
903static int wm_adsp2_ena(struct wm_adsp *dsp)
904{
905 unsigned int val;
906 int ret, count;
907
908 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
909 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
910 if (ret != 0)
911 return ret;
912
913 /* Wait for the RAM to start, should be near instantaneous */
914 count = 0;
915 do {
916 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
917 &val);
918 if (ret != 0)
919 return ret;
920 } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
921
922 if (!(val & ADSP2_RAM_RDY)) {
923 adsp_err(dsp, "Failed to start DSP RAM\n");
924 return -EBUSY;
925 }
926
927 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
928 adsp_info(dsp, "RAM ready after %d polls\n", count);
929
930 return 0;
931}
932
933int wm_adsp2_event(struct snd_soc_dapm_widget *w,
934 struct snd_kcontrol *kcontrol, int event)
935{
936 struct snd_soc_codec *codec = w->codec;
937 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
938 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +0000939 struct wm_adsp_alg_region *alg_region;
Mark Brown973838a2012-11-28 17:20:32 +0000940 unsigned int val;
Mark Brown2159ad92012-10-11 11:54:02 +0900941 int ret;
942
943 switch (event) {
944 case SND_SOC_DAPM_POST_PMU:
Mark Browndd49e2c2012-12-02 21:50:46 +0900945 /*
946 * For simplicity set the DSP clock rate to be the
947 * SYSCLK rate rather than making it configurable.
948 */
949 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
950 if (ret != 0) {
951 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
952 ret);
953 return ret;
954 }
955 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
956 >> ARIZONA_SYSCLK_FREQ_SHIFT;
957
958 ret = regmap_update_bits(dsp->regmap,
959 dsp->base + ADSP2_CLOCKING,
960 ADSP2_CLK_SEL_MASK, val);
961 if (ret != 0) {
962 adsp_err(dsp, "Failed to set clock rate: %d\n",
963 ret);
964 return ret;
965 }
966
Mark Brown973838a2012-11-28 17:20:32 +0000967 if (dsp->dvfs) {
968 ret = regmap_read(dsp->regmap,
969 dsp->base + ADSP2_CLOCKING, &val);
970 if (ret != 0) {
971 dev_err(dsp->dev,
972 "Failed to read clocking: %d\n", ret);
973 return ret;
974 }
975
Mark Brown25c6fdb2012-11-29 15:16:10 +0000976 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
Mark Brown973838a2012-11-28 17:20:32 +0000977 ret = regulator_enable(dsp->dvfs);
978 if (ret != 0) {
979 dev_err(dsp->dev,
980 "Failed to enable supply: %d\n",
981 ret);
982 return ret;
983 }
984
985 ret = regulator_set_voltage(dsp->dvfs,
986 1800000,
987 1800000);
988 if (ret != 0) {
989 dev_err(dsp->dev,
990 "Failed to raise supply: %d\n",
991 ret);
992 return ret;
993 }
994 }
995 }
996
Mark Brown2159ad92012-10-11 11:54:02 +0900997 ret = wm_adsp2_ena(dsp);
998 if (ret != 0)
999 return ret;
1000
1001 ret = wm_adsp_load(dsp);
1002 if (ret != 0)
1003 goto err;
1004
Mark Browndb405172012-10-26 19:30:40 +01001005 ret = wm_adsp_setup_algs(dsp);
1006 if (ret != 0)
1007 goto err;
1008
Mark Brown2159ad92012-10-11 11:54:02 +09001009 ret = wm_adsp_load_coeff(dsp);
1010 if (ret != 0)
1011 goto err;
1012
1013 ret = regmap_update_bits(dsp->regmap,
1014 dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001015 ADSP2_CORE_ENA | ADSP2_START,
1016 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad92012-10-11 11:54:02 +09001017 if (ret != 0)
1018 goto err;
Mark Brown1023dbd2013-01-11 22:58:28 +00001019
1020 dsp->running = true;
Mark Brown2159ad92012-10-11 11:54:02 +09001021 break;
1022
1023 case SND_SOC_DAPM_PRE_PMD:
Mark Brown1023dbd2013-01-11 22:58:28 +00001024 dsp->running = false;
1025
Mark Brown2159ad92012-10-11 11:54:02 +09001026 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001027 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1028 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00001029
1030 if (dsp->dvfs) {
1031 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1032 1800000);
1033 if (ret != 0)
1034 dev_warn(dsp->dev,
1035 "Failed to lower supply: %d\n",
1036 ret);
1037
1038 ret = regulator_disable(dsp->dvfs);
1039 if (ret != 0)
1040 dev_err(dsp->dev,
1041 "Failed to enable supply: %d\n",
1042 ret);
1043 }
Mark Brown471f4882013-01-08 16:09:31 +00001044
1045 while (!list_empty(&dsp->alg_regions)) {
1046 alg_region = list_first_entry(&dsp->alg_regions,
1047 struct wm_adsp_alg_region,
1048 list);
1049 list_del(&alg_region->list);
1050 kfree(alg_region);
1051 }
Mark Brown2159ad92012-10-11 11:54:02 +09001052 break;
1053
1054 default:
1055 break;
1056 }
1057
1058 return 0;
1059err:
1060 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001061 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad92012-10-11 11:54:02 +09001062 return ret;
1063}
1064EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00001065
1066int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1067{
1068 int ret;
1069
Mark Brown10a2b662012-12-02 21:37:00 +09001070 /*
1071 * Disable the DSP memory by default when in reset for a small
1072 * power saving.
1073 */
1074 ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
1075 ADSP2_MEM_ENA, 0);
1076 if (ret != 0) {
1077 adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
1078 return ret;
1079 }
1080
Mark Brown471f4882013-01-08 16:09:31 +00001081 INIT_LIST_HEAD(&adsp->alg_regions);
1082
Mark Brown973838a2012-11-28 17:20:32 +00001083 if (dvfs) {
1084 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1085 if (IS_ERR(adsp->dvfs)) {
1086 ret = PTR_ERR(adsp->dvfs);
1087 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1088 return ret;
1089 }
1090
1091 ret = regulator_enable(adsp->dvfs);
1092 if (ret != 0) {
1093 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1094 ret);
1095 return ret;
1096 }
1097
1098 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1099 if (ret != 0) {
1100 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1101 ret);
1102 return ret;
1103 }
1104
1105 ret = regulator_disable(adsp->dvfs);
1106 if (ret != 0) {
1107 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1108 ret);
1109 return ret;
1110 }
1111 }
1112
1113 return 0;
1114}
1115EXPORT_SYMBOL_GPL(wm_adsp2_init);