blob: f6642c1c9ea4b666cf71d161d4754dfb9a9e2528 [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;
Charles Keepax26c22a12015-04-20 13:52:45 +0100243 unsigned int flags;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100244};
245
Mark Brown1023dbd2013-01-11 22:58:28 +0000246static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
247 struct snd_ctl_elem_value *ucontrol)
248{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100249 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000250 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100251 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Mark Brown1023dbd2013-01-11 22:58:28 +0000252
Charles Keepax3809f002015-04-13 13:27:54 +0100253 ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
Mark Brown1023dbd2013-01-11 22:58:28 +0000254
255 return 0;
256}
257
258static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
259 struct snd_ctl_elem_value *ucontrol)
260{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100261 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000262 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100263 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Mark Brown1023dbd2013-01-11 22:58:28 +0000264
Charles Keepax3809f002015-04-13 13:27:54 +0100265 if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
Mark Brown1023dbd2013-01-11 22:58:28 +0000266 return 0;
267
268 if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
269 return -EINVAL;
270
Charles Keepax3809f002015-04-13 13:27:54 +0100271 if (dsp[e->shift_l].running)
Mark Brown1023dbd2013-01-11 22:58:28 +0000272 return -EBUSY;
273
Charles Keepax3809f002015-04-13 13:27:54 +0100274 dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
Mark Brown1023dbd2013-01-11 22:58:28 +0000275
276 return 0;
277}
278
279static const struct soc_enum wm_adsp_fw_enum[] = {
280 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
281 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
282 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
283 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
284};
285
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000286const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000287 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
288 wm_adsp_fw_get, wm_adsp_fw_put),
289 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
290 wm_adsp_fw_get, wm_adsp_fw_put),
291 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
292 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000293};
294EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
295
296#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
297static const struct soc_enum wm_adsp2_rate_enum[] = {
Mark Browndc914282013-02-18 19:09:23 +0000298 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
299 ARIZONA_DSP1_RATE_SHIFT, 0xf,
300 ARIZONA_RATE_ENUM_SIZE,
301 arizona_rate_text, arizona_rate_val),
302 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
303 ARIZONA_DSP1_RATE_SHIFT, 0xf,
304 ARIZONA_RATE_ENUM_SIZE,
305 arizona_rate_text, arizona_rate_val),
306 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
307 ARIZONA_DSP1_RATE_SHIFT, 0xf,
308 ARIZONA_RATE_ENUM_SIZE,
309 arizona_rate_text, arizona_rate_val),
Charles Keepax5be9c5b2013-06-14 14:19:36 +0100310 SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
Mark Browndc914282013-02-18 19:09:23 +0000311 ARIZONA_DSP1_RATE_SHIFT, 0xf,
312 ARIZONA_RATE_ENUM_SIZE,
313 arizona_rate_text, arizona_rate_val),
314};
315
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000316const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000317 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
318 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000319 SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000320 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
321 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000322 SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000323 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
324 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000325 SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000326 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
327 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000328 SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
Mark Brown1023dbd2013-01-11 22:58:28 +0000329};
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000330EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
331#endif
Mark Brown2159ad92012-10-11 11:54:02 +0900332
333static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
334 int type)
335{
336 int i;
337
338 for (i = 0; i < dsp->num_mems; i++)
339 if (dsp->mem[i].type == type)
340 return &dsp->mem[i];
341
342 return NULL;
343}
344
Charles Keepax3809f002015-04-13 13:27:54 +0100345static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
Mark Brown45b9ee72013-01-08 16:02:06 +0000346 unsigned int offset)
347{
Charles Keepax3809f002015-04-13 13:27:54 +0100348 if (WARN_ON(!mem))
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100349 return offset;
Charles Keepax3809f002015-04-13 13:27:54 +0100350 switch (mem->type) {
Mark Brown45b9ee72013-01-08 16:02:06 +0000351 case WMFW_ADSP1_PM:
Charles Keepax3809f002015-04-13 13:27:54 +0100352 return mem->base + (offset * 3);
Mark Brown45b9ee72013-01-08 16:02:06 +0000353 case WMFW_ADSP1_DM:
Charles Keepax3809f002015-04-13 13:27:54 +0100354 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000355 case WMFW_ADSP2_XM:
Charles Keepax3809f002015-04-13 13:27:54 +0100356 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000357 case WMFW_ADSP2_YM:
Charles Keepax3809f002015-04-13 13:27:54 +0100358 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000359 case WMFW_ADSP1_ZM:
Charles Keepax3809f002015-04-13 13:27:54 +0100360 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000361 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100362 WARN(1, "Unknown memory region type");
Mark Brown45b9ee72013-01-08 16:02:06 +0000363 return offset;
364 }
365}
366
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100367static int wm_coeff_info(struct snd_kcontrol *kcontrol,
368 struct snd_ctl_elem_info *uinfo)
369{
370 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
371
372 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
373 uinfo->count = ctl->len;
374 return 0;
375}
376
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100377static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100378 const void *buf, size_t len)
379{
Charles Keepax3809f002015-04-13 13:27:54 +0100380 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100381 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100382 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100383 void *scratch;
384 int ret;
385 unsigned int reg;
386
Charles Keepax3809f002015-04-13 13:27:54 +0100387 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100388 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100389 adsp_err(dsp, "No base for region %x\n",
390 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100391 return -EINVAL;
392 }
393
Charles Keepax23237362015-04-13 13:28:02 +0100394 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100395 reg = wm_adsp_region_to_reg(mem, reg);
396
397 scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
398 if (!scratch)
399 return -ENOMEM;
400
Charles Keepax3809f002015-04-13 13:27:54 +0100401 ret = regmap_raw_write(dsp->regmap, reg, scratch,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100402 ctl->len);
403 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100404 adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +0000405 ctl->len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100406 kfree(scratch);
407 return ret;
408 }
Charles Keepax3809f002015-04-13 13:27:54 +0100409 adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100410
411 kfree(scratch);
412
413 return 0;
414}
415
416static int wm_coeff_put(struct snd_kcontrol *kcontrol,
417 struct snd_ctl_elem_value *ucontrol)
418{
419 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
420 char *p = ucontrol->value.bytes.data;
421
422 memcpy(ctl->cache, p, ctl->len);
423
Nikesh Oswal65d17a92015-02-16 15:25:48 +0000424 ctl->set = 1;
425 if (!ctl->enabled)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100426 return 0;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100427
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100428 return wm_coeff_write_control(ctl, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100429}
430
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100431static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100432 void *buf, size_t len)
433{
Charles Keepax3809f002015-04-13 13:27:54 +0100434 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100435 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100436 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100437 void *scratch;
438 int ret;
439 unsigned int reg;
440
Charles Keepax3809f002015-04-13 13:27:54 +0100441 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100442 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100443 adsp_err(dsp, "No base for region %x\n",
444 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100445 return -EINVAL;
446 }
447
Charles Keepax23237362015-04-13 13:28:02 +0100448 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100449 reg = wm_adsp_region_to_reg(mem, reg);
450
451 scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
452 if (!scratch)
453 return -ENOMEM;
454
Charles Keepax3809f002015-04-13 13:27:54 +0100455 ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100456 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100457 adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +0000458 ctl->len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100459 kfree(scratch);
460 return ret;
461 }
Charles Keepax3809f002015-04-13 13:27:54 +0100462 adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100463
464 memcpy(buf, scratch, ctl->len);
465 kfree(scratch);
466
467 return 0;
468}
469
470static int wm_coeff_get(struct snd_kcontrol *kcontrol,
471 struct snd_ctl_elem_value *ucontrol)
472{
473 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
474 char *p = ucontrol->value.bytes.data;
475
Charles Keepax26c22a12015-04-20 13:52:45 +0100476 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
477 if (ctl->enabled)
478 return wm_coeff_read_control(ctl, p, ctl->len);
479 else
480 return -EPERM;
481 }
482
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100483 memcpy(p, ctl->cache, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +0100484
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100485 return 0;
486}
487
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100488struct wmfw_ctl_work {
Charles Keepax3809f002015-04-13 13:27:54 +0100489 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100490 struct wm_coeff_ctl *ctl;
491 struct work_struct work;
492};
493
Charles Keepax3809f002015-04-13 13:27:54 +0100494static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100495{
496 struct snd_kcontrol_new *kcontrol;
497 int ret;
498
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +0100499 if (!ctl || !ctl->name)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100500 return -EINVAL;
501
502 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
503 if (!kcontrol)
504 return -ENOMEM;
505 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
506
507 kcontrol->name = ctl->name;
508 kcontrol->info = wm_coeff_info;
509 kcontrol->get = wm_coeff_get;
510 kcontrol->put = wm_coeff_put;
511 kcontrol->private_value = (unsigned long)ctl;
512
Charles Keepax26c22a12015-04-20 13:52:45 +0100513 if (ctl->flags) {
514 if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
515 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
516 if (ctl->flags & WMFW_CTL_FLAG_READABLE)
517 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
518 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
519 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
520 }
521
Charles Keepax3809f002015-04-13 13:27:54 +0100522 ret = snd_soc_add_card_controls(dsp->card,
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +0100523 kcontrol, 1);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100524 if (ret < 0)
525 goto err_kcontrol;
526
527 kfree(kcontrol);
528
Charles Keepax3809f002015-04-13 13:27:54 +0100529 ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +0100530 ctl->name);
531
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100532 return 0;
533
534err_kcontrol:
535 kfree(kcontrol);
536 return ret;
537}
538
Charles Keepaxb21acc12015-04-13 13:28:01 +0100539static int wm_coeff_init_control_caches(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 || ctl->set)
546 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +0100547 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
548 continue;
549
Charles Keepaxb21acc12015-04-13 13:28:01 +0100550 ret = wm_coeff_read_control(ctl,
551 ctl->cache,
552 ctl->len);
553 if (ret < 0)
554 return ret;
555 }
556
557 return 0;
558}
559
560static int wm_coeff_sync_controls(struct wm_adsp *dsp)
561{
562 struct wm_coeff_ctl *ctl;
563 int ret;
564
565 list_for_each_entry(ctl, &dsp->ctl_list, list) {
566 if (!ctl->enabled)
567 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +0100568 if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
Charles Keepaxb21acc12015-04-13 13:28:01 +0100569 ret = wm_coeff_write_control(ctl,
570 ctl->cache,
571 ctl->len);
572 if (ret < 0)
573 return ret;
574 }
575 }
576
577 return 0;
578}
579
580static void wm_adsp_ctl_work(struct work_struct *work)
581{
582 struct wmfw_ctl_work *ctl_work = container_of(work,
583 struct wmfw_ctl_work,
584 work);
585
586 wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
587 kfree(ctl_work);
588}
589
590static int wm_adsp_create_control(struct wm_adsp *dsp,
591 const struct wm_adsp_alg_region *alg_region,
Charles Keepax23237362015-04-13 13:28:02 +0100592 unsigned int offset, unsigned int len,
Charles Keepax26c22a12015-04-20 13:52:45 +0100593 const char *subname, unsigned int subname_len,
594 unsigned int flags)
Charles Keepaxb21acc12015-04-13 13:28:01 +0100595{
596 struct wm_coeff_ctl *ctl;
597 struct wmfw_ctl_work *ctl_work;
598 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
599 char *region_name;
600 int ret;
601
Charles Keepax26c22a12015-04-20 13:52:45 +0100602 if (flags & WMFW_CTL_FLAG_SYS)
603 return 0;
604
Charles Keepaxb21acc12015-04-13 13:28:01 +0100605 switch (alg_region->type) {
606 case WMFW_ADSP1_PM:
607 region_name = "PM";
608 break;
609 case WMFW_ADSP1_DM:
610 region_name = "DM";
611 break;
612 case WMFW_ADSP2_XM:
613 region_name = "XM";
614 break;
615 case WMFW_ADSP2_YM:
616 region_name = "YM";
617 break;
618 case WMFW_ADSP1_ZM:
619 region_name = "ZM";
620 break;
621 default:
Charles Keepax23237362015-04-13 13:28:02 +0100622 adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
Charles Keepaxb21acc12015-04-13 13:28:01 +0100623 return -EINVAL;
624 }
625
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100626 switch (dsp->fw_ver) {
627 case 0:
628 case 1:
629 snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
630 dsp->num, region_name, alg_region->alg);
631 break;
632 default:
633 ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
634 "DSP%d%c %.12s %x", dsp->num, *region_name,
635 wm_adsp_fw_text[dsp->fw], alg_region->alg);
636
637 /* Truncate the subname from the start if it is too long */
638 if (subname) {
639 int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
640 int skip = 0;
641
642 if (subname_len > avail)
643 skip = subname_len - avail;
644
645 snprintf(name + ret,
646 SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
647 subname_len - skip, subname + skip);
648 }
649 break;
650 }
Charles Keepaxb21acc12015-04-13 13:28:01 +0100651
652 list_for_each_entry(ctl, &dsp->ctl_list,
653 list) {
654 if (!strcmp(ctl->name, name)) {
655 if (!ctl->enabled)
656 ctl->enabled = 1;
657 return 0;
658 }
659 }
660
661 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
662 if (!ctl)
663 return -ENOMEM;
Charles Keepax23237362015-04-13 13:28:02 +0100664 ctl->fw_name = wm_adsp_fw_text[dsp->fw];
Charles Keepaxb21acc12015-04-13 13:28:01 +0100665 ctl->alg_region = *alg_region;
666 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
667 if (!ctl->name) {
668 ret = -ENOMEM;
669 goto err_ctl;
670 }
671 ctl->enabled = 1;
672 ctl->set = 0;
673 ctl->ops.xget = wm_coeff_get;
674 ctl->ops.xput = wm_coeff_put;
675 ctl->dsp = dsp;
676
Charles Keepax26c22a12015-04-20 13:52:45 +0100677 ctl->flags = flags;
Charles Keepax23237362015-04-13 13:28:02 +0100678 ctl->offset = offset;
Charles Keepaxb21acc12015-04-13 13:28:01 +0100679 if (len > 512) {
680 adsp_warn(dsp, "Truncating control %s from %d\n",
681 ctl->name, len);
682 len = 512;
683 }
684 ctl->len = len;
685 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
686 if (!ctl->cache) {
687 ret = -ENOMEM;
688 goto err_ctl_name;
689 }
690
Charles Keepax23237362015-04-13 13:28:02 +0100691 list_add(&ctl->list, &dsp->ctl_list);
692
Charles Keepaxb21acc12015-04-13 13:28:01 +0100693 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
694 if (!ctl_work) {
695 ret = -ENOMEM;
696 goto err_ctl_cache;
697 }
698
699 ctl_work->dsp = dsp;
700 ctl_work->ctl = ctl;
701 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
702 schedule_work(&ctl_work->work);
703
704 return 0;
705
706err_ctl_cache:
707 kfree(ctl->cache);
708err_ctl_name:
709 kfree(ctl->name);
710err_ctl:
711 kfree(ctl);
712
713 return ret;
714}
715
Charles Keepax23237362015-04-13 13:28:02 +0100716struct wm_coeff_parsed_alg {
717 int id;
718 const u8 *name;
719 int name_len;
720 int ncoeff;
721};
722
723struct wm_coeff_parsed_coeff {
724 int offset;
725 int mem_type;
726 const u8 *name;
727 int name_len;
728 int ctl_type;
729 int flags;
730 int len;
731};
732
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100733static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
734{
735 int length;
736
737 switch (bytes) {
738 case 1:
739 length = **pos;
740 break;
741 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +0100742 length = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100743 break;
744 default:
745 return 0;
746 }
747
748 if (str)
749 *str = *pos + bytes;
750
751 *pos += ((length + bytes) + 3) & ~0x03;
752
753 return length;
754}
755
756static int wm_coeff_parse_int(int bytes, const u8 **pos)
757{
758 int val = 0;
759
760 switch (bytes) {
761 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +0100762 val = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100763 break;
764 case 4:
Charles Keepax8299ee82015-04-20 13:52:44 +0100765 val = le32_to_cpu(*((__le32 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100766 break;
767 default:
768 break;
769 }
770
771 *pos += bytes;
772
773 return val;
774}
775
Charles Keepax23237362015-04-13 13:28:02 +0100776static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
777 struct wm_coeff_parsed_alg *blk)
778{
779 const struct wmfw_adsp_alg_data *raw;
780
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100781 switch (dsp->fw_ver) {
782 case 0:
783 case 1:
784 raw = (const struct wmfw_adsp_alg_data *)*data;
785 *data = raw->data;
Charles Keepax23237362015-04-13 13:28:02 +0100786
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100787 blk->id = le32_to_cpu(raw->id);
788 blk->name = raw->name;
789 blk->name_len = strlen(raw->name);
790 blk->ncoeff = le32_to_cpu(raw->ncoeff);
791 break;
792 default:
793 blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
794 blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
795 &blk->name);
796 wm_coeff_parse_string(sizeof(u16), data, NULL);
797 blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
798 break;
799 }
Charles Keepax23237362015-04-13 13:28:02 +0100800
801 adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
802 adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
803 adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
804}
805
806static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
807 struct wm_coeff_parsed_coeff *blk)
808{
809 const struct wmfw_adsp_coeff_data *raw;
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100810 const u8 *tmp;
811 int length;
Charles Keepax23237362015-04-13 13:28:02 +0100812
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100813 switch (dsp->fw_ver) {
814 case 0:
815 case 1:
816 raw = (const struct wmfw_adsp_coeff_data *)*data;
817 *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
Charles Keepax23237362015-04-13 13:28:02 +0100818
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100819 blk->offset = le16_to_cpu(raw->hdr.offset);
820 blk->mem_type = le16_to_cpu(raw->hdr.type);
821 blk->name = raw->name;
822 blk->name_len = strlen(raw->name);
823 blk->ctl_type = le16_to_cpu(raw->ctl_type);
824 blk->flags = le16_to_cpu(raw->flags);
825 blk->len = le32_to_cpu(raw->len);
826 break;
827 default:
828 tmp = *data;
829 blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
830 blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
831 length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
832 blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
833 &blk->name);
834 wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
835 wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
836 blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
837 blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
838 blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
839
840 *data = *data + sizeof(raw->hdr) + length;
841 break;
842 }
Charles Keepax23237362015-04-13 13:28:02 +0100843
844 adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
845 adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
846 adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
847 adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
848 adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
849 adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
850}
851
852static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
853 const struct wmfw_region *region)
854{
855 struct wm_adsp_alg_region alg_region = {};
856 struct wm_coeff_parsed_alg alg_blk;
857 struct wm_coeff_parsed_coeff coeff_blk;
858 const u8 *data = region->data;
859 int i, ret;
860
861 wm_coeff_parse_alg(dsp, &data, &alg_blk);
862 for (i = 0; i < alg_blk.ncoeff; i++) {
863 wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
864
865 switch (coeff_blk.ctl_type) {
866 case SNDRV_CTL_ELEM_TYPE_BYTES:
867 break;
868 default:
869 adsp_err(dsp, "Unknown control type: %d\n",
870 coeff_blk.ctl_type);
871 return -EINVAL;
872 }
873
874 alg_region.type = coeff_blk.mem_type;
875 alg_region.alg = alg_blk.id;
876
877 ret = wm_adsp_create_control(dsp, &alg_region,
878 coeff_blk.offset,
879 coeff_blk.len,
880 coeff_blk.name,
Charles Keepax26c22a12015-04-20 13:52:45 +0100881 coeff_blk.name_len,
882 coeff_blk.flags);
Charles Keepax23237362015-04-13 13:28:02 +0100883 if (ret < 0)
884 adsp_err(dsp, "Failed to create control: %.*s, %d\n",
885 coeff_blk.name_len, coeff_blk.name, ret);
886 }
887
888 return 0;
889}
890
Mark Brown2159ad92012-10-11 11:54:02 +0900891static int wm_adsp_load(struct wm_adsp *dsp)
892{
Mark Browncf17c832013-01-30 14:37:23 +0800893 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +0900894 const struct firmware *firmware;
895 struct regmap *regmap = dsp->regmap;
896 unsigned int pos = 0;
897 const struct wmfw_header *header;
898 const struct wmfw_adsp1_sizes *adsp1_sizes;
899 const struct wmfw_adsp2_sizes *adsp2_sizes;
900 const struct wmfw_footer *footer;
901 const struct wmfw_region *region;
902 const struct wm_adsp_region *mem;
903 const char *region_name;
904 char *file, *text;
Mark Browncf17c832013-01-30 14:37:23 +0800905 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +0900906 unsigned int reg;
907 int regions = 0;
908 int ret, offset, type, sizes;
909
910 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
911 if (file == NULL)
912 return -ENOMEM;
913
Mark Brown1023dbd2013-01-11 22:58:28 +0000914 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
915 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +0900916 file[PAGE_SIZE - 1] = '\0';
917
918 ret = request_firmware(&firmware, file, dsp->dev);
919 if (ret != 0) {
920 adsp_err(dsp, "Failed to request '%s'\n", file);
921 goto out;
922 }
923 ret = -EINVAL;
924
925 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
926 if (pos >= firmware->size) {
927 adsp_err(dsp, "%s: file too short, %zu bytes\n",
928 file, firmware->size);
929 goto out_fw;
930 }
931
932 header = (void*)&firmware->data[0];
933
934 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
935 adsp_err(dsp, "%s: invalid magic\n", file);
936 goto out_fw;
937 }
938
Charles Keepax23237362015-04-13 13:28:02 +0100939 switch (header->ver) {
940 case 0:
Charles Keepaxc61e59f2015-04-13 13:28:05 +0100941 adsp_warn(dsp, "%s: Depreciated file format %d\n",
942 file, header->ver);
943 break;
Charles Keepax23237362015-04-13 13:28:02 +0100944 case 1:
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100945 case 2:
Charles Keepax23237362015-04-13 13:28:02 +0100946 break;
947 default:
Mark Brown2159ad92012-10-11 11:54:02 +0900948 adsp_err(dsp, "%s: unknown file format %d\n",
949 file, header->ver);
950 goto out_fw;
951 }
Charles Keepax23237362015-04-13 13:28:02 +0100952
Dimitris Papastamos36269922013-11-01 15:56:57 +0000953 adsp_info(dsp, "Firmware version: %d\n", header->ver);
Charles Keepax23237362015-04-13 13:28:02 +0100954 dsp->fw_ver = header->ver;
Mark Brown2159ad92012-10-11 11:54:02 +0900955
956 if (header->core != dsp->type) {
957 adsp_err(dsp, "%s: invalid core %d != %d\n",
958 file, header->core, dsp->type);
959 goto out_fw;
960 }
961
962 switch (dsp->type) {
963 case WMFW_ADSP1:
964 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
965 adsp1_sizes = (void *)&(header[1]);
966 footer = (void *)&(adsp1_sizes[1]);
967 sizes = sizeof(*adsp1_sizes);
968
969 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
970 file, le32_to_cpu(adsp1_sizes->dm),
971 le32_to_cpu(adsp1_sizes->pm),
972 le32_to_cpu(adsp1_sizes->zm));
973 break;
974
975 case WMFW_ADSP2:
976 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
977 adsp2_sizes = (void *)&(header[1]);
978 footer = (void *)&(adsp2_sizes[1]);
979 sizes = sizeof(*adsp2_sizes);
980
981 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
982 file, le32_to_cpu(adsp2_sizes->xm),
983 le32_to_cpu(adsp2_sizes->ym),
984 le32_to_cpu(adsp2_sizes->pm),
985 le32_to_cpu(adsp2_sizes->zm));
986 break;
987
988 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100989 WARN(1, "Unknown DSP type");
Mark Brown2159ad92012-10-11 11:54:02 +0900990 goto out_fw;
991 }
992
993 if (le32_to_cpu(header->len) != sizeof(*header) +
994 sizes + sizeof(*footer)) {
995 adsp_err(dsp, "%s: unexpected header length %d\n",
996 file, le32_to_cpu(header->len));
997 goto out_fw;
998 }
999
1000 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
1001 le64_to_cpu(footer->timestamp));
1002
1003 while (pos < firmware->size &&
1004 pos - firmware->size > sizeof(*region)) {
1005 region = (void *)&(firmware->data[pos]);
1006 region_name = "Unknown";
1007 reg = 0;
1008 text = NULL;
1009 offset = le32_to_cpu(region->offset) & 0xffffff;
1010 type = be32_to_cpu(region->type) & 0xff;
1011 mem = wm_adsp_find_region(dsp, type);
1012
1013 switch (type) {
1014 case WMFW_NAME_TEXT:
1015 region_name = "Firmware name";
1016 text = kzalloc(le32_to_cpu(region->len) + 1,
1017 GFP_KERNEL);
1018 break;
Charles Keepax23237362015-04-13 13:28:02 +01001019 case WMFW_ALGORITHM_DATA:
1020 region_name = "Algorithm";
1021 ret = wm_adsp_parse_coeff(dsp, region);
1022 if (ret != 0)
1023 goto out_fw;
1024 break;
Mark Brown2159ad92012-10-11 11:54:02 +09001025 case WMFW_INFO_TEXT:
1026 region_name = "Information";
1027 text = kzalloc(le32_to_cpu(region->len) + 1,
1028 GFP_KERNEL);
1029 break;
1030 case WMFW_ABSOLUTE:
1031 region_name = "Absolute";
1032 reg = offset;
1033 break;
1034 case WMFW_ADSP1_PM:
Mark Brown2159ad92012-10-11 11:54:02 +09001035 region_name = "PM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001036 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001037 break;
1038 case WMFW_ADSP1_DM:
Mark Brown2159ad92012-10-11 11:54:02 +09001039 region_name = "DM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001040 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001041 break;
1042 case WMFW_ADSP2_XM:
Mark Brown2159ad92012-10-11 11:54:02 +09001043 region_name = "XM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001044 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001045 break;
1046 case WMFW_ADSP2_YM:
Mark Brown2159ad92012-10-11 11:54:02 +09001047 region_name = "YM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001048 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001049 break;
1050 case WMFW_ADSP1_ZM:
Mark Brown2159ad92012-10-11 11:54:02 +09001051 region_name = "ZM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001052 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001053 break;
1054 default:
1055 adsp_warn(dsp,
1056 "%s.%d: Unknown region type %x at %d(%x)\n",
1057 file, regions, type, pos, pos);
1058 break;
1059 }
1060
1061 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
1062 regions, le32_to_cpu(region->len), offset,
1063 region_name);
1064
1065 if (text) {
1066 memcpy(text, region->data, le32_to_cpu(region->len));
1067 adsp_info(dsp, "%s: %s\n", file, text);
1068 kfree(text);
1069 }
1070
1071 if (reg) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001072 buf = wm_adsp_buf_alloc(region->data,
1073 le32_to_cpu(region->len),
1074 &buf_list);
1075 if (!buf) {
1076 adsp_err(dsp, "Out of memory\n");
1077 ret = -ENOMEM;
1078 goto out_fw;
1079 }
Mark Browna76fefa2013-01-07 19:03:17 +00001080
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001081 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1082 le32_to_cpu(region->len));
1083 if (ret != 0) {
1084 adsp_err(dsp,
1085 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
1086 file, regions,
1087 le32_to_cpu(region->len), offset,
1088 region_name, ret);
1089 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +09001090 }
1091 }
1092
1093 pos += le32_to_cpu(region->len) + sizeof(*region);
1094 regions++;
1095 }
Mark Browncf17c832013-01-30 14:37:23 +08001096
1097 ret = regmap_async_complete(regmap);
1098 if (ret != 0) {
1099 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1100 goto out_fw;
1101 }
1102
Mark Brown2159ad92012-10-11 11:54:02 +09001103 if (pos > firmware->size)
1104 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1105 file, regions, pos - firmware->size);
1106
1107out_fw:
Mark Browncf17c832013-01-30 14:37:23 +08001108 regmap_async_complete(regmap);
1109 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001110 release_firmware(firmware);
1111out:
1112 kfree(file);
1113
1114 return ret;
1115}
1116
Charles Keepax23237362015-04-13 13:28:02 +01001117static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
1118 const struct wm_adsp_alg_region *alg_region)
1119{
1120 struct wm_coeff_ctl *ctl;
1121
1122 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1123 if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
1124 alg_region->alg == ctl->alg_region.alg &&
1125 alg_region->type == ctl->alg_region.type) {
1126 ctl->alg_region.base = alg_region->base;
1127 }
1128 }
1129}
1130
Charles Keepax3809f002015-04-13 13:27:54 +01001131static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
Charles Keepaxb618a1852015-04-13 13:27:53 +01001132 unsigned int pos, unsigned int len)
Mark Browndb405172012-10-26 19:30:40 +01001133{
Charles Keepaxb618a1852015-04-13 13:27:53 +01001134 void *alg;
1135 int ret;
Mark Browndb405172012-10-26 19:30:40 +01001136 __be32 val;
Mark Browndb405172012-10-26 19:30:40 +01001137
Charles Keepax3809f002015-04-13 13:27:54 +01001138 if (n_algs == 0) {
Mark Browndb405172012-10-26 19:30:40 +01001139 adsp_err(dsp, "No algorithms\n");
Charles Keepaxb618a1852015-04-13 13:27:53 +01001140 return ERR_PTR(-EINVAL);
Mark Browndb405172012-10-26 19:30:40 +01001141 }
1142
Charles Keepax3809f002015-04-13 13:27:54 +01001143 if (n_algs > 1024) {
1144 adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001145 return ERR_PTR(-EINVAL);
Mark Brownd62f4bc2012-12-19 14:00:30 +00001146 }
1147
Mark Browndb405172012-10-26 19:30:40 +01001148 /* Read the terminator first to validate the length */
Charles Keepaxb618a1852015-04-13 13:27:53 +01001149 ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
Mark Browndb405172012-10-26 19:30:40 +01001150 if (ret != 0) {
1151 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1152 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001153 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001154 }
1155
1156 if (be32_to_cpu(val) != 0xbedead)
1157 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
Charles Keepaxb618a1852015-04-13 13:27:53 +01001158 pos + len, be32_to_cpu(val));
Mark Browndb405172012-10-26 19:30:40 +01001159
Charles Keepaxb618a1852015-04-13 13:27:53 +01001160 alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +01001161 if (!alg)
Charles Keepaxb618a1852015-04-13 13:27:53 +01001162 return ERR_PTR(-ENOMEM);
Mark Browndb405172012-10-26 19:30:40 +01001163
Charles Keepaxb618a1852015-04-13 13:27:53 +01001164 ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
Mark Browndb405172012-10-26 19:30:40 +01001165 if (ret != 0) {
1166 adsp_err(dsp, "Failed to read algorithm list: %d\n",
1167 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001168 kfree(alg);
1169 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001170 }
1171
Charles Keepaxb618a1852015-04-13 13:27:53 +01001172 return alg;
1173}
1174
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001175static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
1176 int type, __be32 id,
1177 __be32 base)
1178{
1179 struct wm_adsp_alg_region *alg_region;
1180
1181 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1182 if (!alg_region)
1183 return ERR_PTR(-ENOMEM);
1184
1185 alg_region->type = type;
1186 alg_region->alg = be32_to_cpu(id);
1187 alg_region->base = be32_to_cpu(base);
1188
1189 list_add_tail(&alg_region->list, &dsp->alg_regions);
1190
Charles Keepax23237362015-04-13 13:28:02 +01001191 if (dsp->fw_ver > 0)
1192 wm_adsp_ctl_fixup_base(dsp, alg_region);
1193
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001194 return alg_region;
1195}
1196
Charles Keepaxb618a1852015-04-13 13:27:53 +01001197static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
1198{
1199 struct wmfw_adsp1_id_hdr adsp1_id;
1200 struct wmfw_adsp1_alg_hdr *adsp1_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001201 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001202 const struct wm_adsp_region *mem;
1203 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001204 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001205 int i, ret;
1206
1207 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
1208 if (WARN_ON(!mem))
1209 return -EINVAL;
1210
1211 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
1212 sizeof(adsp1_id));
1213 if (ret != 0) {
1214 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1215 ret);
1216 return ret;
1217 }
1218
Charles Keepax3809f002015-04-13 13:27:54 +01001219 n_algs = be32_to_cpu(adsp1_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001220 dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
1221 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1222 dsp->fw_id,
1223 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
1224 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
1225 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001226 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001227
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001228 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1229 adsp1_id.fw.id, adsp1_id.zm);
1230 if (IS_ERR(alg_region))
1231 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001232
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001233 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1234 adsp1_id.fw.id, adsp1_id.dm);
1235 if (IS_ERR(alg_region))
1236 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001237
1238 pos = sizeof(adsp1_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001239 len = (sizeof(*adsp1_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001240
Charles Keepax3809f002015-04-13 13:27:54 +01001241 adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001242 if (IS_ERR(adsp1_alg))
1243 return PTR_ERR(adsp1_alg);
Mark Browndb405172012-10-26 19:30:40 +01001244
Charles Keepax3809f002015-04-13 13:27:54 +01001245 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001246 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
1247 i, be32_to_cpu(adsp1_alg[i].alg.id),
1248 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
1249 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
1250 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
1251 be32_to_cpu(adsp1_alg[i].dm),
1252 be32_to_cpu(adsp1_alg[i].zm));
Mark Brown471f4882013-01-08 16:09:31 +00001253
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001254 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1255 adsp1_alg[i].alg.id,
1256 adsp1_alg[i].dm);
1257 if (IS_ERR(alg_region)) {
1258 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001259 goto out;
1260 }
Charles Keepax23237362015-04-13 13:28:02 +01001261 if (dsp->fw_ver == 0) {
1262 if (i + 1 < n_algs) {
1263 len = be32_to_cpu(adsp1_alg[i + 1].dm);
1264 len -= be32_to_cpu(adsp1_alg[i].dm);
1265 len *= 4;
1266 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001267 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001268 } else {
1269 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
1270 be32_to_cpu(adsp1_alg[i].alg.id));
1271 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001272 }
Mark Brown471f4882013-01-08 16:09:31 +00001273
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001274 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1275 adsp1_alg[i].alg.id,
1276 adsp1_alg[i].zm);
1277 if (IS_ERR(alg_region)) {
1278 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001279 goto out;
1280 }
Charles Keepax23237362015-04-13 13:28:02 +01001281 if (dsp->fw_ver == 0) {
1282 if (i + 1 < n_algs) {
1283 len = be32_to_cpu(adsp1_alg[i + 1].zm);
1284 len -= be32_to_cpu(adsp1_alg[i].zm);
1285 len *= 4;
1286 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001287 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001288 } else {
1289 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1290 be32_to_cpu(adsp1_alg[i].alg.id));
1291 }
Mark Browndb405172012-10-26 19:30:40 +01001292 }
1293 }
1294
1295out:
Charles Keepaxb618a1852015-04-13 13:27:53 +01001296 kfree(adsp1_alg);
1297 return ret;
1298}
1299
1300static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
1301{
1302 struct wmfw_adsp2_id_hdr adsp2_id;
1303 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001304 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001305 const struct wm_adsp_region *mem;
1306 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001307 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001308 int i, ret;
1309
1310 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
1311 if (WARN_ON(!mem))
1312 return -EINVAL;
1313
1314 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
1315 sizeof(adsp2_id));
1316 if (ret != 0) {
1317 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1318 ret);
1319 return ret;
1320 }
1321
Charles Keepax3809f002015-04-13 13:27:54 +01001322 n_algs = be32_to_cpu(adsp2_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001323 dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
1324 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1325 dsp->fw_id,
1326 (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
1327 (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
1328 be32_to_cpu(adsp2_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001329 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001330
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001331 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1332 adsp2_id.fw.id, adsp2_id.xm);
1333 if (IS_ERR(alg_region))
1334 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001335
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001336 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1337 adsp2_id.fw.id, adsp2_id.ym);
1338 if (IS_ERR(alg_region))
1339 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001340
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001341 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1342 adsp2_id.fw.id, adsp2_id.zm);
1343 if (IS_ERR(alg_region))
1344 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001345
1346 pos = sizeof(adsp2_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001347 len = (sizeof(*adsp2_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001348
Charles Keepax3809f002015-04-13 13:27:54 +01001349 adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001350 if (IS_ERR(adsp2_alg))
1351 return PTR_ERR(adsp2_alg);
1352
Charles Keepax3809f002015-04-13 13:27:54 +01001353 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001354 adsp_info(dsp,
1355 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1356 i, be32_to_cpu(adsp2_alg[i].alg.id),
1357 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1358 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1359 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1360 be32_to_cpu(adsp2_alg[i].xm),
1361 be32_to_cpu(adsp2_alg[i].ym),
1362 be32_to_cpu(adsp2_alg[i].zm));
1363
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001364 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1365 adsp2_alg[i].alg.id,
1366 adsp2_alg[i].xm);
1367 if (IS_ERR(alg_region)) {
1368 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001369 goto out;
1370 }
Charles Keepax23237362015-04-13 13:28:02 +01001371 if (dsp->fw_ver == 0) {
1372 if (i + 1 < n_algs) {
1373 len = be32_to_cpu(adsp2_alg[i + 1].xm);
1374 len -= be32_to_cpu(adsp2_alg[i].xm);
1375 len *= 4;
1376 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001377 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001378 } else {
1379 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
1380 be32_to_cpu(adsp2_alg[i].alg.id));
1381 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001382 }
1383
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001384 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1385 adsp2_alg[i].alg.id,
1386 adsp2_alg[i].ym);
1387 if (IS_ERR(alg_region)) {
1388 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001389 goto out;
1390 }
Charles Keepax23237362015-04-13 13:28:02 +01001391 if (dsp->fw_ver == 0) {
1392 if (i + 1 < n_algs) {
1393 len = be32_to_cpu(adsp2_alg[i + 1].ym);
1394 len -= be32_to_cpu(adsp2_alg[i].ym);
1395 len *= 4;
1396 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001397 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001398 } else {
1399 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
1400 be32_to_cpu(adsp2_alg[i].alg.id));
1401 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001402 }
1403
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001404 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1405 adsp2_alg[i].alg.id,
1406 adsp2_alg[i].zm);
1407 if (IS_ERR(alg_region)) {
1408 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001409 goto out;
1410 }
Charles Keepax23237362015-04-13 13:28:02 +01001411 if (dsp->fw_ver == 0) {
1412 if (i + 1 < n_algs) {
1413 len = be32_to_cpu(adsp2_alg[i + 1].zm);
1414 len -= be32_to_cpu(adsp2_alg[i].zm);
1415 len *= 4;
1416 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001417 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001418 } else {
1419 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1420 be32_to_cpu(adsp2_alg[i].alg.id));
1421 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001422 }
1423 }
1424
1425out:
1426 kfree(adsp2_alg);
Mark Browndb405172012-10-26 19:30:40 +01001427 return ret;
1428}
1429
Mark Brown2159ad92012-10-11 11:54:02 +09001430static int wm_adsp_load_coeff(struct wm_adsp *dsp)
1431{
Mark Browncf17c832013-01-30 14:37:23 +08001432 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001433 struct regmap *regmap = dsp->regmap;
1434 struct wmfw_coeff_hdr *hdr;
1435 struct wmfw_coeff_item *blk;
1436 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +00001437 const struct wm_adsp_region *mem;
1438 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad92012-10-11 11:54:02 +09001439 const char *region_name;
1440 int ret, pos, blocks, type, offset, reg;
1441 char *file;
Mark Browncf17c832013-01-30 14:37:23 +08001442 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +09001443
1444 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1445 if (file == NULL)
1446 return -ENOMEM;
1447
Mark Brown1023dbd2013-01-11 22:58:28 +00001448 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
1449 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +09001450 file[PAGE_SIZE - 1] = '\0';
1451
1452 ret = request_firmware(&firmware, file, dsp->dev);
1453 if (ret != 0) {
1454 adsp_warn(dsp, "Failed to request '%s'\n", file);
1455 ret = 0;
1456 goto out;
1457 }
1458 ret = -EINVAL;
1459
1460 if (sizeof(*hdr) >= firmware->size) {
1461 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1462 file, firmware->size);
1463 goto out_fw;
1464 }
1465
1466 hdr = (void*)&firmware->data[0];
1467 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
1468 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +00001469 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +09001470 }
1471
Mark Brownc7123262013-01-16 16:59:04 +09001472 switch (be32_to_cpu(hdr->rev) & 0xff) {
1473 case 1:
1474 break;
1475 default:
1476 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
1477 file, be32_to_cpu(hdr->rev) & 0xff);
1478 ret = -EINVAL;
1479 goto out_fw;
1480 }
1481
Mark Brown2159ad92012-10-11 11:54:02 +09001482 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
1483 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
1484 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
1485 le32_to_cpu(hdr->ver) & 0xff);
1486
1487 pos = le32_to_cpu(hdr->len);
1488
1489 blocks = 0;
1490 while (pos < firmware->size &&
1491 pos - firmware->size > sizeof(*blk)) {
1492 blk = (void*)(&firmware->data[pos]);
1493
Mark Brownc7123262013-01-16 16:59:04 +09001494 type = le16_to_cpu(blk->type);
1495 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001496
1497 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
1498 file, blocks, le32_to_cpu(blk->id),
1499 (le32_to_cpu(blk->ver) >> 16) & 0xff,
1500 (le32_to_cpu(blk->ver) >> 8) & 0xff,
1501 le32_to_cpu(blk->ver) & 0xff);
1502 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
1503 file, blocks, le32_to_cpu(blk->len), offset, type);
1504
1505 reg = 0;
1506 region_name = "Unknown";
1507 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +09001508 case (WMFW_NAME_TEXT << 8):
1509 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad92012-10-11 11:54:02 +09001510 break;
Mark Brownc7123262013-01-16 16:59:04 +09001511 case (WMFW_ABSOLUTE << 8):
Mark Brownf395a212013-03-05 22:39:54 +08001512 /*
1513 * Old files may use this for global
1514 * coefficients.
1515 */
1516 if (le32_to_cpu(blk->id) == dsp->fw_id &&
1517 offset == 0) {
1518 region_name = "global coefficients";
1519 mem = wm_adsp_find_region(dsp, type);
1520 if (!mem) {
1521 adsp_err(dsp, "No ZM\n");
1522 break;
1523 }
1524 reg = wm_adsp_region_to_reg(mem, 0);
1525
1526 } else {
1527 region_name = "register";
1528 reg = offset;
1529 }
Mark Brown2159ad92012-10-11 11:54:02 +09001530 break;
Mark Brown471f4882013-01-08 16:09:31 +00001531
1532 case WMFW_ADSP1_DM:
1533 case WMFW_ADSP1_ZM:
1534 case WMFW_ADSP2_XM:
1535 case WMFW_ADSP2_YM:
1536 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
1537 file, blocks, le32_to_cpu(blk->len),
1538 type, le32_to_cpu(blk->id));
1539
1540 mem = wm_adsp_find_region(dsp, type);
1541 if (!mem) {
1542 adsp_err(dsp, "No base for region %x\n", type);
1543 break;
1544 }
1545
1546 reg = 0;
1547 list_for_each_entry(alg_region,
1548 &dsp->alg_regions, list) {
1549 if (le32_to_cpu(blk->id) == alg_region->alg &&
1550 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +08001551 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +00001552 reg = wm_adsp_region_to_reg(mem,
1553 reg);
Mark Brown338c5182013-01-24 00:35:48 +08001554 reg += offset;
Charles Keepaxd733dc02013-11-28 16:37:51 +00001555 break;
Mark Brown471f4882013-01-08 16:09:31 +00001556 }
1557 }
1558
1559 if (reg == 0)
1560 adsp_err(dsp, "No %x for algorithm %x\n",
1561 type, le32_to_cpu(blk->id));
1562 break;
1563
Mark Brown2159ad92012-10-11 11:54:02 +09001564 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +09001565 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
1566 file, blocks, type, pos);
Mark Brown2159ad92012-10-11 11:54:02 +09001567 break;
1568 }
1569
1570 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +08001571 buf = wm_adsp_buf_alloc(blk->data,
1572 le32_to_cpu(blk->len),
1573 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +00001574 if (!buf) {
1575 adsp_err(dsp, "Out of memory\n");
Wei Yongjunf4b82812013-03-12 00:23:15 +08001576 ret = -ENOMEM;
1577 goto out_fw;
Mark Browna76fefa2013-01-07 19:03:17 +00001578 }
1579
Mark Brown20da6d52013-01-12 19:58:17 +00001580 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
1581 file, blocks, le32_to_cpu(blk->len),
1582 reg);
Mark Browncf17c832013-01-30 14:37:23 +08001583 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1584 le32_to_cpu(blk->len));
Mark Brown2159ad92012-10-11 11:54:02 +09001585 if (ret != 0) {
1586 adsp_err(dsp,
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +00001587 "%s.%d: Failed to write to %x in %s: %d\n",
1588 file, blocks, reg, region_name, ret);
Mark Brown2159ad92012-10-11 11:54:02 +09001589 }
1590 }
1591
Charles Keepaxbe951012015-02-16 15:25:49 +00001592 pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
Mark Brown2159ad92012-10-11 11:54:02 +09001593 blocks++;
1594 }
1595
Mark Browncf17c832013-01-30 14:37:23 +08001596 ret = regmap_async_complete(regmap);
1597 if (ret != 0)
1598 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1599
Mark Brown2159ad92012-10-11 11:54:02 +09001600 if (pos > firmware->size)
1601 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1602 file, blocks, pos - firmware->size);
1603
1604out_fw:
Charles Keepax9da7a5a2014-11-17 10:48:21 +00001605 regmap_async_complete(regmap);
Mark Brown2159ad92012-10-11 11:54:02 +09001606 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +08001607 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001608out:
1609 kfree(file);
Wei Yongjunf4b82812013-03-12 00:23:15 +08001610 return ret;
Mark Brown2159ad92012-10-11 11:54:02 +09001611}
1612
Charles Keepax3809f002015-04-13 13:27:54 +01001613int wm_adsp1_init(struct wm_adsp *dsp)
Mark Brown5e7a7a22013-01-16 10:03:56 +09001614{
Charles Keepax3809f002015-04-13 13:27:54 +01001615 INIT_LIST_HEAD(&dsp->alg_regions);
Mark Brown5e7a7a22013-01-16 10:03:56 +09001616
1617 return 0;
1618}
1619EXPORT_SYMBOL_GPL(wm_adsp1_init);
1620
Mark Brown2159ad92012-10-11 11:54:02 +09001621int wm_adsp1_event(struct snd_soc_dapm_widget *w,
1622 struct snd_kcontrol *kcontrol,
1623 int event)
1624{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01001625 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad92012-10-11 11:54:02 +09001626 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1627 struct wm_adsp *dsp = &dsps[w->shift];
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00001628 struct wm_adsp_alg_region *alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001629 struct wm_coeff_ctl *ctl;
Mark Brown2159ad92012-10-11 11:54:02 +09001630 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +00001631 int val;
Mark Brown2159ad92012-10-11 11:54:02 +09001632
Lars-Peter Clausen00200102014-07-17 22:01:07 +02001633 dsp->card = codec->component.card;
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01001634
Mark Brown2159ad92012-10-11 11:54:02 +09001635 switch (event) {
1636 case SND_SOC_DAPM_POST_PMU:
1637 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1638 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
1639
Chris Rattray94e205b2013-01-18 08:43:09 +00001640 /*
1641 * For simplicity set the DSP clock rate to be the
1642 * SYSCLK rate rather than making it configurable.
1643 */
1644 if(dsp->sysclk_reg) {
1645 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
1646 if (ret != 0) {
1647 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
1648 ret);
1649 return ret;
1650 }
1651
1652 val = (val & dsp->sysclk_mask)
1653 >> dsp->sysclk_shift;
1654
1655 ret = regmap_update_bits(dsp->regmap,
1656 dsp->base + ADSP1_CONTROL_31,
1657 ADSP1_CLK_SEL_MASK, val);
1658 if (ret != 0) {
1659 adsp_err(dsp, "Failed to set clock rate: %d\n",
1660 ret);
1661 return ret;
1662 }
1663 }
1664
Mark Brown2159ad92012-10-11 11:54:02 +09001665 ret = wm_adsp_load(dsp);
1666 if (ret != 0)
1667 goto err;
1668
Charles Keepaxb618a1852015-04-13 13:27:53 +01001669 ret = wm_adsp1_setup_algs(dsp);
Mark Browndb405172012-10-26 19:30:40 +01001670 if (ret != 0)
1671 goto err;
1672
Mark Brown2159ad92012-10-11 11:54:02 +09001673 ret = wm_adsp_load_coeff(dsp);
1674 if (ret != 0)
1675 goto err;
1676
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01001677 /* Initialize caches for enabled and unset controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001678 ret = wm_coeff_init_control_caches(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001679 if (ret != 0)
1680 goto err;
1681
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01001682 /* Sync set controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001683 ret = wm_coeff_sync_controls(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001684 if (ret != 0)
1685 goto err;
1686
Mark Brown2159ad92012-10-11 11:54:02 +09001687 /* Start the core running */
1688 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1689 ADSP1_CORE_ENA | ADSP1_START,
1690 ADSP1_CORE_ENA | ADSP1_START);
1691 break;
1692
1693 case SND_SOC_DAPM_PRE_PMD:
1694 /* Halt the core */
1695 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1696 ADSP1_CORE_ENA | ADSP1_START, 0);
1697
1698 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
1699 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
1700
1701 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1702 ADSP1_SYS_ENA, 0);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001703
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001704 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001705 ctl->enabled = 0;
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00001706
1707 while (!list_empty(&dsp->alg_regions)) {
1708 alg_region = list_first_entry(&dsp->alg_regions,
1709 struct wm_adsp_alg_region,
1710 list);
1711 list_del(&alg_region->list);
1712 kfree(alg_region);
1713 }
Mark Brown2159ad92012-10-11 11:54:02 +09001714 break;
1715
1716 default:
1717 break;
1718 }
1719
1720 return 0;
1721
1722err:
1723 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1724 ADSP1_SYS_ENA, 0);
1725 return ret;
1726}
1727EXPORT_SYMBOL_GPL(wm_adsp1_event);
1728
1729static int wm_adsp2_ena(struct wm_adsp *dsp)
1730{
1731 unsigned int val;
1732 int ret, count;
1733
Mark Brown1552c322013-11-28 18:11:38 +00001734 ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
1735 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
Mark Brown2159ad92012-10-11 11:54:02 +09001736 if (ret != 0)
1737 return ret;
1738
1739 /* Wait for the RAM to start, should be near instantaneous */
Charles Keepax939fd1e2013-12-18 09:25:49 +00001740 for (count = 0; count < 10; ++count) {
Mark Brown2159ad92012-10-11 11:54:02 +09001741 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
1742 &val);
1743 if (ret != 0)
1744 return ret;
Charles Keepax939fd1e2013-12-18 09:25:49 +00001745
1746 if (val & ADSP2_RAM_RDY)
1747 break;
1748
1749 msleep(1);
1750 }
Mark Brown2159ad92012-10-11 11:54:02 +09001751
1752 if (!(val & ADSP2_RAM_RDY)) {
1753 adsp_err(dsp, "Failed to start DSP RAM\n");
1754 return -EBUSY;
1755 }
1756
1757 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
Mark Brown2159ad92012-10-11 11:54:02 +09001758
1759 return 0;
1760}
1761
Charles Keepax18b1a902014-01-09 09:06:54 +00001762static void wm_adsp2_boot_work(struct work_struct *work)
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001763{
1764 struct wm_adsp *dsp = container_of(work,
1765 struct wm_adsp,
1766 boot_work);
1767 int ret;
1768 unsigned int val;
1769
1770 /*
1771 * For simplicity set the DSP clock rate to be the
1772 * SYSCLK rate rather than making it configurable.
1773 */
1774 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1775 if (ret != 0) {
1776 adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
1777 return;
1778 }
1779 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1780 >> ARIZONA_SYSCLK_FREQ_SHIFT;
1781
1782 ret = regmap_update_bits_async(dsp->regmap,
1783 dsp->base + ADSP2_CLOCKING,
1784 ADSP2_CLK_SEL_MASK, val);
1785 if (ret != 0) {
1786 adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
1787 return;
1788 }
1789
1790 if (dsp->dvfs) {
1791 ret = regmap_read(dsp->regmap,
1792 dsp->base + ADSP2_CLOCKING, &val);
1793 if (ret != 0) {
Charles Keepax62c35b32014-05-27 13:08:43 +01001794 adsp_err(dsp, "Failed to read clocking: %d\n", ret);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001795 return;
1796 }
1797
1798 if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
1799 ret = regulator_enable(dsp->dvfs);
1800 if (ret != 0) {
Charles Keepax62c35b32014-05-27 13:08:43 +01001801 adsp_err(dsp,
1802 "Failed to enable supply: %d\n",
1803 ret);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001804 return;
1805 }
1806
1807 ret = regulator_set_voltage(dsp->dvfs,
1808 1800000,
1809 1800000);
1810 if (ret != 0) {
Charles Keepax62c35b32014-05-27 13:08:43 +01001811 adsp_err(dsp,
1812 "Failed to raise supply: %d\n",
1813 ret);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001814 return;
1815 }
1816 }
1817 }
1818
1819 ret = wm_adsp2_ena(dsp);
1820 if (ret != 0)
1821 return;
1822
1823 ret = wm_adsp_load(dsp);
1824 if (ret != 0)
1825 goto err;
1826
Charles Keepaxb618a1852015-04-13 13:27:53 +01001827 ret = wm_adsp2_setup_algs(dsp);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001828 if (ret != 0)
1829 goto err;
1830
1831 ret = wm_adsp_load_coeff(dsp);
1832 if (ret != 0)
1833 goto err;
1834
1835 /* Initialize caches for enabled and unset controls */
1836 ret = wm_coeff_init_control_caches(dsp);
1837 if (ret != 0)
1838 goto err;
1839
1840 /* Sync set controls */
1841 ret = wm_coeff_sync_controls(dsp);
1842 if (ret != 0)
1843 goto err;
1844
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001845 dsp->running = true;
1846
1847 return;
1848
1849err:
1850 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1851 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
1852}
1853
Charles Keepax12db5ed2014-01-08 17:42:19 +00001854int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
1855 struct snd_kcontrol *kcontrol, int event)
1856{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01001857 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Charles Keepax12db5ed2014-01-08 17:42:19 +00001858 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1859 struct wm_adsp *dsp = &dsps[w->shift];
1860
Lars-Peter Clausen00200102014-07-17 22:01:07 +02001861 dsp->card = codec->component.card;
Charles Keepax12db5ed2014-01-08 17:42:19 +00001862
1863 switch (event) {
1864 case SND_SOC_DAPM_PRE_PMU:
1865 queue_work(system_unbound_wq, &dsp->boot_work);
1866 break;
1867 default:
1868 break;
Charles Keepaxcab27252014-04-17 13:42:54 +01001869 }
Charles Keepax12db5ed2014-01-08 17:42:19 +00001870
1871 return 0;
1872}
1873EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
1874
Mark Brown2159ad92012-10-11 11:54:02 +09001875int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1876 struct snd_kcontrol *kcontrol, int event)
1877{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01001878 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad92012-10-11 11:54:02 +09001879 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1880 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +00001881 struct wm_adsp_alg_region *alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001882 struct wm_coeff_ctl *ctl;
Mark Brown2159ad92012-10-11 11:54:02 +09001883 int ret;
1884
1885 switch (event) {
1886 case SND_SOC_DAPM_POST_PMU:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001887 flush_work(&dsp->boot_work);
Mark Browndd49e2c2012-12-02 21:50:46 +09001888
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001889 if (!dsp->running)
1890 return -EIO;
Mark Browndd49e2c2012-12-02 21:50:46 +09001891
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001892 ret = regmap_update_bits(dsp->regmap,
1893 dsp->base + ADSP2_CONTROL,
Charles Keepax00e4c3b2014-11-18 16:25:27 +00001894 ADSP2_CORE_ENA | ADSP2_START,
1895 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad92012-10-11 11:54:02 +09001896 if (ret != 0)
1897 goto err;
Mark Brown2159ad92012-10-11 11:54:02 +09001898 break;
1899
1900 case SND_SOC_DAPM_PRE_PMD:
Mark Brown1023dbd2013-01-11 22:58:28 +00001901 dsp->running = false;
1902
Mark Brown2159ad92012-10-11 11:54:02 +09001903 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001904 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1905 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00001906
Mark Brown2d30b572013-01-28 20:18:17 +08001907 /* Make sure DMAs are quiesced */
1908 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
1909 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
1910 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
1911
Mark Brown973838a2012-11-28 17:20:32 +00001912 if (dsp->dvfs) {
1913 ret = regulator_set_voltage(dsp->dvfs, 1200000,
1914 1800000);
1915 if (ret != 0)
Charles Keepax62c35b32014-05-27 13:08:43 +01001916 adsp_warn(dsp,
1917 "Failed to lower supply: %d\n",
1918 ret);
Mark Brown973838a2012-11-28 17:20:32 +00001919
1920 ret = regulator_disable(dsp->dvfs);
1921 if (ret != 0)
Charles Keepax62c35b32014-05-27 13:08:43 +01001922 adsp_err(dsp,
1923 "Failed to enable supply: %d\n",
1924 ret);
Mark Brown973838a2012-11-28 17:20:32 +00001925 }
Mark Brown471f4882013-01-08 16:09:31 +00001926
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001927 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001928 ctl->enabled = 0;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001929
Mark Brown471f4882013-01-08 16:09:31 +00001930 while (!list_empty(&dsp->alg_regions)) {
1931 alg_region = list_first_entry(&dsp->alg_regions,
1932 struct wm_adsp_alg_region,
1933 list);
1934 list_del(&alg_region->list);
1935 kfree(alg_region);
1936 }
Charles Keepaxddbc5ef2014-01-22 10:09:11 +00001937
1938 adsp_dbg(dsp, "Shutdown complete\n");
Mark Brown2159ad92012-10-11 11:54:02 +09001939 break;
1940
1941 default:
1942 break;
1943 }
1944
1945 return 0;
1946err:
1947 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00001948 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad92012-10-11 11:54:02 +09001949 return ret;
1950}
1951EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00001952
Charles Keepax3809f002015-04-13 13:27:54 +01001953int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs)
Mark Brown973838a2012-11-28 17:20:32 +00001954{
1955 int ret;
1956
Mark Brown10a2b662012-12-02 21:37:00 +09001957 /*
1958 * Disable the DSP memory by default when in reset for a small
1959 * power saving.
1960 */
Charles Keepax3809f002015-04-13 13:27:54 +01001961 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Brown10a2b662012-12-02 21:37:00 +09001962 ADSP2_MEM_ENA, 0);
1963 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001964 adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
Mark Brown10a2b662012-12-02 21:37:00 +09001965 return ret;
1966 }
1967
Charles Keepax3809f002015-04-13 13:27:54 +01001968 INIT_LIST_HEAD(&dsp->alg_regions);
1969 INIT_LIST_HEAD(&dsp->ctl_list);
1970 INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001971
Mark Brown973838a2012-11-28 17:20:32 +00001972 if (dvfs) {
Charles Keepax3809f002015-04-13 13:27:54 +01001973 dsp->dvfs = devm_regulator_get(dsp->dev, "DCVDD");
1974 if (IS_ERR(dsp->dvfs)) {
1975 ret = PTR_ERR(dsp->dvfs);
1976 adsp_err(dsp, "Failed to get DCVDD: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001977 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001978 }
1979
Charles Keepax3809f002015-04-13 13:27:54 +01001980 ret = regulator_enable(dsp->dvfs);
Mark Brown973838a2012-11-28 17:20:32 +00001981 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001982 adsp_err(dsp, "Failed to enable DCVDD: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001983 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001984 }
1985
Charles Keepax3809f002015-04-13 13:27:54 +01001986 ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000);
Mark Brown973838a2012-11-28 17:20:32 +00001987 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001988 adsp_err(dsp, "Failed to initialise DVFS: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001989 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001990 }
1991
Charles Keepax3809f002015-04-13 13:27:54 +01001992 ret = regulator_disable(dsp->dvfs);
Mark Brown973838a2012-11-28 17:20:32 +00001993 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01001994 adsp_err(dsp, "Failed to disable DCVDD: %d\n", ret);
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001995 return ret;
Mark Brown973838a2012-11-28 17:20:32 +00001996 }
1997 }
1998
1999 return 0;
2000}
2001EXPORT_SYMBOL_GPL(wm_adsp2_init);
Praveen Diwakar0a37c6e2014-07-04 11:17:41 +05302002
2003MODULE_LICENSE("GPL v2");