blob: f3a9b559e0feaeb44bc0fc0c38641f73d74f133e [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 Brown2d30b572013-01-28 20:18:17 +0800113#define ADSP2_CONTROL 0x0
114#define ADSP2_CLOCKING 0x1
115#define ADSP2_STATUS1 0x4
116#define ADSP2_WDMA_CONFIG_1 0x30
117#define ADSP2_WDMA_CONFIG_2 0x31
118#define ADSP2_RDMA_CONFIG_1 0x34
Mark Brown2159ad932012-10-11 11:54:02 +0900119
120/*
121 * ADSP2 Control
122 */
123
124#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
125#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
126#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
127#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
128#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
129#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
130#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
131#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
132#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
133#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
134#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
135#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
136#define ADSP2_START 0x0001 /* DSP1_START */
137#define ADSP2_START_MASK 0x0001 /* DSP1_START */
138#define ADSP2_START_SHIFT 0 /* DSP1_START */
139#define ADSP2_START_WIDTH 1 /* DSP1_START */
140
141/*
Mark Brown973838a2012-11-28 17:20:32 +0000142 * ADSP2 clocking
143 */
144#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
145#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
146#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
147
148/*
Mark Brown2159ad932012-10-11 11:54:02 +0900149 * ADSP2 Status 1
150 */
151#define ADSP2_RAM_RDY 0x0001
152#define ADSP2_RAM_RDY_MASK 0x0001
153#define ADSP2_RAM_RDY_SHIFT 0
154#define ADSP2_RAM_RDY_WIDTH 1
155
Mark Brown36e8fe92013-01-25 17:47:48 +0800156#define WM_ADSP_NUM_FW 4
Mark Brown1023dbd2013-01-11 22:58:28 +0000157
158static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Mark Brown36e8fe92013-01-25 17:47:48 +0800159 "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC"
Mark Brown1023dbd2013-01-11 22:58:28 +0000160};
161
162static struct {
163 const char *file;
164} wm_adsp_fw[WM_ADSP_NUM_FW] = {
165 { .file = "mbc-vss" },
166 { .file = "tx" },
Mark Brown36e8fe92013-01-25 17:47:48 +0800167 { .file = "tx-spk" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000168 { .file = "rx-anc" },
169};
170
171static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
172 struct snd_ctl_elem_value *ucontrol)
173{
174 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
175 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
176 struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
177
178 ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
179
180 return 0;
181}
182
183static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
184 struct snd_ctl_elem_value *ucontrol)
185{
186 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
187 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
188 struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
189
190 if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
191 return 0;
192
193 if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
194 return -EINVAL;
195
196 if (adsp[e->shift_l].running)
197 return -EBUSY;
198
199 adsp->fw = ucontrol->value.integer.value[0];
200
201 return 0;
202}
203
204static const struct soc_enum wm_adsp_fw_enum[] = {
205 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
206 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
207 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
208 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
209};
210
211const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
212 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
213 wm_adsp_fw_get, wm_adsp_fw_put),
214 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
215 wm_adsp_fw_get, wm_adsp_fw_put),
216 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
217 wm_adsp_fw_get, wm_adsp_fw_put),
218 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
219 wm_adsp_fw_get, wm_adsp_fw_put),
220};
221EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
Mark Brown2159ad932012-10-11 11:54:02 +0900222
223static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
224 int type)
225{
226 int i;
227
228 for (i = 0; i < dsp->num_mems; i++)
229 if (dsp->mem[i].type == type)
230 return &dsp->mem[i];
231
232 return NULL;
233}
234
Mark Brown45b9ee72013-01-08 16:02:06 +0000235static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
236 unsigned int offset)
237{
238 switch (region->type) {
239 case WMFW_ADSP1_PM:
240 return region->base + (offset * 3);
241 case WMFW_ADSP1_DM:
242 return region->base + (offset * 2);
243 case WMFW_ADSP2_XM:
244 return region->base + (offset * 2);
245 case WMFW_ADSP2_YM:
246 return region->base + (offset * 2);
247 case WMFW_ADSP1_ZM:
248 return region->base + (offset * 2);
249 default:
250 WARN_ON(NULL != "Unknown memory region type");
251 return offset;
252 }
253}
254
Mark Brown2159ad932012-10-11 11:54:02 +0900255static int wm_adsp_load(struct wm_adsp *dsp)
256{
257 const struct firmware *firmware;
258 struct regmap *regmap = dsp->regmap;
259 unsigned int pos = 0;
260 const struct wmfw_header *header;
261 const struct wmfw_adsp1_sizes *adsp1_sizes;
262 const struct wmfw_adsp2_sizes *adsp2_sizes;
263 const struct wmfw_footer *footer;
264 const struct wmfw_region *region;
265 const struct wm_adsp_region *mem;
266 const char *region_name;
267 char *file, *text;
Mark Browna76fefa2013-01-07 19:03:17 +0000268 void *buf;
Mark Brown2159ad932012-10-11 11:54:02 +0900269 unsigned int reg;
270 int regions = 0;
271 int ret, offset, type, sizes;
272
273 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
274 if (file == NULL)
275 return -ENOMEM;
276
Mark Brown1023dbd2013-01-11 22:58:28 +0000277 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
278 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +0900279 file[PAGE_SIZE - 1] = '\0';
280
281 ret = request_firmware(&firmware, file, dsp->dev);
282 if (ret != 0) {
283 adsp_err(dsp, "Failed to request '%s'\n", file);
284 goto out;
285 }
286 ret = -EINVAL;
287
288 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
289 if (pos >= firmware->size) {
290 adsp_err(dsp, "%s: file too short, %zu bytes\n",
291 file, firmware->size);
292 goto out_fw;
293 }
294
295 header = (void*)&firmware->data[0];
296
297 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
298 adsp_err(dsp, "%s: invalid magic\n", file);
299 goto out_fw;
300 }
301
302 if (header->ver != 0) {
303 adsp_err(dsp, "%s: unknown file format %d\n",
304 file, header->ver);
305 goto out_fw;
306 }
307
308 if (header->core != dsp->type) {
309 adsp_err(dsp, "%s: invalid core %d != %d\n",
310 file, header->core, dsp->type);
311 goto out_fw;
312 }
313
314 switch (dsp->type) {
315 case WMFW_ADSP1:
316 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
317 adsp1_sizes = (void *)&(header[1]);
318 footer = (void *)&(adsp1_sizes[1]);
319 sizes = sizeof(*adsp1_sizes);
320
321 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
322 file, le32_to_cpu(adsp1_sizes->dm),
323 le32_to_cpu(adsp1_sizes->pm),
324 le32_to_cpu(adsp1_sizes->zm));
325 break;
326
327 case WMFW_ADSP2:
328 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
329 adsp2_sizes = (void *)&(header[1]);
330 footer = (void *)&(adsp2_sizes[1]);
331 sizes = sizeof(*adsp2_sizes);
332
333 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
334 file, le32_to_cpu(adsp2_sizes->xm),
335 le32_to_cpu(adsp2_sizes->ym),
336 le32_to_cpu(adsp2_sizes->pm),
337 le32_to_cpu(adsp2_sizes->zm));
338 break;
339
340 default:
341 BUG_ON(NULL == "Unknown DSP type");
342 goto out_fw;
343 }
344
345 if (le32_to_cpu(header->len) != sizeof(*header) +
346 sizes + sizeof(*footer)) {
347 adsp_err(dsp, "%s: unexpected header length %d\n",
348 file, le32_to_cpu(header->len));
349 goto out_fw;
350 }
351
352 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
353 le64_to_cpu(footer->timestamp));
354
355 while (pos < firmware->size &&
356 pos - firmware->size > sizeof(*region)) {
357 region = (void *)&(firmware->data[pos]);
358 region_name = "Unknown";
359 reg = 0;
360 text = NULL;
361 offset = le32_to_cpu(region->offset) & 0xffffff;
362 type = be32_to_cpu(region->type) & 0xff;
363 mem = wm_adsp_find_region(dsp, type);
364
365 switch (type) {
366 case WMFW_NAME_TEXT:
367 region_name = "Firmware name";
368 text = kzalloc(le32_to_cpu(region->len) + 1,
369 GFP_KERNEL);
370 break;
371 case WMFW_INFO_TEXT:
372 region_name = "Information";
373 text = kzalloc(le32_to_cpu(region->len) + 1,
374 GFP_KERNEL);
375 break;
376 case WMFW_ABSOLUTE:
377 region_name = "Absolute";
378 reg = offset;
379 break;
380 case WMFW_ADSP1_PM:
381 BUG_ON(!mem);
382 region_name = "PM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000383 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900384 break;
385 case WMFW_ADSP1_DM:
386 BUG_ON(!mem);
387 region_name = "DM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000388 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900389 break;
390 case WMFW_ADSP2_XM:
391 BUG_ON(!mem);
392 region_name = "XM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000393 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900394 break;
395 case WMFW_ADSP2_YM:
396 BUG_ON(!mem);
397 region_name = "YM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000398 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900399 break;
400 case WMFW_ADSP1_ZM:
401 BUG_ON(!mem);
402 region_name = "ZM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000403 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900404 break;
405 default:
406 adsp_warn(dsp,
407 "%s.%d: Unknown region type %x at %d(%x)\n",
408 file, regions, type, pos, pos);
409 break;
410 }
411
412 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
413 regions, le32_to_cpu(region->len), offset,
414 region_name);
415
416 if (text) {
417 memcpy(text, region->data, le32_to_cpu(region->len));
418 adsp_info(dsp, "%s: %s\n", file, text);
419 kfree(text);
420 }
421
422 if (reg) {
Mark Browna76fefa2013-01-07 19:03:17 +0000423 buf = kmemdup(region->data, le32_to_cpu(region->len),
Mark Brown7881fd02013-01-20 19:01:03 +0900424 GFP_KERNEL | GFP_DMA);
Mark Browna76fefa2013-01-07 19:03:17 +0000425 if (!buf) {
426 adsp_err(dsp, "Out of memory\n");
427 return -ENOMEM;
428 }
429
430 ret = regmap_raw_write(regmap, reg, buf,
Mark Brown2159ad932012-10-11 11:54:02 +0900431 le32_to_cpu(region->len));
Mark Browna76fefa2013-01-07 19:03:17 +0000432
433 kfree(buf);
434
Mark Brown2159ad932012-10-11 11:54:02 +0900435 if (ret != 0) {
436 adsp_err(dsp,
437 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
438 file, regions,
439 le32_to_cpu(region->len), offset,
440 region_name, ret);
441 goto out_fw;
442 }
443 }
444
445 pos += le32_to_cpu(region->len) + sizeof(*region);
446 regions++;
447 }
448
449 if (pos > firmware->size)
450 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
451 file, regions, pos - firmware->size);
452
453out_fw:
454 release_firmware(firmware);
455out:
456 kfree(file);
457
458 return ret;
459}
460
Mark Browndb405172012-10-26 19:30:40 +0100461static int wm_adsp_setup_algs(struct wm_adsp *dsp)
462{
463 struct regmap *regmap = dsp->regmap;
464 struct wmfw_adsp1_id_hdr adsp1_id;
465 struct wmfw_adsp2_id_hdr adsp2_id;
466 struct wmfw_adsp1_alg_hdr *adsp1_alg;
467 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000468 void *alg, *buf;
Mark Brown471f4882013-01-08 16:09:31 +0000469 struct wm_adsp_alg_region *region;
Mark Browndb405172012-10-26 19:30:40 +0100470 const struct wm_adsp_region *mem;
471 unsigned int pos, term;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000472 size_t algs, buf_size;
Mark Browndb405172012-10-26 19:30:40 +0100473 __be32 val;
474 int i, ret;
475
476 switch (dsp->type) {
477 case WMFW_ADSP1:
478 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
479 break;
480 case WMFW_ADSP2:
481 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
482 break;
483 default:
484 mem = NULL;
485 break;
486 }
487
488 if (mem == NULL) {
489 BUG_ON(mem != NULL);
490 return -EINVAL;
491 }
492
493 switch (dsp->type) {
494 case WMFW_ADSP1:
495 ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
496 sizeof(adsp1_id));
497 if (ret != 0) {
498 adsp_err(dsp, "Failed to read algorithm info: %d\n",
499 ret);
500 return ret;
501 }
502
Mark Brownd62f4bc2012-12-19 14:00:30 +0000503 buf = &adsp1_id;
504 buf_size = sizeof(adsp1_id);
505
Mark Browndb405172012-10-26 19:30:40 +0100506 algs = be32_to_cpu(adsp1_id.algs);
507 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
508 be32_to_cpu(adsp1_id.fw.id),
509 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
510 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
511 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
512 algs);
513
514 pos = sizeof(adsp1_id) / 2;
515 term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
516 break;
517
518 case WMFW_ADSP2:
519 ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
520 sizeof(adsp2_id));
521 if (ret != 0) {
522 adsp_err(dsp, "Failed to read algorithm info: %d\n",
523 ret);
524 return ret;
525 }
526
Mark Brownd62f4bc2012-12-19 14:00:30 +0000527 buf = &adsp2_id;
528 buf_size = sizeof(adsp2_id);
529
Mark Browndb405172012-10-26 19:30:40 +0100530 algs = be32_to_cpu(adsp2_id.algs);
531 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
532 be32_to_cpu(adsp2_id.fw.id),
533 (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
534 (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
535 be32_to_cpu(adsp2_id.fw.ver) & 0xff,
536 algs);
537
538 pos = sizeof(adsp2_id) / 2;
539 term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
540 break;
541
542 default:
543 BUG_ON(NULL == "Unknown DSP type");
544 return -EINVAL;
545 }
546
547 if (algs == 0) {
548 adsp_err(dsp, "No algorithms\n");
549 return -EINVAL;
550 }
551
Mark Brownd62f4bc2012-12-19 14:00:30 +0000552 if (algs > 1024) {
553 adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
554 print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
555 buf, buf_size);
556 return -EINVAL;
557 }
558
Mark Browndb405172012-10-26 19:30:40 +0100559 /* Read the terminator first to validate the length */
560 ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
561 if (ret != 0) {
562 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
563 ret);
564 return ret;
565 }
566
567 if (be32_to_cpu(val) != 0xbedead)
568 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
569 term, be32_to_cpu(val));
570
Mark Brownf2a93e22013-01-20 22:17:30 +0900571 alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +0100572 if (!alg)
573 return -ENOMEM;
574
575 ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
576 if (ret != 0) {
577 adsp_err(dsp, "Failed to read algorithm list: %d\n",
578 ret);
579 goto out;
580 }
581
582 adsp1_alg = alg;
583 adsp2_alg = alg;
584
585 for (i = 0; i < algs; i++) {
586 switch (dsp->type) {
587 case WMFW_ADSP1:
Mark Brown471f4882013-01-08 16:09:31 +0000588 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100589 i, be32_to_cpu(adsp1_alg[i].alg.id),
590 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
591 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000592 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
593 be32_to_cpu(adsp1_alg[i].dm),
594 be32_to_cpu(adsp1_alg[i].zm));
595
Mark Brown74808002013-01-26 00:29:51 +0800596 region = kzalloc(sizeof(*region), GFP_KERNEL);
597 if (!region)
598 return -ENOMEM;
599 region->type = WMFW_ADSP1_DM;
600 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
601 region->base = be32_to_cpu(adsp1_alg[i].dm);
602 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000603
Mark Brown74808002013-01-26 00:29:51 +0800604 region = kzalloc(sizeof(*region), GFP_KERNEL);
605 if (!region)
606 return -ENOMEM;
607 region->type = WMFW_ADSP1_ZM;
608 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
609 region->base = be32_to_cpu(adsp1_alg[i].zm);
610 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100611 break;
612
613 case WMFW_ADSP2:
Mark Brown471f4882013-01-08 16:09:31 +0000614 adsp_info(dsp,
615 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100616 i, be32_to_cpu(adsp2_alg[i].alg.id),
617 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
618 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000619 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
620 be32_to_cpu(adsp2_alg[i].xm),
621 be32_to_cpu(adsp2_alg[i].ym),
622 be32_to_cpu(adsp2_alg[i].zm));
623
Mark Brown74808002013-01-26 00:29:51 +0800624 region = kzalloc(sizeof(*region), GFP_KERNEL);
625 if (!region)
626 return -ENOMEM;
627 region->type = WMFW_ADSP2_XM;
628 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
629 region->base = be32_to_cpu(adsp2_alg[i].xm);
630 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000631
Mark Brown74808002013-01-26 00:29:51 +0800632 region = kzalloc(sizeof(*region), GFP_KERNEL);
633 if (!region)
634 return -ENOMEM;
635 region->type = WMFW_ADSP2_YM;
636 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
637 region->base = be32_to_cpu(adsp2_alg[i].ym);
638 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000639
Mark Brown74808002013-01-26 00:29:51 +0800640 region = kzalloc(sizeof(*region), GFP_KERNEL);
641 if (!region)
642 return -ENOMEM;
643 region->type = WMFW_ADSP2_ZM;
644 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
645 region->base = be32_to_cpu(adsp2_alg[i].zm);
646 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100647 break;
648 }
649 }
650
651out:
652 kfree(alg);
653 return ret;
654}
655
Mark Brown2159ad932012-10-11 11:54:02 +0900656static int wm_adsp_load_coeff(struct wm_adsp *dsp)
657{
658 struct regmap *regmap = dsp->regmap;
659 struct wmfw_coeff_hdr *hdr;
660 struct wmfw_coeff_item *blk;
661 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +0000662 const struct wm_adsp_region *mem;
663 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad932012-10-11 11:54:02 +0900664 const char *region_name;
665 int ret, pos, blocks, type, offset, reg;
666 char *file;
Mark Browna76fefa2013-01-07 19:03:17 +0000667 void *buf;
Mark Brown2159ad932012-10-11 11:54:02 +0900668
669 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
670 if (file == NULL)
671 return -ENOMEM;
672
Mark Brown1023dbd2013-01-11 22:58:28 +0000673 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
674 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad932012-10-11 11:54:02 +0900675 file[PAGE_SIZE - 1] = '\0';
676
677 ret = request_firmware(&firmware, file, dsp->dev);
678 if (ret != 0) {
679 adsp_warn(dsp, "Failed to request '%s'\n", file);
680 ret = 0;
681 goto out;
682 }
683 ret = -EINVAL;
684
685 if (sizeof(*hdr) >= firmware->size) {
686 adsp_err(dsp, "%s: file too short, %zu bytes\n",
687 file, firmware->size);
688 goto out_fw;
689 }
690
691 hdr = (void*)&firmware->data[0];
692 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
693 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +0000694 goto out_fw;
Mark Brown2159ad932012-10-11 11:54:02 +0900695 }
696
Mark Brownc7123262013-01-16 16:59:04 +0900697 switch (be32_to_cpu(hdr->rev) & 0xff) {
698 case 1:
699 break;
700 default:
701 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
702 file, be32_to_cpu(hdr->rev) & 0xff);
703 ret = -EINVAL;
704 goto out_fw;
705 }
706
Mark Brown2159ad932012-10-11 11:54:02 +0900707 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
708 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
709 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
710 le32_to_cpu(hdr->ver) & 0xff);
711
712 pos = le32_to_cpu(hdr->len);
713
714 blocks = 0;
715 while (pos < firmware->size &&
716 pos - firmware->size > sizeof(*blk)) {
717 blk = (void*)(&firmware->data[pos]);
718
Mark Brownc7123262013-01-16 16:59:04 +0900719 type = le16_to_cpu(blk->type);
720 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad932012-10-11 11:54:02 +0900721
722 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
723 file, blocks, le32_to_cpu(blk->id),
724 (le32_to_cpu(blk->ver) >> 16) & 0xff,
725 (le32_to_cpu(blk->ver) >> 8) & 0xff,
726 le32_to_cpu(blk->ver) & 0xff);
727 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
728 file, blocks, le32_to_cpu(blk->len), offset, type);
729
730 reg = 0;
731 region_name = "Unknown";
732 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +0900733 case (WMFW_NAME_TEXT << 8):
734 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad932012-10-11 11:54:02 +0900735 break;
Mark Brownc7123262013-01-16 16:59:04 +0900736 case (WMFW_ABSOLUTE << 8):
Mark Brown2159ad932012-10-11 11:54:02 +0900737 region_name = "register";
738 reg = offset;
739 break;
Mark Brown471f4882013-01-08 16:09:31 +0000740
741 case WMFW_ADSP1_DM:
742 case WMFW_ADSP1_ZM:
743 case WMFW_ADSP2_XM:
744 case WMFW_ADSP2_YM:
745 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
746 file, blocks, le32_to_cpu(blk->len),
747 type, le32_to_cpu(blk->id));
748
749 mem = wm_adsp_find_region(dsp, type);
750 if (!mem) {
751 adsp_err(dsp, "No base for region %x\n", type);
752 break;
753 }
754
755 reg = 0;
756 list_for_each_entry(alg_region,
757 &dsp->alg_regions, list) {
758 if (le32_to_cpu(blk->id) == alg_region->alg &&
759 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +0800760 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +0000761 reg = wm_adsp_region_to_reg(mem,
762 reg);
Mark Brown338c5182013-01-24 00:35:48 +0800763 reg += offset;
Mark Brown471f4882013-01-08 16:09:31 +0000764 }
765 }
766
767 if (reg == 0)
768 adsp_err(dsp, "No %x for algorithm %x\n",
769 type, le32_to_cpu(blk->id));
770 break;
771
Mark Brown2159ad932012-10-11 11:54:02 +0900772 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +0900773 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
774 file, blocks, type, pos);
Mark Brown2159ad932012-10-11 11:54:02 +0900775 break;
776 }
777
778 if (reg) {
Mark Browna76fefa2013-01-07 19:03:17 +0000779 buf = kmemdup(blk->data, le32_to_cpu(blk->len),
Mark Brown7881fd02013-01-20 19:01:03 +0900780 GFP_KERNEL | GFP_DMA);
Mark Browna76fefa2013-01-07 19:03:17 +0000781 if (!buf) {
782 adsp_err(dsp, "Out of memory\n");
783 return -ENOMEM;
784 }
785
Mark Brown20da6d52013-01-12 19:58:17 +0000786 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
787 file, blocks, le32_to_cpu(blk->len),
788 reg);
Mark Brown2159ad932012-10-11 11:54:02 +0900789 ret = regmap_raw_write(regmap, reg, blk->data,
790 le32_to_cpu(blk->len));
791 if (ret != 0) {
792 adsp_err(dsp,
793 "%s.%d: Failed to write to %x in %s\n",
794 file, blocks, reg, region_name);
795 }
Mark Browna76fefa2013-01-07 19:03:17 +0000796
797 kfree(buf);
Mark Brown2159ad932012-10-11 11:54:02 +0900798 }
799
800 pos += le32_to_cpu(blk->len) + sizeof(*blk);
801 blocks++;
802 }
803
804 if (pos > firmware->size)
805 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
806 file, blocks, pos - firmware->size);
807
808out_fw:
809 release_firmware(firmware);
810out:
811 kfree(file);
812 return 0;
813}
814
Mark Brown5e7a7a22013-01-16 10:03:56 +0900815int wm_adsp1_init(struct wm_adsp *adsp)
816{
817 INIT_LIST_HEAD(&adsp->alg_regions);
818
819 return 0;
820}
821EXPORT_SYMBOL_GPL(wm_adsp1_init);
822
Mark Brown2159ad932012-10-11 11:54:02 +0900823int wm_adsp1_event(struct snd_soc_dapm_widget *w,
824 struct snd_kcontrol *kcontrol,
825 int event)
826{
827 struct snd_soc_codec *codec = w->codec;
828 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
829 struct wm_adsp *dsp = &dsps[w->shift];
830 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +0000831 int val;
Mark Brown2159ad932012-10-11 11:54:02 +0900832
833 switch (event) {
834 case SND_SOC_DAPM_POST_PMU:
835 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
836 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
837
Chris Rattray94e205b2013-01-18 08:43:09 +0000838 /*
839 * For simplicity set the DSP clock rate to be the
840 * SYSCLK rate rather than making it configurable.
841 */
842 if(dsp->sysclk_reg) {
843 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
844 if (ret != 0) {
845 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
846 ret);
847 return ret;
848 }
849
850 val = (val & dsp->sysclk_mask)
851 >> dsp->sysclk_shift;
852
853 ret = regmap_update_bits(dsp->regmap,
854 dsp->base + ADSP1_CONTROL_31,
855 ADSP1_CLK_SEL_MASK, val);
856 if (ret != 0) {
857 adsp_err(dsp, "Failed to set clock rate: %d\n",
858 ret);
859 return ret;
860 }
861 }
862
Mark Brown2159ad932012-10-11 11:54:02 +0900863 ret = wm_adsp_load(dsp);
864 if (ret != 0)
865 goto err;
866
Mark Browndb405172012-10-26 19:30:40 +0100867 ret = wm_adsp_setup_algs(dsp);
868 if (ret != 0)
869 goto err;
870
Mark Brown2159ad932012-10-11 11:54:02 +0900871 ret = wm_adsp_load_coeff(dsp);
872 if (ret != 0)
873 goto err;
874
875 /* Start the core running */
876 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
877 ADSP1_CORE_ENA | ADSP1_START,
878 ADSP1_CORE_ENA | ADSP1_START);
879 break;
880
881 case SND_SOC_DAPM_PRE_PMD:
882 /* Halt the core */
883 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
884 ADSP1_CORE_ENA | ADSP1_START, 0);
885
886 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
887 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
888
889 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
890 ADSP1_SYS_ENA, 0);
891 break;
892
893 default:
894 break;
895 }
896
897 return 0;
898
899err:
900 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
901 ADSP1_SYS_ENA, 0);
902 return ret;
903}
904EXPORT_SYMBOL_GPL(wm_adsp1_event);
905
906static int wm_adsp2_ena(struct wm_adsp *dsp)
907{
908 unsigned int val;
909 int ret, count;
910
911 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
912 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
913 if (ret != 0)
914 return ret;
915
916 /* Wait for the RAM to start, should be near instantaneous */
917 count = 0;
918 do {
919 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
920 &val);
921 if (ret != 0)
922 return ret;
923 } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
924
925 if (!(val & ADSP2_RAM_RDY)) {
926 adsp_err(dsp, "Failed to start DSP RAM\n");
927 return -EBUSY;
928 }
929
930 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
931 adsp_info(dsp, "RAM ready after %d polls\n", count);
932
933 return 0;
934}
935
936int wm_adsp2_event(struct snd_soc_dapm_widget *w,
937 struct snd_kcontrol *kcontrol, int event)
938{
939 struct snd_soc_codec *codec = w->codec;
940 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
941 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +0000942 struct wm_adsp_alg_region *alg_region;
Mark Brown973838a2012-11-28 17:20:32 +0000943 unsigned int val;
Mark Brown2159ad932012-10-11 11:54:02 +0900944 int ret;
945
946 switch (event) {
947 case SND_SOC_DAPM_POST_PMU:
Mark Browndd49e2c2012-12-02 21:50:46 +0900948 /*
949 * For simplicity set the DSP clock rate to be the
950 * SYSCLK rate rather than making it configurable.
951 */
952 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
953 if (ret != 0) {
954 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
955 ret);
956 return ret;
957 }
958 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
959 >> ARIZONA_SYSCLK_FREQ_SHIFT;
960
961 ret = regmap_update_bits(dsp->regmap,
962 dsp->base + ADSP2_CLOCKING,
963 ADSP2_CLK_SEL_MASK, val);
964 if (ret != 0) {
965 adsp_err(dsp, "Failed to set clock rate: %d\n",
966 ret);
967 return ret;
968 }
969
Mark Brown973838a2012-11-28 17:20:32 +0000970 if (dsp->dvfs) {
971 ret = regmap_read(dsp->regmap,
972 dsp->base + ADSP2_CLOCKING, &val);
973 if (ret != 0) {
974 dev_err(dsp->dev,
975 "Failed to read clocking: %d\n", ret);
976 return ret;
977 }
978
Mark Brown25c6fdb2012-11-29 15:16:10 +0000979 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
Mark Brown973838a2012-11-28 17:20:32 +0000980 ret = regulator_enable(dsp->dvfs);
981 if (ret != 0) {
982 dev_err(dsp->dev,
983 "Failed to enable supply: %d\n",
984 ret);
985 return ret;
986 }
987
988 ret = regulator_set_voltage(dsp->dvfs,
989 1800000,
990 1800000);
991 if (ret != 0) {
992 dev_err(dsp->dev,
993 "Failed to raise supply: %d\n",
994 ret);
995 return ret;
996 }
997 }
998 }
999
Mark Brown2159ad932012-10-11 11:54:02 +09001000 ret = wm_adsp2_ena(dsp);
1001 if (ret != 0)
1002 return ret;
1003
1004 ret = wm_adsp_load(dsp);
1005 if (ret != 0)
1006 goto err;
1007
Mark Browndb405172012-10-26 19:30:40 +01001008 ret = wm_adsp_setup_algs(dsp);
1009 if (ret != 0)
1010 goto err;
1011
Mark Brown2159ad932012-10-11 11:54:02 +09001012 ret = wm_adsp_load_coeff(dsp);
1013 if (ret != 0)
1014 goto err;
1015
1016 ret = regmap_update_bits(dsp->regmap,
1017 dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001018 ADSP2_CORE_ENA | ADSP2_START,
1019 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad932012-10-11 11:54:02 +09001020 if (ret != 0)
1021 goto err;
Mark Brown1023dbd2013-01-11 22:58:28 +00001022
1023 dsp->running = true;
Mark Brown2159ad932012-10-11 11:54:02 +09001024 break;
1025
1026 case SND_SOC_DAPM_PRE_PMD:
Mark Brown1023dbd2013-01-11 22:58:28 +00001027 dsp->running = false;
1028
Mark Brown2159ad932012-10-11 11:54:02 +09001029 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001030 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1031 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00001032
Mark Brown2d30b572013-01-28 20:18:17 +08001033 /* Make sure DMAs are quiesced */
1034 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
1035 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
1036 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
1037
Mark Brown973838a2012-11-28 17:20:32 +00001038 if (dsp->dvfs) {
1039 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1040 1800000);
1041 if (ret != 0)
1042 dev_warn(dsp->dev,
1043 "Failed to lower supply: %d\n",
1044 ret);
1045
1046 ret = regulator_disable(dsp->dvfs);
1047 if (ret != 0)
1048 dev_err(dsp->dev,
1049 "Failed to enable supply: %d\n",
1050 ret);
1051 }
Mark Brown471f4882013-01-08 16:09:31 +00001052
1053 while (!list_empty(&dsp->alg_regions)) {
1054 alg_region = list_first_entry(&dsp->alg_regions,
1055 struct wm_adsp_alg_region,
1056 list);
1057 list_del(&alg_region->list);
1058 kfree(alg_region);
1059 }
Mark Brown2159ad932012-10-11 11:54:02 +09001060 break;
1061
1062 default:
1063 break;
1064 }
1065
1066 return 0;
1067err:
1068 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001069 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad932012-10-11 11:54:02 +09001070 return ret;
1071}
1072EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00001073
1074int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1075{
1076 int ret;
1077
Mark Brown10a2b662012-12-02 21:37:00 +09001078 /*
1079 * Disable the DSP memory by default when in reset for a small
1080 * power saving.
1081 */
1082 ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
1083 ADSP2_MEM_ENA, 0);
1084 if (ret != 0) {
1085 adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
1086 return ret;
1087 }
1088
Mark Brown471f4882013-01-08 16:09:31 +00001089 INIT_LIST_HEAD(&adsp->alg_regions);
1090
Mark Brown973838a2012-11-28 17:20:32 +00001091 if (dvfs) {
1092 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1093 if (IS_ERR(adsp->dvfs)) {
1094 ret = PTR_ERR(adsp->dvfs);
1095 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1096 return ret;
1097 }
1098
1099 ret = regulator_enable(adsp->dvfs);
1100 if (ret != 0) {
1101 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1102 ret);
1103 return ret;
1104 }
1105
1106 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1107 if (ret != 0) {
1108 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1109 ret);
1110 return ret;
1111 }
1112
1113 ret = regulator_disable(adsp->dvfs);
1114 if (ret != 0) {
1115 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1116 ret);
1117 return ret;
1118 }
1119 }
1120
1121 return 0;
1122}
1123EXPORT_SYMBOL_GPL(wm_adsp2_init);