blob: 16b308a6bfbbd395a6e59e07663ab004a6024fa4 [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>
Charles Keepaxcdcd7f72014-11-14 15:40:45 +000024#include <linux/vmalloc.h>
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +010025#include <linux/workqueue.h>
Mark Brown2159ad92012-10-11 11:54:02 +090026#include <sound/core.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/jack.h>
31#include <sound/initval.h>
32#include <sound/tlv.h>
33
34#include <linux/mfd/arizona/registers.h>
35
Mark Browndc914282013-02-18 19:09:23 +000036#include "arizona.h"
Mark Brown2159ad92012-10-11 11:54:02 +090037#include "wm_adsp.h"
38
39#define adsp_crit(_dsp, fmt, ...) \
40 dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
41#define adsp_err(_dsp, fmt, ...) \
42 dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
43#define adsp_warn(_dsp, fmt, ...) \
44 dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
45#define adsp_info(_dsp, fmt, ...) \
46 dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
47#define adsp_dbg(_dsp, fmt, ...) \
48 dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
49
50#define ADSP1_CONTROL_1 0x00
51#define ADSP1_CONTROL_2 0x02
52#define ADSP1_CONTROL_3 0x03
53#define ADSP1_CONTROL_4 0x04
54#define ADSP1_CONTROL_5 0x06
55#define ADSP1_CONTROL_6 0x07
56#define ADSP1_CONTROL_7 0x08
57#define ADSP1_CONTROL_8 0x09
58#define ADSP1_CONTROL_9 0x0A
59#define ADSP1_CONTROL_10 0x0B
60#define ADSP1_CONTROL_11 0x0C
61#define ADSP1_CONTROL_12 0x0D
62#define ADSP1_CONTROL_13 0x0F
63#define ADSP1_CONTROL_14 0x10
64#define ADSP1_CONTROL_15 0x11
65#define ADSP1_CONTROL_16 0x12
66#define ADSP1_CONTROL_17 0x13
67#define ADSP1_CONTROL_18 0x14
68#define ADSP1_CONTROL_19 0x16
69#define ADSP1_CONTROL_20 0x17
70#define ADSP1_CONTROL_21 0x18
71#define ADSP1_CONTROL_22 0x1A
72#define ADSP1_CONTROL_23 0x1B
73#define ADSP1_CONTROL_24 0x1C
74#define ADSP1_CONTROL_25 0x1E
75#define ADSP1_CONTROL_26 0x20
76#define ADSP1_CONTROL_27 0x21
77#define ADSP1_CONTROL_28 0x22
78#define ADSP1_CONTROL_29 0x23
79#define ADSP1_CONTROL_30 0x24
80#define ADSP1_CONTROL_31 0x26
81
82/*
83 * ADSP1 Control 19
84 */
85#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
86#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
87#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
88
89
90/*
91 * ADSP1 Control 30
92 */
93#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
94#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
95#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
96#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
97#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
98#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
99#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
100#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
101#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
102#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
103#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
104#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
105#define ADSP1_START 0x0001 /* DSP1_START */
106#define ADSP1_START_MASK 0x0001 /* DSP1_START */
107#define ADSP1_START_SHIFT 0 /* DSP1_START */
108#define ADSP1_START_WIDTH 1 /* DSP1_START */
109
Chris Rattray94e205b2013-01-18 08:43:09 +0000110/*
111 * ADSP1 Control 31
112 */
113#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
114#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
115#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
116
Mark Brown2d30b572013-01-28 20:18:17 +0800117#define ADSP2_CONTROL 0x0
118#define ADSP2_CLOCKING 0x1
119#define ADSP2_STATUS1 0x4
120#define ADSP2_WDMA_CONFIG_1 0x30
121#define ADSP2_WDMA_CONFIG_2 0x31
122#define ADSP2_RDMA_CONFIG_1 0x34
Mark Brown2159ad92012-10-11 11:54:02 +0900123
124/*
125 * ADSP2 Control
126 */
127
128#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
129#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
130#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
131#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
132#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
133#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
134#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
135#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
136#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
137#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
138#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
139#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
140#define ADSP2_START 0x0001 /* DSP1_START */
141#define ADSP2_START_MASK 0x0001 /* DSP1_START */
142#define ADSP2_START_SHIFT 0 /* DSP1_START */
143#define ADSP2_START_WIDTH 1 /* DSP1_START */
144
145/*
Mark Brown973838a2012-11-28 17:20:32 +0000146 * ADSP2 clocking
147 */
148#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
149#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
150#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
151
152/*
Mark Brown2159ad92012-10-11 11:54:02 +0900153 * ADSP2 Status 1
154 */
155#define ADSP2_RAM_RDY 0x0001
156#define ADSP2_RAM_RDY_MASK 0x0001
157#define ADSP2_RAM_RDY_SHIFT 0
158#define ADSP2_RAM_RDY_WIDTH 1
159
Mark Browncf17c832013-01-30 14:37:23 +0800160struct wm_adsp_buf {
161 struct list_head list;
162 void *buf;
163};
164
165static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
166 struct list_head *list)
167{
168 struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
169
170 if (buf == NULL)
171 return NULL;
172
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000173 buf->buf = vmalloc(len);
Mark Browncf17c832013-01-30 14:37:23 +0800174 if (!buf->buf) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000175 vfree(buf);
Mark Browncf17c832013-01-30 14:37:23 +0800176 return NULL;
177 }
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000178 memcpy(buf->buf, src, len);
Mark Browncf17c832013-01-30 14:37:23 +0800179
180 if (list)
181 list_add_tail(&buf->list, list);
182
183 return buf;
184}
185
186static void wm_adsp_buf_free(struct list_head *list)
187{
188 while (!list_empty(list)) {
189 struct wm_adsp_buf *buf = list_first_entry(list,
190 struct wm_adsp_buf,
191 list);
192 list_del(&buf->list);
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000193 vfree(buf->buf);
Mark Browncf17c832013-01-30 14:37:23 +0800194 kfree(buf);
195 }
196}
197
Mark Brown36e8fe92013-01-25 17:47:48 +0800198#define WM_ADSP_NUM_FW 4
Mark Brown1023dbd2013-01-11 22:58:28 +0000199
Mark Browndd84f922013-03-08 15:25:58 +0800200#define WM_ADSP_FW_MBC_VSS 0
201#define WM_ADSP_FW_TX 1
202#define WM_ADSP_FW_TX_SPK 2
203#define WM_ADSP_FW_RX_ANC 3
204
Mark Brown1023dbd2013-01-11 22:58:28 +0000205static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Mark Browndd84f922013-03-08 15:25:58 +0800206 [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
207 [WM_ADSP_FW_TX] = "Tx",
208 [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
209 [WM_ADSP_FW_RX_ANC] = "Rx ANC",
Mark Brown1023dbd2013-01-11 22:58:28 +0000210};
211
212static struct {
213 const char *file;
214} wm_adsp_fw[WM_ADSP_NUM_FW] = {
Mark Browndd84f922013-03-08 15:25:58 +0800215 [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
216 [WM_ADSP_FW_TX] = { .file = "tx" },
217 [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
218 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000219};
220
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100221struct wm_coeff_ctl_ops {
222 int (*xget)(struct snd_kcontrol *kcontrol,
223 struct snd_ctl_elem_value *ucontrol);
224 int (*xput)(struct snd_kcontrol *kcontrol,
225 struct snd_ctl_elem_value *ucontrol);
226 int (*xinfo)(struct snd_kcontrol *kcontrol,
227 struct snd_ctl_elem_info *uinfo);
228};
229
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100230struct wm_coeff_ctl {
231 const char *name;
Charles Keepax23237362015-04-13 13:28:02 +0100232 const char *fw_name;
Charles Keepax3809f002015-04-13 13:27:54 +0100233 struct wm_adsp_alg_region alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100234 struct wm_coeff_ctl_ops ops;
Charles Keepax3809f002015-04-13 13:27:54 +0100235 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100236 unsigned int enabled:1;
237 struct list_head list;
238 void *cache;
Charles Keepax23237362015-04-13 13:28:02 +0100239 unsigned int offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100240 size_t len;
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +0100241 unsigned int set:1;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100242 struct snd_kcontrol *kcontrol;
243};
244
Mark Brown1023dbd2013-01-11 22:58:28 +0000245static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
246 struct snd_ctl_elem_value *ucontrol)
247{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100248 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000249 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100250 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Mark Brown1023dbd2013-01-11 22:58:28 +0000251
Charles Keepax3809f002015-04-13 13:27:54 +0100252 ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
Mark Brown1023dbd2013-01-11 22:58:28 +0000253
254 return 0;
255}
256
257static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
258 struct snd_ctl_elem_value *ucontrol)
259{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100260 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000261 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100262 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Mark Brown1023dbd2013-01-11 22:58:28 +0000263
Charles Keepax3809f002015-04-13 13:27:54 +0100264 if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
Mark Brown1023dbd2013-01-11 22:58:28 +0000265 return 0;
266
267 if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
268 return -EINVAL;
269
Charles Keepax3809f002015-04-13 13:27:54 +0100270 if (dsp[e->shift_l].running)
Mark Brown1023dbd2013-01-11 22:58:28 +0000271 return -EBUSY;
272
Charles Keepax3809f002015-04-13 13:27:54 +0100273 dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
Mark Brown1023dbd2013-01-11 22:58:28 +0000274
275 return 0;
276}
277
278static const struct soc_enum wm_adsp_fw_enum[] = {
279 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
280 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
281 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
282 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
283};
284
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000285const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000286 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
287 wm_adsp_fw_get, wm_adsp_fw_put),
288 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
289 wm_adsp_fw_get, wm_adsp_fw_put),
290 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
291 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000292};
293EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
294
295#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
296static const struct soc_enum wm_adsp2_rate_enum[] = {
Mark Browndc914282013-02-18 19:09:23 +0000297 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
298 ARIZONA_DSP1_RATE_SHIFT, 0xf,
299 ARIZONA_RATE_ENUM_SIZE,
300 arizona_rate_text, arizona_rate_val),
301 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
302 ARIZONA_DSP1_RATE_SHIFT, 0xf,
303 ARIZONA_RATE_ENUM_SIZE,
304 arizona_rate_text, arizona_rate_val),
305 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
306 ARIZONA_DSP1_RATE_SHIFT, 0xf,
307 ARIZONA_RATE_ENUM_SIZE,
308 arizona_rate_text, arizona_rate_val),
Charles Keepax5be9c5b2013-06-14 14:19:36 +0100309 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
Mark Browndc914282013-02-18 19:09:23 +0000310 ARIZONA_DSP1_RATE_SHIFT, 0xf,
311 ARIZONA_RATE_ENUM_SIZE,
312 arizona_rate_text, arizona_rate_val),
313};
314
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000315const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000316 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
317 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000318 SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000319 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
320 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000321 SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000322 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
323 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000324 SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000325 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
326 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000327 SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000328};
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000329EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
330#endif
Mark Brown2159ad92012-10-11 11:54:02 +0900331
332static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
333 int type)
334{
335 int i;
336
337 for (i = 0; i < dsp->num_mems; i++)
338 if (dsp->mem[i].type == type)
339 return &dsp->mem[i];
340
341 return NULL;
342}
343
Charles Keepax3809f002015-04-13 13:27:54 +0100344static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
Mark Brown45b9ee72013-01-08 16:02:06 +0000345 unsigned int offset)
346{
Charles Keepax3809f002015-04-13 13:27:54 +0100347 if (WARN_ON(!mem))
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100348 return offset;
Charles Keepax3809f002015-04-13 13:27:54 +0100349 switch (mem->type) {
Mark Brown45b9ee72013-01-08 16:02:06 +0000350 case WMFW_ADSP1_PM:
Charles Keepax3809f002015-04-13 13:27:54 +0100351 return mem->base + (offset * 3);
Mark Brown45b9ee72013-01-08 16:02:06 +0000352 case WMFW_ADSP1_DM:
Charles Keepax3809f002015-04-13 13:27:54 +0100353 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000354 case WMFW_ADSP2_XM:
Charles Keepax3809f002015-04-13 13:27:54 +0100355 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000356 case WMFW_ADSP2_YM:
Charles Keepax3809f002015-04-13 13:27:54 +0100357 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000358 case WMFW_ADSP1_ZM:
Charles Keepax3809f002015-04-13 13:27:54 +0100359 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000360 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100361 WARN(1, "Unknown memory region type");
Mark Brown45b9ee72013-01-08 16:02:06 +0000362 return offset;
363 }
364}
365
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100366static int wm_coeff_info(struct snd_kcontrol *kcontrol,
367 struct snd_ctl_elem_info *uinfo)
368{
369 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
370
371 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
372 uinfo->count = ctl->len;
373 return 0;
374}
375
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100376static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100377 const void *buf, size_t len)
378{
Charles Keepax3809f002015-04-13 13:27:54 +0100379 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100380 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100381 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100382 void *scratch;
383 int ret;
384 unsigned int reg;
385
Charles Keepax3809f002015-04-13 13:27:54 +0100386 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100387 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100388 adsp_err(dsp, "No base for region %x\n",
389 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100390 return -EINVAL;
391 }
392
Charles Keepax23237362015-04-13 13:28:02 +0100393 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100394 reg = wm_adsp_region_to_reg(mem, reg);
395
396 scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
397 if (!scratch)
398 return -ENOMEM;
399
Charles Keepax3809f002015-04-13 13:27:54 +0100400 ret = regmap_raw_write(dsp->regmap, reg, scratch,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100401 ctl->len);
402 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100403 adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +0000404 ctl->len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100405 kfree(scratch);
406 return ret;
407 }
Charles Keepax3809f002015-04-13 13:27:54 +0100408 adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100409
410 kfree(scratch);
411
412 return 0;
413}
414
415static int wm_coeff_put(struct snd_kcontrol *kcontrol,
416 struct snd_ctl_elem_value *ucontrol)
417{
418 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
419 char *p = ucontrol->value.bytes.data;
420
421 memcpy(ctl->cache, p, ctl->len);
422
Nikesh Oswal65d17a92015-02-16 15:25:48 +0000423 ctl->set = 1;
424 if (!ctl->enabled)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100425 return 0;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100426
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100427 return wm_coeff_write_control(ctl, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100428}
429
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100430static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100431 void *buf, size_t len)
432{
Charles Keepax3809f002015-04-13 13:27:54 +0100433 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100434 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100435 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100436 void *scratch;
437 int ret;
438 unsigned int reg;
439
Charles Keepax3809f002015-04-13 13:27:54 +0100440 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100441 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100442 adsp_err(dsp, "No base for region %x\n",
443 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100444 return -EINVAL;
445 }
446
Charles Keepax23237362015-04-13 13:28:02 +0100447 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100448 reg = wm_adsp_region_to_reg(mem, reg);
449
450 scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
451 if (!scratch)
452 return -ENOMEM;
453
Charles Keepax3809f002015-04-13 13:27:54 +0100454 ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100455 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100456 adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +0000457 ctl->len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100458 kfree(scratch);
459 return ret;
460 }
Charles Keepax3809f002015-04-13 13:27:54 +0100461 adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100462
463 memcpy(buf, scratch, ctl->len);
464 kfree(scratch);
465
466 return 0;
467}
468
469static int wm_coeff_get(struct snd_kcontrol *kcontrol,
470 struct snd_ctl_elem_value *ucontrol)
471{
472 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
473 char *p = ucontrol->value.bytes.data;
474
475 memcpy(p, ctl->cache, ctl->len);
476 return 0;
477}
478
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100479struct wmfw_ctl_work {
Charles Keepax3809f002015-04-13 13:27:54 +0100480 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100481 struct wm_coeff_ctl *ctl;
482 struct work_struct work;
483};
484
Charles Keepax3809f002015-04-13 13:27:54 +0100485static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100486{
487 struct snd_kcontrol_new *kcontrol;
488 int ret;
489
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +0100490 if (!ctl || !ctl->name)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100491 return -EINVAL;
492
493 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
494 if (!kcontrol)
495 return -ENOMEM;
496 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
497
498 kcontrol->name = ctl->name;
499 kcontrol->info = wm_coeff_info;
500 kcontrol->get = wm_coeff_get;
501 kcontrol->put = wm_coeff_put;
502 kcontrol->private_value = (unsigned long)ctl;
503
Charles Keepax3809f002015-04-13 13:27:54 +0100504 ret = snd_soc_add_card_controls(dsp->card,
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +0100505 kcontrol, 1);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100506 if (ret < 0)
507 goto err_kcontrol;
508
509 kfree(kcontrol);
510
Charles Keepax3809f002015-04-13 13:27:54 +0100511 ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +0100512 ctl->name);
513
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100514 return 0;
515
516err_kcontrol:
517 kfree(kcontrol);
518 return ret;
519}
520
Charles Keepaxb21acc12015-04-13 13:28:01 +0100521static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
522{
523 struct wm_coeff_ctl *ctl;
524 int ret;
525
526 list_for_each_entry(ctl, &dsp->ctl_list, list) {
527 if (!ctl->enabled || ctl->set)
528 continue;
529 ret = wm_coeff_read_control(ctl,
530 ctl->cache,
531 ctl->len);
532 if (ret < 0)
533 return ret;
534 }
535
536 return 0;
537}
538
539static int wm_coeff_sync_controls(struct wm_adsp *dsp)
540{
541 struct wm_coeff_ctl *ctl;
542 int ret;
543
544 list_for_each_entry(ctl, &dsp->ctl_list, list) {
545 if (!ctl->enabled)
546 continue;
547 if (ctl->set) {
548 ret = wm_coeff_write_control(ctl,
549 ctl->cache,
550 ctl->len);
551 if (ret < 0)
552 return ret;
553 }
554 }
555
556 return 0;
557}
558
559static void wm_adsp_ctl_work(struct work_struct *work)
560{
561 struct wmfw_ctl_work *ctl_work = container_of(work,
562 struct wmfw_ctl_work,
563 work);
564
565 wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
566 kfree(ctl_work);
567}
568
569static int wm_adsp_create_control(struct wm_adsp *dsp,
570 const struct wm_adsp_alg_region *alg_region,
Charles Keepax23237362015-04-13 13:28:02 +0100571 unsigned int offset, unsigned int len,
572 const char *subname, unsigned int subname_len)
Charles Keepaxb21acc12015-04-13 13:28:01 +0100573{
574 struct wm_coeff_ctl *ctl;
575 struct wmfw_ctl_work *ctl_work;
576 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
577 char *region_name;
578 int ret;
579
580 switch (alg_region->type) {
581 case WMFW_ADSP1_PM:
582 region_name = "PM";
583 break;
584 case WMFW_ADSP1_DM:
585 region_name = "DM";
586 break;
587 case WMFW_ADSP2_XM:
588 region_name = "XM";
589 break;
590 case WMFW_ADSP2_YM:
591 region_name = "YM";
592 break;
593 case WMFW_ADSP1_ZM:
594 region_name = "ZM";
595 break;
596 default:
Charles Keepax23237362015-04-13 13:28:02 +0100597 adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
Charles Keepaxb21acc12015-04-13 13:28:01 +0100598 return -EINVAL;
599 }
600
601 snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
602 dsp->num, region_name, alg_region->alg);
603
604 list_for_each_entry(ctl, &dsp->ctl_list,
605 list) {
606 if (!strcmp(ctl->name, name)) {
607 if (!ctl->enabled)
608 ctl->enabled = 1;
609 return 0;
610 }
611 }
612
613 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
614 if (!ctl)
615 return -ENOMEM;
Charles Keepax23237362015-04-13 13:28:02 +0100616 ctl->fw_name = wm_adsp_fw_text[dsp->fw];
Charles Keepaxb21acc12015-04-13 13:28:01 +0100617 ctl->alg_region = *alg_region;
618 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
619 if (!ctl->name) {
620 ret = -ENOMEM;
621 goto err_ctl;
622 }
623 ctl->enabled = 1;
624 ctl->set = 0;
625 ctl->ops.xget = wm_coeff_get;
626 ctl->ops.xput = wm_coeff_put;
627 ctl->dsp = dsp;
628
Charles Keepax23237362015-04-13 13:28:02 +0100629 ctl->offset = offset;
Charles Keepaxb21acc12015-04-13 13:28:01 +0100630 if (len > 512) {
631 adsp_warn(dsp, "Truncating control %s from %d\n",
632 ctl->name, len);
633 len = 512;
634 }
635 ctl->len = len;
636 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
637 if (!ctl->cache) {
638 ret = -ENOMEM;
639 goto err_ctl_name;
640 }
641
Charles Keepax23237362015-04-13 13:28:02 +0100642 list_add(&ctl->list, &dsp->ctl_list);
643
Charles Keepaxb21acc12015-04-13 13:28:01 +0100644 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
645 if (!ctl_work) {
646 ret = -ENOMEM;
647 goto err_ctl_cache;
648 }
649
650 ctl_work->dsp = dsp;
651 ctl_work->ctl = ctl;
652 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
653 schedule_work(&ctl_work->work);
654
655 return 0;
656
657err_ctl_cache:
658 kfree(ctl->cache);
659err_ctl_name:
660 kfree(ctl->name);
661err_ctl:
662 kfree(ctl);
663
664 return ret;
665}
666
Charles Keepax23237362015-04-13 13:28:02 +0100667struct wm_coeff_parsed_alg {
668 int id;
669 const u8 *name;
670 int name_len;
671 int ncoeff;
672};
673
674struct wm_coeff_parsed_coeff {
675 int offset;
676 int mem_type;
677 const u8 *name;
678 int name_len;
679 int ctl_type;
680 int flags;
681 int len;
682};
683
684static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
685 struct wm_coeff_parsed_alg *blk)
686{
687 const struct wmfw_adsp_alg_data *raw;
688
689 raw = (const struct wmfw_adsp_alg_data *)*data;
690 *data = raw->data;
691
692 blk->id = le32_to_cpu(raw->id);
693 blk->name = raw->name;
694 blk->name_len = strlen(raw->name);
695 blk->ncoeff = le32_to_cpu(raw->ncoeff);
696
697 adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
698 adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
699 adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
700}
701
702static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
703 struct wm_coeff_parsed_coeff *blk)
704{
705 const struct wmfw_adsp_coeff_data *raw;
706
707 raw = (const struct wmfw_adsp_coeff_data *)*data;
708 *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
709
710 blk->offset = le16_to_cpu(raw->hdr.offset);
711 blk->mem_type = le16_to_cpu(raw->hdr.type);
712 blk->name = raw->name;
713 blk->name_len = strlen(raw->name);
714 blk->ctl_type = le16_to_cpu(raw->ctl_type);
715 blk->flags = le16_to_cpu(raw->flags);
716 blk->len = le32_to_cpu(raw->len);
717
718 adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
719 adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
720 adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
721 adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
722 adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
723 adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
724}
725
726static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
727 const struct wmfw_region *region)
728{
729 struct wm_adsp_alg_region alg_region = {};
730 struct wm_coeff_parsed_alg alg_blk;
731 struct wm_coeff_parsed_coeff coeff_blk;
732 const u8 *data = region->data;
733 int i, ret;
734
735 wm_coeff_parse_alg(dsp, &data, &alg_blk);
736 for (i = 0; i < alg_blk.ncoeff; i++) {
737 wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
738
739 switch (coeff_blk.ctl_type) {
740 case SNDRV_CTL_ELEM_TYPE_BYTES:
741 break;
742 default:
743 adsp_err(dsp, "Unknown control type: %d\n",
744 coeff_blk.ctl_type);
745 return -EINVAL;
746 }
747
748 alg_region.type = coeff_blk.mem_type;
749 alg_region.alg = alg_blk.id;
750
751 ret = wm_adsp_create_control(dsp, &alg_region,
752 coeff_blk.offset,
753 coeff_blk.len,
754 coeff_blk.name,
755 coeff_blk.name_len);
756 if (ret < 0)
757 adsp_err(dsp, "Failed to create control: %.*s, %d\n",
758 coeff_blk.name_len, coeff_blk.name, ret);
759 }
760
761 return 0;
762}
763
Mark Brown2159ad92012-10-11 11:54:02 +0900764static int wm_adsp_load(struct wm_adsp *dsp)
765{
Mark Browncf17c832013-01-30 14:37:23 +0800766 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900767 const struct firmware *firmware;
768 struct regmap *regmap = dsp->regmap;
769 unsigned int pos = 0;
770 const struct wmfw_header *header;
771 const struct wmfw_adsp1_sizes *adsp1_sizes;
772 const struct wmfw_adsp2_sizes *adsp2_sizes;
773 const struct wmfw_footer *footer;
774 const struct wmfw_region *region;
775 const struct wm_adsp_region *mem;
776 const char *region_name;
777 char *file, *text;
Mark Browncf17c832013-01-30 14:37:23 +0800778 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +0900779 unsigned int reg;
780 int regions = 0;
781 int ret, offset, type, sizes;
782
783 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
784 if (file == NULL)
785 return -ENOMEM;
786
Mark Brown1023dbd2013-01-11 22:58:28 +0000787 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
788 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +0900789 file[PAGE_SIZE - 1] = '\0';
790
791 ret = request_firmware(&firmware, file, dsp->dev);
792 if (ret != 0) {
793 adsp_err(dsp, "Failed to request '%s'\n", file);
794 goto out;
795 }
796 ret = -EINVAL;
797
798 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
799 if (pos >= firmware->size) {
800 adsp_err(dsp, "%s: file too short, %zu bytes\n",
801 file, firmware->size);
802 goto out_fw;
803 }
804
805 header = (void*)&firmware->data[0];
806
807 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
808 adsp_err(dsp, "%s: invalid magic\n", file);
809 goto out_fw;
810 }
811
Charles Keepax23237362015-04-13 13:28:02 +0100812 switch (header->ver) {
813 case 0:
814 case 1:
815 break;
816 default:
Mark Brown2159ad92012-10-11 11:54:02 +0900817 adsp_err(dsp, "%s: unknown file format %d\n",
818 file, header->ver);
819 goto out_fw;
820 }
Charles Keepax23237362015-04-13 13:28:02 +0100821
Dimitris Papastamos36269922013-11-01 15:56:57 +0000822 adsp_info(dsp, "Firmware version: %d\n", header->ver);
Charles Keepax23237362015-04-13 13:28:02 +0100823 dsp->fw_ver = header->ver;
Mark Brown2159ad92012-10-11 11:54:02 +0900824
825 if (header->core != dsp->type) {
826 adsp_err(dsp, "%s: invalid core %d != %d\n",
827 file, header->core, dsp->type);
828 goto out_fw;
829 }
830
831 switch (dsp->type) {
832 case WMFW_ADSP1:
833 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
834 adsp1_sizes = (void *)&(header[1]);
835 footer = (void *)&(adsp1_sizes[1]);
836 sizes = sizeof(*adsp1_sizes);
837
838 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
839 file, le32_to_cpu(adsp1_sizes->dm),
840 le32_to_cpu(adsp1_sizes->pm),
841 le32_to_cpu(adsp1_sizes->zm));
842 break;
843
844 case WMFW_ADSP2:
845 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
846 adsp2_sizes = (void *)&(header[1]);
847 footer = (void *)&(adsp2_sizes[1]);
848 sizes = sizeof(*adsp2_sizes);
849
850 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
851 file, le32_to_cpu(adsp2_sizes->xm),
852 le32_to_cpu(adsp2_sizes->ym),
853 le32_to_cpu(adsp2_sizes->pm),
854 le32_to_cpu(adsp2_sizes->zm));
855 break;
856
857 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100858 WARN(1, "Unknown DSP type");
Mark Brown2159ad92012-10-11 11:54:02 +0900859 goto out_fw;
860 }
861
862 if (le32_to_cpu(header->len) != sizeof(*header) +
863 sizes + sizeof(*footer)) {
864 adsp_err(dsp, "%s: unexpected header length %d\n",
865 file, le32_to_cpu(header->len));
866 goto out_fw;
867 }
868
869 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
870 le64_to_cpu(footer->timestamp));
871
872 while (pos < firmware->size &&
873 pos - firmware->size > sizeof(*region)) {
874 region = (void *)&(firmware->data[pos]);
875 region_name = "Unknown";
876 reg = 0;
877 text = NULL;
878 offset = le32_to_cpu(region->offset) & 0xffffff;
879 type = be32_to_cpu(region->type) & 0xff;
880 mem = wm_adsp_find_region(dsp, type);
881
882 switch (type) {
883 case WMFW_NAME_TEXT:
884 region_name = "Firmware name";
885 text = kzalloc(le32_to_cpu(region->len) + 1,
886 GFP_KERNEL);
887 break;
Charles Keepax23237362015-04-13 13:28:02 +0100888 case WMFW_ALGORITHM_DATA:
889 region_name = "Algorithm";
890 ret = wm_adsp_parse_coeff(dsp, region);
891 if (ret != 0)
892 goto out_fw;
893 break;
Mark Brown2159ad92012-10-11 11:54:02 +0900894 case WMFW_INFO_TEXT:
895 region_name = "Information";
896 text = kzalloc(le32_to_cpu(region->len) + 1,
897 GFP_KERNEL);
898 break;
899 case WMFW_ABSOLUTE:
900 region_name = "Absolute";
901 reg = offset;
902 break;
903 case WMFW_ADSP1_PM:
Mark Brown2159ad92012-10-11 11:54:02 +0900904 region_name = "PM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000905 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900906 break;
907 case WMFW_ADSP1_DM:
Mark Brown2159ad92012-10-11 11:54:02 +0900908 region_name = "DM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000909 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900910 break;
911 case WMFW_ADSP2_XM:
Mark Brown2159ad92012-10-11 11:54:02 +0900912 region_name = "XM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000913 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900914 break;
915 case WMFW_ADSP2_YM:
Mark Brown2159ad92012-10-11 11:54:02 +0900916 region_name = "YM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000917 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900918 break;
919 case WMFW_ADSP1_ZM:
Mark Brown2159ad92012-10-11 11:54:02 +0900920 region_name = "ZM";
Mark Brown45b9ee72013-01-08 16:02:06 +0000921 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +0900922 break;
923 default:
924 adsp_warn(dsp,
925 "%s.%d: Unknown region type %x at %d(%x)\n",
926 file, regions, type, pos, pos);
927 break;
928 }
929
930 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
931 regions, le32_to_cpu(region->len), offset,
932 region_name);
933
934 if (text) {
935 memcpy(text, region->data, le32_to_cpu(region->len));
936 adsp_info(dsp, "%s: %s\n", file, text);
937 kfree(text);
938 }
939
940 if (reg) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000941 buf = wm_adsp_buf_alloc(region->data,
942 le32_to_cpu(region->len),
943 &buf_list);
944 if (!buf) {
945 adsp_err(dsp, "Out of memory\n");
946 ret = -ENOMEM;
947 goto out_fw;
948 }
Mark Browna76fefa2013-01-07 19:03:17 +0000949
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000950 ret = regmap_raw_write_async(regmap, reg, buf->buf,
951 le32_to_cpu(region->len));
952 if (ret != 0) {
953 adsp_err(dsp,
954 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
955 file, regions,
956 le32_to_cpu(region->len), offset,
957 region_name, ret);
958 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +0900959 }
960 }
961
962 pos += le32_to_cpu(region->len) + sizeof(*region);
963 regions++;
964 }
Mark Browncf17c832013-01-30 14:37:23 +0800965
966 ret = regmap_async_complete(regmap);
967 if (ret != 0) {
968 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
969 goto out_fw;
970 }
971
Mark Brown2159ad92012-10-11 11:54:02 +0900972 if (pos > firmware->size)
973 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
974 file, regions, pos - firmware->size);
975
976out_fw:
Mark Browncf17c832013-01-30 14:37:23 +0800977 regmap_async_complete(regmap);
978 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900979 release_firmware(firmware);
980out:
981 kfree(file);
982
983 return ret;
984}
985
Charles Keepax23237362015-04-13 13:28:02 +0100986static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
987 const struct wm_adsp_alg_region *alg_region)
988{
989 struct wm_coeff_ctl *ctl;
990
991 list_for_each_entry(ctl, &dsp->ctl_list, list) {
992 if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
993 alg_region->alg == ctl->alg_region.alg &&
994 alg_region->type == ctl->alg_region.type) {
995 ctl->alg_region.base = alg_region->base;
996 }
997 }
998}
999
Charles Keepax3809f002015-04-13 13:27:54 +01001000static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
Charles Keepaxb618a1852015-04-13 13:27:53 +01001001 unsigned int pos, unsigned int len)
Mark Browndb405172012-10-26 19:30:40 +01001002{
Charles Keepaxb618a1852015-04-13 13:27:53 +01001003 void *alg;
1004 int ret;
Mark Browndb405172012-10-26 19:30:40 +01001005 __be32 val;
Mark Browndb405172012-10-26 19:30:40 +01001006
Charles Keepax3809f002015-04-13 13:27:54 +01001007 if (n_algs == 0) {
Mark Browndb405172012-10-26 19:30:40 +01001008 adsp_err(dsp, "No algorithms\n");
Charles Keepaxb618a1852015-04-13 13:27:53 +01001009 return ERR_PTR(-EINVAL);
Mark Browndb405172012-10-26 19:30:40 +01001010 }
1011
Charles Keepax3809f002015-04-13 13:27:54 +01001012 if (n_algs > 1024) {
1013 adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001014 return ERR_PTR(-EINVAL);
Mark Brownd62f4bc2012-12-19 14:00:30 +00001015 }
1016
Mark Browndb405172012-10-26 19:30:40 +01001017 /* Read the terminator first to validate the length */
Charles Keepaxb618a1852015-04-13 13:27:53 +01001018 ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
Mark Browndb405172012-10-26 19:30:40 +01001019 if (ret != 0) {
1020 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1021 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001022 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001023 }
1024
1025 if (be32_to_cpu(val) != 0xbedead)
1026 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
Charles Keepaxb618a1852015-04-13 13:27:53 +01001027 pos + len, be32_to_cpu(val));
Mark Browndb405172012-10-26 19:30:40 +01001028
Charles Keepaxb618a1852015-04-13 13:27:53 +01001029 alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +01001030 if (!alg)
Charles Keepaxb618a1852015-04-13 13:27:53 +01001031 return ERR_PTR(-ENOMEM);
Mark Browndb405172012-10-26 19:30:40 +01001032
Charles Keepaxb618a1852015-04-13 13:27:53 +01001033 ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
Mark Browndb405172012-10-26 19:30:40 +01001034 if (ret != 0) {
1035 adsp_err(dsp, "Failed to read algorithm list: %d\n",
1036 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001037 kfree(alg);
1038 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001039 }
1040
Charles Keepaxb618a1852015-04-13 13:27:53 +01001041 return alg;
1042}
1043
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001044static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
1045 int type, __be32 id,
1046 __be32 base)
1047{
1048 struct wm_adsp_alg_region *alg_region;
1049
1050 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1051 if (!alg_region)
1052 return ERR_PTR(-ENOMEM);
1053
1054 alg_region->type = type;
1055 alg_region->alg = be32_to_cpu(id);
1056 alg_region->base = be32_to_cpu(base);
1057
1058 list_add_tail(&alg_region->list, &dsp->alg_regions);
1059
Charles Keepax23237362015-04-13 13:28:02 +01001060 if (dsp->fw_ver > 0)
1061 wm_adsp_ctl_fixup_base(dsp, alg_region);
1062
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001063 return alg_region;
1064}
1065
Charles Keepaxb618a1852015-04-13 13:27:53 +01001066static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
1067{
1068 struct wmfw_adsp1_id_hdr adsp1_id;
1069 struct wmfw_adsp1_alg_hdr *adsp1_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001070 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001071 const struct wm_adsp_region *mem;
1072 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001073 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001074 int i, ret;
1075
1076 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
1077 if (WARN_ON(!mem))
1078 return -EINVAL;
1079
1080 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
1081 sizeof(adsp1_id));
1082 if (ret != 0) {
1083 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1084 ret);
1085 return ret;
1086 }
1087
Charles Keepax3809f002015-04-13 13:27:54 +01001088 n_algs = be32_to_cpu(adsp1_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001089 dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
1090 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1091 dsp->fw_id,
1092 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
1093 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
1094 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001095 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001096
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001097 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1098 adsp1_id.fw.id, adsp1_id.zm);
1099 if (IS_ERR(alg_region))
1100 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001101
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001102 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1103 adsp1_id.fw.id, adsp1_id.dm);
1104 if (IS_ERR(alg_region))
1105 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001106
1107 pos = sizeof(adsp1_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001108 len = (sizeof(*adsp1_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001109
Charles Keepax3809f002015-04-13 13:27:54 +01001110 adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001111 if (IS_ERR(adsp1_alg))
1112 return PTR_ERR(adsp1_alg);
Mark Browndb405172012-10-26 19:30:40 +01001113
Charles Keepax3809f002015-04-13 13:27:54 +01001114 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001115 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
1116 i, be32_to_cpu(adsp1_alg[i].alg.id),
1117 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
1118 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
1119 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
1120 be32_to_cpu(adsp1_alg[i].dm),
1121 be32_to_cpu(adsp1_alg[i].zm));
Mark Brown471f4882013-01-08 16:09:31 +00001122
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001123 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1124 adsp1_alg[i].alg.id,
1125 adsp1_alg[i].dm);
1126 if (IS_ERR(alg_region)) {
1127 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001128 goto out;
1129 }
Charles Keepax23237362015-04-13 13:28:02 +01001130 if (dsp->fw_ver == 0) {
1131 if (i + 1 < n_algs) {
1132 len = be32_to_cpu(adsp1_alg[i + 1].dm);
1133 len -= be32_to_cpu(adsp1_alg[i].dm);
1134 len *= 4;
1135 wm_adsp_create_control(dsp, alg_region, 0,
1136 len, NULL, 0);
1137 } else {
1138 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
1139 be32_to_cpu(adsp1_alg[i].alg.id));
1140 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001141 }
Mark Brown471f4882013-01-08 16:09:31 +00001142
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001143 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1144 adsp1_alg[i].alg.id,
1145 adsp1_alg[i].zm);
1146 if (IS_ERR(alg_region)) {
1147 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001148 goto out;
1149 }
Charles Keepax23237362015-04-13 13:28:02 +01001150 if (dsp->fw_ver == 0) {
1151 if (i + 1 < n_algs) {
1152 len = be32_to_cpu(adsp1_alg[i + 1].zm);
1153 len -= be32_to_cpu(adsp1_alg[i].zm);
1154 len *= 4;
1155 wm_adsp_create_control(dsp, alg_region, 0,
1156 len, NULL, 0);
1157 } else {
1158 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1159 be32_to_cpu(adsp1_alg[i].alg.id));
1160 }
Mark Browndb405172012-10-26 19:30:40 +01001161 }
1162 }
1163
1164out:
Charles Keepaxb618a1852015-04-13 13:27:53 +01001165 kfree(adsp1_alg);
1166 return ret;
1167}
1168
1169static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
1170{
1171 struct wmfw_adsp2_id_hdr adsp2_id;
1172 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001173 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001174 const struct wm_adsp_region *mem;
1175 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001176 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001177 int i, ret;
1178
1179 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
1180 if (WARN_ON(!mem))
1181 return -EINVAL;
1182
1183 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
1184 sizeof(adsp2_id));
1185 if (ret != 0) {
1186 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1187 ret);
1188 return ret;
1189 }
1190
Charles Keepax3809f002015-04-13 13:27:54 +01001191 n_algs = be32_to_cpu(adsp2_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001192 dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
1193 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1194 dsp->fw_id,
1195 (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
1196 (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
1197 be32_to_cpu(adsp2_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001198 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001199
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001200 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1201 adsp2_id.fw.id, adsp2_id.xm);
1202 if (IS_ERR(alg_region))
1203 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001204
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001205 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1206 adsp2_id.fw.id, adsp2_id.ym);
1207 if (IS_ERR(alg_region))
1208 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001209
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001210 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1211 adsp2_id.fw.id, adsp2_id.zm);
1212 if (IS_ERR(alg_region))
1213 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001214
1215 pos = sizeof(adsp2_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001216 len = (sizeof(*adsp2_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001217
Charles Keepax3809f002015-04-13 13:27:54 +01001218 adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001219 if (IS_ERR(adsp2_alg))
1220 return PTR_ERR(adsp2_alg);
1221
Charles Keepax3809f002015-04-13 13:27:54 +01001222 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001223 adsp_info(dsp,
1224 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1225 i, be32_to_cpu(adsp2_alg[i].alg.id),
1226 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1227 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1228 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1229 be32_to_cpu(adsp2_alg[i].xm),
1230 be32_to_cpu(adsp2_alg[i].ym),
1231 be32_to_cpu(adsp2_alg[i].zm));
1232
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001233 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1234 adsp2_alg[i].alg.id,
1235 adsp2_alg[i].xm);
1236 if (IS_ERR(alg_region)) {
1237 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001238 goto out;
1239 }
Charles Keepax23237362015-04-13 13:28:02 +01001240 if (dsp->fw_ver == 0) {
1241 if (i + 1 < n_algs) {
1242 len = be32_to_cpu(adsp2_alg[i + 1].xm);
1243 len -= be32_to_cpu(adsp2_alg[i].xm);
1244 len *= 4;
1245 wm_adsp_create_control(dsp, alg_region, 0,
1246 len, NULL, 0);
1247 } else {
1248 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
1249 be32_to_cpu(adsp2_alg[i].alg.id));
1250 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001251 }
1252
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001253 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1254 adsp2_alg[i].alg.id,
1255 adsp2_alg[i].ym);
1256 if (IS_ERR(alg_region)) {
1257 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001258 goto out;
1259 }
Charles Keepax23237362015-04-13 13:28:02 +01001260 if (dsp->fw_ver == 0) {
1261 if (i + 1 < n_algs) {
1262 len = be32_to_cpu(adsp2_alg[i + 1].ym);
1263 len -= be32_to_cpu(adsp2_alg[i].ym);
1264 len *= 4;
1265 wm_adsp_create_control(dsp, alg_region, 0,
1266 len, NULL, 0);
1267 } else {
1268 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
1269 be32_to_cpu(adsp2_alg[i].alg.id));
1270 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001271 }
1272
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001273 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1274 adsp2_alg[i].alg.id,
1275 adsp2_alg[i].zm);
1276 if (IS_ERR(alg_region)) {
1277 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001278 goto out;
1279 }
Charles Keepax23237362015-04-13 13:28:02 +01001280 if (dsp->fw_ver == 0) {
1281 if (i + 1 < n_algs) {
1282 len = be32_to_cpu(adsp2_alg[i + 1].zm);
1283 len -= be32_to_cpu(adsp2_alg[i].zm);
1284 len *= 4;
1285 wm_adsp_create_control(dsp, alg_region, 0,
1286 len, NULL, 0);
1287 } else {
1288 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1289 be32_to_cpu(adsp2_alg[i].alg.id));
1290 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001291 }
1292 }
1293
1294out:
1295 kfree(adsp2_alg);
Mark Browndb405172012-10-26 19:30:40 +01001296 return ret;
1297}
1298
Mark Brown2159ad92012-10-11 11:54:02 +09001299static int wm_adsp_load_coeff(struct wm_adsp *dsp)
1300{
Mark Browncf17c832013-01-30 14:37:23 +08001301 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001302 struct regmap *regmap = dsp->regmap;
1303 struct wmfw_coeff_hdr *hdr;
1304 struct wmfw_coeff_item *blk;
1305 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +00001306 const struct wm_adsp_region *mem;
1307 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad92012-10-11 11:54:02 +09001308 const char *region_name;
1309 int ret, pos, blocks, type, offset, reg;
1310 char *file;
Mark Browncf17c832013-01-30 14:37:23 +08001311 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +09001312
1313 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1314 if (file == NULL)
1315 return -ENOMEM;
1316
Mark Brown1023dbd2013-01-11 22:58:28 +00001317 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
1318 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +09001319 file[PAGE_SIZE - 1] = '\0';
1320
1321 ret = request_firmware(&firmware, file, dsp->dev);
1322 if (ret != 0) {
1323 adsp_warn(dsp, "Failed to request '%s'\n", file);
1324 ret = 0;
1325 goto out;
1326 }
1327 ret = -EINVAL;
1328
1329 if (sizeof(*hdr) >= firmware->size) {
1330 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1331 file, firmware->size);
1332 goto out_fw;
1333 }
1334
1335 hdr = (void*)&firmware->data[0];
1336 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
1337 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +00001338 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +09001339 }
1340
Mark Brownc7123262013-01-16 16:59:04 +09001341 switch (be32_to_cpu(hdr->rev) & 0xff) {
1342 case 1:
1343 break;
1344 default:
1345 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
1346 file, be32_to_cpu(hdr->rev) & 0xff);
1347 ret = -EINVAL;
1348 goto out_fw;
1349 }
1350
Mark Brown2159ad92012-10-11 11:54:02 +09001351 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
1352 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
1353 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
1354 le32_to_cpu(hdr->ver) & 0xff);
1355
1356 pos = le32_to_cpu(hdr->len);
1357
1358 blocks = 0;
1359 while (pos < firmware->size &&
1360 pos - firmware->size > sizeof(*blk)) {
1361 blk = (void*)(&firmware->data[pos]);
1362
Mark Brownc7123262013-01-16 16:59:04 +09001363 type = le16_to_cpu(blk->type);
1364 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001365
1366 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
1367 file, blocks, le32_to_cpu(blk->id),
1368 (le32_to_cpu(blk->ver) >> 16) & 0xff,
1369 (le32_to_cpu(blk->ver) >> 8) & 0xff,
1370 le32_to_cpu(blk->ver) & 0xff);
1371 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
1372 file, blocks, le32_to_cpu(blk->len), offset, type);
1373
1374 reg = 0;
1375 region_name = "Unknown";
1376 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +09001377 case (WMFW_NAME_TEXT << 8):
1378 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad92012-10-11 11:54:02 +09001379 break;
Mark Brownc7123262013-01-16 16:59:04 +09001380 case (WMFW_ABSOLUTE << 8):
Mark Brownf395a212013-03-05 22:39:54 +08001381 /*
1382 * Old files may use this for global
1383 * coefficients.
1384 */
1385 if (le32_to_cpu(blk->id) == dsp->fw_id &&
1386 offset == 0) {
1387 region_name = "global coefficients";
1388 mem = wm_adsp_find_region(dsp, type);
1389 if (!mem) {
1390 adsp_err(dsp, "No ZM\n");
1391 break;
1392 }
1393 reg = wm_adsp_region_to_reg(mem, 0);
1394
1395 } else {
1396 region_name = "register";
1397 reg = offset;
1398 }
Mark Brown2159ad92012-10-11 11:54:02 +09001399 break;
Mark Brown471f4882013-01-08 16:09:31 +00001400
1401 case WMFW_ADSP1_DM:
1402 case WMFW_ADSP1_ZM:
1403 case WMFW_ADSP2_XM:
1404 case WMFW_ADSP2_YM:
1405 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
1406 file, blocks, le32_to_cpu(blk->len),
1407 type, le32_to_cpu(blk->id));
1408
1409 mem = wm_adsp_find_region(dsp, type);
1410 if (!mem) {
1411 adsp_err(dsp, "No base for region %x\n", type);
1412 break;
1413 }
1414
1415 reg = 0;
1416 list_for_each_entry(alg_region,
1417 &dsp->alg_regions, list) {
1418 if (le32_to_cpu(blk->id) == alg_region->alg &&
1419 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +08001420 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +00001421 reg = wm_adsp_region_to_reg(mem,
1422 reg);
Mark Brown338c5182013-01-24 00:35:48 +08001423 reg += offset;
Charles Keepaxd733dc02013-11-28 16:37:51 +00001424 break;
Mark Brown471f4882013-01-08 16:09:31 +00001425 }
1426 }
1427
1428 if (reg == 0)
1429 adsp_err(dsp, "No %x for algorithm %x\n",
1430 type, le32_to_cpu(blk->id));
1431 break;
1432
Mark Brown2159ad92012-10-11 11:54:02 +09001433 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +09001434 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
1435 file, blocks, type, pos);
Mark Brown2159ad92012-10-11 11:54:02 +09001436 break;
1437 }
1438
1439 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +08001440 buf = wm_adsp_buf_alloc(blk->data,
1441 le32_to_cpu(blk->len),
1442 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +00001443 if (!buf) {
1444 adsp_err(dsp, "Out of memory\n");
Wei Yongjunf4b82812013-03-12 00:23:15 +08001445 ret = -ENOMEM;
1446 goto out_fw;
Mark Browna76fefa2013-01-07 19:03:17 +00001447 }
1448
Mark Brown20da6d52013-01-12 19:58:17 +00001449 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
1450 file, blocks, le32_to_cpu(blk->len),
1451 reg);
Mark Browncf17c832013-01-30 14:37:23 +08001452 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1453 le32_to_cpu(blk->len));
Mark Brown2159ad92012-10-11 11:54:02 +09001454 if (ret != 0) {
1455 adsp_err(dsp,
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +00001456 "%s.%d: Failed to write to %x in %s: %d\n",
1457 file, blocks, reg, region_name, ret);
Mark Brown2159ad92012-10-11 11:54:02 +09001458 }
1459 }
1460
Charles Keepaxbe951012015-02-16 15:25:49 +00001461 pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
Mark Brown2159ad92012-10-11 11:54:02 +09001462 blocks++;
1463 }
1464
Mark Browncf17c832013-01-30 14:37:23 +08001465 ret = regmap_async_complete(regmap);
1466 if (ret != 0)
1467 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1468
Mark Brown2159ad92012-10-11 11:54:02 +09001469 if (pos > firmware->size)
1470 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1471 file, blocks, pos - firmware->size);
1472
1473out_fw:
Charles Keepax9da7a5a2014-11-17 10:48:21 +00001474 regmap_async_complete(regmap);
Mark Brown2159ad92012-10-11 11:54:02 +09001475 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +08001476 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001477out:
1478 kfree(file);
Wei Yongjunf4b82812013-03-12 00:23:15 +08001479 return ret;
Mark Brown2159ad92012-10-11 11:54:02 +09001480}
1481
Charles Keepax3809f002015-04-13 13:27:54 +01001482int wm_adsp1_init(struct wm_adsp *dsp)
Mark Brown5e7a7a22013-01-16 10:03:56 +09001483{
Charles Keepax3809f002015-04-13 13:27:54 +01001484 INIT_LIST_HEAD(&dsp->alg_regions);
Mark Brown5e7a7a22013-01-16 10:03:56 +09001485
1486 return 0;
1487}
1488EXPORT_SYMBOL_GPL(wm_adsp1_init);
1489
Mark Brown2159ad92012-10-11 11:54:02 +09001490int wm_adsp1_event(struct snd_soc_dapm_widget *w,
1491 struct snd_kcontrol *kcontrol,
1492 int event)
1493{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01001494 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad92012-10-11 11:54:02 +09001495 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1496 struct wm_adsp *dsp = &dsps[w->shift];
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00001497 struct wm_adsp_alg_region *alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001498 struct wm_coeff_ctl *ctl;
Mark Brown2159ad92012-10-11 11:54:02 +09001499 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +00001500 int val;
Mark Brown2159ad92012-10-11 11:54:02 +09001501
Lars-Peter Clausen00200102014-07-17 22:01:07 +02001502 dsp->card = codec->component.card;
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01001503
Mark Brown2159ad92012-10-11 11:54:02 +09001504 switch (event) {
1505 case SND_SOC_DAPM_POST_PMU:
1506 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1507 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
1508
Chris Rattray94e205b2013-01-18 08:43:09 +00001509 /*
1510 * For simplicity set the DSP clock rate to be the
1511 * SYSCLK rate rather than making it configurable.
1512 */
1513 if(dsp->sysclk_reg) {
1514 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
1515 if (ret != 0) {
1516 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
1517 ret);
1518 return ret;
1519 }
1520
1521 val = (val & dsp->sysclk_mask)
1522 >> dsp->sysclk_shift;
1523
1524 ret = regmap_update_bits(dsp->regmap,
1525 dsp->base + ADSP1_CONTROL_31,
1526 ADSP1_CLK_SEL_MASK, val);
1527 if (ret != 0) {
1528 adsp_err(dsp, "Failed to set clock rate: %d\n",
1529 ret);
1530 return ret;
1531 }
1532 }
1533
Mark Brown2159ad92012-10-11 11:54:02 +09001534 ret = wm_adsp_load(dsp);
1535 if (ret != 0)
1536 goto err;
1537
Charles Keepaxb618a1852015-04-13 13:27:53 +01001538 ret = wm_adsp1_setup_algs(dsp);
Mark Browndb405172012-10-26 19:30:40 +01001539 if (ret != 0)
1540 goto err;
1541
Mark Brown2159ad92012-10-11 11:54:02 +09001542 ret = wm_adsp_load_coeff(dsp);
1543 if (ret != 0)
1544 goto err;
1545
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01001546 /* Initialize caches for enabled and unset controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001547 ret = wm_coeff_init_control_caches(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001548 if (ret != 0)
1549 goto err;
1550
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01001551 /* Sync set controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001552 ret = wm_coeff_sync_controls(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001553 if (ret != 0)
1554 goto err;
1555
Mark Brown2159ad92012-10-11 11:54:02 +09001556 /* Start the core running */
1557 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1558 ADSP1_CORE_ENA | ADSP1_START,
1559 ADSP1_CORE_ENA | ADSP1_START);
1560 break;
1561
1562 case SND_SOC_DAPM_PRE_PMD:
1563 /* Halt the core */
1564 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1565 ADSP1_CORE_ENA | ADSP1_START, 0);
1566
1567 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
1568 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
1569
1570 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1571 ADSP1_SYS_ENA, 0);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001572
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001573 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001574 ctl->enabled = 0;
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00001575
1576 while (!list_empty(&dsp->alg_regions)) {
1577 alg_region = list_first_entry(&dsp->alg_regions,
1578 struct wm_adsp_alg_region,
1579 list);
1580 list_del(&alg_region->list);
1581 kfree(alg_region);
1582 }
Mark Brown2159ad92012-10-11 11:54:02 +09001583 break;
1584
1585 default:
1586 break;
1587 }
1588
1589 return 0;
1590
1591err:
1592 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1593 ADSP1_SYS_ENA, 0);
1594 return ret;
1595}
1596EXPORT_SYMBOL_GPL(wm_adsp1_event);
1597
1598static int wm_adsp2_ena(struct wm_adsp *dsp)
1599{
1600 unsigned int val;
1601 int ret, count;
1602
Mark Brown1552c322013-11-28 18:11:38 +00001603 ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
1604 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
Mark Brown2159ad92012-10-11 11:54:02 +09001605 if (ret != 0)
1606 return ret;
1607
1608 /* Wait for the RAM to start, should be near instantaneous */
Charles Keepax939fd1e2013-12-18 09:25:49 +00001609 for (count = 0; count < 10; ++count) {
Mark Brown2159ad92012-10-11 11:54:02 +09001610 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
1611 &val);
1612 if (ret != 0)
1613 return ret;
Charles Keepax939fd1e2013-12-18 09:25:49 +00001614
1615 if (val & ADSP2_RAM_RDY)
1616 break;
1617
1618 msleep(1);
1619 }
Mark Brown2159ad92012-10-11 11:54:02 +09001620
1621 if (!(val & ADSP2_RAM_RDY)) {
1622 adsp_err(dsp, "Failed to start DSP RAM\n");
1623 return -EBUSY;
1624 }
1625
1626 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
Mark Brown2159ad92012-10-11 11:54:02 +09001627
1628 return 0;
1629}
1630
Charles Keepax18b1a902014-01-09 09:06:54 +00001631static void wm_adsp2_boot_work(struct work_struct *work)
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001632{
1633 struct wm_adsp *dsp = container_of(work,
1634 struct wm_adsp,
1635 boot_work);
1636 int ret;
1637 unsigned int val;
1638
1639 /*
1640 * For simplicity set the DSP clock rate to be the
1641 * SYSCLK rate rather than making it configurable.
1642 */
1643 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1644 if (ret != 0) {
1645 adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
1646 return;
1647 }
1648 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1649 >> ARIZONA_SYSCLK_FREQ_SHIFT;
1650
1651 ret = regmap_update_bits_async(dsp->regmap,
1652 dsp->base + ADSP2_CLOCKING,
1653 ADSP2_CLK_SEL_MASK, val);
1654 if (ret != 0) {
1655 adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
1656 return;
1657 }
1658
1659 if (dsp->dvfs) {
1660 ret = regmap_read(dsp->regmap,
1661 dsp->base + ADSP2_CLOCKING, &val);
1662 if (ret != 0) {
Charles Keepax62c35b32014-05-27 13:08:43 +01001663 adsp_err(dsp, "Failed to read clocking: %d\n", ret);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001664 return;
1665 }
1666
1667 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
1668 ret = regulator_enable(dsp->dvfs);
1669 if (ret != 0) {
Charles Keepax62c35b32014-05-27 13:08:43 +01001670 adsp_err(dsp,
1671 "Failed to enable supply: %d\n",
1672 ret);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001673 return;
1674 }
1675
1676 ret = regulator_set_voltage(dsp->dvfs,
1677 1800000,
1678 1800000);
1679 if (ret != 0) {
Charles Keepax62c35b32014-05-27 13:08:43 +01001680 adsp_err(dsp,
1681 "Failed to raise supply: %d\n",
1682 ret);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001683 return;
1684 }
1685 }
1686 }
1687
1688 ret = wm_adsp2_ena(dsp);
1689 if (ret != 0)
1690 return;
1691
1692 ret = wm_adsp_load(dsp);
1693 if (ret != 0)
1694 goto err;
1695
Charles Keepaxb618a1852015-04-13 13:27:53 +01001696 ret = wm_adsp2_setup_algs(dsp);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001697 if (ret != 0)
1698 goto err;
1699
1700 ret = wm_adsp_load_coeff(dsp);
1701 if (ret != 0)
1702 goto err;
1703
1704 /* Initialize caches for enabled and unset controls */
1705 ret = wm_coeff_init_control_caches(dsp);
1706 if (ret != 0)
1707 goto err;
1708
1709 /* Sync set controls */
1710 ret = wm_coeff_sync_controls(dsp);
1711 if (ret != 0)
1712 goto err;
1713
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001714 dsp->running = true;
1715
1716 return;
1717
1718err:
1719 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1720 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
1721}
1722
Charles Keepax12db5ed2014-01-08 17:42:19 +00001723int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
1724 struct snd_kcontrol *kcontrol, int event)
1725{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01001726 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Charles Keepax12db5ed2014-01-08 17:42:19 +00001727 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1728 struct wm_adsp *dsp = &dsps[w->shift];
1729
Lars-Peter Clausen00200102014-07-17 22:01:07 +02001730 dsp->card = codec->component.card;
Charles Keepax12db5ed2014-01-08 17:42:19 +00001731
1732 switch (event) {
1733 case SND_SOC_DAPM_PRE_PMU:
1734 queue_work(system_unbound_wq, &dsp->boot_work);
1735 break;
1736 default:
1737 break;
Charles Keepaxcab27252014-04-17 13:42:54 +01001738 }
Charles Keepax12db5ed2014-01-08 17:42:19 +00001739
1740 return 0;
1741}
1742EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
1743
Mark Brown2159ad92012-10-11 11:54:02 +09001744int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1745 struct snd_kcontrol *kcontrol, int event)
1746{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01001747 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad92012-10-11 11:54:02 +09001748 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1749 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +00001750 struct wm_adsp_alg_region *alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001751 struct wm_coeff_ctl *ctl;
Mark Brown2159ad92012-10-11 11:54:02 +09001752 int ret;
1753
1754 switch (event) {
1755 case SND_SOC_DAPM_POST_PMU:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001756 flush_work(&dsp->boot_work);
Mark Browndd49e2c2012-12-02 21:50:46 +09001757
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001758 if (!dsp->running)
1759 return -EIO;
Mark Browndd49e2c2012-12-02 21:50:46 +09001760
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001761 ret = regmap_update_bits(dsp->regmap,
1762 dsp->base + ADSP2_CONTROL,
Charles Keepax00e4c3b2014-11-18 16:25:27 +00001763 ADSP2_CORE_ENA | ADSP2_START,
1764 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad92012-10-11 11:54:02 +09001765 if (ret != 0)
1766 goto err;
Mark Brown2159ad92012-10-11 11:54:02 +09001767 break;
1768
1769 case SND_SOC_DAPM_PRE_PMD:
Mark Brown1023dbd2013-01-11 22:58:28 +00001770 dsp->running = false;
1771
Mark Brown2159ad92012-10-11 11:54:02 +09001772 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001773 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1774 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00001775
Mark Brown2d30b572013-01-28 20:18:17 +08001776 /* Make sure DMAs are quiesced */
1777 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
1778 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
1779 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
1780
Mark Brown973838a2012-11-28 17:20:32 +00001781 if (dsp->dvfs) {
1782 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1783 1800000);
1784 if (ret != 0)
Charles Keepax62c35b32014-05-27 13:08:43 +01001785 adsp_warn(dsp,
1786 "Failed to lower supply: %d\n",
1787 ret);
Mark Brown973838a2012-11-28 17:20:32 +00001788
1789 ret = regulator_disable(dsp->dvfs);
1790 if (ret != 0)
Charles Keepax62c35b32014-05-27 13:08:43 +01001791 adsp_err(dsp,
1792 "Failed to enable supply: %d\n",
1793 ret);
Mark Brown973838a2012-11-28 17:20:32 +00001794 }
Mark Brown471f4882013-01-08 16:09:31 +00001795
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001796 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001797 ctl->enabled = 0;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001798
Mark Brown471f4882013-01-08 16:09:31 +00001799 while (!list_empty(&dsp->alg_regions)) {
1800 alg_region = list_first_entry(&dsp->alg_regions,
1801 struct wm_adsp_alg_region,
1802 list);
1803 list_del(&alg_region->list);
1804 kfree(alg_region);
1805 }
Charles Keepaxddbc5ef2014-01-22 10:09:11 +00001806
1807 adsp_dbg(dsp, "Shutdown complete\n");
Mark Brown2159ad92012-10-11 11:54:02 +09001808 break;
1809
1810 default:
1811 break;
1812 }
1813
1814 return 0;
1815err:
1816 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001817 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad92012-10-11 11:54:02 +09001818 return ret;
1819}
1820EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00001821
Charles Keepax3809f002015-04-13 13:27:54 +01001822int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs)
Mark Brown973838a2012-11-28 17:20:32 +00001823{
1824 int ret;
1825
Mark Brown10a2b662012-12-02 21:37:00 +09001826 /*
1827 * Disable the DSP memory by default when in reset for a small
1828 * power saving.
1829 */
Charles Keepax3809f002015-04-13 13:27:54 +01001830 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Brown10a2b662012-12-02 21:37:00 +09001831 ADSP2_MEM_ENA, 0);
1832 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001833 adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
Mark Brown10a2b662012-12-02 21:37:00 +09001834 return ret;
1835 }
1836
Charles Keepax3809f002015-04-13 13:27:54 +01001837 INIT_LIST_HEAD(&dsp->alg_regions);
1838 INIT_LIST_HEAD(&dsp->ctl_list);
1839 INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001840
Mark Brown973838a2012-11-28 17:20:32 +00001841 if (dvfs) {
Charles Keepax3809f002015-04-13 13:27:54 +01001842 dsp->dvfs = devm_regulator_get(dsp->dev, "DCVDD");
1843 if (IS_ERR(dsp->dvfs)) {
1844 ret = PTR_ERR(dsp->dvfs);
1845 adsp_err(dsp, "Failed to get DCVDD: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001846 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001847 }
1848
Charles Keepax3809f002015-04-13 13:27:54 +01001849 ret = regulator_enable(dsp->dvfs);
Mark Brown973838a2012-11-28 17:20:32 +00001850 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001851 adsp_err(dsp, "Failed to enable DCVDD: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001852 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001853 }
1854
Charles Keepax3809f002015-04-13 13:27:54 +01001855 ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000);
Mark Brown973838a2012-11-28 17:20:32 +00001856 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001857 adsp_err(dsp, "Failed to initialise DVFS: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001858 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001859 }
1860
Charles Keepax3809f002015-04-13 13:27:54 +01001861 ret = regulator_disable(dsp->dvfs);
Mark Brown973838a2012-11-28 17:20:32 +00001862 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001863 adsp_err(dsp, "Failed to disable DCVDD: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001864 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001865 }
1866 }
1867
1868 return 0;
1869}
1870EXPORT_SYMBOL_GPL(wm_adsp2_init);
Praveen Diwakar0a37c6e2014-07-04 11:17:41 +05301871
1872MODULE_LICENSE("GPL v2");