blob: 3a481fd1bf9a25465723482e78891e7176ec2301 [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 Browndc914282013-02-18 19:09:23 +0000250static const struct soc_enum wm_adsp_rate_enum[] = {
251 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
252 ARIZONA_DSP1_RATE_SHIFT, 0xf,
253 ARIZONA_RATE_ENUM_SIZE,
254 arizona_rate_text, arizona_rate_val),
255 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
256 ARIZONA_DSP1_RATE_SHIFT, 0xf,
257 ARIZONA_RATE_ENUM_SIZE,
258 arizona_rate_text, arizona_rate_val),
259 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
260 ARIZONA_DSP1_RATE_SHIFT, 0xf,
261 ARIZONA_RATE_ENUM_SIZE,
262 arizona_rate_text, arizona_rate_val),
263 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
264 ARIZONA_DSP1_RATE_SHIFT, 0xf,
265 ARIZONA_RATE_ENUM_SIZE,
266 arizona_rate_text, arizona_rate_val),
267};
268
Mark Brown1023dbd2013-01-11 22:58:28 +0000269const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
270 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
271 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Browndc914282013-02-18 19:09:23 +0000272 SOC_ENUM("DSP1 Rate", wm_adsp_rate_enum[0]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000273 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
274 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Browndc914282013-02-18 19:09:23 +0000275 SOC_ENUM("DSP2 Rate", wm_adsp_rate_enum[1]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000276 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
277 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Browndc914282013-02-18 19:09:23 +0000278 SOC_ENUM("DSP3 Rate", wm_adsp_rate_enum[2]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000279 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
280 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Browndc914282013-02-18 19:09:23 +0000281 SOC_ENUM("DSP4 Rate", wm_adsp_rate_enum[3]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000282};
283EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
Mark Brown2159ad92012-10-11 11:54:02 +0900284
285static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
286 int type)
287{
288 int i;
289
290 for (i = 0; i < dsp->num_mems; i++)
291 if (dsp->mem[i].type == type)
292 return &dsp->mem[i];
293
294 return NULL;
295}
296
Mark Brown45b9ee72013-01-08 16:02:06 +0000297static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
298 unsigned int offset)
299{
300 switch (region->type) {
301 case WMFW_ADSP1_PM:
302 return region->base + (offset * 3);
303 case WMFW_ADSP1_DM:
304 return region->base + (offset * 2);
305 case WMFW_ADSP2_XM:
306 return region->base + (offset * 2);
307 case WMFW_ADSP2_YM:
308 return region->base + (offset * 2);
309 case WMFW_ADSP1_ZM:
310 return region->base + (offset * 2);
311 default:
312 WARN_ON(NULL != "Unknown memory region type");
313 return offset;
314 }
315}
316
Mark Brown2159ad92012-10-11 11:54:02 +0900317static int wm_adsp_load(struct wm_adsp *dsp)
318{
Mark Browncf17c832013-01-30 14:37:23 +0800319 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900320 const struct firmware *firmware;
321 struct regmap *regmap = dsp->regmap;
322 unsigned int pos = 0;
323 const struct wmfw_header *header;
324 const struct wmfw_adsp1_sizes *adsp1_sizes;
325 const struct wmfw_adsp2_sizes *adsp2_sizes;
326 const struct wmfw_footer *footer;
327 const struct wmfw_region *region;
328 const struct wm_adsp_region *mem;
329 const char *region_name;
330 char *file, *text;
Mark Browncf17c832013-01-30 14:37:23 +0800331 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +0900332 unsigned int reg;
333 int regions = 0;
334 int ret, offset, type, sizes;
335
336 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
337 if (file == NULL)
338 return -ENOMEM;
339
Mark Brown1023dbd2013-01-11 22:58:28 +0000340 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
341 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +0900342 file[PAGE_SIZE - 1] = '\0';
343
344 ret = request_firmware(&firmware, file, dsp->dev);
345 if (ret != 0) {
346 adsp_err(dsp, "Failed to request '%s'\n", file);
347 goto out;
348 }
349 ret = -EINVAL;
350
351 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
352 if (pos >= firmware->size) {
353 adsp_err(dsp, "%s: file too short, %zu bytes\n",
354 file, firmware->size);
355 goto out_fw;
356 }
357
358 header = (void*)&firmware->data[0];
359
360 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
361 adsp_err(dsp, "%s: invalid magic\n", file);
362 goto out_fw;
363 }
364
365 if (header->ver != 0) {
366 adsp_err(dsp, "%s: unknown file format %d\n",
367 file, header->ver);
368 goto out_fw;
369 }
370
371 if (header->core != dsp->type) {
372 adsp_err(dsp, "%s: invalid core %d != %d\n",
373 file, header->core, dsp->type);
374 goto out_fw;
375 }
376
377 switch (dsp->type) {
378 case WMFW_ADSP1:
379 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
380 adsp1_sizes = (void *)&(header[1]);
381 footer = (void *)&(adsp1_sizes[1]);
382 sizes = sizeof(*adsp1_sizes);
383
384 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
385 file, le32_to_cpu(adsp1_sizes->dm),
386 le32_to_cpu(adsp1_sizes->pm),
387 le32_to_cpu(adsp1_sizes->zm));
388 break;
389
390 case WMFW_ADSP2:
391 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
392 adsp2_sizes = (void *)&(header[1]);
393 footer = (void *)&(adsp2_sizes[1]);
394 sizes = sizeof(*adsp2_sizes);
395
396 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
397 file, le32_to_cpu(adsp2_sizes->xm),
398 le32_to_cpu(adsp2_sizes->ym),
399 le32_to_cpu(adsp2_sizes->pm),
400 le32_to_cpu(adsp2_sizes->zm));
401 break;
402
403 default:
404 BUG_ON(NULL == "Unknown DSP type");
405 goto out_fw;
406 }
407
408 if (le32_to_cpu(header->len) != sizeof(*header) +
409 sizes + sizeof(*footer)) {
410 adsp_err(dsp, "%s: unexpected header length %d\n",
411 file, le32_to_cpu(header->len));
412 goto out_fw;
413 }
414
415 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
416 le64_to_cpu(footer->timestamp));
417
418 while (pos < firmware->size &&
419 pos - firmware->size > sizeof(*region)) {
420 region = (void *)&(firmware->data[pos]);
421 region_name = "Unknown";
422 reg = 0;
423 text = NULL;
424 offset = le32_to_cpu(region->offset) & 0xffffff;
425 type = be32_to_cpu(region->type) & 0xff;
426 mem = wm_adsp_find_region(dsp, type);
427
428 switch (type) {
429 case WMFW_NAME_TEXT:
430 region_name = "Firmware name";
431 text = kzalloc(le32_to_cpu(region->len) + 1,
432 GFP_KERNEL);
433 break;
434 case WMFW_INFO_TEXT:
435 region_name = "Information";
436 text = kzalloc(le32_to_cpu(region->len) + 1,
437 GFP_KERNEL);
438 break;
439 case WMFW_ABSOLUTE:
440 region_name = "Absolute";
441 reg = offset;
442 break;
443 case WMFW_ADSP1_PM:
444 BUG_ON(!mem);
445 region_name = "PM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000446 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900447 break;
448 case WMFW_ADSP1_DM:
449 BUG_ON(!mem);
450 region_name = "DM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000451 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900452 break;
453 case WMFW_ADSP2_XM:
454 BUG_ON(!mem);
455 region_name = "XM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000456 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900457 break;
458 case WMFW_ADSP2_YM:
459 BUG_ON(!mem);
460 region_name = "YM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000461 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900462 break;
463 case WMFW_ADSP1_ZM:
464 BUG_ON(!mem);
465 region_name = "ZM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000466 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900467 break;
468 default:
469 adsp_warn(dsp,
470 "%s.%d: Unknown region type %x at %d(%x)\n",
471 file, regions, type, pos, pos);
472 break;
473 }
474
475 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
476 regions, le32_to_cpu(region->len), offset,
477 region_name);
478
479 if (text) {
480 memcpy(text, region->data, le32_to_cpu(region->len));
481 adsp_info(dsp, "%s: %s\n", file, text);
482 kfree(text);
483 }
484
485 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +0800486 buf = wm_adsp_buf_alloc(region->data,
487 le32_to_cpu(region->len),
488 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +0000489 if (!buf) {
490 adsp_err(dsp, "Out of memory\n");
491 return -ENOMEM;
492 }
493
Mark Browncf17c832013-01-30 14:37:23 +0800494 ret = regmap_raw_write_async(regmap, reg, buf->buf,
495 le32_to_cpu(region->len));
Mark Brown2159ad92012-10-11 11:54:02 +0900496 if (ret != 0) {
497 adsp_err(dsp,
498 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
499 file, regions,
500 le32_to_cpu(region->len), offset,
501 region_name, ret);
502 goto out_fw;
503 }
504 }
505
506 pos += le32_to_cpu(region->len) + sizeof(*region);
507 regions++;
508 }
Mark Browncf17c832013-01-30 14:37:23 +0800509
510 ret = regmap_async_complete(regmap);
511 if (ret != 0) {
512 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
513 goto out_fw;
514 }
515
Mark Brown2159ad92012-10-11 11:54:02 +0900516 if (pos > firmware->size)
517 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
518 file, regions, pos - firmware->size);
519
520out_fw:
Mark Browncf17c832013-01-30 14:37:23 +0800521 regmap_async_complete(regmap);
522 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900523 release_firmware(firmware);
524out:
525 kfree(file);
526
527 return ret;
528}
529
Mark Browndb405172012-10-26 19:30:40 +0100530static int wm_adsp_setup_algs(struct wm_adsp *dsp)
531{
532 struct regmap *regmap = dsp->regmap;
533 struct wmfw_adsp1_id_hdr adsp1_id;
534 struct wmfw_adsp2_id_hdr adsp2_id;
535 struct wmfw_adsp1_alg_hdr *adsp1_alg;
536 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000537 void *alg, *buf;
Mark Brown471f4882013-01-08 16:09:31 +0000538 struct wm_adsp_alg_region *region;
Mark Browndb405172012-10-26 19:30:40 +0100539 const struct wm_adsp_region *mem;
540 unsigned int pos, term;
Mark Brownd62f4bc2012-12-19 14:00:30 +0000541 size_t algs, buf_size;
Mark Browndb405172012-10-26 19:30:40 +0100542 __be32 val;
543 int i, ret;
544
545 switch (dsp->type) {
546 case WMFW_ADSP1:
547 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
548 break;
549 case WMFW_ADSP2:
550 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
551 break;
552 default:
553 mem = NULL;
554 break;
555 }
556
557 if (mem == NULL) {
558 BUG_ON(mem != NULL);
559 return -EINVAL;
560 }
561
562 switch (dsp->type) {
563 case WMFW_ADSP1:
564 ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
565 sizeof(adsp1_id));
566 if (ret != 0) {
567 adsp_err(dsp, "Failed to read algorithm info: %d\n",
568 ret);
569 return ret;
570 }
571
Mark Brownd62f4bc2012-12-19 14:00:30 +0000572 buf = &adsp1_id;
573 buf_size = sizeof(adsp1_id);
574
Mark Browndb405172012-10-26 19:30:40 +0100575 algs = be32_to_cpu(adsp1_id.algs);
576 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
577 be32_to_cpu(adsp1_id.fw.id),
578 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
579 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
580 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
581 algs);
582
583 pos = sizeof(adsp1_id) / 2;
584 term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
585 break;
586
587 case WMFW_ADSP2:
588 ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
589 sizeof(adsp2_id));
590 if (ret != 0) {
591 adsp_err(dsp, "Failed to read algorithm info: %d\n",
592 ret);
593 return ret;
594 }
595
Mark Brownd62f4bc2012-12-19 14:00:30 +0000596 buf = &adsp2_id;
597 buf_size = sizeof(adsp2_id);
598
Mark Browndb405172012-10-26 19:30:40 +0100599 algs = be32_to_cpu(adsp2_id.algs);
600 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
601 be32_to_cpu(adsp2_id.fw.id),
602 (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
603 (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
604 be32_to_cpu(adsp2_id.fw.ver) & 0xff,
605 algs);
606
607 pos = sizeof(adsp2_id) / 2;
608 term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
609 break;
610
611 default:
612 BUG_ON(NULL == "Unknown DSP type");
613 return -EINVAL;
614 }
615
616 if (algs == 0) {
617 adsp_err(dsp, "No algorithms\n");
618 return -EINVAL;
619 }
620
Mark Brownd62f4bc2012-12-19 14:00:30 +0000621 if (algs > 1024) {
622 adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
623 print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
624 buf, buf_size);
625 return -EINVAL;
626 }
627
Mark Browndb405172012-10-26 19:30:40 +0100628 /* Read the terminator first to validate the length */
629 ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
630 if (ret != 0) {
631 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
632 ret);
633 return ret;
634 }
635
636 if (be32_to_cpu(val) != 0xbedead)
637 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
638 term, be32_to_cpu(val));
639
Mark Brownf2a93e22013-01-20 22:17:30 +0900640 alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +0100641 if (!alg)
642 return -ENOMEM;
643
644 ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
645 if (ret != 0) {
646 adsp_err(dsp, "Failed to read algorithm list: %d\n",
647 ret);
648 goto out;
649 }
650
651 adsp1_alg = alg;
652 adsp2_alg = alg;
653
654 for (i = 0; i < algs; i++) {
655 switch (dsp->type) {
656 case WMFW_ADSP1:
Mark Brown471f4882013-01-08 16:09:31 +0000657 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100658 i, be32_to_cpu(adsp1_alg[i].alg.id),
659 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
660 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000661 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
662 be32_to_cpu(adsp1_alg[i].dm),
663 be32_to_cpu(adsp1_alg[i].zm));
664
Mark Brown74808002013-01-26 00:29:51 +0800665 region = kzalloc(sizeof(*region), GFP_KERNEL);
666 if (!region)
667 return -ENOMEM;
668 region->type = WMFW_ADSP1_DM;
669 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
670 region->base = be32_to_cpu(adsp1_alg[i].dm);
671 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000672
Mark Brown74808002013-01-26 00:29:51 +0800673 region = kzalloc(sizeof(*region), GFP_KERNEL);
674 if (!region)
675 return -ENOMEM;
676 region->type = WMFW_ADSP1_ZM;
677 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
678 region->base = be32_to_cpu(adsp1_alg[i].zm);
679 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100680 break;
681
682 case WMFW_ADSP2:
Mark Brown471f4882013-01-08 16:09:31 +0000683 adsp_info(dsp,
684 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
Mark Browndb405172012-10-26 19:30:40 +0100685 i, be32_to_cpu(adsp2_alg[i].alg.id),
686 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
687 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
Mark Brown471f4882013-01-08 16:09:31 +0000688 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
689 be32_to_cpu(adsp2_alg[i].xm),
690 be32_to_cpu(adsp2_alg[i].ym),
691 be32_to_cpu(adsp2_alg[i].zm));
692
Mark Brown74808002013-01-26 00:29:51 +0800693 region = kzalloc(sizeof(*region), GFP_KERNEL);
694 if (!region)
695 return -ENOMEM;
696 region->type = WMFW_ADSP2_XM;
697 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
698 region->base = be32_to_cpu(adsp2_alg[i].xm);
699 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000700
Mark Brown74808002013-01-26 00:29:51 +0800701 region = kzalloc(sizeof(*region), GFP_KERNEL);
702 if (!region)
703 return -ENOMEM;
704 region->type = WMFW_ADSP2_YM;
705 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
706 region->base = be32_to_cpu(adsp2_alg[i].ym);
707 list_add_tail(&region->list, &dsp->alg_regions);
Mark Brown471f4882013-01-08 16:09:31 +0000708
Mark Brown74808002013-01-26 00:29:51 +0800709 region = kzalloc(sizeof(*region), GFP_KERNEL);
710 if (!region)
711 return -ENOMEM;
712 region->type = WMFW_ADSP2_ZM;
713 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
714 region->base = be32_to_cpu(adsp2_alg[i].zm);
715 list_add_tail(&region->list, &dsp->alg_regions);
Mark Browndb405172012-10-26 19:30:40 +0100716 break;
717 }
718 }
719
720out:
721 kfree(alg);
722 return ret;
723}
724
Mark Brown2159ad92012-10-11 11:54:02 +0900725static int wm_adsp_load_coeff(struct wm_adsp *dsp)
726{
Mark Browncf17c832013-01-30 14:37:23 +0800727 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900728 struct regmap *regmap = dsp->regmap;
729 struct wmfw_coeff_hdr *hdr;
730 struct wmfw_coeff_item *blk;
731 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +0000732 const struct wm_adsp_region *mem;
733 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad92012-10-11 11:54:02 +0900734 const char *region_name;
735 int ret, pos, blocks, type, offset, reg;
736 char *file;
Mark Browncf17c832013-01-30 14:37:23 +0800737 struct wm_adsp_buf *buf;
Chris Rattraybdaacea2013-02-08 14:32:15 +0000738 int tmp;
Mark Brown2159ad92012-10-11 11:54:02 +0900739
740 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
741 if (file == NULL)
742 return -ENOMEM;
743
Mark Brown1023dbd2013-01-11 22:58:28 +0000744 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
745 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +0900746 file[PAGE_SIZE - 1] = '\0';
747
748 ret = request_firmware(&firmware, file, dsp->dev);
749 if (ret != 0) {
750 adsp_warn(dsp, "Failed to request '%s'\n", file);
751 ret = 0;
752 goto out;
753 }
754 ret = -EINVAL;
755
756 if (sizeof(*hdr) >= firmware->size) {
757 adsp_err(dsp, "%s: file too short, %zu bytes\n",
758 file, firmware->size);
759 goto out_fw;
760 }
761
762 hdr = (void*)&firmware->data[0];
763 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
764 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +0000765 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +0900766 }
767
Mark Brownc7123262013-01-16 16:59:04 +0900768 switch (be32_to_cpu(hdr->rev) & 0xff) {
769 case 1:
770 break;
771 default:
772 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
773 file, be32_to_cpu(hdr->rev) & 0xff);
774 ret = -EINVAL;
775 goto out_fw;
776 }
777
Mark Brown2159ad92012-10-11 11:54:02 +0900778 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
779 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
780 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
781 le32_to_cpu(hdr->ver) & 0xff);
782
783 pos = le32_to_cpu(hdr->len);
784
785 blocks = 0;
786 while (pos < firmware->size &&
787 pos - firmware->size > sizeof(*blk)) {
788 blk = (void*)(&firmware->data[pos]);
789
Mark Brownc7123262013-01-16 16:59:04 +0900790 type = le16_to_cpu(blk->type);
791 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900792
793 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
794 file, blocks, le32_to_cpu(blk->id),
795 (le32_to_cpu(blk->ver) >> 16) & 0xff,
796 (le32_to_cpu(blk->ver) >> 8) & 0xff,
797 le32_to_cpu(blk->ver) & 0xff);
798 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
799 file, blocks, le32_to_cpu(blk->len), offset, type);
800
801 reg = 0;
802 region_name = "Unknown";
803 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +0900804 case (WMFW_NAME_TEXT << 8):
805 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad92012-10-11 11:54:02 +0900806 break;
Mark Brownc7123262013-01-16 16:59:04 +0900807 case (WMFW_ABSOLUTE << 8):
Mark Brown2159ad92012-10-11 11:54:02 +0900808 region_name = "register";
809 reg = offset;
810 break;
Mark Brown471f4882013-01-08 16:09:31 +0000811
812 case WMFW_ADSP1_DM:
813 case WMFW_ADSP1_ZM:
814 case WMFW_ADSP2_XM:
815 case WMFW_ADSP2_YM:
816 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
817 file, blocks, le32_to_cpu(blk->len),
818 type, le32_to_cpu(blk->id));
819
820 mem = wm_adsp_find_region(dsp, type);
821 if (!mem) {
822 adsp_err(dsp, "No base for region %x\n", type);
823 break;
824 }
825
826 reg = 0;
827 list_for_each_entry(alg_region,
828 &dsp->alg_regions, list) {
829 if (le32_to_cpu(blk->id) == alg_region->alg &&
830 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +0800831 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +0000832 reg = wm_adsp_region_to_reg(mem,
833 reg);
Mark Brown338c5182013-01-24 00:35:48 +0800834 reg += offset;
Mark Brown471f4882013-01-08 16:09:31 +0000835 }
836 }
837
838 if (reg == 0)
839 adsp_err(dsp, "No %x for algorithm %x\n",
840 type, le32_to_cpu(blk->id));
841 break;
842
Mark Brown2159ad92012-10-11 11:54:02 +0900843 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +0900844 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
845 file, blocks, type, pos);
Mark Brown2159ad92012-10-11 11:54:02 +0900846 break;
847 }
848
849 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +0800850 buf = wm_adsp_buf_alloc(blk->data,
851 le32_to_cpu(blk->len),
852 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +0000853 if (!buf) {
854 adsp_err(dsp, "Out of memory\n");
855 return -ENOMEM;
856 }
857
Mark Brown20da6d52013-01-12 19:58:17 +0000858 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
859 file, blocks, le32_to_cpu(blk->len),
860 reg);
Mark Browncf17c832013-01-30 14:37:23 +0800861 ret = regmap_raw_write_async(regmap, reg, buf->buf,
862 le32_to_cpu(blk->len));
Mark Brown2159ad92012-10-11 11:54:02 +0900863 if (ret != 0) {
864 adsp_err(dsp,
865 "%s.%d: Failed to write to %x in %s\n",
866 file, blocks, reg, region_name);
867 }
868 }
869
Chris Rattraybdaacea2013-02-08 14:32:15 +0000870 tmp = le32_to_cpu(blk->len) % 4;
871 if (tmp)
872 pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk);
873 else
874 pos += le32_to_cpu(blk->len) + sizeof(*blk);
875
Mark Brown2159ad92012-10-11 11:54:02 +0900876 blocks++;
877 }
878
Mark Browncf17c832013-01-30 14:37:23 +0800879 ret = regmap_async_complete(regmap);
880 if (ret != 0)
881 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
882
Mark Brown2159ad92012-10-11 11:54:02 +0900883 if (pos > firmware->size)
884 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
885 file, blocks, pos - firmware->size);
886
887out_fw:
888 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +0800889 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900890out:
891 kfree(file);
892 return 0;
893}
894
Mark Brown5e7a7a22013-01-16 10:03:56 +0900895int wm_adsp1_init(struct wm_adsp *adsp)
896{
897 INIT_LIST_HEAD(&adsp->alg_regions);
898
899 return 0;
900}
901EXPORT_SYMBOL_GPL(wm_adsp1_init);
902
Mark Brown2159ad92012-10-11 11:54:02 +0900903int wm_adsp1_event(struct snd_soc_dapm_widget *w,
904 struct snd_kcontrol *kcontrol,
905 int event)
906{
907 struct snd_soc_codec *codec = w->codec;
908 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
909 struct wm_adsp *dsp = &dsps[w->shift];
910 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +0000911 int val;
Mark Brown2159ad92012-10-11 11:54:02 +0900912
913 switch (event) {
914 case SND_SOC_DAPM_POST_PMU:
915 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
916 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
917
Chris Rattray94e205b2013-01-18 08:43:09 +0000918 /*
919 * For simplicity set the DSP clock rate to be the
920 * SYSCLK rate rather than making it configurable.
921 */
922 if(dsp->sysclk_reg) {
923 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
924 if (ret != 0) {
925 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
926 ret);
927 return ret;
928 }
929
930 val = (val & dsp->sysclk_mask)
931 >> dsp->sysclk_shift;
932
933 ret = regmap_update_bits(dsp->regmap,
934 dsp->base + ADSP1_CONTROL_31,
935 ADSP1_CLK_SEL_MASK, val);
936 if (ret != 0) {
937 adsp_err(dsp, "Failed to set clock rate: %d\n",
938 ret);
939 return ret;
940 }
941 }
942
Mark Brown2159ad92012-10-11 11:54:02 +0900943 ret = wm_adsp_load(dsp);
944 if (ret != 0)
945 goto err;
946
Mark Browndb405172012-10-26 19:30:40 +0100947 ret = wm_adsp_setup_algs(dsp);
948 if (ret != 0)
949 goto err;
950
Mark Brown2159ad92012-10-11 11:54:02 +0900951 ret = wm_adsp_load_coeff(dsp);
952 if (ret != 0)
953 goto err;
954
955 /* Start the core running */
956 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
957 ADSP1_CORE_ENA | ADSP1_START,
958 ADSP1_CORE_ENA | ADSP1_START);
959 break;
960
961 case SND_SOC_DAPM_PRE_PMD:
962 /* Halt the core */
963 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
964 ADSP1_CORE_ENA | ADSP1_START, 0);
965
966 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
967 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
968
969 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
970 ADSP1_SYS_ENA, 0);
971 break;
972
973 default:
974 break;
975 }
976
977 return 0;
978
979err:
980 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
981 ADSP1_SYS_ENA, 0);
982 return ret;
983}
984EXPORT_SYMBOL_GPL(wm_adsp1_event);
985
986static int wm_adsp2_ena(struct wm_adsp *dsp)
987{
988 unsigned int val;
989 int ret, count;
990
991 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
992 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
993 if (ret != 0)
994 return ret;
995
996 /* Wait for the RAM to start, should be near instantaneous */
997 count = 0;
998 do {
999 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
1000 &val);
1001 if (ret != 0)
1002 return ret;
1003 } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
1004
1005 if (!(val & ADSP2_RAM_RDY)) {
1006 adsp_err(dsp, "Failed to start DSP RAM\n");
1007 return -EBUSY;
1008 }
1009
1010 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
1011 adsp_info(dsp, "RAM ready after %d polls\n", count);
1012
1013 return 0;
1014}
1015
1016int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1017 struct snd_kcontrol *kcontrol, int event)
1018{
1019 struct snd_soc_codec *codec = w->codec;
1020 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1021 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +00001022 struct wm_adsp_alg_region *alg_region;
Mark Brown973838a2012-11-28 17:20:32 +00001023 unsigned int val;
Mark Brown2159ad92012-10-11 11:54:02 +09001024 int ret;
1025
1026 switch (event) {
1027 case SND_SOC_DAPM_POST_PMU:
Mark Browndd49e2c2012-12-02 21:50:46 +09001028 /*
1029 * For simplicity set the DSP clock rate to be the
1030 * SYSCLK rate rather than making it configurable.
1031 */
1032 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1033 if (ret != 0) {
1034 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
1035 ret);
1036 return ret;
1037 }
1038 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1039 >> ARIZONA_SYSCLK_FREQ_SHIFT;
1040
1041 ret = regmap_update_bits(dsp->regmap,
1042 dsp->base + ADSP2_CLOCKING,
1043 ADSP2_CLK_SEL_MASK, val);
1044 if (ret != 0) {
1045 adsp_err(dsp, "Failed to set clock rate: %d\n",
1046 ret);
1047 return ret;
1048 }
1049
Mark Brown973838a2012-11-28 17:20:32 +00001050 if (dsp->dvfs) {
1051 ret = regmap_read(dsp->regmap,
1052 dsp->base + ADSP2_CLOCKING, &val);
1053 if (ret != 0) {
1054 dev_err(dsp->dev,
1055 "Failed to read clocking: %d\n", ret);
1056 return ret;
1057 }
1058
Mark Brown25c6fdb2012-11-29 15:16:10 +00001059 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
Mark Brown973838a2012-11-28 17:20:32 +00001060 ret = regulator_enable(dsp->dvfs);
1061 if (ret != 0) {
1062 dev_err(dsp->dev,
1063 "Failed to enable supply: %d\n",
1064 ret);
1065 return ret;
1066 }
1067
1068 ret = regulator_set_voltage(dsp->dvfs,
1069 1800000,
1070 1800000);
1071 if (ret != 0) {
1072 dev_err(dsp->dev,
1073 "Failed to raise supply: %d\n",
1074 ret);
1075 return ret;
1076 }
1077 }
1078 }
1079
Mark Brown2159ad92012-10-11 11:54:02 +09001080 ret = wm_adsp2_ena(dsp);
1081 if (ret != 0)
1082 return ret;
1083
1084 ret = wm_adsp_load(dsp);
1085 if (ret != 0)
1086 goto err;
1087
Mark Browndb405172012-10-26 19:30:40 +01001088 ret = wm_adsp_setup_algs(dsp);
1089 if (ret != 0)
1090 goto err;
1091
Mark Brown2159ad92012-10-11 11:54:02 +09001092 ret = wm_adsp_load_coeff(dsp);
1093 if (ret != 0)
1094 goto err;
1095
1096 ret = regmap_update_bits(dsp->regmap,
1097 dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001098 ADSP2_CORE_ENA | ADSP2_START,
1099 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad92012-10-11 11:54:02 +09001100 if (ret != 0)
1101 goto err;
Mark Brown1023dbd2013-01-11 22:58:28 +00001102
1103 dsp->running = true;
Mark Brown2159ad92012-10-11 11:54:02 +09001104 break;
1105
1106 case SND_SOC_DAPM_PRE_PMD:
Mark Brown1023dbd2013-01-11 22:58:28 +00001107 dsp->running = false;
1108
Mark Brown2159ad92012-10-11 11:54:02 +09001109 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001110 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1111 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00001112
Mark Brown2d30b572013-01-28 20:18:17 +08001113 /* Make sure DMAs are quiesced */
1114 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
1115 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
1116 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
1117
Mark Brown973838a2012-11-28 17:20:32 +00001118 if (dsp->dvfs) {
1119 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1120 1800000);
1121 if (ret != 0)
1122 dev_warn(dsp->dev,
1123 "Failed to lower supply: %d\n",
1124 ret);
1125
1126 ret = regulator_disable(dsp->dvfs);
1127 if (ret != 0)
1128 dev_err(dsp->dev,
1129 "Failed to enable supply: %d\n",
1130 ret);
1131 }
Mark Brown471f4882013-01-08 16:09:31 +00001132
1133 while (!list_empty(&dsp->alg_regions)) {
1134 alg_region = list_first_entry(&dsp->alg_regions,
1135 struct wm_adsp_alg_region,
1136 list);
1137 list_del(&alg_region->list);
1138 kfree(alg_region);
1139 }
Mark Brown2159ad92012-10-11 11:54:02 +09001140 break;
1141
1142 default:
1143 break;
1144 }
1145
1146 return 0;
1147err:
1148 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001149 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad92012-10-11 11:54:02 +09001150 return ret;
1151}
1152EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00001153
1154int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1155{
1156 int ret;
1157
Mark Brown10a2b662012-12-02 21:37:00 +09001158 /*
1159 * Disable the DSP memory by default when in reset for a small
1160 * power saving.
1161 */
1162 ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
1163 ADSP2_MEM_ENA, 0);
1164 if (ret != 0) {
1165 adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
1166 return ret;
1167 }
1168
Mark Brown471f4882013-01-08 16:09:31 +00001169 INIT_LIST_HEAD(&adsp->alg_regions);
1170
Mark Brown973838a2012-11-28 17:20:32 +00001171 if (dvfs) {
1172 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1173 if (IS_ERR(adsp->dvfs)) {
1174 ret = PTR_ERR(adsp->dvfs);
1175 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1176 return ret;
1177 }
1178
1179 ret = regulator_enable(adsp->dvfs);
1180 if (ret != 0) {
1181 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1182 ret);
1183 return ret;
1184 }
1185
1186 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1187 if (ret != 0) {
1188 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1189 ret);
1190 return ret;
1191 }
1192
1193 ret = regulator_disable(adsp->dvfs);
1194 if (ret != 0) {
1195 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1196 ret);
1197 return ret;
1198 }
1199 }
1200
1201 return 0;
1202}
1203EXPORT_SYMBOL_GPL(wm_adsp2_init);