blob: bc03baef39fad925af3a5a4a3f2ad1c35cf7dcca [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>
Mark Browncf17c832013-01-30 14:37:23 +080018#include <linux/list.h>
Mark Brown2159ad92012-10-11 11:54:02 +090019#include <linux/pm.h>
20#include <linux/pm_runtime.h>
21#include <linux/regmap.h>
Mark Brown973838a2012-11-28 17:20:32 +000022#include <linux/regulator/consumer.h>
Mark Brown2159ad92012-10-11 11:54:02 +090023#include <linux/slab.h>
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/soc.h>
28#include <sound/jack.h>
29#include <sound/initval.h>
30#include <sound/tlv.h>
31
32#include <linux/mfd/arizona/registers.h>
33
Mark Browndc914282013-02-18 19:09:23 +000034#include "arizona.h"
Mark Brown2159ad92012-10-11 11:54:02 +090035#include "wm_adsp.h"
36
37#define adsp_crit(_dsp, fmt, ...) \
38 dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
39#define adsp_err(_dsp, fmt, ...) \
40 dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
41#define adsp_warn(_dsp, fmt, ...) \
42 dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
43#define adsp_info(_dsp, fmt, ...) \
44 dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
45#define adsp_dbg(_dsp, fmt, ...) \
46 dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
47
48#define ADSP1_CONTROL_1 0x00
49#define ADSP1_CONTROL_2 0x02
50#define ADSP1_CONTROL_3 0x03
51#define ADSP1_CONTROL_4 0x04
52#define ADSP1_CONTROL_5 0x06
53#define ADSP1_CONTROL_6 0x07
54#define ADSP1_CONTROL_7 0x08
55#define ADSP1_CONTROL_8 0x09
56#define ADSP1_CONTROL_9 0x0A
57#define ADSP1_CONTROL_10 0x0B
58#define ADSP1_CONTROL_11 0x0C
59#define ADSP1_CONTROL_12 0x0D
60#define ADSP1_CONTROL_13 0x0F
61#define ADSP1_CONTROL_14 0x10
62#define ADSP1_CONTROL_15 0x11
63#define ADSP1_CONTROL_16 0x12
64#define ADSP1_CONTROL_17 0x13
65#define ADSP1_CONTROL_18 0x14
66#define ADSP1_CONTROL_19 0x16
67#define ADSP1_CONTROL_20 0x17
68#define ADSP1_CONTROL_21 0x18
69#define ADSP1_CONTROL_22 0x1A
70#define ADSP1_CONTROL_23 0x1B
71#define ADSP1_CONTROL_24 0x1C
72#define ADSP1_CONTROL_25 0x1E
73#define ADSP1_CONTROL_26 0x20
74#define ADSP1_CONTROL_27 0x21
75#define ADSP1_CONTROL_28 0x22
76#define ADSP1_CONTROL_29 0x23
77#define ADSP1_CONTROL_30 0x24
78#define ADSP1_CONTROL_31 0x26
79
80/*
81 * ADSP1 Control 19
82 */
83#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
84#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
85#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
86
87
88/*
89 * ADSP1 Control 30
90 */
91#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
92#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
93#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
94#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
95#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
96#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
97#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
98#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
99#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
100#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
101#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
102#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
103#define ADSP1_START 0x0001 /* DSP1_START */
104#define ADSP1_START_MASK 0x0001 /* DSP1_START */
105#define ADSP1_START_SHIFT 0 /* DSP1_START */
106#define ADSP1_START_WIDTH 1 /* DSP1_START */
107
Chris Rattray94e205b2013-01-18 08:43:09 +0000108/*
109 * ADSP1 Control 31
110 */
111#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
112#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
113#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
114
Mark Brown2d30b572013-01-28 20:18:17 +0800115#define ADSP2_CONTROL 0x0
116#define ADSP2_CLOCKING 0x1
117#define ADSP2_STATUS1 0x4
118#define ADSP2_WDMA_CONFIG_1 0x30
119#define ADSP2_WDMA_CONFIG_2 0x31
120#define ADSP2_RDMA_CONFIG_1 0x34
Mark Brown2159ad92012-10-11 11:54:02 +0900121
122/*
123 * ADSP2 Control
124 */
125
126#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
127#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
128#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
129#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
130#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
131#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
132#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
133#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
134#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
135#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
136#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
137#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
138#define ADSP2_START 0x0001 /* DSP1_START */
139#define ADSP2_START_MASK 0x0001 /* DSP1_START */
140#define ADSP2_START_SHIFT 0 /* DSP1_START */
141#define ADSP2_START_WIDTH 1 /* DSP1_START */
142
143/*
Mark Brown973838a2012-11-28 17:20:32 +0000144 * ADSP2 clocking
145 */
146#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
147#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
148#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
149
150/*
Mark Brown2159ad92012-10-11 11:54:02 +0900151 * ADSP2 Status 1
152 */
153#define ADSP2_RAM_RDY 0x0001
154#define ADSP2_RAM_RDY_MASK 0x0001
155#define ADSP2_RAM_RDY_SHIFT 0
156#define ADSP2_RAM_RDY_WIDTH 1
157
Mark Browncf17c832013-01-30 14:37:23 +0800158struct wm_adsp_buf {
159 struct list_head list;
160 void *buf;
161};
162
163static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
164 struct list_head *list)
165{
166 struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
167
168 if (buf == NULL)
169 return NULL;
170
171 buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
172 if (!buf->buf) {
173 kfree(buf);
174 return NULL;
175 }
176
177 if (list)
178 list_add_tail(&buf->list, list);
179
180 return buf;
181}
182
183static void wm_adsp_buf_free(struct list_head *list)
184{
185 while (!list_empty(list)) {
186 struct wm_adsp_buf *buf = list_first_entry(list,
187 struct wm_adsp_buf,
188 list);
189 list_del(&buf->list);
190 kfree(buf->buf);
191 kfree(buf);
192 }
193}
194
Mark Brown36e8fe92013-01-25 17:47:48 +0800195#define WM_ADSP_NUM_FW 4
Mark Brown1023dbd2013-01-11 22:58:28 +0000196
197static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Mark Brown36e8fe92013-01-25 17:47:48 +0800198 "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC"
Mark Brown1023dbd2013-01-11 22:58:28 +0000199};
200
201static struct {
202 const char *file;
203} wm_adsp_fw[WM_ADSP_NUM_FW] = {
204 { .file = "mbc-vss" },
205 { .file = "tx" },
Mark Brown36e8fe92013-01-25 17:47:48 +0800206 { .file = "tx-spk" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000207 { .file = "rx-anc" },
208};
209
210static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
211 struct snd_ctl_elem_value *ucontrol)
212{
213 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
214 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
215 struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
216
217 ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
218
219 return 0;
220}
221
222static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
223 struct snd_ctl_elem_value *ucontrol)
224{
225 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
226 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
227 struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
228
229 if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
230 return 0;
231
232 if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
233 return -EINVAL;
234
235 if (adsp[e->shift_l].running)
236 return -EBUSY;
237
Mark Brown31522762013-01-30 20:11:01 +0800238 adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
Mark Brown1023dbd2013-01-11 22:58:28 +0000239
240 return 0;
241}
242
243static const struct soc_enum wm_adsp_fw_enum[] = {
244 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
245 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
246 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
247 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
248};
249
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000250const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
251 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
252 wm_adsp_fw_get, wm_adsp_fw_put),
253 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
254 wm_adsp_fw_get, wm_adsp_fw_put),
255 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
256 wm_adsp_fw_get, wm_adsp_fw_put),
257};
258EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
259
260#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
261static const struct soc_enum wm_adsp2_rate_enum[] = {
Mark Browndc914282013-02-18 19:09:23 +0000262 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
263 ARIZONA_DSP1_RATE_SHIFT, 0xf,
264 ARIZONA_RATE_ENUM_SIZE,
265 arizona_rate_text, arizona_rate_val),
266 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
267 ARIZONA_DSP1_RATE_SHIFT, 0xf,
268 ARIZONA_RATE_ENUM_SIZE,
269 arizona_rate_text, arizona_rate_val),
270 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
271 ARIZONA_DSP1_RATE_SHIFT, 0xf,
272 ARIZONA_RATE_ENUM_SIZE,
273 arizona_rate_text, arizona_rate_val),
274 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
275 ARIZONA_DSP1_RATE_SHIFT, 0xf,
276 ARIZONA_RATE_ENUM_SIZE,
277 arizona_rate_text, arizona_rate_val),
278};
279
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000280const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000281 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
282 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000283 SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000284 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
285 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000286 SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000287 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
288 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000289 SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000290 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
291 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000292 SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000293};
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000294EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
295#endif
Mark Brown2159ad92012-10-11 11:54:02 +0900296
297static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
298 int type)
299{
300 int i;
301
302 for (i = 0; i < dsp->num_mems; i++)
303 if (dsp->mem[i].type == type)
304 return &dsp->mem[i];
305
306 return NULL;
307}
308
Mark Brown45b9ee72013-01-08 16:02:06 +0000309static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
310 unsigned int offset)
311{
312 switch (region->type) {
313 case WMFW_ADSP1_PM:
314 return region->base + (offset * 3);
315 case WMFW_ADSP1_DM:
316 return region->base + (offset * 2);
317 case WMFW_ADSP2_XM:
318 return region->base + (offset * 2);
319 case WMFW_ADSP2_YM:
320 return region->base + (offset * 2);
321 case WMFW_ADSP1_ZM:
322 return region->base + (offset * 2);
323 default:
324 WARN_ON(NULL != "Unknown memory region type");
325 return offset;
326 }
327}
328
Mark Brown2159ad92012-10-11 11:54:02 +0900329static int wm_adsp_load(struct wm_adsp *dsp)
330{
Mark Browncf17c832013-01-30 14:37:23 +0800331 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900332 const struct firmware *firmware;
333 struct regmap *regmap = dsp->regmap;
334 unsigned int pos = 0;
335 const struct wmfw_header *header;
336 const struct wmfw_adsp1_sizes *adsp1_sizes;
337 const struct wmfw_adsp2_sizes *adsp2_sizes;
338 const struct wmfw_footer *footer;
339 const struct wmfw_region *region;
340 const struct wm_adsp_region *mem;
341 const char *region_name;
342 char *file, *text;
Mark Browncf17c832013-01-30 14:37:23 +0800343 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +0900344 unsigned int reg;
345 int regions = 0;
346 int ret, offset, type, sizes;
347
348 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
349 if (file == NULL)
350 return -ENOMEM;
351
Mark Brown1023dbd2013-01-11 22:58:28 +0000352 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
353 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +0900354 file[PAGE_SIZE - 1] = '\0';
355
356 ret = request_firmware(&firmware, file, dsp->dev);
357 if (ret != 0) {
358 adsp_err(dsp, "Failed to request '%s'\n", file);
359 goto out;
360 }
361 ret = -EINVAL;
362
363 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
364 if (pos >= firmware->size) {
365 adsp_err(dsp, "%s: file too short, %zu bytes\n",
366 file, firmware->size);
367 goto out_fw;
368 }
369
370 header = (void*)&firmware->data[0];
371
372 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
373 adsp_err(dsp, "%s: invalid magic\n", file);
374 goto out_fw;
375 }
376
377 if (header->ver != 0) {
378 adsp_err(dsp, "%s: unknown file format %d\n",
379 file, header->ver);
380 goto out_fw;
381 }
382
383 if (header->core != dsp->type) {
384 adsp_err(dsp, "%s: invalid core %d != %d\n",
385 file, header->core, dsp->type);
386 goto out_fw;
387 }
388
389 switch (dsp->type) {
390 case WMFW_ADSP1:
391 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
392 adsp1_sizes = (void *)&(header[1]);
393 footer = (void *)&(adsp1_sizes[1]);
394 sizes = sizeof(*adsp1_sizes);
395
396 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
397 file, le32_to_cpu(adsp1_sizes->dm),
398 le32_to_cpu(adsp1_sizes->pm),
399 le32_to_cpu(adsp1_sizes->zm));
400 break;
401
402 case WMFW_ADSP2:
403 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
404 adsp2_sizes = (void *)&(header[1]);
405 footer = (void *)&(adsp2_sizes[1]);
406 sizes = sizeof(*adsp2_sizes);
407
408 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
409 file, le32_to_cpu(adsp2_sizes->xm),
410 le32_to_cpu(adsp2_sizes->ym),
411 le32_to_cpu(adsp2_sizes->pm),
412 le32_to_cpu(adsp2_sizes->zm));
413 break;
414
415 default:
416 BUG_ON(NULL == "Unknown DSP type");
417 goto out_fw;
418 }
419
420 if (le32_to_cpu(header->len) != sizeof(*header) +
421 sizes + sizeof(*footer)) {
422 adsp_err(dsp, "%s: unexpected header length %d\n",
423 file, le32_to_cpu(header->len));
424 goto out_fw;
425 }
426
427 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
428 le64_to_cpu(footer->timestamp));
429
430 while (pos < firmware->size &&
431 pos - firmware->size > sizeof(*region)) {
432 region = (void *)&(firmware->data[pos]);
433 region_name = "Unknown";
434 reg = 0;
435 text = NULL;
436 offset = le32_to_cpu(region->offset) & 0xffffff;
437 type = be32_to_cpu(region->type) & 0xff;
438 mem = wm_adsp_find_region(dsp, type);
439
440 switch (type) {
441 case WMFW_NAME_TEXT:
442 region_name = "Firmware name";
443 text = kzalloc(le32_to_cpu(region->len) + 1,
444 GFP_KERNEL);
445 break;
446 case WMFW_INFO_TEXT:
447 region_name = "Information";
448 text = kzalloc(le32_to_cpu(region->len) + 1,
449 GFP_KERNEL);
450 break;
451 case WMFW_ABSOLUTE:
452 region_name = "Absolute";
453 reg = offset;
454 break;
455 case WMFW_ADSP1_PM:
456 BUG_ON(!mem);
457 region_name = "PM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000458 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900459 break;
460 case WMFW_ADSP1_DM:
461 BUG_ON(!mem);
462 region_name = "DM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000463 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900464 break;
465 case WMFW_ADSP2_XM:
466 BUG_ON(!mem);
467 region_name = "XM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000468 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900469 break;
470 case WMFW_ADSP2_YM:
471 BUG_ON(!mem);
472 region_name = "YM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000473 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900474 break;
475 case WMFW_ADSP1_ZM:
476 BUG_ON(!mem);
477 region_name = "ZM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000478 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900479 break;
480 default:
481 adsp_warn(dsp,
482 "%s.%d: Unknown region type %x at %d(%x)\n",
483 file, regions, type, pos, pos);
484 break;
485 }
486
487 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
488 regions, le32_to_cpu(region->len), offset,
489 region_name);
490
491 if (text) {
492 memcpy(text, region->data, le32_to_cpu(region->len));
493 adsp_info(dsp, "%s: %s\n", file, text);
494 kfree(text);
495 }
496
497 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +0800498 buf = wm_adsp_buf_alloc(region->data,
499 le32_to_cpu(region->len),
500 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +0000501 if (!buf) {
502 adsp_err(dsp, "Out of memory\n");
503 return -ENOMEM;
504 }
505
Mark Browncf17c832013-01-30 14:37:23 +0800506 ret = regmap_raw_write_async(regmap, reg, buf->buf,
507 le32_to_cpu(region->len));
Mark Brown2159ad92012-10-11 11:54:02 +0900508 if (ret != 0) {
509 adsp_err(dsp,
510 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
511 file, regions,
512 le32_to_cpu(region->len), offset,
513 region_name, ret);
514 goto out_fw;
515 }
516 }
517
518 pos += le32_to_cpu(region->len) + sizeof(*region);
519 regions++;
520 }
Mark Browncf17c832013-01-30 14:37:23 +0800521
522 ret = regmap_async_complete(regmap);
523 if (ret != 0) {
524 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
525 goto out_fw;
526 }
527
Mark Brown2159ad92012-10-11 11:54:02 +0900528 if (pos > firmware->size)
529 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
530 file, regions, pos - firmware->size);
531
532out_fw:
Mark Browncf17c832013-01-30 14:37:23 +0800533 regmap_async_complete(regmap);
534 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900535 release_firmware(firmware);
536out:
537 kfree(file);
538
539 return ret;
540}
541
Mark Browndb405172012-10-26 19:30:40 +0100542static int wm_adsp_setup_algs(struct wm_adsp *dsp)
543{
544 struct regmap *regmap = dsp->regmap;
545 struct wmfw_adsp1_id_hdr adsp1_id;
546 struct wmfw_adsp2_id_hdr adsp2_id;
547 struct wmfw_adsp1_alg_hdr *adsp1_alg;
548 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000549 void *alg, *buf;
Mark Brown471f4882013-01-08 16:09:31 +0000550 struct wm_adsp_alg_region *region;
Mark Browndb405172012-10-26 19:30:40 +0100551 const struct wm_adsp_region *mem;
552 unsigned int pos, term;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000553 size_t algs, buf_size;
Mark Browndb405172012-10-26 19:30:40 +0100554 __be32 val;
555 int i, ret;
556
557 switch (dsp->type) {
558 case WMFW_ADSP1:
559 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
560 break;
561 case WMFW_ADSP2:
562 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
563 break;
564 default:
565 mem = NULL;
566 break;
567 }
568
569 if (mem == NULL) {
570 BUG_ON(mem != NULL);
571 return -EINVAL;
572 }
573
574 switch (dsp->type) {
575 case WMFW_ADSP1:
576 ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
577 sizeof(adsp1_id));
578 if (ret != 0) {
579 adsp_err(dsp, "Failed to read algorithm info: %d\n",
580 ret);
581 return ret;
582 }
583
Mark Brownd62f4bc2012-12-19 14:00:30 +0000584 buf = &adsp1_id;
585 buf_size = sizeof(adsp1_id);
586
Mark Browndb405172012-10-26 19:30:40 +0100587 algs = be32_to_cpu(adsp1_id.algs);
588 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
589 be32_to_cpu(adsp1_id.fw.id),
590 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
591 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
592 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
593 algs);
594
595 pos = sizeof(adsp1_id) / 2;
596 term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
597 break;
598
599 case WMFW_ADSP2:
600 ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
601 sizeof(adsp2_id));
602 if (ret != 0) {
603 adsp_err(dsp, "Failed to read algorithm info: %d\n",
604 ret);
605 return ret;
606 }
607
Mark Brownd62f4bc2012-12-19 14:00:30 +0000608 buf = &adsp2_id;
609 buf_size = sizeof(adsp2_id);
610
Mark Browndb405172012-10-26 19:30:40 +0100611 algs = be32_to_cpu(adsp2_id.algs);
612 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
613 be32_to_cpu(adsp2_id.fw.id),
614 (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
615 (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
616 be32_to_cpu(adsp2_id.fw.ver) & 0xff,
617 algs);
618
619 pos = sizeof(adsp2_id) / 2;
620 term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
621 break;
622
623 default:
624 BUG_ON(NULL == "Unknown DSP type");
625 return -EINVAL;
626 }
627
628 if (algs == 0) {
629 adsp_err(dsp, "No algorithms\n");
630 return -EINVAL;
631 }
632
Mark Brownd62f4bc2012-12-19 14:00:30 +0000633 if (algs > 1024) {
634 adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
635 print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
636 buf, buf_size);
637 return -EINVAL;
638 }
639
Mark Browndb405172012-10-26 19:30:40 +0100640 /* Read the terminator first to validate the length */
641 ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
642 if (ret != 0) {
643 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
644 ret);
645 return ret;
646 }
647
648 if (be32_to_cpu(val) != 0xbedead)
649 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
650 term, be32_to_cpu(val));
651
Mark Brownf2a93e22013-01-20 22:17:30 +0900652 alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +0100653 if (!alg)
654 return -ENOMEM;
655
656 ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
657 if (ret != 0) {
658 adsp_err(dsp, "Failed to read algorithm list: %d\n",
659 ret);
660 goto out;
661 }
662
663 adsp1_alg = alg;
664 adsp2_alg = alg;
665
666 for (i = 0; i < algs; i++) {
667 switch (dsp->type) {
668 case WMFW_ADSP1:
Mark Brown471f4882013-01-08 16:09:31 +0000669 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100670 i, be32_to_cpu(adsp1_alg[i].alg.id),
671 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
672 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000673 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
674 be32_to_cpu(adsp1_alg[i].dm),
675 be32_to_cpu(adsp1_alg[i].zm));
676
Mark Brown74808002013-01-26 00:29:51 +0800677 region = kzalloc(sizeof(*region), GFP_KERNEL);
678 if (!region)
679 return -ENOMEM;
680 region->type = WMFW_ADSP1_DM;
681 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
682 region->base = be32_to_cpu(adsp1_alg[i].dm);
683 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000684
Mark Brown74808002013-01-26 00:29:51 +0800685 region = kzalloc(sizeof(*region), GFP_KERNEL);
686 if (!region)
687 return -ENOMEM;
688 region->type = WMFW_ADSP1_ZM;
689 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
690 region->base = be32_to_cpu(adsp1_alg[i].zm);
691 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100692 break;
693
694 case WMFW_ADSP2:
Mark Brown471f4882013-01-08 16:09:31 +0000695 adsp_info(dsp,
696 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100697 i, be32_to_cpu(adsp2_alg[i].alg.id),
698 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
699 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000700 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
701 be32_to_cpu(adsp2_alg[i].xm),
702 be32_to_cpu(adsp2_alg[i].ym),
703 be32_to_cpu(adsp2_alg[i].zm));
704
Mark Brown74808002013-01-26 00:29:51 +0800705 region = kzalloc(sizeof(*region), GFP_KERNEL);
706 if (!region)
707 return -ENOMEM;
708 region->type = WMFW_ADSP2_XM;
709 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
710 region->base = be32_to_cpu(adsp2_alg[i].xm);
711 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000712
Mark Brown74808002013-01-26 00:29:51 +0800713 region = kzalloc(sizeof(*region), GFP_KERNEL);
714 if (!region)
715 return -ENOMEM;
716 region->type = WMFW_ADSP2_YM;
717 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
718 region->base = be32_to_cpu(adsp2_alg[i].ym);
719 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000720
Mark Brown74808002013-01-26 00:29:51 +0800721 region = kzalloc(sizeof(*region), GFP_KERNEL);
722 if (!region)
723 return -ENOMEM;
724 region->type = WMFW_ADSP2_ZM;
725 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
726 region->base = be32_to_cpu(adsp2_alg[i].zm);
727 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100728 break;
729 }
730 }
731
732out:
733 kfree(alg);
734 return ret;
735}
736
Mark Brown2159ad92012-10-11 11:54:02 +0900737static int wm_adsp_load_coeff(struct wm_adsp *dsp)
738{
Mark Browncf17c832013-01-30 14:37:23 +0800739 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900740 struct regmap *regmap = dsp->regmap;
741 struct wmfw_coeff_hdr *hdr;
742 struct wmfw_coeff_item *blk;
743 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +0000744 const struct wm_adsp_region *mem;
745 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad92012-10-11 11:54:02 +0900746 const char *region_name;
747 int ret, pos, blocks, type, offset, reg;
748 char *file;
Mark Browncf17c832013-01-30 14:37:23 +0800749 struct wm_adsp_buf *buf;
Chris Rattraybdaacea2013-02-08 14:32:15 +0000750 int tmp;
Mark Brown2159ad92012-10-11 11:54:02 +0900751
752 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
753 if (file == NULL)
754 return -ENOMEM;
755
Mark Brown1023dbd2013-01-11 22:58:28 +0000756 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
757 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +0900758 file[PAGE_SIZE - 1] = '\0';
759
760 ret = request_firmware(&firmware, file, dsp->dev);
761 if (ret != 0) {
762 adsp_warn(dsp, "Failed to request '%s'\n", file);
763 ret = 0;
764 goto out;
765 }
766 ret = -EINVAL;
767
768 if (sizeof(*hdr) >= firmware->size) {
769 adsp_err(dsp, "%s: file too short, %zu bytes\n",
770 file, firmware->size);
771 goto out_fw;
772 }
773
774 hdr = (void*)&firmware->data[0];
775 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
776 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +0000777 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +0900778 }
779
Mark Brownc7123262013-01-16 16:59:04 +0900780 switch (be32_to_cpu(hdr->rev) & 0xff) {
781 case 1:
782 break;
783 default:
784 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
785 file, be32_to_cpu(hdr->rev) & 0xff);
786 ret = -EINVAL;
787 goto out_fw;
788 }
789
Mark Brown2159ad92012-10-11 11:54:02 +0900790 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
791 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
792 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
793 le32_to_cpu(hdr->ver) & 0xff);
794
795 pos = le32_to_cpu(hdr->len);
796
797 blocks = 0;
798 while (pos < firmware->size &&
799 pos - firmware->size > sizeof(*blk)) {
800 blk = (void*)(&firmware->data[pos]);
801
Mark Brownc7123262013-01-16 16:59:04 +0900802 type = le16_to_cpu(blk->type);
803 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900804
805 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
806 file, blocks, le32_to_cpu(blk->id),
807 (le32_to_cpu(blk->ver) >> 16) & 0xff,
808 (le32_to_cpu(blk->ver) >> 8) & 0xff,
809 le32_to_cpu(blk->ver) & 0xff);
810 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
811 file, blocks, le32_to_cpu(blk->len), offset, type);
812
813 reg = 0;
814 region_name = "Unknown";
815 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +0900816 case (WMFW_NAME_TEXT << 8):
817 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad92012-10-11 11:54:02 +0900818 break;
Mark Brownc7123262013-01-16 16:59:04 +0900819 case (WMFW_ABSOLUTE << 8):
Mark Brown2159ad92012-10-11 11:54:02 +0900820 region_name = "register";
821 reg = offset;
822 break;
Mark Brown471f4882013-01-08 16:09:31 +0000823
824 case WMFW_ADSP1_DM:
825 case WMFW_ADSP1_ZM:
826 case WMFW_ADSP2_XM:
827 case WMFW_ADSP2_YM:
828 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
829 file, blocks, le32_to_cpu(blk->len),
830 type, le32_to_cpu(blk->id));
831
832 mem = wm_adsp_find_region(dsp, type);
833 if (!mem) {
834 adsp_err(dsp, "No base for region %x\n", type);
835 break;
836 }
837
838 reg = 0;
839 list_for_each_entry(alg_region,
840 &dsp->alg_regions, list) {
841 if (le32_to_cpu(blk->id) == alg_region->alg &&
842 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +0800843 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +0000844 reg = wm_adsp_region_to_reg(mem,
845 reg);
Mark Brown338c5182013-01-24 00:35:48 +0800846 reg += offset;
Mark Brown471f4882013-01-08 16:09:31 +0000847 }
848 }
849
850 if (reg == 0)
851 adsp_err(dsp, "No %x for algorithm %x\n",
852 type, le32_to_cpu(blk->id));
853 break;
854
Mark Brown2159ad92012-10-11 11:54:02 +0900855 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +0900856 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
857 file, blocks, type, pos);
Mark Brown2159ad92012-10-11 11:54:02 +0900858 break;
859 }
860
861 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +0800862 buf = wm_adsp_buf_alloc(blk->data,
863 le32_to_cpu(blk->len),
864 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +0000865 if (!buf) {
866 adsp_err(dsp, "Out of memory\n");
867 return -ENOMEM;
868 }
869
Mark Brown20da6d52013-01-12 19:58:17 +0000870 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
871 file, blocks, le32_to_cpu(blk->len),
872 reg);
Mark Browncf17c832013-01-30 14:37:23 +0800873 ret = regmap_raw_write_async(regmap, reg, buf->buf,
874 le32_to_cpu(blk->len));
Mark Brown2159ad92012-10-11 11:54:02 +0900875 if (ret != 0) {
876 adsp_err(dsp,
877 "%s.%d: Failed to write to %x in %s\n",
878 file, blocks, reg, region_name);
879 }
880 }
881
Chris Rattraybdaacea2013-02-08 14:32:15 +0000882 tmp = le32_to_cpu(blk->len) % 4;
883 if (tmp)
884 pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk);
885 else
886 pos += le32_to_cpu(blk->len) + sizeof(*blk);
887
Mark Brown2159ad92012-10-11 11:54:02 +0900888 blocks++;
889 }
890
Mark Browncf17c832013-01-30 14:37:23 +0800891 ret = regmap_async_complete(regmap);
892 if (ret != 0)
893 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
894
Mark Brown2159ad92012-10-11 11:54:02 +0900895 if (pos > firmware->size)
896 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
897 file, blocks, pos - firmware->size);
898
899out_fw:
900 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +0800901 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900902out:
903 kfree(file);
904 return 0;
905}
906
Mark Brown5e7a7a22013-01-16 10:03:56 +0900907int wm_adsp1_init(struct wm_adsp *adsp)
908{
909 INIT_LIST_HEAD(&adsp->alg_regions);
910
911 return 0;
912}
913EXPORT_SYMBOL_GPL(wm_adsp1_init);
914
Mark Brown2159ad92012-10-11 11:54:02 +0900915int wm_adsp1_event(struct snd_soc_dapm_widget *w,
916 struct snd_kcontrol *kcontrol,
917 int event)
918{
919 struct snd_soc_codec *codec = w->codec;
920 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
921 struct wm_adsp *dsp = &dsps[w->shift];
922 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +0000923 int val;
Mark Brown2159ad92012-10-11 11:54:02 +0900924
925 switch (event) {
926 case SND_SOC_DAPM_POST_PMU:
927 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
928 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
929
Chris Rattray94e205b2013-01-18 08:43:09 +0000930 /*
931 * For simplicity set the DSP clock rate to be the
932 * SYSCLK rate rather than making it configurable.
933 */
934 if(dsp->sysclk_reg) {
935 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
936 if (ret != 0) {
937 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
938 ret);
939 return ret;
940 }
941
942 val = (val & dsp->sysclk_mask)
943 >> dsp->sysclk_shift;
944
945 ret = regmap_update_bits(dsp->regmap,
946 dsp->base + ADSP1_CONTROL_31,
947 ADSP1_CLK_SEL_MASK, val);
948 if (ret != 0) {
949 adsp_err(dsp, "Failed to set clock rate: %d\n",
950 ret);
951 return ret;
952 }
953 }
954
Mark Brown2159ad92012-10-11 11:54:02 +0900955 ret = wm_adsp_load(dsp);
956 if (ret != 0)
957 goto err;
958
Mark Browndb405172012-10-26 19:30:40 +0100959 ret = wm_adsp_setup_algs(dsp);
960 if (ret != 0)
961 goto err;
962
Mark Brown2159ad92012-10-11 11:54:02 +0900963 ret = wm_adsp_load_coeff(dsp);
964 if (ret != 0)
965 goto err;
966
967 /* Start the core running */
968 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
969 ADSP1_CORE_ENA | ADSP1_START,
970 ADSP1_CORE_ENA | ADSP1_START);
971 break;
972
973 case SND_SOC_DAPM_PRE_PMD:
974 /* Halt the core */
975 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
976 ADSP1_CORE_ENA | ADSP1_START, 0);
977
978 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
979 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
980
981 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
982 ADSP1_SYS_ENA, 0);
983 break;
984
985 default:
986 break;
987 }
988
989 return 0;
990
991err:
992 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
993 ADSP1_SYS_ENA, 0);
994 return ret;
995}
996EXPORT_SYMBOL_GPL(wm_adsp1_event);
997
998static int wm_adsp2_ena(struct wm_adsp *dsp)
999{
1000 unsigned int val;
1001 int ret, count;
1002
1003 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1004 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
1005 if (ret != 0)
1006 return ret;
1007
1008 /* Wait for the RAM to start, should be near instantaneous */
1009 count = 0;
1010 do {
1011 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
1012 &val);
1013 if (ret != 0)
1014 return ret;
1015 } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
1016
1017 if (!(val & ADSP2_RAM_RDY)) {
1018 adsp_err(dsp, "Failed to start DSP RAM\n");
1019 return -EBUSY;
1020 }
1021
1022 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
1023 adsp_info(dsp, "RAM ready after %d polls\n", count);
1024
1025 return 0;
1026}
1027
1028int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1029 struct snd_kcontrol *kcontrol, int event)
1030{
1031 struct snd_soc_codec *codec = w->codec;
1032 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1033 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +00001034 struct wm_adsp_alg_region *alg_region;
Mark Brown973838a2012-11-28 17:20:32 +00001035 unsigned int val;
Mark Brown2159ad92012-10-11 11:54:02 +09001036 int ret;
1037
1038 switch (event) {
1039 case SND_SOC_DAPM_POST_PMU:
Mark Browndd49e2c2012-12-02 21:50:46 +09001040 /*
1041 * For simplicity set the DSP clock rate to be the
1042 * SYSCLK rate rather than making it configurable.
1043 */
1044 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1045 if (ret != 0) {
1046 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
1047 ret);
1048 return ret;
1049 }
1050 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1051 >> ARIZONA_SYSCLK_FREQ_SHIFT;
1052
1053 ret = regmap_update_bits(dsp->regmap,
1054 dsp->base + ADSP2_CLOCKING,
1055 ADSP2_CLK_SEL_MASK, val);
1056 if (ret != 0) {
1057 adsp_err(dsp, "Failed to set clock rate: %d\n",
1058 ret);
1059 return ret;
1060 }
1061
Mark Brown973838a2012-11-28 17:20:32 +00001062 if (dsp->dvfs) {
1063 ret = regmap_read(dsp->regmap,
1064 dsp->base + ADSP2_CLOCKING, &val);
1065 if (ret != 0) {
1066 dev_err(dsp->dev,
1067 "Failed to read clocking: %d\n", ret);
1068 return ret;
1069 }
1070
Mark Brown25c6fdb2012-11-29 15:16:10 +00001071 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
Mark Brown973838a2012-11-28 17:20:32 +00001072 ret = regulator_enable(dsp->dvfs);
1073 if (ret != 0) {
1074 dev_err(dsp->dev,
1075 "Failed to enable supply: %d\n",
1076 ret);
1077 return ret;
1078 }
1079
1080 ret = regulator_set_voltage(dsp->dvfs,
1081 1800000,
1082 1800000);
1083 if (ret != 0) {
1084 dev_err(dsp->dev,
1085 "Failed to raise supply: %d\n",
1086 ret);
1087 return ret;
1088 }
1089 }
1090 }
1091
Mark Brown2159ad92012-10-11 11:54:02 +09001092 ret = wm_adsp2_ena(dsp);
1093 if (ret != 0)
1094 return ret;
1095
1096 ret = wm_adsp_load(dsp);
1097 if (ret != 0)
1098 goto err;
1099
Mark Browndb405172012-10-26 19:30:40 +01001100 ret = wm_adsp_setup_algs(dsp);
1101 if (ret != 0)
1102 goto err;
1103
Mark Brown2159ad92012-10-11 11:54:02 +09001104 ret = wm_adsp_load_coeff(dsp);
1105 if (ret != 0)
1106 goto err;
1107
1108 ret = regmap_update_bits(dsp->regmap,
1109 dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001110 ADSP2_CORE_ENA | ADSP2_START,
1111 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad92012-10-11 11:54:02 +09001112 if (ret != 0)
1113 goto err;
Mark Brown1023dbd2013-01-11 22:58:28 +00001114
1115 dsp->running = true;
Mark Brown2159ad92012-10-11 11:54:02 +09001116 break;
1117
1118 case SND_SOC_DAPM_PRE_PMD:
Mark Brown1023dbd2013-01-11 22:58:28 +00001119 dsp->running = false;
1120
Mark Brown2159ad92012-10-11 11:54:02 +09001121 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001122 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1123 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00001124
Mark Brown2d30b572013-01-28 20:18:17 +08001125 /* Make sure DMAs are quiesced */
1126 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
1127 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
1128 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
1129
Mark Brown973838a2012-11-28 17:20:32 +00001130 if (dsp->dvfs) {
1131 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1132 1800000);
1133 if (ret != 0)
1134 dev_warn(dsp->dev,
1135 "Failed to lower supply: %d\n",
1136 ret);
1137
1138 ret = regulator_disable(dsp->dvfs);
1139 if (ret != 0)
1140 dev_err(dsp->dev,
1141 "Failed to enable supply: %d\n",
1142 ret);
1143 }
Mark Brown471f4882013-01-08 16:09:31 +00001144
1145 while (!list_empty(&dsp->alg_regions)) {
1146 alg_region = list_first_entry(&dsp->alg_regions,
1147 struct wm_adsp_alg_region,
1148 list);
1149 list_del(&alg_region->list);
1150 kfree(alg_region);
1151 }
Mark Brown2159ad92012-10-11 11:54:02 +09001152 break;
1153
1154 default:
1155 break;
1156 }
1157
1158 return 0;
1159err:
1160 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001161 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad92012-10-11 11:54:02 +09001162 return ret;
1163}
1164EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00001165
1166int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1167{
1168 int ret;
1169
Mark Brown10a2b662012-12-02 21:37:00 +09001170 /*
1171 * Disable the DSP memory by default when in reset for a small
1172 * power saving.
1173 */
1174 ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
1175 ADSP2_MEM_ENA, 0);
1176 if (ret != 0) {
1177 adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
1178 return ret;
1179 }
1180
Mark Brown471f4882013-01-08 16:09:31 +00001181 INIT_LIST_HEAD(&adsp->alg_regions);
1182
Mark Brown973838a2012-11-28 17:20:32 +00001183 if (dvfs) {
1184 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1185 if (IS_ERR(adsp->dvfs)) {
1186 ret = PTR_ERR(adsp->dvfs);
1187 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1188 return ret;
1189 }
1190
1191 ret = regulator_enable(adsp->dvfs);
1192 if (ret != 0) {
1193 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1194 ret);
1195 return ret;
1196 }
1197
1198 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1199 if (ret != 0) {
1200 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1201 ret);
1202 return ret;
1203 }
1204
1205 ret = regulator_disable(adsp->dvfs);
1206 if (ret != 0) {
1207 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1208 ret);
1209 return ret;
1210 }
1211 }
1212
1213 return 0;
1214}
1215EXPORT_SYMBOL_GPL(wm_adsp2_init);