blob: 905ae993440b6f99eb8ffdbdfa5462838df22154 [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>
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +010026#include <linux/debugfs.h>
Mark Brown2159ad92012-10-11 11:54:02 +090027#include <sound/core.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/soc.h>
31#include <sound/jack.h>
32#include <sound/initval.h>
33#include <sound/tlv.h>
34
35#include <linux/mfd/arizona/registers.h>
36
Mark Browndc914282013-02-18 19:09:23 +000037#include "arizona.h"
Mark Brown2159ad92012-10-11 11:54:02 +090038#include "wm_adsp.h"
39
40#define adsp_crit(_dsp, fmt, ...) \
41 dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
42#define adsp_err(_dsp, fmt, ...) \
43 dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
44#define adsp_warn(_dsp, fmt, ...) \
45 dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
46#define adsp_info(_dsp, fmt, ...) \
47 dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
48#define adsp_dbg(_dsp, fmt, ...) \
49 dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
50
51#define ADSP1_CONTROL_1 0x00
52#define ADSP1_CONTROL_2 0x02
53#define ADSP1_CONTROL_3 0x03
54#define ADSP1_CONTROL_4 0x04
55#define ADSP1_CONTROL_5 0x06
56#define ADSP1_CONTROL_6 0x07
57#define ADSP1_CONTROL_7 0x08
58#define ADSP1_CONTROL_8 0x09
59#define ADSP1_CONTROL_9 0x0A
60#define ADSP1_CONTROL_10 0x0B
61#define ADSP1_CONTROL_11 0x0C
62#define ADSP1_CONTROL_12 0x0D
63#define ADSP1_CONTROL_13 0x0F
64#define ADSP1_CONTROL_14 0x10
65#define ADSP1_CONTROL_15 0x11
66#define ADSP1_CONTROL_16 0x12
67#define ADSP1_CONTROL_17 0x13
68#define ADSP1_CONTROL_18 0x14
69#define ADSP1_CONTROL_19 0x16
70#define ADSP1_CONTROL_20 0x17
71#define ADSP1_CONTROL_21 0x18
72#define ADSP1_CONTROL_22 0x1A
73#define ADSP1_CONTROL_23 0x1B
74#define ADSP1_CONTROL_24 0x1C
75#define ADSP1_CONTROL_25 0x1E
76#define ADSP1_CONTROL_26 0x20
77#define ADSP1_CONTROL_27 0x21
78#define ADSP1_CONTROL_28 0x22
79#define ADSP1_CONTROL_29 0x23
80#define ADSP1_CONTROL_30 0x24
81#define ADSP1_CONTROL_31 0x26
82
83/*
84 * ADSP1 Control 19
85 */
86#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
87#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
88#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
89
90
91/*
92 * ADSP1 Control 30
93 */
94#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
95#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
96#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
97#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
98#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
99#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
100#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
101#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
102#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
103#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
104#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
105#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
106#define ADSP1_START 0x0001 /* DSP1_START */
107#define ADSP1_START_MASK 0x0001 /* DSP1_START */
108#define ADSP1_START_SHIFT 0 /* DSP1_START */
109#define ADSP1_START_WIDTH 1 /* DSP1_START */
110
Chris Rattray94e205b2013-01-18 08:43:09 +0000111/*
112 * ADSP1 Control 31
113 */
114#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
115#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
116#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
117
Mark Brown2d30b572013-01-28 20:18:17 +0800118#define ADSP2_CONTROL 0x0
119#define ADSP2_CLOCKING 0x1
120#define ADSP2_STATUS1 0x4
121#define ADSP2_WDMA_CONFIG_1 0x30
122#define ADSP2_WDMA_CONFIG_2 0x31
123#define ADSP2_RDMA_CONFIG_1 0x34
Mark Brown2159ad92012-10-11 11:54:02 +0900124
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100125#define ADSP2_SCRATCH0 0x40
126#define ADSP2_SCRATCH1 0x41
127#define ADSP2_SCRATCH2 0x42
128#define ADSP2_SCRATCH3 0x43
129
Mark Brown2159ad92012-10-11 11:54:02 +0900130/*
131 * ADSP2 Control
132 */
133
134#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
135#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
136#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
137#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
138#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
139#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
140#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
141#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
142#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
143#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
144#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
145#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
146#define ADSP2_START 0x0001 /* DSP1_START */
147#define ADSP2_START_MASK 0x0001 /* DSP1_START */
148#define ADSP2_START_SHIFT 0 /* DSP1_START */
149#define ADSP2_START_WIDTH 1 /* DSP1_START */
150
151/*
Mark Brown973838a2012-11-28 17:20:32 +0000152 * ADSP2 clocking
153 */
154#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
155#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
156#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
157
158/*
Mark Brown2159ad92012-10-11 11:54:02 +0900159 * ADSP2 Status 1
160 */
161#define ADSP2_RAM_RDY 0x0001
162#define ADSP2_RAM_RDY_MASK 0x0001
163#define ADSP2_RAM_RDY_SHIFT 0
164#define ADSP2_RAM_RDY_WIDTH 1
165
Mark Browncf17c832013-01-30 14:37:23 +0800166struct wm_adsp_buf {
167 struct list_head list;
168 void *buf;
169};
170
171static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
172 struct list_head *list)
173{
174 struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
175
176 if (buf == NULL)
177 return NULL;
178
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000179 buf->buf = vmalloc(len);
Mark Browncf17c832013-01-30 14:37:23 +0800180 if (!buf->buf) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000181 vfree(buf);
Mark Browncf17c832013-01-30 14:37:23 +0800182 return NULL;
183 }
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000184 memcpy(buf->buf, src, len);
Mark Browncf17c832013-01-30 14:37:23 +0800185
186 if (list)
187 list_add_tail(&buf->list, list);
188
189 return buf;
190}
191
192static void wm_adsp_buf_free(struct list_head *list)
193{
194 while (!list_empty(list)) {
195 struct wm_adsp_buf *buf = list_first_entry(list,
196 struct wm_adsp_buf,
197 list);
198 list_del(&buf->list);
Charles Keepaxcdcd7f72014-11-14 15:40:45 +0000199 vfree(buf->buf);
Mark Browncf17c832013-01-30 14:37:23 +0800200 kfree(buf);
201 }
202}
203
Charles Keepax04d13002015-11-26 14:01:52 +0000204#define WM_ADSP_FW_MBC_VSS 0
205#define WM_ADSP_FW_HIFI 1
206#define WM_ADSP_FW_TX 2
207#define WM_ADSP_FW_TX_SPK 3
208#define WM_ADSP_FW_RX 4
209#define WM_ADSP_FW_RX_ANC 5
210#define WM_ADSP_FW_CTRL 6
211#define WM_ADSP_FW_ASR 7
212#define WM_ADSP_FW_TRACE 8
213#define WM_ADSP_FW_SPK_PROT 9
214#define WM_ADSP_FW_MISC 10
Mark Brown1023dbd2013-01-11 22:58:28 +0000215
Charles Keepax04d13002015-11-26 14:01:52 +0000216#define WM_ADSP_NUM_FW 11
Mark Browndd84f922013-03-08 15:25:58 +0800217
Mark Brown1023dbd2013-01-11 22:58:28 +0000218static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000219 [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
220 [WM_ADSP_FW_HIFI] = "MasterHiFi",
221 [WM_ADSP_FW_TX] = "Tx",
222 [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
223 [WM_ADSP_FW_RX] = "Rx",
224 [WM_ADSP_FW_RX_ANC] = "Rx ANC",
225 [WM_ADSP_FW_CTRL] = "Voice Ctrl",
226 [WM_ADSP_FW_ASR] = "ASR Assist",
227 [WM_ADSP_FW_TRACE] = "Dbg Trace",
228 [WM_ADSP_FW_SPK_PROT] = "Protection",
229 [WM_ADSP_FW_MISC] = "Misc",
Mark Brown1023dbd2013-01-11 22:58:28 +0000230};
231
232static struct {
233 const char *file;
234} wm_adsp_fw[WM_ADSP_NUM_FW] = {
Charles Keepax04d13002015-11-26 14:01:52 +0000235 [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
236 [WM_ADSP_FW_HIFI] = { .file = "hifi" },
237 [WM_ADSP_FW_TX] = { .file = "tx" },
238 [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
239 [WM_ADSP_FW_RX] = { .file = "rx" },
240 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
241 [WM_ADSP_FW_CTRL] = { .file = "ctrl" },
242 [WM_ADSP_FW_ASR] = { .file = "asr" },
243 [WM_ADSP_FW_TRACE] = { .file = "trace" },
244 [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
245 [WM_ADSP_FW_MISC] = { .file = "misc" },
Mark Brown1023dbd2013-01-11 22:58:28 +0000246};
247
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100248struct wm_coeff_ctl_ops {
249 int (*xget)(struct snd_kcontrol *kcontrol,
250 struct snd_ctl_elem_value *ucontrol);
251 int (*xput)(struct snd_kcontrol *kcontrol,
252 struct snd_ctl_elem_value *ucontrol);
253 int (*xinfo)(struct snd_kcontrol *kcontrol,
254 struct snd_ctl_elem_info *uinfo);
255};
256
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100257struct wm_coeff_ctl {
258 const char *name;
Charles Keepax23237362015-04-13 13:28:02 +0100259 const char *fw_name;
Charles Keepax3809f002015-04-13 13:27:54 +0100260 struct wm_adsp_alg_region alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100261 struct wm_coeff_ctl_ops ops;
Charles Keepax3809f002015-04-13 13:27:54 +0100262 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100263 unsigned int enabled:1;
264 struct list_head list;
265 void *cache;
Charles Keepax23237362015-04-13 13:28:02 +0100266 unsigned int offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100267 size_t len;
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +0100268 unsigned int set:1;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100269 struct snd_kcontrol *kcontrol;
Charles Keepax26c22a12015-04-20 13:52:45 +0100270 unsigned int flags;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100271};
272
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +0100273#ifdef CONFIG_DEBUG_FS
274static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
275{
276 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
277
278 mutex_lock(&dsp->debugfs_lock);
279 kfree(dsp->wmfw_file_name);
280 dsp->wmfw_file_name = tmp;
281 mutex_unlock(&dsp->debugfs_lock);
282}
283
284static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
285{
286 char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
287
288 mutex_lock(&dsp->debugfs_lock);
289 kfree(dsp->bin_file_name);
290 dsp->bin_file_name = tmp;
291 mutex_unlock(&dsp->debugfs_lock);
292}
293
294static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
295{
296 mutex_lock(&dsp->debugfs_lock);
297 kfree(dsp->wmfw_file_name);
298 kfree(dsp->bin_file_name);
299 dsp->wmfw_file_name = NULL;
300 dsp->bin_file_name = NULL;
301 mutex_unlock(&dsp->debugfs_lock);
302}
303
304static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
305 char __user *user_buf,
306 size_t count, loff_t *ppos)
307{
308 struct wm_adsp *dsp = file->private_data;
309 ssize_t ret;
310
311 mutex_lock(&dsp->debugfs_lock);
312
313 if (!dsp->wmfw_file_name || !dsp->running)
314 ret = 0;
315 else
316 ret = simple_read_from_buffer(user_buf, count, ppos,
317 dsp->wmfw_file_name,
318 strlen(dsp->wmfw_file_name));
319
320 mutex_unlock(&dsp->debugfs_lock);
321 return ret;
322}
323
324static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
325 char __user *user_buf,
326 size_t count, loff_t *ppos)
327{
328 struct wm_adsp *dsp = file->private_data;
329 ssize_t ret;
330
331 mutex_lock(&dsp->debugfs_lock);
332
333 if (!dsp->bin_file_name || !dsp->running)
334 ret = 0;
335 else
336 ret = simple_read_from_buffer(user_buf, count, ppos,
337 dsp->bin_file_name,
338 strlen(dsp->bin_file_name));
339
340 mutex_unlock(&dsp->debugfs_lock);
341 return ret;
342}
343
344static const struct {
345 const char *name;
346 const struct file_operations fops;
347} wm_adsp_debugfs_fops[] = {
348 {
349 .name = "wmfw_file_name",
350 .fops = {
351 .open = simple_open,
352 .read = wm_adsp_debugfs_wmfw_read,
353 },
354 },
355 {
356 .name = "bin_file_name",
357 .fops = {
358 .open = simple_open,
359 .read = wm_adsp_debugfs_bin_read,
360 },
361 },
362};
363
364static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
365 struct snd_soc_codec *codec)
366{
367 struct dentry *root = NULL;
368 char *root_name;
369 int i;
370
371 if (!codec->component.debugfs_root) {
372 adsp_err(dsp, "No codec debugfs root\n");
373 goto err;
374 }
375
376 root_name = kmalloc(PAGE_SIZE, GFP_KERNEL);
377 if (!root_name)
378 goto err;
379
380 snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num);
381 root = debugfs_create_dir(root_name, codec->component.debugfs_root);
382 kfree(root_name);
383
384 if (!root)
385 goto err;
386
387 if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
388 goto err;
389
390 if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
391 goto err;
392
393 if (!debugfs_create_x32("fw_version", S_IRUGO, root,
394 &dsp->fw_id_version))
395 goto err;
396
397 for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
398 if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
399 S_IRUGO, root, dsp,
400 &wm_adsp_debugfs_fops[i].fops))
401 goto err;
402 }
403
404 dsp->debugfs_root = root;
405 return;
406
407err:
408 debugfs_remove_recursive(root);
409 adsp_err(dsp, "Failed to create debugfs\n");
410}
411
412static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
413{
414 wm_adsp_debugfs_clear(dsp);
415 debugfs_remove_recursive(dsp->debugfs_root);
416}
417#else
418static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
419 struct snd_soc_codec *codec)
420{
421}
422
423static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
424{
425}
426
427static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
428 const char *s)
429{
430}
431
432static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
433 const char *s)
434{
435}
436
437static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
438{
439}
440#endif
441
Mark Brown1023dbd2013-01-11 22:58:28 +0000442static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
443 struct snd_ctl_elem_value *ucontrol)
444{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100445 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000446 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100447 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Mark Brown1023dbd2013-01-11 22:58:28 +0000448
Charles Keepax3809f002015-04-13 13:27:54 +0100449 ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
Mark Brown1023dbd2013-01-11 22:58:28 +0000450
451 return 0;
452}
453
454static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
455 struct snd_ctl_elem_value *ucontrol)
456{
Lars-Peter Clausenea53bf72014-03-18 09:02:04 +0100457 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Mark Brown1023dbd2013-01-11 22:58:28 +0000458 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
Charles Keepax3809f002015-04-13 13:27:54 +0100459 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
Mark Brown1023dbd2013-01-11 22:58:28 +0000460
Charles Keepax3809f002015-04-13 13:27:54 +0100461 if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
Mark Brown1023dbd2013-01-11 22:58:28 +0000462 return 0;
463
464 if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
465 return -EINVAL;
466
Charles Keepax3809f002015-04-13 13:27:54 +0100467 if (dsp[e->shift_l].running)
Mark Brown1023dbd2013-01-11 22:58:28 +0000468 return -EBUSY;
469
Charles Keepax3809f002015-04-13 13:27:54 +0100470 dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
Mark Brown1023dbd2013-01-11 22:58:28 +0000471
472 return 0;
473}
474
475static const struct soc_enum wm_adsp_fw_enum[] = {
476 SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
477 SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
478 SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
479 SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
480};
481
Richard Fitzgerald336d0442015-06-18 13:43:19 +0100482const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
Mark Brown1023dbd2013-01-11 22:58:28 +0000483 SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
484 wm_adsp_fw_get, wm_adsp_fw_put),
485 SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
486 wm_adsp_fw_get, wm_adsp_fw_put),
487 SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
488 wm_adsp_fw_get, wm_adsp_fw_put),
Richard Fitzgerald336d0442015-06-18 13:43:19 +0100489 SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
490 wm_adsp_fw_get, wm_adsp_fw_put),
Mark Brownb6ed61cf2013-03-29 18:00:24 +0000491};
Richard Fitzgerald336d0442015-06-18 13:43:19 +0100492EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
Mark Brown2159ad92012-10-11 11:54:02 +0900493
494static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
495 int type)
496{
497 int i;
498
499 for (i = 0; i < dsp->num_mems; i++)
500 if (dsp->mem[i].type == type)
501 return &dsp->mem[i];
502
503 return NULL;
504}
505
Charles Keepax3809f002015-04-13 13:27:54 +0100506static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
Mark Brown45b9ee72013-01-08 16:02:06 +0000507 unsigned int offset)
508{
Charles Keepax3809f002015-04-13 13:27:54 +0100509 if (WARN_ON(!mem))
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100510 return offset;
Charles Keepax3809f002015-04-13 13:27:54 +0100511 switch (mem->type) {
Mark Brown45b9ee72013-01-08 16:02:06 +0000512 case WMFW_ADSP1_PM:
Charles Keepax3809f002015-04-13 13:27:54 +0100513 return mem->base + (offset * 3);
Mark Brown45b9ee72013-01-08 16:02:06 +0000514 case WMFW_ADSP1_DM:
Charles Keepax3809f002015-04-13 13:27:54 +0100515 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000516 case WMFW_ADSP2_XM:
Charles Keepax3809f002015-04-13 13:27:54 +0100517 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000518 case WMFW_ADSP2_YM:
Charles Keepax3809f002015-04-13 13:27:54 +0100519 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000520 case WMFW_ADSP1_ZM:
Charles Keepax3809f002015-04-13 13:27:54 +0100521 return mem->base + (offset * 2);
Mark Brown45b9ee72013-01-08 16:02:06 +0000522 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +0100523 WARN(1, "Unknown memory region type");
Mark Brown45b9ee72013-01-08 16:02:06 +0000524 return offset;
525 }
526}
527
Richard Fitzgerald10337b02015-05-29 10:23:07 +0100528static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
529{
530 u16 scratch[4];
531 int ret;
532
533 ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0,
534 scratch, sizeof(scratch));
535 if (ret) {
536 adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
537 return;
538 }
539
540 adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
541 be16_to_cpu(scratch[0]),
542 be16_to_cpu(scratch[1]),
543 be16_to_cpu(scratch[2]),
544 be16_to_cpu(scratch[3]));
545}
546
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100547static int wm_coeff_info(struct snd_kcontrol *kcontrol,
548 struct snd_ctl_elem_info *uinfo)
549{
550 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
551
552 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
553 uinfo->count = ctl->len;
554 return 0;
555}
556
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100557static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100558 const void *buf, size_t len)
559{
Charles Keepax3809f002015-04-13 13:27:54 +0100560 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100561 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100562 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100563 void *scratch;
564 int ret;
565 unsigned int reg;
566
Charles Keepax3809f002015-04-13 13:27:54 +0100567 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100568 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100569 adsp_err(dsp, "No base for region %x\n",
570 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100571 return -EINVAL;
572 }
573
Charles Keepax23237362015-04-13 13:28:02 +0100574 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100575 reg = wm_adsp_region_to_reg(mem, reg);
576
577 scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
578 if (!scratch)
579 return -ENOMEM;
580
Charles Keepax3809f002015-04-13 13:27:54 +0100581 ret = regmap_raw_write(dsp->regmap, reg, scratch,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100582 ctl->len);
583 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100584 adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +0000585 ctl->len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100586 kfree(scratch);
587 return ret;
588 }
Charles Keepax3809f002015-04-13 13:27:54 +0100589 adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100590
591 kfree(scratch);
592
593 return 0;
594}
595
596static int wm_coeff_put(struct snd_kcontrol *kcontrol,
597 struct snd_ctl_elem_value *ucontrol)
598{
599 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
600 char *p = ucontrol->value.bytes.data;
601
602 memcpy(ctl->cache, p, ctl->len);
603
Nikesh Oswal65d17a92015-02-16 15:25:48 +0000604 ctl->set = 1;
605 if (!ctl->enabled)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100606 return 0;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100607
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100608 return wm_coeff_write_control(ctl, p, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100609}
610
Charles Keepaxc9f8dd72015-04-13 13:27:58 +0100611static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100612 void *buf, size_t len)
613{
Charles Keepax3809f002015-04-13 13:27:54 +0100614 struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100615 const struct wm_adsp_region *mem;
Charles Keepax3809f002015-04-13 13:27:54 +0100616 struct wm_adsp *dsp = ctl->dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100617 void *scratch;
618 int ret;
619 unsigned int reg;
620
Charles Keepax3809f002015-04-13 13:27:54 +0100621 mem = wm_adsp_find_region(dsp, alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100622 if (!mem) {
Charles Keepax3809f002015-04-13 13:27:54 +0100623 adsp_err(dsp, "No base for region %x\n",
624 alg_region->type);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100625 return -EINVAL;
626 }
627
Charles Keepax23237362015-04-13 13:28:02 +0100628 reg = ctl->alg_region.base + ctl->offset;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100629 reg = wm_adsp_region_to_reg(mem, reg);
630
631 scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
632 if (!scratch)
633 return -ENOMEM;
634
Charles Keepax3809f002015-04-13 13:27:54 +0100635 ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100636 if (ret) {
Charles Keepax3809f002015-04-13 13:27:54 +0100637 adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +0000638 ctl->len, reg, ret);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100639 kfree(scratch);
640 return ret;
641 }
Charles Keepax3809f002015-04-13 13:27:54 +0100642 adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100643
644 memcpy(buf, scratch, ctl->len);
645 kfree(scratch);
646
647 return 0;
648}
649
650static int wm_coeff_get(struct snd_kcontrol *kcontrol,
651 struct snd_ctl_elem_value *ucontrol)
652{
653 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
654 char *p = ucontrol->value.bytes.data;
655
Charles Keepax26c22a12015-04-20 13:52:45 +0100656 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
657 if (ctl->enabled)
658 return wm_coeff_read_control(ctl, p, ctl->len);
659 else
660 return -EPERM;
661 }
662
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100663 memcpy(p, ctl->cache, ctl->len);
Charles Keepax26c22a12015-04-20 13:52:45 +0100664
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100665 return 0;
666}
667
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100668struct wmfw_ctl_work {
Charles Keepax3809f002015-04-13 13:27:54 +0100669 struct wm_adsp *dsp;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100670 struct wm_coeff_ctl *ctl;
671 struct work_struct work;
672};
673
Charles Keepax3809f002015-04-13 13:27:54 +0100674static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100675{
676 struct snd_kcontrol_new *kcontrol;
677 int ret;
678
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +0100679 if (!ctl || !ctl->name)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100680 return -EINVAL;
681
682 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
683 if (!kcontrol)
684 return -ENOMEM;
685 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
686
687 kcontrol->name = ctl->name;
688 kcontrol->info = wm_coeff_info;
689 kcontrol->get = wm_coeff_get;
690 kcontrol->put = wm_coeff_put;
691 kcontrol->private_value = (unsigned long)ctl;
692
Charles Keepax26c22a12015-04-20 13:52:45 +0100693 if (ctl->flags) {
694 if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
695 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
696 if (ctl->flags & WMFW_CTL_FLAG_READABLE)
697 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
698 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
699 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
700 }
701
Charles Keepax3809f002015-04-13 13:27:54 +0100702 ret = snd_soc_add_card_controls(dsp->card,
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +0100703 kcontrol, 1);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100704 if (ret < 0)
705 goto err_kcontrol;
706
707 kfree(kcontrol);
708
Charles Keepax3809f002015-04-13 13:27:54 +0100709 ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +0100710 ctl->name);
711
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +0100712 return 0;
713
714err_kcontrol:
715 kfree(kcontrol);
716 return ret;
717}
718
Charles Keepaxb21acc12015-04-13 13:28:01 +0100719static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
720{
721 struct wm_coeff_ctl *ctl;
722 int ret;
723
724 list_for_each_entry(ctl, &dsp->ctl_list, list) {
725 if (!ctl->enabled || ctl->set)
726 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +0100727 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
728 continue;
729
Charles Keepaxb21acc12015-04-13 13:28:01 +0100730 ret = wm_coeff_read_control(ctl,
731 ctl->cache,
732 ctl->len);
733 if (ret < 0)
734 return ret;
735 }
736
737 return 0;
738}
739
740static int wm_coeff_sync_controls(struct wm_adsp *dsp)
741{
742 struct wm_coeff_ctl *ctl;
743 int ret;
744
745 list_for_each_entry(ctl, &dsp->ctl_list, list) {
746 if (!ctl->enabled)
747 continue;
Charles Keepax26c22a12015-04-20 13:52:45 +0100748 if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
Charles Keepaxb21acc12015-04-13 13:28:01 +0100749 ret = wm_coeff_write_control(ctl,
750 ctl->cache,
751 ctl->len);
752 if (ret < 0)
753 return ret;
754 }
755 }
756
757 return 0;
758}
759
760static void wm_adsp_ctl_work(struct work_struct *work)
761{
762 struct wmfw_ctl_work *ctl_work = container_of(work,
763 struct wmfw_ctl_work,
764 work);
765
766 wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
767 kfree(ctl_work);
768}
769
770static int wm_adsp_create_control(struct wm_adsp *dsp,
771 const struct wm_adsp_alg_region *alg_region,
Charles Keepax23237362015-04-13 13:28:02 +0100772 unsigned int offset, unsigned int len,
Charles Keepax26c22a12015-04-20 13:52:45 +0100773 const char *subname, unsigned int subname_len,
774 unsigned int flags)
Charles Keepaxb21acc12015-04-13 13:28:01 +0100775{
776 struct wm_coeff_ctl *ctl;
777 struct wmfw_ctl_work *ctl_work;
778 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
779 char *region_name;
780 int ret;
781
Charles Keepax26c22a12015-04-20 13:52:45 +0100782 if (flags & WMFW_CTL_FLAG_SYS)
783 return 0;
784
Charles Keepaxb21acc12015-04-13 13:28:01 +0100785 switch (alg_region->type) {
786 case WMFW_ADSP1_PM:
787 region_name = "PM";
788 break;
789 case WMFW_ADSP1_DM:
790 region_name = "DM";
791 break;
792 case WMFW_ADSP2_XM:
793 region_name = "XM";
794 break;
795 case WMFW_ADSP2_YM:
796 region_name = "YM";
797 break;
798 case WMFW_ADSP1_ZM:
799 region_name = "ZM";
800 break;
801 default:
Charles Keepax23237362015-04-13 13:28:02 +0100802 adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
Charles Keepaxb21acc12015-04-13 13:28:01 +0100803 return -EINVAL;
804 }
805
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100806 switch (dsp->fw_ver) {
807 case 0:
808 case 1:
809 snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
810 dsp->num, region_name, alg_region->alg);
811 break;
812 default:
813 ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
814 "DSP%d%c %.12s %x", dsp->num, *region_name,
815 wm_adsp_fw_text[dsp->fw], alg_region->alg);
816
817 /* Truncate the subname from the start if it is too long */
818 if (subname) {
819 int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
820 int skip = 0;
821
822 if (subname_len > avail)
823 skip = subname_len - avail;
824
825 snprintf(name + ret,
826 SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
827 subname_len - skip, subname + skip);
828 }
829 break;
830 }
Charles Keepaxb21acc12015-04-13 13:28:01 +0100831
832 list_for_each_entry(ctl, &dsp->ctl_list,
833 list) {
834 if (!strcmp(ctl->name, name)) {
835 if (!ctl->enabled)
836 ctl->enabled = 1;
837 return 0;
838 }
839 }
840
841 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
842 if (!ctl)
843 return -ENOMEM;
Charles Keepax23237362015-04-13 13:28:02 +0100844 ctl->fw_name = wm_adsp_fw_text[dsp->fw];
Charles Keepaxb21acc12015-04-13 13:28:01 +0100845 ctl->alg_region = *alg_region;
846 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
847 if (!ctl->name) {
848 ret = -ENOMEM;
849 goto err_ctl;
850 }
851 ctl->enabled = 1;
852 ctl->set = 0;
853 ctl->ops.xget = wm_coeff_get;
854 ctl->ops.xput = wm_coeff_put;
855 ctl->dsp = dsp;
856
Charles Keepax26c22a12015-04-20 13:52:45 +0100857 ctl->flags = flags;
Charles Keepax23237362015-04-13 13:28:02 +0100858 ctl->offset = offset;
Charles Keepaxb21acc12015-04-13 13:28:01 +0100859 if (len > 512) {
860 adsp_warn(dsp, "Truncating control %s from %d\n",
861 ctl->name, len);
862 len = 512;
863 }
864 ctl->len = len;
865 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
866 if (!ctl->cache) {
867 ret = -ENOMEM;
868 goto err_ctl_name;
869 }
870
Charles Keepax23237362015-04-13 13:28:02 +0100871 list_add(&ctl->list, &dsp->ctl_list);
872
Charles Keepaxb21acc12015-04-13 13:28:01 +0100873 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
874 if (!ctl_work) {
875 ret = -ENOMEM;
876 goto err_ctl_cache;
877 }
878
879 ctl_work->dsp = dsp;
880 ctl_work->ctl = ctl;
881 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
882 schedule_work(&ctl_work->work);
883
884 return 0;
885
886err_ctl_cache:
887 kfree(ctl->cache);
888err_ctl_name:
889 kfree(ctl->name);
890err_ctl:
891 kfree(ctl);
892
893 return ret;
894}
895
Charles Keepax23237362015-04-13 13:28:02 +0100896struct wm_coeff_parsed_alg {
897 int id;
898 const u8 *name;
899 int name_len;
900 int ncoeff;
901};
902
903struct wm_coeff_parsed_coeff {
904 int offset;
905 int mem_type;
906 const u8 *name;
907 int name_len;
908 int ctl_type;
909 int flags;
910 int len;
911};
912
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100913static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
914{
915 int length;
916
917 switch (bytes) {
918 case 1:
919 length = **pos;
920 break;
921 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +0100922 length = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100923 break;
924 default:
925 return 0;
926 }
927
928 if (str)
929 *str = *pos + bytes;
930
931 *pos += ((length + bytes) + 3) & ~0x03;
932
933 return length;
934}
935
936static int wm_coeff_parse_int(int bytes, const u8 **pos)
937{
938 int val = 0;
939
940 switch (bytes) {
941 case 2:
Charles Keepax8299ee82015-04-20 13:52:44 +0100942 val = le16_to_cpu(*((__le16 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100943 break;
944 case 4:
Charles Keepax8299ee82015-04-20 13:52:44 +0100945 val = le32_to_cpu(*((__le32 *)*pos));
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100946 break;
947 default:
948 break;
949 }
950
951 *pos += bytes;
952
953 return val;
954}
955
Charles Keepax23237362015-04-13 13:28:02 +0100956static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
957 struct wm_coeff_parsed_alg *blk)
958{
959 const struct wmfw_adsp_alg_data *raw;
960
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100961 switch (dsp->fw_ver) {
962 case 0:
963 case 1:
964 raw = (const struct wmfw_adsp_alg_data *)*data;
965 *data = raw->data;
Charles Keepax23237362015-04-13 13:28:02 +0100966
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100967 blk->id = le32_to_cpu(raw->id);
968 blk->name = raw->name;
969 blk->name_len = strlen(raw->name);
970 blk->ncoeff = le32_to_cpu(raw->ncoeff);
971 break;
972 default:
973 blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
974 blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
975 &blk->name);
976 wm_coeff_parse_string(sizeof(u16), data, NULL);
977 blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
978 break;
979 }
Charles Keepax23237362015-04-13 13:28:02 +0100980
981 adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
982 adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
983 adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
984}
985
986static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
987 struct wm_coeff_parsed_coeff *blk)
988{
989 const struct wmfw_adsp_coeff_data *raw;
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100990 const u8 *tmp;
991 int length;
Charles Keepax23237362015-04-13 13:28:02 +0100992
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100993 switch (dsp->fw_ver) {
994 case 0:
995 case 1:
996 raw = (const struct wmfw_adsp_coeff_data *)*data;
997 *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
Charles Keepax23237362015-04-13 13:28:02 +0100998
Charles Keepaxcb5b57a2015-04-13 13:28:04 +0100999 blk->offset = le16_to_cpu(raw->hdr.offset);
1000 blk->mem_type = le16_to_cpu(raw->hdr.type);
1001 blk->name = raw->name;
1002 blk->name_len = strlen(raw->name);
1003 blk->ctl_type = le16_to_cpu(raw->ctl_type);
1004 blk->flags = le16_to_cpu(raw->flags);
1005 blk->len = le32_to_cpu(raw->len);
1006 break;
1007 default:
1008 tmp = *data;
1009 blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
1010 blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
1011 length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
1012 blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
1013 &blk->name);
1014 wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
1015 wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
1016 blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
1017 blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
1018 blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
1019
1020 *data = *data + sizeof(raw->hdr) + length;
1021 break;
1022 }
Charles Keepax23237362015-04-13 13:28:02 +01001023
1024 adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
1025 adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
1026 adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
1027 adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
1028 adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
1029 adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
1030}
1031
1032static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
1033 const struct wmfw_region *region)
1034{
1035 struct wm_adsp_alg_region alg_region = {};
1036 struct wm_coeff_parsed_alg alg_blk;
1037 struct wm_coeff_parsed_coeff coeff_blk;
1038 const u8 *data = region->data;
1039 int i, ret;
1040
1041 wm_coeff_parse_alg(dsp, &data, &alg_blk);
1042 for (i = 0; i < alg_blk.ncoeff; i++) {
1043 wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
1044
1045 switch (coeff_blk.ctl_type) {
1046 case SNDRV_CTL_ELEM_TYPE_BYTES:
1047 break;
1048 default:
1049 adsp_err(dsp, "Unknown control type: %d\n",
1050 coeff_blk.ctl_type);
1051 return -EINVAL;
1052 }
1053
1054 alg_region.type = coeff_blk.mem_type;
1055 alg_region.alg = alg_blk.id;
1056
1057 ret = wm_adsp_create_control(dsp, &alg_region,
1058 coeff_blk.offset,
1059 coeff_blk.len,
1060 coeff_blk.name,
Charles Keepax26c22a12015-04-20 13:52:45 +01001061 coeff_blk.name_len,
1062 coeff_blk.flags);
Charles Keepax23237362015-04-13 13:28:02 +01001063 if (ret < 0)
1064 adsp_err(dsp, "Failed to create control: %.*s, %d\n",
1065 coeff_blk.name_len, coeff_blk.name, ret);
1066 }
1067
1068 return 0;
1069}
1070
Mark Brown2159ad92012-10-11 11:54:02 +09001071static int wm_adsp_load(struct wm_adsp *dsp)
1072{
Mark Browncf17c832013-01-30 14:37:23 +08001073 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001074 const struct firmware *firmware;
1075 struct regmap *regmap = dsp->regmap;
1076 unsigned int pos = 0;
1077 const struct wmfw_header *header;
1078 const struct wmfw_adsp1_sizes *adsp1_sizes;
1079 const struct wmfw_adsp2_sizes *adsp2_sizes;
1080 const struct wmfw_footer *footer;
1081 const struct wmfw_region *region;
1082 const struct wm_adsp_region *mem;
1083 const char *region_name;
1084 char *file, *text;
Mark Browncf17c832013-01-30 14:37:23 +08001085 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +09001086 unsigned int reg;
1087 int regions = 0;
1088 int ret, offset, type, sizes;
1089
1090 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1091 if (file == NULL)
1092 return -ENOMEM;
1093
Mark Brown1023dbd2013-01-11 22:58:28 +00001094 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
1095 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +09001096 file[PAGE_SIZE - 1] = '\0';
1097
1098 ret = request_firmware(&firmware, file, dsp->dev);
1099 if (ret != 0) {
1100 adsp_err(dsp, "Failed to request '%s'\n", file);
1101 goto out;
1102 }
1103 ret = -EINVAL;
1104
1105 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1106 if (pos >= firmware->size) {
1107 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1108 file, firmware->size);
1109 goto out_fw;
1110 }
1111
1112 header = (void*)&firmware->data[0];
1113
1114 if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
1115 adsp_err(dsp, "%s: invalid magic\n", file);
1116 goto out_fw;
1117 }
1118
Charles Keepax23237362015-04-13 13:28:02 +01001119 switch (header->ver) {
1120 case 0:
Charles Keepaxc61e59f2015-04-13 13:28:05 +01001121 adsp_warn(dsp, "%s: Depreciated file format %d\n",
1122 file, header->ver);
1123 break;
Charles Keepax23237362015-04-13 13:28:02 +01001124 case 1:
Charles Keepaxcb5b57a2015-04-13 13:28:04 +01001125 case 2:
Charles Keepax23237362015-04-13 13:28:02 +01001126 break;
1127 default:
Mark Brown2159ad92012-10-11 11:54:02 +09001128 adsp_err(dsp, "%s: unknown file format %d\n",
1129 file, header->ver);
1130 goto out_fw;
1131 }
Charles Keepax23237362015-04-13 13:28:02 +01001132
Dimitris Papastamos36269922013-11-01 15:56:57 +00001133 adsp_info(dsp, "Firmware version: %d\n", header->ver);
Charles Keepax23237362015-04-13 13:28:02 +01001134 dsp->fw_ver = header->ver;
Mark Brown2159ad92012-10-11 11:54:02 +09001135
1136 if (header->core != dsp->type) {
1137 adsp_err(dsp, "%s: invalid core %d != %d\n",
1138 file, header->core, dsp->type);
1139 goto out_fw;
1140 }
1141
1142 switch (dsp->type) {
1143 case WMFW_ADSP1:
1144 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1145 adsp1_sizes = (void *)&(header[1]);
1146 footer = (void *)&(adsp1_sizes[1]);
1147 sizes = sizeof(*adsp1_sizes);
1148
1149 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
1150 file, le32_to_cpu(adsp1_sizes->dm),
1151 le32_to_cpu(adsp1_sizes->pm),
1152 le32_to_cpu(adsp1_sizes->zm));
1153 break;
1154
1155 case WMFW_ADSP2:
1156 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
1157 adsp2_sizes = (void *)&(header[1]);
1158 footer = (void *)&(adsp2_sizes[1]);
1159 sizes = sizeof(*adsp2_sizes);
1160
1161 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
1162 file, le32_to_cpu(adsp2_sizes->xm),
1163 le32_to_cpu(adsp2_sizes->ym),
1164 le32_to_cpu(adsp2_sizes->pm),
1165 le32_to_cpu(adsp2_sizes->zm));
1166 break;
1167
1168 default:
Takashi Iwai6c452bd2013-11-05 18:40:00 +01001169 WARN(1, "Unknown DSP type");
Mark Brown2159ad92012-10-11 11:54:02 +09001170 goto out_fw;
1171 }
1172
1173 if (le32_to_cpu(header->len) != sizeof(*header) +
1174 sizes + sizeof(*footer)) {
1175 adsp_err(dsp, "%s: unexpected header length %d\n",
1176 file, le32_to_cpu(header->len));
1177 goto out_fw;
1178 }
1179
1180 adsp_dbg(dsp, "%s: timestamp %llu\n", file,
1181 le64_to_cpu(footer->timestamp));
1182
1183 while (pos < firmware->size &&
1184 pos - firmware->size > sizeof(*region)) {
1185 region = (void *)&(firmware->data[pos]);
1186 region_name = "Unknown";
1187 reg = 0;
1188 text = NULL;
1189 offset = le32_to_cpu(region->offset) & 0xffffff;
1190 type = be32_to_cpu(region->type) & 0xff;
1191 mem = wm_adsp_find_region(dsp, type);
1192
1193 switch (type) {
1194 case WMFW_NAME_TEXT:
1195 region_name = "Firmware name";
1196 text = kzalloc(le32_to_cpu(region->len) + 1,
1197 GFP_KERNEL);
1198 break;
Charles Keepax23237362015-04-13 13:28:02 +01001199 case WMFW_ALGORITHM_DATA:
1200 region_name = "Algorithm";
1201 ret = wm_adsp_parse_coeff(dsp, region);
1202 if (ret != 0)
1203 goto out_fw;
1204 break;
Mark Brown2159ad92012-10-11 11:54:02 +09001205 case WMFW_INFO_TEXT:
1206 region_name = "Information";
1207 text = kzalloc(le32_to_cpu(region->len) + 1,
1208 GFP_KERNEL);
1209 break;
1210 case WMFW_ABSOLUTE:
1211 region_name = "Absolute";
1212 reg = offset;
1213 break;
1214 case WMFW_ADSP1_PM:
Mark Brown2159ad92012-10-11 11:54:02 +09001215 region_name = "PM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001216 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001217 break;
1218 case WMFW_ADSP1_DM:
Mark Brown2159ad92012-10-11 11:54:02 +09001219 region_name = "DM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001220 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001221 break;
1222 case WMFW_ADSP2_XM:
Mark Brown2159ad92012-10-11 11:54:02 +09001223 region_name = "XM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001224 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001225 break;
1226 case WMFW_ADSP2_YM:
Mark Brown2159ad92012-10-11 11:54:02 +09001227 region_name = "YM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001228 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001229 break;
1230 case WMFW_ADSP1_ZM:
Mark Brown2159ad92012-10-11 11:54:02 +09001231 region_name = "ZM";
Mark Brown45b9ee72013-01-08 16:02:06 +00001232 reg = wm_adsp_region_to_reg(mem, offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001233 break;
1234 default:
1235 adsp_warn(dsp,
1236 "%s.%d: Unknown region type %x at %d(%x)\n",
1237 file, regions, type, pos, pos);
1238 break;
1239 }
1240
1241 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
1242 regions, le32_to_cpu(region->len), offset,
1243 region_name);
1244
1245 if (text) {
1246 memcpy(text, region->data, le32_to_cpu(region->len));
1247 adsp_info(dsp, "%s: %s\n", file, text);
1248 kfree(text);
1249 }
1250
1251 if (reg) {
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001252 buf = wm_adsp_buf_alloc(region->data,
1253 le32_to_cpu(region->len),
1254 &buf_list);
1255 if (!buf) {
1256 adsp_err(dsp, "Out of memory\n");
1257 ret = -ENOMEM;
1258 goto out_fw;
1259 }
Mark Browna76fefa2013-01-07 19:03:17 +00001260
Charles Keepaxcdcd7f72014-11-14 15:40:45 +00001261 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1262 le32_to_cpu(region->len));
1263 if (ret != 0) {
1264 adsp_err(dsp,
1265 "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
1266 file, regions,
1267 le32_to_cpu(region->len), offset,
1268 region_name, ret);
1269 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +09001270 }
1271 }
1272
1273 pos += le32_to_cpu(region->len) + sizeof(*region);
1274 regions++;
1275 }
Mark Browncf17c832013-01-30 14:37:23 +08001276
1277 ret = regmap_async_complete(regmap);
1278 if (ret != 0) {
1279 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1280 goto out_fw;
1281 }
1282
Mark Brown2159ad92012-10-11 11:54:02 +09001283 if (pos > firmware->size)
1284 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1285 file, regions, pos - firmware->size);
1286
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001287 wm_adsp_debugfs_save_wmfwname(dsp, file);
1288
Mark Brown2159ad92012-10-11 11:54:02 +09001289out_fw:
Mark Browncf17c832013-01-30 14:37:23 +08001290 regmap_async_complete(regmap);
1291 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001292 release_firmware(firmware);
1293out:
1294 kfree(file);
1295
1296 return ret;
1297}
1298
Charles Keepax23237362015-04-13 13:28:02 +01001299static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
1300 const struct wm_adsp_alg_region *alg_region)
1301{
1302 struct wm_coeff_ctl *ctl;
1303
1304 list_for_each_entry(ctl, &dsp->ctl_list, list) {
1305 if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
1306 alg_region->alg == ctl->alg_region.alg &&
1307 alg_region->type == ctl->alg_region.type) {
1308 ctl->alg_region.base = alg_region->base;
1309 }
1310 }
1311}
1312
Charles Keepax3809f002015-04-13 13:27:54 +01001313static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
Charles Keepaxb618a1852015-04-13 13:27:53 +01001314 unsigned int pos, unsigned int len)
Mark Browndb405172012-10-26 19:30:40 +01001315{
Charles Keepaxb618a1852015-04-13 13:27:53 +01001316 void *alg;
1317 int ret;
Mark Browndb405172012-10-26 19:30:40 +01001318 __be32 val;
Mark Browndb405172012-10-26 19:30:40 +01001319
Charles Keepax3809f002015-04-13 13:27:54 +01001320 if (n_algs == 0) {
Mark Browndb405172012-10-26 19:30:40 +01001321 adsp_err(dsp, "No algorithms\n");
Charles Keepaxb618a1852015-04-13 13:27:53 +01001322 return ERR_PTR(-EINVAL);
Mark Browndb405172012-10-26 19:30:40 +01001323 }
1324
Charles Keepax3809f002015-04-13 13:27:54 +01001325 if (n_algs > 1024) {
1326 adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001327 return ERR_PTR(-EINVAL);
Mark Brownd62f4bc2012-12-19 14:00:30 +00001328 }
1329
Mark Browndb405172012-10-26 19:30:40 +01001330 /* Read the terminator first to validate the length */
Charles Keepaxb618a1852015-04-13 13:27:53 +01001331 ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
Mark Browndb405172012-10-26 19:30:40 +01001332 if (ret != 0) {
1333 adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1334 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001335 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001336 }
1337
1338 if (be32_to_cpu(val) != 0xbedead)
1339 adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
Charles Keepaxb618a1852015-04-13 13:27:53 +01001340 pos + len, be32_to_cpu(val));
Mark Browndb405172012-10-26 19:30:40 +01001341
Charles Keepaxb618a1852015-04-13 13:27:53 +01001342 alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
Mark Browndb405172012-10-26 19:30:40 +01001343 if (!alg)
Charles Keepaxb618a1852015-04-13 13:27:53 +01001344 return ERR_PTR(-ENOMEM);
Mark Browndb405172012-10-26 19:30:40 +01001345
Charles Keepaxb618a1852015-04-13 13:27:53 +01001346 ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
Mark Browndb405172012-10-26 19:30:40 +01001347 if (ret != 0) {
1348 adsp_err(dsp, "Failed to read algorithm list: %d\n",
1349 ret);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001350 kfree(alg);
1351 return ERR_PTR(ret);
Mark Browndb405172012-10-26 19:30:40 +01001352 }
1353
Charles Keepaxb618a1852015-04-13 13:27:53 +01001354 return alg;
1355}
1356
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001357static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
1358 int type, __be32 id,
1359 __be32 base)
1360{
1361 struct wm_adsp_alg_region *alg_region;
1362
1363 alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1364 if (!alg_region)
1365 return ERR_PTR(-ENOMEM);
1366
1367 alg_region->type = type;
1368 alg_region->alg = be32_to_cpu(id);
1369 alg_region->base = be32_to_cpu(base);
1370
1371 list_add_tail(&alg_region->list, &dsp->alg_regions);
1372
Charles Keepax23237362015-04-13 13:28:02 +01001373 if (dsp->fw_ver > 0)
1374 wm_adsp_ctl_fixup_base(dsp, alg_region);
1375
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001376 return alg_region;
1377}
1378
Charles Keepaxb618a1852015-04-13 13:27:53 +01001379static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
1380{
1381 struct wmfw_adsp1_id_hdr adsp1_id;
1382 struct wmfw_adsp1_alg_hdr *adsp1_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001383 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001384 const struct wm_adsp_region *mem;
1385 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001386 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001387 int i, ret;
1388
1389 mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
1390 if (WARN_ON(!mem))
1391 return -EINVAL;
1392
1393 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
1394 sizeof(adsp1_id));
1395 if (ret != 0) {
1396 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1397 ret);
1398 return ret;
1399 }
1400
Charles Keepax3809f002015-04-13 13:27:54 +01001401 n_algs = be32_to_cpu(adsp1_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001402 dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
1403 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1404 dsp->fw_id,
1405 (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
1406 (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
1407 be32_to_cpu(adsp1_id.fw.ver) & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001408 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001409
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001410 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1411 adsp1_id.fw.id, adsp1_id.zm);
1412 if (IS_ERR(alg_region))
1413 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001414
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001415 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1416 adsp1_id.fw.id, adsp1_id.dm);
1417 if (IS_ERR(alg_region))
1418 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001419
1420 pos = sizeof(adsp1_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001421 len = (sizeof(*adsp1_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001422
Charles Keepax3809f002015-04-13 13:27:54 +01001423 adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001424 if (IS_ERR(adsp1_alg))
1425 return PTR_ERR(adsp1_alg);
Mark Browndb405172012-10-26 19:30:40 +01001426
Charles Keepax3809f002015-04-13 13:27:54 +01001427 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001428 adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
1429 i, be32_to_cpu(adsp1_alg[i].alg.id),
1430 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
1431 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
1432 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
1433 be32_to_cpu(adsp1_alg[i].dm),
1434 be32_to_cpu(adsp1_alg[i].zm));
Mark Brown471f4882013-01-08 16:09:31 +00001435
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001436 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1437 adsp1_alg[i].alg.id,
1438 adsp1_alg[i].dm);
1439 if (IS_ERR(alg_region)) {
1440 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001441 goto out;
1442 }
Charles Keepax23237362015-04-13 13:28:02 +01001443 if (dsp->fw_ver == 0) {
1444 if (i + 1 < n_algs) {
1445 len = be32_to_cpu(adsp1_alg[i + 1].dm);
1446 len -= be32_to_cpu(adsp1_alg[i].dm);
1447 len *= 4;
1448 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001449 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001450 } else {
1451 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
1452 be32_to_cpu(adsp1_alg[i].alg.id));
1453 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001454 }
Mark Brown471f4882013-01-08 16:09:31 +00001455
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001456 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1457 adsp1_alg[i].alg.id,
1458 adsp1_alg[i].zm);
1459 if (IS_ERR(alg_region)) {
1460 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001461 goto out;
1462 }
Charles Keepax23237362015-04-13 13:28:02 +01001463 if (dsp->fw_ver == 0) {
1464 if (i + 1 < n_algs) {
1465 len = be32_to_cpu(adsp1_alg[i + 1].zm);
1466 len -= be32_to_cpu(adsp1_alg[i].zm);
1467 len *= 4;
1468 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001469 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001470 } else {
1471 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1472 be32_to_cpu(adsp1_alg[i].alg.id));
1473 }
Mark Browndb405172012-10-26 19:30:40 +01001474 }
1475 }
1476
1477out:
Charles Keepaxb618a1852015-04-13 13:27:53 +01001478 kfree(adsp1_alg);
1479 return ret;
1480}
1481
1482static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
1483{
1484 struct wmfw_adsp2_id_hdr adsp2_id;
1485 struct wmfw_adsp2_alg_hdr *adsp2_alg;
Charles Keepax3809f002015-04-13 13:27:54 +01001486 struct wm_adsp_alg_region *alg_region;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001487 const struct wm_adsp_region *mem;
1488 unsigned int pos, len;
Charles Keepax3809f002015-04-13 13:27:54 +01001489 size_t n_algs;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001490 int i, ret;
1491
1492 mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
1493 if (WARN_ON(!mem))
1494 return -EINVAL;
1495
1496 ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
1497 sizeof(adsp2_id));
1498 if (ret != 0) {
1499 adsp_err(dsp, "Failed to read algorithm info: %d\n",
1500 ret);
1501 return ret;
1502 }
1503
Charles Keepax3809f002015-04-13 13:27:54 +01001504 n_algs = be32_to_cpu(adsp2_id.n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001505 dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001506 dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001507 adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1508 dsp->fw_id,
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001509 (dsp->fw_id_version & 0xff0000) >> 16,
1510 (dsp->fw_id_version & 0xff00) >> 8,
1511 dsp->fw_id_version & 0xff,
Charles Keepax3809f002015-04-13 13:27:54 +01001512 n_algs);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001513
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001514 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1515 adsp2_id.fw.id, adsp2_id.xm);
1516 if (IS_ERR(alg_region))
1517 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001518
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001519 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1520 adsp2_id.fw.id, adsp2_id.ym);
1521 if (IS_ERR(alg_region))
1522 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001523
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001524 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1525 adsp2_id.fw.id, adsp2_id.zm);
1526 if (IS_ERR(alg_region))
1527 return PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001528
1529 pos = sizeof(adsp2_id) / 2;
Charles Keepax3809f002015-04-13 13:27:54 +01001530 len = (sizeof(*adsp2_alg) * n_algs) / 2;
Charles Keepaxb618a1852015-04-13 13:27:53 +01001531
Charles Keepax3809f002015-04-13 13:27:54 +01001532 adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001533 if (IS_ERR(adsp2_alg))
1534 return PTR_ERR(adsp2_alg);
1535
Charles Keepax3809f002015-04-13 13:27:54 +01001536 for (i = 0; i < n_algs; i++) {
Charles Keepaxb618a1852015-04-13 13:27:53 +01001537 adsp_info(dsp,
1538 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1539 i, be32_to_cpu(adsp2_alg[i].alg.id),
1540 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1541 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1542 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1543 be32_to_cpu(adsp2_alg[i].xm),
1544 be32_to_cpu(adsp2_alg[i].ym),
1545 be32_to_cpu(adsp2_alg[i].zm));
1546
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001547 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1548 adsp2_alg[i].alg.id,
1549 adsp2_alg[i].xm);
1550 if (IS_ERR(alg_region)) {
1551 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001552 goto out;
1553 }
Charles Keepax23237362015-04-13 13:28:02 +01001554 if (dsp->fw_ver == 0) {
1555 if (i + 1 < n_algs) {
1556 len = be32_to_cpu(adsp2_alg[i + 1].xm);
1557 len -= be32_to_cpu(adsp2_alg[i].xm);
1558 len *= 4;
1559 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001560 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001561 } else {
1562 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
1563 be32_to_cpu(adsp2_alg[i].alg.id));
1564 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001565 }
1566
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001567 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1568 adsp2_alg[i].alg.id,
1569 adsp2_alg[i].ym);
1570 if (IS_ERR(alg_region)) {
1571 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001572 goto out;
1573 }
Charles Keepax23237362015-04-13 13:28:02 +01001574 if (dsp->fw_ver == 0) {
1575 if (i + 1 < n_algs) {
1576 len = be32_to_cpu(adsp2_alg[i + 1].ym);
1577 len -= be32_to_cpu(adsp2_alg[i].ym);
1578 len *= 4;
1579 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001580 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001581 } else {
1582 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
1583 be32_to_cpu(adsp2_alg[i].alg.id));
1584 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001585 }
1586
Charles Keepaxd9d20e12015-04-13 13:27:59 +01001587 alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1588 adsp2_alg[i].alg.id,
1589 adsp2_alg[i].zm);
1590 if (IS_ERR(alg_region)) {
1591 ret = PTR_ERR(alg_region);
Charles Keepaxb618a1852015-04-13 13:27:53 +01001592 goto out;
1593 }
Charles Keepax23237362015-04-13 13:28:02 +01001594 if (dsp->fw_ver == 0) {
1595 if (i + 1 < n_algs) {
1596 len = be32_to_cpu(adsp2_alg[i + 1].zm);
1597 len -= be32_to_cpu(adsp2_alg[i].zm);
1598 len *= 4;
1599 wm_adsp_create_control(dsp, alg_region, 0,
Charles Keepax26c22a12015-04-20 13:52:45 +01001600 len, NULL, 0, 0);
Charles Keepax23237362015-04-13 13:28:02 +01001601 } else {
1602 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1603 be32_to_cpu(adsp2_alg[i].alg.id));
1604 }
Charles Keepaxb618a1852015-04-13 13:27:53 +01001605 }
1606 }
1607
1608out:
1609 kfree(adsp2_alg);
Mark Browndb405172012-10-26 19:30:40 +01001610 return ret;
1611}
1612
Mark Brown2159ad92012-10-11 11:54:02 +09001613static int wm_adsp_load_coeff(struct wm_adsp *dsp)
1614{
Mark Browncf17c832013-01-30 14:37:23 +08001615 LIST_HEAD(buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001616 struct regmap *regmap = dsp->regmap;
1617 struct wmfw_coeff_hdr *hdr;
1618 struct wmfw_coeff_item *blk;
1619 const struct firmware *firmware;
Mark Brown471f4882013-01-08 16:09:31 +00001620 const struct wm_adsp_region *mem;
1621 struct wm_adsp_alg_region *alg_region;
Mark Brown2159ad92012-10-11 11:54:02 +09001622 const char *region_name;
1623 int ret, pos, blocks, type, offset, reg;
1624 char *file;
Mark Browncf17c832013-01-30 14:37:23 +08001625 struct wm_adsp_buf *buf;
Mark Brown2159ad92012-10-11 11:54:02 +09001626
1627 file = kzalloc(PAGE_SIZE, GFP_KERNEL);
1628 if (file == NULL)
1629 return -ENOMEM;
1630
Mark Brown1023dbd2013-01-11 22:58:28 +00001631 snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
1632 wm_adsp_fw[dsp->fw].file);
Mark Brown2159ad92012-10-11 11:54:02 +09001633 file[PAGE_SIZE - 1] = '\0';
1634
1635 ret = request_firmware(&firmware, file, dsp->dev);
1636 if (ret != 0) {
1637 adsp_warn(dsp, "Failed to request '%s'\n", file);
1638 ret = 0;
1639 goto out;
1640 }
1641 ret = -EINVAL;
1642
1643 if (sizeof(*hdr) >= firmware->size) {
1644 adsp_err(dsp, "%s: file too short, %zu bytes\n",
1645 file, firmware->size);
1646 goto out_fw;
1647 }
1648
1649 hdr = (void*)&firmware->data[0];
1650 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
1651 adsp_err(dsp, "%s: invalid magic\n", file);
Charles Keepaxa4cdbec2013-01-21 09:02:31 +00001652 goto out_fw;
Mark Brown2159ad92012-10-11 11:54:02 +09001653 }
1654
Mark Brownc7123262013-01-16 16:59:04 +09001655 switch (be32_to_cpu(hdr->rev) & 0xff) {
1656 case 1:
1657 break;
1658 default:
1659 adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
1660 file, be32_to_cpu(hdr->rev) & 0xff);
1661 ret = -EINVAL;
1662 goto out_fw;
1663 }
1664
Mark Brown2159ad92012-10-11 11:54:02 +09001665 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
1666 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
1667 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
1668 le32_to_cpu(hdr->ver) & 0xff);
1669
1670 pos = le32_to_cpu(hdr->len);
1671
1672 blocks = 0;
1673 while (pos < firmware->size &&
1674 pos - firmware->size > sizeof(*blk)) {
1675 blk = (void*)(&firmware->data[pos]);
1676
Mark Brownc7123262013-01-16 16:59:04 +09001677 type = le16_to_cpu(blk->type);
1678 offset = le16_to_cpu(blk->offset);
Mark Brown2159ad92012-10-11 11:54:02 +09001679
1680 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
1681 file, blocks, le32_to_cpu(blk->id),
1682 (le32_to_cpu(blk->ver) >> 16) & 0xff,
1683 (le32_to_cpu(blk->ver) >> 8) & 0xff,
1684 le32_to_cpu(blk->ver) & 0xff);
1685 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
1686 file, blocks, le32_to_cpu(blk->len), offset, type);
1687
1688 reg = 0;
1689 region_name = "Unknown";
1690 switch (type) {
Mark Brownc7123262013-01-16 16:59:04 +09001691 case (WMFW_NAME_TEXT << 8):
1692 case (WMFW_INFO_TEXT << 8):
Mark Brown2159ad92012-10-11 11:54:02 +09001693 break;
Mark Brownc7123262013-01-16 16:59:04 +09001694 case (WMFW_ABSOLUTE << 8):
Mark Brownf395a212013-03-05 22:39:54 +08001695 /*
1696 * Old files may use this for global
1697 * coefficients.
1698 */
1699 if (le32_to_cpu(blk->id) == dsp->fw_id &&
1700 offset == 0) {
1701 region_name = "global coefficients";
1702 mem = wm_adsp_find_region(dsp, type);
1703 if (!mem) {
1704 adsp_err(dsp, "No ZM\n");
1705 break;
1706 }
1707 reg = wm_adsp_region_to_reg(mem, 0);
1708
1709 } else {
1710 region_name = "register";
1711 reg = offset;
1712 }
Mark Brown2159ad92012-10-11 11:54:02 +09001713 break;
Mark Brown471f4882013-01-08 16:09:31 +00001714
1715 case WMFW_ADSP1_DM:
1716 case WMFW_ADSP1_ZM:
1717 case WMFW_ADSP2_XM:
1718 case WMFW_ADSP2_YM:
1719 adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
1720 file, blocks, le32_to_cpu(blk->len),
1721 type, le32_to_cpu(blk->id));
1722
1723 mem = wm_adsp_find_region(dsp, type);
1724 if (!mem) {
1725 adsp_err(dsp, "No base for region %x\n", type);
1726 break;
1727 }
1728
1729 reg = 0;
1730 list_for_each_entry(alg_region,
1731 &dsp->alg_regions, list) {
1732 if (le32_to_cpu(blk->id) == alg_region->alg &&
1733 type == alg_region->type) {
Mark Brown338c5182013-01-24 00:35:48 +08001734 reg = alg_region->base;
Mark Brown471f4882013-01-08 16:09:31 +00001735 reg = wm_adsp_region_to_reg(mem,
1736 reg);
Mark Brown338c5182013-01-24 00:35:48 +08001737 reg += offset;
Charles Keepaxd733dc02013-11-28 16:37:51 +00001738 break;
Mark Brown471f4882013-01-08 16:09:31 +00001739 }
1740 }
1741
1742 if (reg == 0)
1743 adsp_err(dsp, "No %x for algorithm %x\n",
1744 type, le32_to_cpu(blk->id));
1745 break;
1746
Mark Brown2159ad92012-10-11 11:54:02 +09001747 default:
Mark Brown25c62f7e2013-01-20 19:02:19 +09001748 adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
1749 file, blocks, type, pos);
Mark Brown2159ad92012-10-11 11:54:02 +09001750 break;
1751 }
1752
1753 if (reg) {
Mark Browncf17c832013-01-30 14:37:23 +08001754 buf = wm_adsp_buf_alloc(blk->data,
1755 le32_to_cpu(blk->len),
1756 &buf_list);
Mark Browna76fefa2013-01-07 19:03:17 +00001757 if (!buf) {
1758 adsp_err(dsp, "Out of memory\n");
Wei Yongjunf4b82812013-03-12 00:23:15 +08001759 ret = -ENOMEM;
1760 goto out_fw;
Mark Browna76fefa2013-01-07 19:03:17 +00001761 }
1762
Mark Brown20da6d52013-01-12 19:58:17 +00001763 adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
1764 file, blocks, le32_to_cpu(blk->len),
1765 reg);
Mark Browncf17c832013-01-30 14:37:23 +08001766 ret = regmap_raw_write_async(regmap, reg, buf->buf,
1767 le32_to_cpu(blk->len));
Mark Brown2159ad92012-10-11 11:54:02 +09001768 if (ret != 0) {
1769 adsp_err(dsp,
Dimitris Papastamos43bc3bf2013-11-01 15:56:52 +00001770 "%s.%d: Failed to write to %x in %s: %d\n",
1771 file, blocks, reg, region_name, ret);
Mark Brown2159ad92012-10-11 11:54:02 +09001772 }
1773 }
1774
Charles Keepaxbe951012015-02-16 15:25:49 +00001775 pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
Mark Brown2159ad92012-10-11 11:54:02 +09001776 blocks++;
1777 }
1778
Mark Browncf17c832013-01-30 14:37:23 +08001779 ret = regmap_async_complete(regmap);
1780 if (ret != 0)
1781 adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1782
Mark Brown2159ad92012-10-11 11:54:02 +09001783 if (pos > firmware->size)
1784 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
1785 file, blocks, pos - firmware->size);
1786
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001787 wm_adsp_debugfs_save_binname(dsp, file);
1788
Mark Brown2159ad92012-10-11 11:54:02 +09001789out_fw:
Charles Keepax9da7a5a2014-11-17 10:48:21 +00001790 regmap_async_complete(regmap);
Mark Brown2159ad92012-10-11 11:54:02 +09001791 release_firmware(firmware);
Mark Browncf17c832013-01-30 14:37:23 +08001792 wm_adsp_buf_free(&buf_list);
Mark Brown2159ad92012-10-11 11:54:02 +09001793out:
1794 kfree(file);
Wei Yongjunf4b82812013-03-12 00:23:15 +08001795 return ret;
Mark Brown2159ad92012-10-11 11:54:02 +09001796}
1797
Charles Keepax3809f002015-04-13 13:27:54 +01001798int wm_adsp1_init(struct wm_adsp *dsp)
Mark Brown5e7a7a22013-01-16 10:03:56 +09001799{
Charles Keepax3809f002015-04-13 13:27:54 +01001800 INIT_LIST_HEAD(&dsp->alg_regions);
Mark Brown5e7a7a22013-01-16 10:03:56 +09001801
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01001802#ifdef CONFIG_DEBUG_FS
1803 mutex_init(&dsp->debugfs_lock);
1804#endif
Mark Brown5e7a7a22013-01-16 10:03:56 +09001805 return 0;
1806}
1807EXPORT_SYMBOL_GPL(wm_adsp1_init);
1808
Mark Brown2159ad92012-10-11 11:54:02 +09001809int wm_adsp1_event(struct snd_soc_dapm_widget *w,
1810 struct snd_kcontrol *kcontrol,
1811 int event)
1812{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01001813 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad92012-10-11 11:54:02 +09001814 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1815 struct wm_adsp *dsp = &dsps[w->shift];
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00001816 struct wm_adsp_alg_region *alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001817 struct wm_coeff_ctl *ctl;
Mark Brown2159ad92012-10-11 11:54:02 +09001818 int ret;
Chris Rattray94e205b2013-01-18 08:43:09 +00001819 int val;
Mark Brown2159ad92012-10-11 11:54:02 +09001820
Lars-Peter Clausen00200102014-07-17 22:01:07 +02001821 dsp->card = codec->component.card;
Dimitris Papastamos92bb4c32013-08-01 11:11:28 +01001822
Mark Brown2159ad92012-10-11 11:54:02 +09001823 switch (event) {
1824 case SND_SOC_DAPM_POST_PMU:
1825 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1826 ADSP1_SYS_ENA, ADSP1_SYS_ENA);
1827
Chris Rattray94e205b2013-01-18 08:43:09 +00001828 /*
1829 * For simplicity set the DSP clock rate to be the
1830 * SYSCLK rate rather than making it configurable.
1831 */
1832 if(dsp->sysclk_reg) {
1833 ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
1834 if (ret != 0) {
1835 adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
1836 ret);
1837 return ret;
1838 }
1839
1840 val = (val & dsp->sysclk_mask)
1841 >> dsp->sysclk_shift;
1842
1843 ret = regmap_update_bits(dsp->regmap,
1844 dsp->base + ADSP1_CONTROL_31,
1845 ADSP1_CLK_SEL_MASK, val);
1846 if (ret != 0) {
1847 adsp_err(dsp, "Failed to set clock rate: %d\n",
1848 ret);
1849 return ret;
1850 }
1851 }
1852
Mark Brown2159ad92012-10-11 11:54:02 +09001853 ret = wm_adsp_load(dsp);
1854 if (ret != 0)
1855 goto err;
1856
Charles Keepaxb618a1852015-04-13 13:27:53 +01001857 ret = wm_adsp1_setup_algs(dsp);
Mark Browndb405172012-10-26 19:30:40 +01001858 if (ret != 0)
1859 goto err;
1860
Mark Brown2159ad92012-10-11 11:54:02 +09001861 ret = wm_adsp_load_coeff(dsp);
1862 if (ret != 0)
1863 goto err;
1864
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01001865 /* Initialize caches for enabled and unset controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001866 ret = wm_coeff_init_control_caches(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001867 if (ret != 0)
1868 goto err;
1869
Dimitris Papastamos0c2e3f32013-05-28 12:01:50 +01001870 /* Sync set controls */
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001871 ret = wm_coeff_sync_controls(dsp);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001872 if (ret != 0)
1873 goto err;
1874
Mark Brown2159ad92012-10-11 11:54:02 +09001875 /* Start the core running */
1876 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1877 ADSP1_CORE_ENA | ADSP1_START,
1878 ADSP1_CORE_ENA | ADSP1_START);
1879 break;
1880
1881 case SND_SOC_DAPM_PRE_PMD:
1882 /* Halt the core */
1883 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1884 ADSP1_CORE_ENA | ADSP1_START, 0);
1885
1886 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
1887 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
1888
1889 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1890 ADSP1_SYS_ENA, 0);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001891
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01001892 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01001893 ctl->enabled = 0;
Dimitris Papastamosb0101b42013-11-01 15:56:56 +00001894
1895 while (!list_empty(&dsp->alg_regions)) {
1896 alg_region = list_first_entry(&dsp->alg_regions,
1897 struct wm_adsp_alg_region,
1898 list);
1899 list_del(&alg_region->list);
1900 kfree(alg_region);
1901 }
Mark Brown2159ad92012-10-11 11:54:02 +09001902 break;
1903
1904 default:
1905 break;
1906 }
1907
1908 return 0;
1909
1910err:
1911 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1912 ADSP1_SYS_ENA, 0);
1913 return ret;
1914}
1915EXPORT_SYMBOL_GPL(wm_adsp1_event);
1916
1917static int wm_adsp2_ena(struct wm_adsp *dsp)
1918{
1919 unsigned int val;
1920 int ret, count;
1921
Mark Brown1552c322013-11-28 18:11:38 +00001922 ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
1923 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
Mark Brown2159ad92012-10-11 11:54:02 +09001924 if (ret != 0)
1925 return ret;
1926
1927 /* Wait for the RAM to start, should be near instantaneous */
Charles Keepax939fd1e2013-12-18 09:25:49 +00001928 for (count = 0; count < 10; ++count) {
Mark Brown2159ad92012-10-11 11:54:02 +09001929 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
1930 &val);
1931 if (ret != 0)
1932 return ret;
Charles Keepax939fd1e2013-12-18 09:25:49 +00001933
1934 if (val & ADSP2_RAM_RDY)
1935 break;
1936
1937 msleep(1);
1938 }
Mark Brown2159ad92012-10-11 11:54:02 +09001939
1940 if (!(val & ADSP2_RAM_RDY)) {
1941 adsp_err(dsp, "Failed to start DSP RAM\n");
1942 return -EBUSY;
1943 }
1944
1945 adsp_dbg(dsp, "RAM ready after %d polls\n", count);
Mark Brown2159ad92012-10-11 11:54:02 +09001946
1947 return 0;
1948}
1949
Charles Keepax18b1a902014-01-09 09:06:54 +00001950static void wm_adsp2_boot_work(struct work_struct *work)
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001951{
1952 struct wm_adsp *dsp = container_of(work,
1953 struct wm_adsp,
1954 boot_work);
1955 int ret;
1956 unsigned int val;
1957
1958 /*
1959 * For simplicity set the DSP clock rate to be the
1960 * SYSCLK rate rather than making it configurable.
1961 */
1962 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1963 if (ret != 0) {
1964 adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
1965 return;
1966 }
1967 val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1968 >> ARIZONA_SYSCLK_FREQ_SHIFT;
1969
1970 ret = regmap_update_bits_async(dsp->regmap,
1971 dsp->base + ADSP2_CLOCKING,
1972 ADSP2_CLK_SEL_MASK, val);
1973 if (ret != 0) {
1974 adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
1975 return;
1976 }
1977
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001978 ret = wm_adsp2_ena(dsp);
1979 if (ret != 0)
1980 return;
1981
1982 ret = wm_adsp_load(dsp);
1983 if (ret != 0)
1984 goto err;
1985
Charles Keepaxb618a1852015-04-13 13:27:53 +01001986 ret = wm_adsp2_setup_algs(dsp);
Charles Keepaxd8a64d62014-01-08 17:42:18 +00001987 if (ret != 0)
1988 goto err;
1989
1990 ret = wm_adsp_load_coeff(dsp);
1991 if (ret != 0)
1992 goto err;
1993
1994 /* Initialize caches for enabled and unset controls */
1995 ret = wm_coeff_init_control_caches(dsp);
1996 if (ret != 0)
1997 goto err;
1998
1999 /* Sync set controls */
2000 ret = wm_coeff_sync_controls(dsp);
2001 if (ret != 0)
2002 goto err;
2003
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002004 dsp->running = true;
2005
2006 return;
2007
2008err:
2009 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
2010 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
2011}
2012
Charles Keepax12db5ed2014-01-08 17:42:19 +00002013int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
2014 struct snd_kcontrol *kcontrol, int event)
2015{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01002016 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Charles Keepax12db5ed2014-01-08 17:42:19 +00002017 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
2018 struct wm_adsp *dsp = &dsps[w->shift];
2019
Lars-Peter Clausen00200102014-07-17 22:01:07 +02002020 dsp->card = codec->component.card;
Charles Keepax12db5ed2014-01-08 17:42:19 +00002021
2022 switch (event) {
2023 case SND_SOC_DAPM_PRE_PMU:
2024 queue_work(system_unbound_wq, &dsp->boot_work);
2025 break;
2026 default:
2027 break;
Charles Keepaxcab27252014-04-17 13:42:54 +01002028 }
Charles Keepax12db5ed2014-01-08 17:42:19 +00002029
2030 return 0;
2031}
2032EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
2033
Mark Brown2159ad92012-10-11 11:54:02 +09002034int wm_adsp2_event(struct snd_soc_dapm_widget *w,
2035 struct snd_kcontrol *kcontrol, int event)
2036{
Lars-Peter Clausen72718512015-01-13 10:27:34 +01002037 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
Mark Brown2159ad92012-10-11 11:54:02 +09002038 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
2039 struct wm_adsp *dsp = &dsps[w->shift];
Mark Brown471f4882013-01-08 16:09:31 +00002040 struct wm_adsp_alg_region *alg_region;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002041 struct wm_coeff_ctl *ctl;
Mark Brown2159ad92012-10-11 11:54:02 +09002042 int ret;
2043
2044 switch (event) {
2045 case SND_SOC_DAPM_POST_PMU:
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002046 flush_work(&dsp->boot_work);
Mark Browndd49e2c2012-12-02 21:50:46 +09002047
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002048 if (!dsp->running)
2049 return -EIO;
Mark Browndd49e2c2012-12-02 21:50:46 +09002050
Charles Keepaxd8a64d62014-01-08 17:42:18 +00002051 ret = regmap_update_bits(dsp->regmap,
2052 dsp->base + ADSP2_CONTROL,
Charles Keepax00e4c3b2014-11-18 16:25:27 +00002053 ADSP2_CORE_ENA | ADSP2_START,
2054 ADSP2_CORE_ENA | ADSP2_START);
Mark Brown2159ad92012-10-11 11:54:02 +09002055 if (ret != 0)
2056 goto err;
Mark Brown2159ad92012-10-11 11:54:02 +09002057 break;
2058
2059 case SND_SOC_DAPM_PRE_PMD:
Richard Fitzgerald10337b02015-05-29 10:23:07 +01002060 /* Log firmware state, it can be useful for analysis */
2061 wm_adsp2_show_fw_status(dsp);
2062
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002063 wm_adsp_debugfs_clear(dsp);
2064
2065 dsp->fw_id = 0;
2066 dsp->fw_id_version = 0;
Mark Brown1023dbd2013-01-11 22:58:28 +00002067 dsp->running = false;
2068
Mark Brown2159ad92012-10-11 11:54:02 +09002069 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00002070 ADSP2_SYS_ENA | ADSP2_CORE_ENA |
2071 ADSP2_START, 0);
Mark Brown973838a2012-11-28 17:20:32 +00002072
Mark Brown2d30b572013-01-28 20:18:17 +08002073 /* Make sure DMAs are quiesced */
2074 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
2075 regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
2076 regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
2077
Dimitris Papastamos81ad93e2013-07-29 13:51:59 +01002078 list_for_each_entry(ctl, &dsp->ctl_list, list)
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002079 ctl->enabled = 0;
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002080
Mark Brown471f4882013-01-08 16:09:31 +00002081 while (!list_empty(&dsp->alg_regions)) {
2082 alg_region = list_first_entry(&dsp->alg_regions,
2083 struct wm_adsp_alg_region,
2084 list);
2085 list_del(&alg_region->list);
2086 kfree(alg_region);
2087 }
Charles Keepaxddbc5ef2014-01-22 10:09:11 +00002088
2089 adsp_dbg(dsp, "Shutdown complete\n");
Mark Brown2159ad92012-10-11 11:54:02 +09002090 break;
2091
2092 default:
2093 break;
2094 }
2095
2096 return 0;
2097err:
2098 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Browna7f9be72012-11-28 19:53:59 +00002099 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
Mark Brown2159ad92012-10-11 11:54:02 +09002100 return ret;
2101}
2102EXPORT_SYMBOL_GPL(wm_adsp2_event);
Mark Brown973838a2012-11-28 17:20:32 +00002103
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002104int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
2105{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002106 wm_adsp2_init_debugfs(dsp, codec);
2107
Richard Fitzgerald218e5082015-06-11 11:32:31 +01002108 return snd_soc_add_codec_controls(codec,
Richard Fitzgerald336d0442015-06-18 13:43:19 +01002109 &wm_adsp_fw_controls[dsp->num - 1],
2110 1);
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002111}
2112EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe);
2113
2114int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec)
2115{
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002116 wm_adsp2_cleanup_debugfs(dsp);
2117
Richard Fitzgeraldf5e2ce92015-06-11 11:32:30 +01002118 return 0;
2119}
2120EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove);
2121
Richard Fitzgerald81ac58b2015-06-02 11:53:34 +01002122int wm_adsp2_init(struct wm_adsp *dsp)
Mark Brown973838a2012-11-28 17:20:32 +00002123{
2124 int ret;
2125
Mark Brown10a2b662012-12-02 21:37:00 +09002126 /*
2127 * Disable the DSP memory by default when in reset for a small
2128 * power saving.
2129 */
Charles Keepax3809f002015-04-13 13:27:54 +01002130 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
Mark Brown10a2b662012-12-02 21:37:00 +09002131 ADSP2_MEM_ENA, 0);
2132 if (ret != 0) {
Charles Keepax3809f002015-04-13 13:27:54 +01002133 adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
Mark Brown10a2b662012-12-02 21:37:00 +09002134 return ret;
2135 }
2136
Charles Keepax3809f002015-04-13 13:27:54 +01002137 INIT_LIST_HEAD(&dsp->alg_regions);
2138 INIT_LIST_HEAD(&dsp->ctl_list);
2139 INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
Dimitris Papastamos6ab2b7b2013-05-08 14:15:35 +01002140
Richard Fitzgeraldf9f55e32015-06-11 11:32:32 +01002141#ifdef CONFIG_DEBUG_FS
2142 mutex_init(&dsp->debugfs_lock);
2143#endif
Mark Brown973838a2012-11-28 17:20:32 +00002144 return 0;
2145}
2146EXPORT_SYMBOL_GPL(wm_adsp2_init);
Praveen Diwakar0a37c6e2014-07-04 11:17:41 +05302147
2148MODULE_LICENSE("GPL v2");