xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision 62c35b3b)
12159ad93SMark Brown /*
22159ad93SMark Brown  * wm_adsp.c  --  Wolfson ADSP support
32159ad93SMark Brown  *
42159ad93SMark Brown  * Copyright 2012 Wolfson Microelectronics plc
52159ad93SMark Brown  *
62159ad93SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
72159ad93SMark Brown  *
82159ad93SMark Brown  * This program is free software; you can redistribute it and/or modify
92159ad93SMark Brown  * it under the terms of the GNU General Public License version 2 as
102159ad93SMark Brown  * published by the Free Software Foundation.
112159ad93SMark Brown  */
122159ad93SMark Brown 
132159ad93SMark Brown #include <linux/module.h>
142159ad93SMark Brown #include <linux/moduleparam.h>
152159ad93SMark Brown #include <linux/init.h>
162159ad93SMark Brown #include <linux/delay.h>
172159ad93SMark Brown #include <linux/firmware.h>
18cf17c83cSMark Brown #include <linux/list.h>
192159ad93SMark Brown #include <linux/pm.h>
202159ad93SMark Brown #include <linux/pm_runtime.h>
212159ad93SMark Brown #include <linux/regmap.h>
22973838a0SMark Brown #include <linux/regulator/consumer.h>
232159ad93SMark Brown #include <linux/slab.h>
246ab2b7b4SDimitris Papastamos #include <linux/workqueue.h>
252159ad93SMark Brown #include <sound/core.h>
262159ad93SMark Brown #include <sound/pcm.h>
272159ad93SMark Brown #include <sound/pcm_params.h>
282159ad93SMark Brown #include <sound/soc.h>
292159ad93SMark Brown #include <sound/jack.h>
302159ad93SMark Brown #include <sound/initval.h>
312159ad93SMark Brown #include <sound/tlv.h>
322159ad93SMark Brown 
332159ad93SMark Brown #include <linux/mfd/arizona/registers.h>
342159ad93SMark Brown 
35dc91428aSMark Brown #include "arizona.h"
362159ad93SMark Brown #include "wm_adsp.h"
372159ad93SMark Brown 
382159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \
392159ad93SMark Brown 	dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
402159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \
412159ad93SMark Brown 	dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
422159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \
432159ad93SMark Brown 	dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
442159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \
452159ad93SMark Brown 	dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
462159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \
472159ad93SMark Brown 	dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
482159ad93SMark Brown 
492159ad93SMark Brown #define ADSP1_CONTROL_1                   0x00
502159ad93SMark Brown #define ADSP1_CONTROL_2                   0x02
512159ad93SMark Brown #define ADSP1_CONTROL_3                   0x03
522159ad93SMark Brown #define ADSP1_CONTROL_4                   0x04
532159ad93SMark Brown #define ADSP1_CONTROL_5                   0x06
542159ad93SMark Brown #define ADSP1_CONTROL_6                   0x07
552159ad93SMark Brown #define ADSP1_CONTROL_7                   0x08
562159ad93SMark Brown #define ADSP1_CONTROL_8                   0x09
572159ad93SMark Brown #define ADSP1_CONTROL_9                   0x0A
582159ad93SMark Brown #define ADSP1_CONTROL_10                  0x0B
592159ad93SMark Brown #define ADSP1_CONTROL_11                  0x0C
602159ad93SMark Brown #define ADSP1_CONTROL_12                  0x0D
612159ad93SMark Brown #define ADSP1_CONTROL_13                  0x0F
622159ad93SMark Brown #define ADSP1_CONTROL_14                  0x10
632159ad93SMark Brown #define ADSP1_CONTROL_15                  0x11
642159ad93SMark Brown #define ADSP1_CONTROL_16                  0x12
652159ad93SMark Brown #define ADSP1_CONTROL_17                  0x13
662159ad93SMark Brown #define ADSP1_CONTROL_18                  0x14
672159ad93SMark Brown #define ADSP1_CONTROL_19                  0x16
682159ad93SMark Brown #define ADSP1_CONTROL_20                  0x17
692159ad93SMark Brown #define ADSP1_CONTROL_21                  0x18
702159ad93SMark Brown #define ADSP1_CONTROL_22                  0x1A
712159ad93SMark Brown #define ADSP1_CONTROL_23                  0x1B
722159ad93SMark Brown #define ADSP1_CONTROL_24                  0x1C
732159ad93SMark Brown #define ADSP1_CONTROL_25                  0x1E
742159ad93SMark Brown #define ADSP1_CONTROL_26                  0x20
752159ad93SMark Brown #define ADSP1_CONTROL_27                  0x21
762159ad93SMark Brown #define ADSP1_CONTROL_28                  0x22
772159ad93SMark Brown #define ADSP1_CONTROL_29                  0x23
782159ad93SMark Brown #define ADSP1_CONTROL_30                  0x24
792159ad93SMark Brown #define ADSP1_CONTROL_31                  0x26
802159ad93SMark Brown 
812159ad93SMark Brown /*
822159ad93SMark Brown  * ADSP1 Control 19
832159ad93SMark Brown  */
842159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
852159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
862159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
872159ad93SMark Brown 
882159ad93SMark Brown 
892159ad93SMark Brown /*
902159ad93SMark Brown  * ADSP1 Control 30
912159ad93SMark Brown  */
922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
932159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
952159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
962159ad93SMark Brown #define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
972159ad93SMark Brown #define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
982159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
992159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
1002159ad93SMark Brown #define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
1012159ad93SMark Brown #define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
1022159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1032159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1042159ad93SMark Brown #define ADSP1_START                       0x0001  /* DSP1_START */
1052159ad93SMark Brown #define ADSP1_START_MASK                  0x0001  /* DSP1_START */
1062159ad93SMark Brown #define ADSP1_START_SHIFT                      0  /* DSP1_START */
1072159ad93SMark Brown #define ADSP1_START_WIDTH                      1  /* DSP1_START */
1082159ad93SMark Brown 
10994e205bfSChris Rattray /*
11094e205bfSChris Rattray  * ADSP1 Control 31
11194e205bfSChris Rattray  */
11294e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
11394e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
11494e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
11594e205bfSChris Rattray 
1162d30b575SMark Brown #define ADSP2_CONTROL        0x0
1172d30b575SMark Brown #define ADSP2_CLOCKING       0x1
1182d30b575SMark Brown #define ADSP2_STATUS1        0x4
1192d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30
1202d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31
1212d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34
1222159ad93SMark Brown 
1232159ad93SMark Brown /*
1242159ad93SMark Brown  * ADSP2 Control
1252159ad93SMark Brown  */
1262159ad93SMark Brown 
1272159ad93SMark Brown #define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
1282159ad93SMark Brown #define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
1292159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
1302159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
1312159ad93SMark Brown #define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
1322159ad93SMark Brown #define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
1332159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
1342159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
1352159ad93SMark Brown #define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
1362159ad93SMark Brown #define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
1372159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1382159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1392159ad93SMark Brown #define ADSP2_START                       0x0001  /* DSP1_START */
1402159ad93SMark Brown #define ADSP2_START_MASK                  0x0001  /* DSP1_START */
1412159ad93SMark Brown #define ADSP2_START_SHIFT                      0  /* DSP1_START */
1422159ad93SMark Brown #define ADSP2_START_WIDTH                      1  /* DSP1_START */
1432159ad93SMark Brown 
1442159ad93SMark Brown /*
145973838a0SMark Brown  * ADSP2 clocking
146973838a0SMark Brown  */
147973838a0SMark Brown #define ADSP2_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
148973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
149973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
150973838a0SMark Brown 
151973838a0SMark Brown /*
1522159ad93SMark Brown  * ADSP2 Status 1
1532159ad93SMark Brown  */
1542159ad93SMark Brown #define ADSP2_RAM_RDY                     0x0001
1552159ad93SMark Brown #define ADSP2_RAM_RDY_MASK                0x0001
1562159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT                    0
1572159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH                    1
1582159ad93SMark Brown 
159cf17c83cSMark Brown struct wm_adsp_buf {
160cf17c83cSMark Brown 	struct list_head list;
161cf17c83cSMark Brown 	void *buf;
162cf17c83cSMark Brown };
163cf17c83cSMark Brown 
164cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
165cf17c83cSMark Brown 					     struct list_head *list)
166cf17c83cSMark Brown {
167cf17c83cSMark Brown 	struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
168cf17c83cSMark Brown 
169cf17c83cSMark Brown 	if (buf == NULL)
170cf17c83cSMark Brown 		return NULL;
171cf17c83cSMark Brown 
172cf17c83cSMark Brown 	buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
173cf17c83cSMark Brown 	if (!buf->buf) {
174cf17c83cSMark Brown 		kfree(buf);
175cf17c83cSMark Brown 		return NULL;
176cf17c83cSMark Brown 	}
177cf17c83cSMark Brown 
178cf17c83cSMark Brown 	if (list)
179cf17c83cSMark Brown 		list_add_tail(&buf->list, list);
180cf17c83cSMark Brown 
181cf17c83cSMark Brown 	return buf;
182cf17c83cSMark Brown }
183cf17c83cSMark Brown 
184cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list)
185cf17c83cSMark Brown {
186cf17c83cSMark Brown 	while (!list_empty(list)) {
187cf17c83cSMark Brown 		struct wm_adsp_buf *buf = list_first_entry(list,
188cf17c83cSMark Brown 							   struct wm_adsp_buf,
189cf17c83cSMark Brown 							   list);
190cf17c83cSMark Brown 		list_del(&buf->list);
191cf17c83cSMark Brown 		kfree(buf->buf);
192cf17c83cSMark Brown 		kfree(buf);
193cf17c83cSMark Brown 	}
194cf17c83cSMark Brown }
195cf17c83cSMark Brown 
19636e8fe99SMark Brown #define WM_ADSP_NUM_FW 4
1971023dbd9SMark Brown 
198dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0
199dd84f925SMark Brown #define WM_ADSP_FW_TX      1
200dd84f925SMark Brown #define WM_ADSP_FW_TX_SPK  2
201dd84f925SMark Brown #define WM_ADSP_FW_RX_ANC  3
202dd84f925SMark Brown 
2031023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
204dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
205dd84f925SMark Brown 	[WM_ADSP_FW_TX] =      "Tx",
206dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =  "Tx Speaker",
207dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =  "Rx ANC",
2081023dbd9SMark Brown };
2091023dbd9SMark Brown 
2101023dbd9SMark Brown static struct {
2111023dbd9SMark Brown 	const char *file;
2121023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = {
213dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
214dd84f925SMark Brown 	[WM_ADSP_FW_TX] =      { .file = "tx" },
215dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =  { .file = "tx-spk" },
216dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =  { .file = "rx-anc" },
2171023dbd9SMark Brown };
2181023dbd9SMark Brown 
2196ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops {
2206ab2b7b4SDimitris Papastamos 	int (*xget)(struct snd_kcontrol *kcontrol,
2216ab2b7b4SDimitris Papastamos 		    struct snd_ctl_elem_value *ucontrol);
2226ab2b7b4SDimitris Papastamos 	int (*xput)(struct snd_kcontrol *kcontrol,
2236ab2b7b4SDimitris Papastamos 		    struct snd_ctl_elem_value *ucontrol);
2246ab2b7b4SDimitris Papastamos 	int (*xinfo)(struct snd_kcontrol *kcontrol,
2256ab2b7b4SDimitris Papastamos 		     struct snd_ctl_elem_info *uinfo);
2266ab2b7b4SDimitris Papastamos };
2276ab2b7b4SDimitris Papastamos 
2286ab2b7b4SDimitris Papastamos struct wm_coeff_ctl {
2296ab2b7b4SDimitris Papastamos 	const char *name;
2306ab2b7b4SDimitris Papastamos 	struct wm_adsp_alg_region region;
2316ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl_ops ops;
2326ab2b7b4SDimitris Papastamos 	struct wm_adsp *adsp;
2336ab2b7b4SDimitris Papastamos 	void *private;
2346ab2b7b4SDimitris Papastamos 	unsigned int enabled:1;
2356ab2b7b4SDimitris Papastamos 	struct list_head list;
2366ab2b7b4SDimitris Papastamos 	void *cache;
2376ab2b7b4SDimitris Papastamos 	size_t len;
2380c2e3f34SDimitris Papastamos 	unsigned int set:1;
2396ab2b7b4SDimitris Papastamos 	struct snd_kcontrol *kcontrol;
2406ab2b7b4SDimitris Papastamos };
2416ab2b7b4SDimitris Papastamos 
2421023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
2431023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2441023dbd9SMark Brown {
2451023dbd9SMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2461023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2471023dbd9SMark Brown 	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
2481023dbd9SMark Brown 
2491023dbd9SMark Brown 	ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
2501023dbd9SMark Brown 
2511023dbd9SMark Brown 	return 0;
2521023dbd9SMark Brown }
2531023dbd9SMark Brown 
2541023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
2551023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2561023dbd9SMark Brown {
2571023dbd9SMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2581023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2591023dbd9SMark Brown 	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
2601023dbd9SMark Brown 
2611023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
2621023dbd9SMark Brown 		return 0;
2631023dbd9SMark Brown 
2641023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
2651023dbd9SMark Brown 		return -EINVAL;
2661023dbd9SMark Brown 
2671023dbd9SMark Brown 	if (adsp[e->shift_l].running)
2681023dbd9SMark Brown 		return -EBUSY;
2691023dbd9SMark Brown 
27031522764SMark Brown 	adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
2711023dbd9SMark Brown 
2721023dbd9SMark Brown 	return 0;
2731023dbd9SMark Brown }
2741023dbd9SMark Brown 
2751023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = {
2761023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2771023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2781023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2791023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2801023dbd9SMark Brown };
2811023dbd9SMark Brown 
282b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
2831023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
2841023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2851023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
2861023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2871023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
2881023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
289b6ed61cfSMark Brown };
290b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
291b6ed61cfSMark Brown 
292b6ed61cfSMark Brown #if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
293b6ed61cfSMark Brown static const struct soc_enum wm_adsp2_rate_enum[] = {
294dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
295dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
296dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
297dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
298dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
299dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
300dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
301dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
302dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
303dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
304dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
305dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
3065be9c5b4SCharles Keepax 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
307dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
308dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
309dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
310dc91428aSMark Brown };
311dc91428aSMark Brown 
312b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
3131023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
3141023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
315b6ed61cfSMark Brown 	SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
3161023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
3171023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
318b6ed61cfSMark Brown 	SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
3191023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
3201023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
321b6ed61cfSMark Brown 	SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
3221023dbd9SMark Brown 	SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
3231023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
324b6ed61cfSMark Brown 	SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
3251023dbd9SMark Brown };
326b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
327b6ed61cfSMark Brown #endif
3282159ad93SMark Brown 
3292159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
3302159ad93SMark Brown 							int type)
3312159ad93SMark Brown {
3322159ad93SMark Brown 	int i;
3332159ad93SMark Brown 
3342159ad93SMark Brown 	for (i = 0; i < dsp->num_mems; i++)
3352159ad93SMark Brown 		if (dsp->mem[i].type == type)
3362159ad93SMark Brown 			return &dsp->mem[i];
3372159ad93SMark Brown 
3382159ad93SMark Brown 	return NULL;
3392159ad93SMark Brown }
3402159ad93SMark Brown 
34145b9ee72SMark Brown static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
34245b9ee72SMark Brown 					  unsigned int offset)
34345b9ee72SMark Brown {
3446c452bdaSTakashi Iwai 	if (WARN_ON(!region))
3456c452bdaSTakashi Iwai 		return offset;
34645b9ee72SMark Brown 	switch (region->type) {
34745b9ee72SMark Brown 	case WMFW_ADSP1_PM:
34845b9ee72SMark Brown 		return region->base + (offset * 3);
34945b9ee72SMark Brown 	case WMFW_ADSP1_DM:
35045b9ee72SMark Brown 		return region->base + (offset * 2);
35145b9ee72SMark Brown 	case WMFW_ADSP2_XM:
35245b9ee72SMark Brown 		return region->base + (offset * 2);
35345b9ee72SMark Brown 	case WMFW_ADSP2_YM:
35445b9ee72SMark Brown 		return region->base + (offset * 2);
35545b9ee72SMark Brown 	case WMFW_ADSP1_ZM:
35645b9ee72SMark Brown 		return region->base + (offset * 2);
35745b9ee72SMark Brown 	default:
3586c452bdaSTakashi Iwai 		WARN(1, "Unknown memory region type");
35945b9ee72SMark Brown 		return offset;
36045b9ee72SMark Brown 	}
36145b9ee72SMark Brown }
36245b9ee72SMark Brown 
3636ab2b7b4SDimitris Papastamos static int wm_coeff_info(struct snd_kcontrol *kcontrol,
3646ab2b7b4SDimitris Papastamos 			 struct snd_ctl_elem_info *uinfo)
3656ab2b7b4SDimitris Papastamos {
3666ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
3676ab2b7b4SDimitris Papastamos 
3686ab2b7b4SDimitris Papastamos 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
3696ab2b7b4SDimitris Papastamos 	uinfo->count = ctl->len;
3706ab2b7b4SDimitris Papastamos 	return 0;
3716ab2b7b4SDimitris Papastamos }
3726ab2b7b4SDimitris Papastamos 
3736ab2b7b4SDimitris Papastamos static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
3746ab2b7b4SDimitris Papastamos 				  const void *buf, size_t len)
3756ab2b7b4SDimitris Papastamos {
3766ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
3776ab2b7b4SDimitris Papastamos 	struct wm_adsp_alg_region *region = &ctl->region;
3786ab2b7b4SDimitris Papastamos 	const struct wm_adsp_region *mem;
3796ab2b7b4SDimitris Papastamos 	struct wm_adsp *adsp = ctl->adsp;
3806ab2b7b4SDimitris Papastamos 	void *scratch;
3816ab2b7b4SDimitris Papastamos 	int ret;
3826ab2b7b4SDimitris Papastamos 	unsigned int reg;
3836ab2b7b4SDimitris Papastamos 
3846ab2b7b4SDimitris Papastamos 	mem = wm_adsp_find_region(adsp, region->type);
3856ab2b7b4SDimitris Papastamos 	if (!mem) {
3866ab2b7b4SDimitris Papastamos 		adsp_err(adsp, "No base for region %x\n",
3876ab2b7b4SDimitris Papastamos 			 region->type);
3886ab2b7b4SDimitris Papastamos 		return -EINVAL;
3896ab2b7b4SDimitris Papastamos 	}
3906ab2b7b4SDimitris Papastamos 
3916ab2b7b4SDimitris Papastamos 	reg = ctl->region.base;
3926ab2b7b4SDimitris Papastamos 	reg = wm_adsp_region_to_reg(mem, reg);
3936ab2b7b4SDimitris Papastamos 
3946ab2b7b4SDimitris Papastamos 	scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
3956ab2b7b4SDimitris Papastamos 	if (!scratch)
3966ab2b7b4SDimitris Papastamos 		return -ENOMEM;
3976ab2b7b4SDimitris Papastamos 
39881ad93ecSDimitris Papastamos 	ret = regmap_raw_write(adsp->regmap, reg, scratch,
3996ab2b7b4SDimitris Papastamos 			       ctl->len);
4006ab2b7b4SDimitris Papastamos 	if (ret) {
40143bc3bf6SDimitris Papastamos 		adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n",
40243bc3bf6SDimitris Papastamos 			 ctl->len, reg, ret);
4036ab2b7b4SDimitris Papastamos 		kfree(scratch);
4046ab2b7b4SDimitris Papastamos 		return ret;
4056ab2b7b4SDimitris Papastamos 	}
406562c5e6fSDimitris Papastamos 	adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
4076ab2b7b4SDimitris Papastamos 
4086ab2b7b4SDimitris Papastamos 	kfree(scratch);
4096ab2b7b4SDimitris Papastamos 
4106ab2b7b4SDimitris Papastamos 	return 0;
4116ab2b7b4SDimitris Papastamos }
4126ab2b7b4SDimitris Papastamos 
4136ab2b7b4SDimitris Papastamos static int wm_coeff_put(struct snd_kcontrol *kcontrol,
4146ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4156ab2b7b4SDimitris Papastamos {
4166ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
4176ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
4186ab2b7b4SDimitris Papastamos 
4196ab2b7b4SDimitris Papastamos 	memcpy(ctl->cache, p, ctl->len);
4206ab2b7b4SDimitris Papastamos 
4216ab2b7b4SDimitris Papastamos 	if (!ctl->enabled) {
4220c2e3f34SDimitris Papastamos 		ctl->set = 1;
4236ab2b7b4SDimitris Papastamos 		return 0;
4246ab2b7b4SDimitris Papastamos 	}
4256ab2b7b4SDimitris Papastamos 
4266ab2b7b4SDimitris Papastamos 	return wm_coeff_write_control(kcontrol, p, ctl->len);
4276ab2b7b4SDimitris Papastamos }
4286ab2b7b4SDimitris Papastamos 
4296ab2b7b4SDimitris Papastamos static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
4306ab2b7b4SDimitris Papastamos 				 void *buf, size_t len)
4316ab2b7b4SDimitris Papastamos {
4326ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
4336ab2b7b4SDimitris Papastamos 	struct wm_adsp_alg_region *region = &ctl->region;
4346ab2b7b4SDimitris Papastamos 	const struct wm_adsp_region *mem;
4356ab2b7b4SDimitris Papastamos 	struct wm_adsp *adsp = ctl->adsp;
4366ab2b7b4SDimitris Papastamos 	void *scratch;
4376ab2b7b4SDimitris Papastamos 	int ret;
4386ab2b7b4SDimitris Papastamos 	unsigned int reg;
4396ab2b7b4SDimitris Papastamos 
4406ab2b7b4SDimitris Papastamos 	mem = wm_adsp_find_region(adsp, region->type);
4416ab2b7b4SDimitris Papastamos 	if (!mem) {
4426ab2b7b4SDimitris Papastamos 		adsp_err(adsp, "No base for region %x\n",
4436ab2b7b4SDimitris Papastamos 			 region->type);
4446ab2b7b4SDimitris Papastamos 		return -EINVAL;
4456ab2b7b4SDimitris Papastamos 	}
4466ab2b7b4SDimitris Papastamos 
4476ab2b7b4SDimitris Papastamos 	reg = ctl->region.base;
4486ab2b7b4SDimitris Papastamos 	reg = wm_adsp_region_to_reg(mem, reg);
4496ab2b7b4SDimitris Papastamos 
4506ab2b7b4SDimitris Papastamos 	scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
4516ab2b7b4SDimitris Papastamos 	if (!scratch)
4526ab2b7b4SDimitris Papastamos 		return -ENOMEM;
4536ab2b7b4SDimitris Papastamos 
45481ad93ecSDimitris Papastamos 	ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);
4556ab2b7b4SDimitris Papastamos 	if (ret) {
45643bc3bf6SDimitris Papastamos 		adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n",
45743bc3bf6SDimitris Papastamos 			 ctl->len, reg, ret);
4586ab2b7b4SDimitris Papastamos 		kfree(scratch);
4596ab2b7b4SDimitris Papastamos 		return ret;
4606ab2b7b4SDimitris Papastamos 	}
461562c5e6fSDimitris Papastamos 	adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg);
4626ab2b7b4SDimitris Papastamos 
4636ab2b7b4SDimitris Papastamos 	memcpy(buf, scratch, ctl->len);
4646ab2b7b4SDimitris Papastamos 	kfree(scratch);
4656ab2b7b4SDimitris Papastamos 
4666ab2b7b4SDimitris Papastamos 	return 0;
4676ab2b7b4SDimitris Papastamos }
4686ab2b7b4SDimitris Papastamos 
4696ab2b7b4SDimitris Papastamos static int wm_coeff_get(struct snd_kcontrol *kcontrol,
4706ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4716ab2b7b4SDimitris Papastamos {
4726ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
4736ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
4746ab2b7b4SDimitris Papastamos 
4756ab2b7b4SDimitris Papastamos 	memcpy(p, ctl->cache, ctl->len);
4766ab2b7b4SDimitris Papastamos 	return 0;
4776ab2b7b4SDimitris Papastamos }
4786ab2b7b4SDimitris Papastamos 
4796ab2b7b4SDimitris Papastamos struct wmfw_ctl_work {
48081ad93ecSDimitris Papastamos 	struct wm_adsp *adsp;
4816ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
4826ab2b7b4SDimitris Papastamos 	struct work_struct work;
4836ab2b7b4SDimitris Papastamos };
4846ab2b7b4SDimitris Papastamos 
48581ad93ecSDimitris Papastamos static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
4866ab2b7b4SDimitris Papastamos {
4876ab2b7b4SDimitris Papastamos 	struct snd_kcontrol_new *kcontrol;
4886ab2b7b4SDimitris Papastamos 	int ret;
4896ab2b7b4SDimitris Papastamos 
49092bb4c32SDimitris Papastamos 	if (!ctl || !ctl->name)
4916ab2b7b4SDimitris Papastamos 		return -EINVAL;
4926ab2b7b4SDimitris Papastamos 
4936ab2b7b4SDimitris Papastamos 	kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
4946ab2b7b4SDimitris Papastamos 	if (!kcontrol)
4956ab2b7b4SDimitris Papastamos 		return -ENOMEM;
4966ab2b7b4SDimitris Papastamos 	kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
4976ab2b7b4SDimitris Papastamos 
4986ab2b7b4SDimitris Papastamos 	kcontrol->name = ctl->name;
4996ab2b7b4SDimitris Papastamos 	kcontrol->info = wm_coeff_info;
5006ab2b7b4SDimitris Papastamos 	kcontrol->get = wm_coeff_get;
5016ab2b7b4SDimitris Papastamos 	kcontrol->put = wm_coeff_put;
5026ab2b7b4SDimitris Papastamos 	kcontrol->private_value = (unsigned long)ctl;
5036ab2b7b4SDimitris Papastamos 
50492bb4c32SDimitris Papastamos 	ret = snd_soc_add_card_controls(adsp->card,
50581ad93ecSDimitris Papastamos 					kcontrol, 1);
5066ab2b7b4SDimitris Papastamos 	if (ret < 0)
5076ab2b7b4SDimitris Papastamos 		goto err_kcontrol;
5086ab2b7b4SDimitris Papastamos 
5096ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
5106ab2b7b4SDimitris Papastamos 
51192bb4c32SDimitris Papastamos 	ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card,
51281ad93ecSDimitris Papastamos 						  ctl->name);
51381ad93ecSDimitris Papastamos 
51481ad93ecSDimitris Papastamos 	list_add(&ctl->list, &adsp->ctl_list);
5156ab2b7b4SDimitris Papastamos 	return 0;
5166ab2b7b4SDimitris Papastamos 
5176ab2b7b4SDimitris Papastamos err_kcontrol:
5186ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
5196ab2b7b4SDimitris Papastamos 	return ret;
5206ab2b7b4SDimitris Papastamos }
5216ab2b7b4SDimitris Papastamos 
5222159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp)
5232159ad93SMark Brown {
524cf17c83cSMark Brown 	LIST_HEAD(buf_list);
5252159ad93SMark Brown 	const struct firmware *firmware;
5262159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
5272159ad93SMark Brown 	unsigned int pos = 0;
5282159ad93SMark Brown 	const struct wmfw_header *header;
5292159ad93SMark Brown 	const struct wmfw_adsp1_sizes *adsp1_sizes;
5302159ad93SMark Brown 	const struct wmfw_adsp2_sizes *adsp2_sizes;
5312159ad93SMark Brown 	const struct wmfw_footer *footer;
5322159ad93SMark Brown 	const struct wmfw_region *region;
5332159ad93SMark Brown 	const struct wm_adsp_region *mem;
5342159ad93SMark Brown 	const char *region_name;
5352159ad93SMark Brown 	char *file, *text;
536cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
5372159ad93SMark Brown 	unsigned int reg;
5382159ad93SMark Brown 	int regions = 0;
5392159ad93SMark Brown 	int ret, offset, type, sizes;
5402159ad93SMark Brown 
5412159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
5422159ad93SMark Brown 	if (file == NULL)
5432159ad93SMark Brown 		return -ENOMEM;
5442159ad93SMark Brown 
5451023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
5461023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
5472159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
5482159ad93SMark Brown 
5492159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
5502159ad93SMark Brown 	if (ret != 0) {
5512159ad93SMark Brown 		adsp_err(dsp, "Failed to request '%s'\n", file);
5522159ad93SMark Brown 		goto out;
5532159ad93SMark Brown 	}
5542159ad93SMark Brown 	ret = -EINVAL;
5552159ad93SMark Brown 
5562159ad93SMark Brown 	pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
5572159ad93SMark Brown 	if (pos >= firmware->size) {
5582159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
5592159ad93SMark Brown 			 file, firmware->size);
5602159ad93SMark Brown 		goto out_fw;
5612159ad93SMark Brown 	}
5622159ad93SMark Brown 
5632159ad93SMark Brown 	header = (void*)&firmware->data[0];
5642159ad93SMark Brown 
5652159ad93SMark Brown 	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
5662159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
5672159ad93SMark Brown 		goto out_fw;
5682159ad93SMark Brown 	}
5692159ad93SMark Brown 
5702159ad93SMark Brown 	if (header->ver != 0) {
5712159ad93SMark Brown 		adsp_err(dsp, "%s: unknown file format %d\n",
5722159ad93SMark Brown 			 file, header->ver);
5732159ad93SMark Brown 		goto out_fw;
5742159ad93SMark Brown 	}
5753626992aSDimitris Papastamos 	adsp_info(dsp, "Firmware version: %d\n", header->ver);
5762159ad93SMark Brown 
5772159ad93SMark Brown 	if (header->core != dsp->type) {
5782159ad93SMark Brown 		adsp_err(dsp, "%s: invalid core %d != %d\n",
5792159ad93SMark Brown 			 file, header->core, dsp->type);
5802159ad93SMark Brown 		goto out_fw;
5812159ad93SMark Brown 	}
5822159ad93SMark Brown 
5832159ad93SMark Brown 	switch (dsp->type) {
5842159ad93SMark Brown 	case WMFW_ADSP1:
5852159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
5862159ad93SMark Brown 		adsp1_sizes = (void *)&(header[1]);
5872159ad93SMark Brown 		footer = (void *)&(adsp1_sizes[1]);
5882159ad93SMark Brown 		sizes = sizeof(*adsp1_sizes);
5892159ad93SMark Brown 
5902159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
5912159ad93SMark Brown 			 file, le32_to_cpu(adsp1_sizes->dm),
5922159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->pm),
5932159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->zm));
5942159ad93SMark Brown 		break;
5952159ad93SMark Brown 
5962159ad93SMark Brown 	case WMFW_ADSP2:
5972159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
5982159ad93SMark Brown 		adsp2_sizes = (void *)&(header[1]);
5992159ad93SMark Brown 		footer = (void *)&(adsp2_sizes[1]);
6002159ad93SMark Brown 		sizes = sizeof(*adsp2_sizes);
6012159ad93SMark Brown 
6022159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
6032159ad93SMark Brown 			 file, le32_to_cpu(adsp2_sizes->xm),
6042159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->ym),
6052159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->pm),
6062159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->zm));
6072159ad93SMark Brown 		break;
6082159ad93SMark Brown 
6092159ad93SMark Brown 	default:
6106c452bdaSTakashi Iwai 		WARN(1, "Unknown DSP type");
6112159ad93SMark Brown 		goto out_fw;
6122159ad93SMark Brown 	}
6132159ad93SMark Brown 
6142159ad93SMark Brown 	if (le32_to_cpu(header->len) != sizeof(*header) +
6152159ad93SMark Brown 	    sizes + sizeof(*footer)) {
6162159ad93SMark Brown 		adsp_err(dsp, "%s: unexpected header length %d\n",
6172159ad93SMark Brown 			 file, le32_to_cpu(header->len));
6182159ad93SMark Brown 		goto out_fw;
6192159ad93SMark Brown 	}
6202159ad93SMark Brown 
6212159ad93SMark Brown 	adsp_dbg(dsp, "%s: timestamp %llu\n", file,
6222159ad93SMark Brown 		 le64_to_cpu(footer->timestamp));
6232159ad93SMark Brown 
6242159ad93SMark Brown 	while (pos < firmware->size &&
6252159ad93SMark Brown 	       pos - firmware->size > sizeof(*region)) {
6262159ad93SMark Brown 		region = (void *)&(firmware->data[pos]);
6272159ad93SMark Brown 		region_name = "Unknown";
6282159ad93SMark Brown 		reg = 0;
6292159ad93SMark Brown 		text = NULL;
6302159ad93SMark Brown 		offset = le32_to_cpu(region->offset) & 0xffffff;
6312159ad93SMark Brown 		type = be32_to_cpu(region->type) & 0xff;
6322159ad93SMark Brown 		mem = wm_adsp_find_region(dsp, type);
6332159ad93SMark Brown 
6342159ad93SMark Brown 		switch (type) {
6352159ad93SMark Brown 		case WMFW_NAME_TEXT:
6362159ad93SMark Brown 			region_name = "Firmware name";
6372159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
6382159ad93SMark Brown 				       GFP_KERNEL);
6392159ad93SMark Brown 			break;
6402159ad93SMark Brown 		case WMFW_INFO_TEXT:
6412159ad93SMark Brown 			region_name = "Information";
6422159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
6432159ad93SMark Brown 				       GFP_KERNEL);
6442159ad93SMark Brown 			break;
6452159ad93SMark Brown 		case WMFW_ABSOLUTE:
6462159ad93SMark Brown 			region_name = "Absolute";
6472159ad93SMark Brown 			reg = offset;
6482159ad93SMark Brown 			break;
6492159ad93SMark Brown 		case WMFW_ADSP1_PM:
6502159ad93SMark Brown 			region_name = "PM";
65145b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6522159ad93SMark Brown 			break;
6532159ad93SMark Brown 		case WMFW_ADSP1_DM:
6542159ad93SMark Brown 			region_name = "DM";
65545b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6562159ad93SMark Brown 			break;
6572159ad93SMark Brown 		case WMFW_ADSP2_XM:
6582159ad93SMark Brown 			region_name = "XM";
65945b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6602159ad93SMark Brown 			break;
6612159ad93SMark Brown 		case WMFW_ADSP2_YM:
6622159ad93SMark Brown 			region_name = "YM";
66345b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6642159ad93SMark Brown 			break;
6652159ad93SMark Brown 		case WMFW_ADSP1_ZM:
6662159ad93SMark Brown 			region_name = "ZM";
66745b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6682159ad93SMark Brown 			break;
6692159ad93SMark Brown 		default:
6702159ad93SMark Brown 			adsp_warn(dsp,
6712159ad93SMark Brown 				  "%s.%d: Unknown region type %x at %d(%x)\n",
6722159ad93SMark Brown 				  file, regions, type, pos, pos);
6732159ad93SMark Brown 			break;
6742159ad93SMark Brown 		}
6752159ad93SMark Brown 
6762159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
6772159ad93SMark Brown 			 regions, le32_to_cpu(region->len), offset,
6782159ad93SMark Brown 			 region_name);
6792159ad93SMark Brown 
6802159ad93SMark Brown 		if (text) {
6812159ad93SMark Brown 			memcpy(text, region->data, le32_to_cpu(region->len));
6822159ad93SMark Brown 			adsp_info(dsp, "%s: %s\n", file, text);
6832159ad93SMark Brown 			kfree(text);
6842159ad93SMark Brown 		}
6852159ad93SMark Brown 
6862159ad93SMark Brown 		if (reg) {
687c1a7898dSCharles Keepax 			size_t to_write = PAGE_SIZE;
688c1a7898dSCharles Keepax 			size_t remain = le32_to_cpu(region->len);
689c1a7898dSCharles Keepax 			const u8 *data = region->data;
690c1a7898dSCharles Keepax 
691c1a7898dSCharles Keepax 			while (remain > 0) {
692c1a7898dSCharles Keepax 				if (remain < PAGE_SIZE)
693c1a7898dSCharles Keepax 					to_write = remain;
694c1a7898dSCharles Keepax 
695c1a7898dSCharles Keepax 				buf = wm_adsp_buf_alloc(data,
696c1a7898dSCharles Keepax 							to_write,
697cf17c83cSMark Brown 							&buf_list);
698a76fefabSMark Brown 				if (!buf) {
699a76fefabSMark Brown 					adsp_err(dsp, "Out of memory\n");
7007328823dSDimitris Papastamos 					ret = -ENOMEM;
7017328823dSDimitris Papastamos 					goto out_fw;
702a76fefabSMark Brown 				}
703a76fefabSMark Brown 
704c1a7898dSCharles Keepax 				ret = regmap_raw_write_async(regmap, reg,
705c1a7898dSCharles Keepax 							     buf->buf,
706c1a7898dSCharles Keepax 							     to_write);
7072159ad93SMark Brown 				if (ret != 0) {
7082159ad93SMark Brown 					adsp_err(dsp,
709fab800ccSCharles Keepax 						"%s.%d: Failed to write %zd bytes at %d in %s: %d\n",
7102159ad93SMark Brown 						file, regions,
711c1a7898dSCharles Keepax 						to_write, offset,
7122159ad93SMark Brown 						region_name, ret);
7132159ad93SMark Brown 					goto out_fw;
7142159ad93SMark Brown 				}
715c1a7898dSCharles Keepax 
716c1a7898dSCharles Keepax 				data += to_write;
717c1a7898dSCharles Keepax 				reg += to_write / 2;
718c1a7898dSCharles Keepax 				remain -= to_write;
719c1a7898dSCharles Keepax 			}
7202159ad93SMark Brown 		}
7212159ad93SMark Brown 
7222159ad93SMark Brown 		pos += le32_to_cpu(region->len) + sizeof(*region);
7232159ad93SMark Brown 		regions++;
7242159ad93SMark Brown 	}
7252159ad93SMark Brown 
726cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
727cf17c83cSMark Brown 	if (ret != 0) {
728cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
729cf17c83cSMark Brown 		goto out_fw;
730cf17c83cSMark Brown 	}
731cf17c83cSMark Brown 
7322159ad93SMark Brown 	if (pos > firmware->size)
7332159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
7342159ad93SMark Brown 			  file, regions, pos - firmware->size);
7352159ad93SMark Brown 
7362159ad93SMark Brown out_fw:
737cf17c83cSMark Brown 	regmap_async_complete(regmap);
738cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
7392159ad93SMark Brown 	release_firmware(firmware);
7402159ad93SMark Brown out:
7412159ad93SMark Brown 	kfree(file);
7422159ad93SMark Brown 
7432159ad93SMark Brown 	return ret;
7442159ad93SMark Brown }
7452159ad93SMark Brown 
74681ad93ecSDimitris Papastamos static int wm_coeff_init_control_caches(struct wm_adsp *adsp)
7476ab2b7b4SDimitris Papastamos {
7486ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
7496ab2b7b4SDimitris Papastamos 	int ret;
7506ab2b7b4SDimitris Papastamos 
75181ad93ecSDimitris Papastamos 	list_for_each_entry(ctl, &adsp->ctl_list, list) {
7520c2e3f34SDimitris Papastamos 		if (!ctl->enabled || ctl->set)
7536ab2b7b4SDimitris Papastamos 			continue;
7546ab2b7b4SDimitris Papastamos 		ret = wm_coeff_read_control(ctl->kcontrol,
7556ab2b7b4SDimitris Papastamos 					    ctl->cache,
7566ab2b7b4SDimitris Papastamos 					    ctl->len);
7576ab2b7b4SDimitris Papastamos 		if (ret < 0)
7586ab2b7b4SDimitris Papastamos 			return ret;
7596ab2b7b4SDimitris Papastamos 	}
7606ab2b7b4SDimitris Papastamos 
7616ab2b7b4SDimitris Papastamos 	return 0;
7626ab2b7b4SDimitris Papastamos }
7636ab2b7b4SDimitris Papastamos 
76481ad93ecSDimitris Papastamos static int wm_coeff_sync_controls(struct wm_adsp *adsp)
7656ab2b7b4SDimitris Papastamos {
7666ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
7676ab2b7b4SDimitris Papastamos 	int ret;
7686ab2b7b4SDimitris Papastamos 
76981ad93ecSDimitris Papastamos 	list_for_each_entry(ctl, &adsp->ctl_list, list) {
7706ab2b7b4SDimitris Papastamos 		if (!ctl->enabled)
7716ab2b7b4SDimitris Papastamos 			continue;
7720c2e3f34SDimitris Papastamos 		if (ctl->set) {
7736ab2b7b4SDimitris Papastamos 			ret = wm_coeff_write_control(ctl->kcontrol,
7746ab2b7b4SDimitris Papastamos 						     ctl->cache,
7756ab2b7b4SDimitris Papastamos 						     ctl->len);
7766ab2b7b4SDimitris Papastamos 			if (ret < 0)
7776ab2b7b4SDimitris Papastamos 				return ret;
7786ab2b7b4SDimitris Papastamos 		}
7796ab2b7b4SDimitris Papastamos 	}
7806ab2b7b4SDimitris Papastamos 
7816ab2b7b4SDimitris Papastamos 	return 0;
7826ab2b7b4SDimitris Papastamos }
7836ab2b7b4SDimitris Papastamos 
7846ab2b7b4SDimitris Papastamos static void wm_adsp_ctl_work(struct work_struct *work)
7856ab2b7b4SDimitris Papastamos {
7866ab2b7b4SDimitris Papastamos 	struct wmfw_ctl_work *ctl_work = container_of(work,
7876ab2b7b4SDimitris Papastamos 						      struct wmfw_ctl_work,
7886ab2b7b4SDimitris Papastamos 						      work);
7896ab2b7b4SDimitris Papastamos 
79081ad93ecSDimitris Papastamos 	wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
7916ab2b7b4SDimitris Papastamos 	kfree(ctl_work);
7926ab2b7b4SDimitris Papastamos }
7936ab2b7b4SDimitris Papastamos 
79492bb4c32SDimitris Papastamos static int wm_adsp_create_control(struct wm_adsp *dsp,
7956ab2b7b4SDimitris Papastamos 				  const struct wm_adsp_alg_region *region)
7966ab2b7b4SDimitris Papastamos 
7976ab2b7b4SDimitris Papastamos {
7986ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
7996ab2b7b4SDimitris Papastamos 	struct wmfw_ctl_work *ctl_work;
8006ab2b7b4SDimitris Papastamos 	char *name;
8016ab2b7b4SDimitris Papastamos 	char *region_name;
8026ab2b7b4SDimitris Papastamos 	int ret;
8036ab2b7b4SDimitris Papastamos 
8046ab2b7b4SDimitris Papastamos 	name = kmalloc(PAGE_SIZE, GFP_KERNEL);
8056ab2b7b4SDimitris Papastamos 	if (!name)
8066ab2b7b4SDimitris Papastamos 		return -ENOMEM;
8076ab2b7b4SDimitris Papastamos 
8086ab2b7b4SDimitris Papastamos 	switch (region->type) {
8096ab2b7b4SDimitris Papastamos 	case WMFW_ADSP1_PM:
8106ab2b7b4SDimitris Papastamos 		region_name = "PM";
8116ab2b7b4SDimitris Papastamos 		break;
8126ab2b7b4SDimitris Papastamos 	case WMFW_ADSP1_DM:
8136ab2b7b4SDimitris Papastamos 		region_name = "DM";
8146ab2b7b4SDimitris Papastamos 		break;
8156ab2b7b4SDimitris Papastamos 	case WMFW_ADSP2_XM:
8166ab2b7b4SDimitris Papastamos 		region_name = "XM";
8176ab2b7b4SDimitris Papastamos 		break;
8186ab2b7b4SDimitris Papastamos 	case WMFW_ADSP2_YM:
8196ab2b7b4SDimitris Papastamos 		region_name = "YM";
8206ab2b7b4SDimitris Papastamos 		break;
8216ab2b7b4SDimitris Papastamos 	case WMFW_ADSP1_ZM:
8226ab2b7b4SDimitris Papastamos 		region_name = "ZM";
8236ab2b7b4SDimitris Papastamos 		break;
8246ab2b7b4SDimitris Papastamos 	default:
8259dbce044SDan Carpenter 		ret = -EINVAL;
8269dbce044SDan Carpenter 		goto err_name;
8276ab2b7b4SDimitris Papastamos 	}
8286ab2b7b4SDimitris Papastamos 
8296ab2b7b4SDimitris Papastamos 	snprintf(name, PAGE_SIZE, "DSP%d %s %x",
8306ab2b7b4SDimitris Papastamos 		 dsp->num, region_name, region->alg);
8316ab2b7b4SDimitris Papastamos 
83281ad93ecSDimitris Papastamos 	list_for_each_entry(ctl, &dsp->ctl_list,
8336ab2b7b4SDimitris Papastamos 			    list) {
8346ab2b7b4SDimitris Papastamos 		if (!strcmp(ctl->name, name)) {
8356ab2b7b4SDimitris Papastamos 			if (!ctl->enabled)
8366ab2b7b4SDimitris Papastamos 				ctl->enabled = 1;
8379dbce044SDan Carpenter 			goto found;
8386ab2b7b4SDimitris Papastamos 		}
8396ab2b7b4SDimitris Papastamos 	}
8406ab2b7b4SDimitris Papastamos 
8416ab2b7b4SDimitris Papastamos 	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
8426ab2b7b4SDimitris Papastamos 	if (!ctl) {
8436ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8446ab2b7b4SDimitris Papastamos 		goto err_name;
8456ab2b7b4SDimitris Papastamos 	}
8466ab2b7b4SDimitris Papastamos 	ctl->region = *region;
8476ab2b7b4SDimitris Papastamos 	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
8486ab2b7b4SDimitris Papastamos 	if (!ctl->name) {
8496ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8506ab2b7b4SDimitris Papastamos 		goto err_ctl;
8516ab2b7b4SDimitris Papastamos 	}
8526ab2b7b4SDimitris Papastamos 	ctl->enabled = 1;
8530c2e3f34SDimitris Papastamos 	ctl->set = 0;
8546ab2b7b4SDimitris Papastamos 	ctl->ops.xget = wm_coeff_get;
8556ab2b7b4SDimitris Papastamos 	ctl->ops.xput = wm_coeff_put;
8566ab2b7b4SDimitris Papastamos 	ctl->adsp = dsp;
8576ab2b7b4SDimitris Papastamos 
8586ab2b7b4SDimitris Papastamos 	ctl->len = region->len;
8596ab2b7b4SDimitris Papastamos 	ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
8606ab2b7b4SDimitris Papastamos 	if (!ctl->cache) {
8616ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8626ab2b7b4SDimitris Papastamos 		goto err_ctl_name;
8636ab2b7b4SDimitris Papastamos 	}
8646ab2b7b4SDimitris Papastamos 
8656ab2b7b4SDimitris Papastamos 	ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
8666ab2b7b4SDimitris Papastamos 	if (!ctl_work) {
8676ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8686ab2b7b4SDimitris Papastamos 		goto err_ctl_cache;
8696ab2b7b4SDimitris Papastamos 	}
8706ab2b7b4SDimitris Papastamos 
87181ad93ecSDimitris Papastamos 	ctl_work->adsp = dsp;
8726ab2b7b4SDimitris Papastamos 	ctl_work->ctl = ctl;
8736ab2b7b4SDimitris Papastamos 	INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
8746ab2b7b4SDimitris Papastamos 	schedule_work(&ctl_work->work);
8756ab2b7b4SDimitris Papastamos 
8769dbce044SDan Carpenter found:
8776ab2b7b4SDimitris Papastamos 	kfree(name);
8786ab2b7b4SDimitris Papastamos 
8796ab2b7b4SDimitris Papastamos 	return 0;
8806ab2b7b4SDimitris Papastamos 
8816ab2b7b4SDimitris Papastamos err_ctl_cache:
8826ab2b7b4SDimitris Papastamos 	kfree(ctl->cache);
8836ab2b7b4SDimitris Papastamos err_ctl_name:
8846ab2b7b4SDimitris Papastamos 	kfree(ctl->name);
8856ab2b7b4SDimitris Papastamos err_ctl:
8866ab2b7b4SDimitris Papastamos 	kfree(ctl);
8876ab2b7b4SDimitris Papastamos err_name:
8886ab2b7b4SDimitris Papastamos 	kfree(name);
8896ab2b7b4SDimitris Papastamos 	return ret;
8906ab2b7b4SDimitris Papastamos }
8916ab2b7b4SDimitris Papastamos 
89292bb4c32SDimitris Papastamos static int wm_adsp_setup_algs(struct wm_adsp *dsp)
893db40517cSMark Brown {
894db40517cSMark Brown 	struct regmap *regmap = dsp->regmap;
895db40517cSMark Brown 	struct wmfw_adsp1_id_hdr adsp1_id;
896db40517cSMark Brown 	struct wmfw_adsp2_id_hdr adsp2_id;
897db40517cSMark Brown 	struct wmfw_adsp1_alg_hdr *adsp1_alg;
898db40517cSMark Brown 	struct wmfw_adsp2_alg_hdr *adsp2_alg;
899d62f4bc6SMark Brown 	void *alg, *buf;
900471f4885SMark Brown 	struct wm_adsp_alg_region *region;
901db40517cSMark Brown 	const struct wm_adsp_region *mem;
902db40517cSMark Brown 	unsigned int pos, term;
903d62f4bc6SMark Brown 	size_t algs, buf_size;
904db40517cSMark Brown 	__be32 val;
905db40517cSMark Brown 	int i, ret;
906db40517cSMark Brown 
907db40517cSMark Brown 	switch (dsp->type) {
908db40517cSMark Brown 	case WMFW_ADSP1:
909db40517cSMark Brown 		mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
910db40517cSMark Brown 		break;
911db40517cSMark Brown 	case WMFW_ADSP2:
912db40517cSMark Brown 		mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
913db40517cSMark Brown 		break;
914db40517cSMark Brown 	default:
915db40517cSMark Brown 		mem = NULL;
916db40517cSMark Brown 		break;
917db40517cSMark Brown 	}
918db40517cSMark Brown 
9196c452bdaSTakashi Iwai 	if (WARN_ON(!mem))
920db40517cSMark Brown 		return -EINVAL;
921db40517cSMark Brown 
922db40517cSMark Brown 	switch (dsp->type) {
923db40517cSMark Brown 	case WMFW_ADSP1:
924db40517cSMark Brown 		ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
925db40517cSMark Brown 				      sizeof(adsp1_id));
926db40517cSMark Brown 		if (ret != 0) {
927db40517cSMark Brown 			adsp_err(dsp, "Failed to read algorithm info: %d\n",
928db40517cSMark Brown 				 ret);
929db40517cSMark Brown 			return ret;
930db40517cSMark Brown 		}
931db40517cSMark Brown 
932d62f4bc6SMark Brown 		buf = &adsp1_id;
933d62f4bc6SMark Brown 		buf_size = sizeof(adsp1_id);
934d62f4bc6SMark Brown 
935db40517cSMark Brown 		algs = be32_to_cpu(adsp1_id.algs);
936f395a218SMark Brown 		dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
937db40517cSMark Brown 		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
938f395a218SMark Brown 			  dsp->fw_id,
939db40517cSMark Brown 			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
940db40517cSMark Brown 			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
941db40517cSMark Brown 			  be32_to_cpu(adsp1_id.fw.ver) & 0xff,
942db40517cSMark Brown 			  algs);
943db40517cSMark Brown 
944ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
945ac50009fSMark Brown 		if (!region)
946ac50009fSMark Brown 			return -ENOMEM;
947ac50009fSMark Brown 		region->type = WMFW_ADSP1_ZM;
948ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp1_id.fw.id);
949ac50009fSMark Brown 		region->base = be32_to_cpu(adsp1_id.zm);
950ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
951ac50009fSMark Brown 
952ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
953ac50009fSMark Brown 		if (!region)
954ac50009fSMark Brown 			return -ENOMEM;
955ac50009fSMark Brown 		region->type = WMFW_ADSP1_DM;
956ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp1_id.fw.id);
957ac50009fSMark Brown 		region->base = be32_to_cpu(adsp1_id.dm);
958ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
959ac50009fSMark Brown 
960db40517cSMark Brown 		pos = sizeof(adsp1_id) / 2;
961db40517cSMark Brown 		term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
962db40517cSMark Brown 		break;
963db40517cSMark Brown 
964db40517cSMark Brown 	case WMFW_ADSP2:
965db40517cSMark Brown 		ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
966db40517cSMark Brown 				      sizeof(adsp2_id));
967db40517cSMark Brown 		if (ret != 0) {
968db40517cSMark Brown 			adsp_err(dsp, "Failed to read algorithm info: %d\n",
969db40517cSMark Brown 				 ret);
970db40517cSMark Brown 			return ret;
971db40517cSMark Brown 		}
972db40517cSMark Brown 
973d62f4bc6SMark Brown 		buf = &adsp2_id;
974d62f4bc6SMark Brown 		buf_size = sizeof(adsp2_id);
975d62f4bc6SMark Brown 
976db40517cSMark Brown 		algs = be32_to_cpu(adsp2_id.algs);
977f395a218SMark Brown 		dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
978db40517cSMark Brown 		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
979f395a218SMark Brown 			  dsp->fw_id,
980db40517cSMark Brown 			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
981db40517cSMark Brown 			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
982db40517cSMark Brown 			  be32_to_cpu(adsp2_id.fw.ver) & 0xff,
983db40517cSMark Brown 			  algs);
984db40517cSMark Brown 
985ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
986ac50009fSMark Brown 		if (!region)
987ac50009fSMark Brown 			return -ENOMEM;
988ac50009fSMark Brown 		region->type = WMFW_ADSP2_XM;
989ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp2_id.fw.id);
990ac50009fSMark Brown 		region->base = be32_to_cpu(adsp2_id.xm);
991ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
992ac50009fSMark Brown 
993ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
994ac50009fSMark Brown 		if (!region)
995ac50009fSMark Brown 			return -ENOMEM;
996ac50009fSMark Brown 		region->type = WMFW_ADSP2_YM;
997ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp2_id.fw.id);
998ac50009fSMark Brown 		region->base = be32_to_cpu(adsp2_id.ym);
999ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
1000ac50009fSMark Brown 
1001ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
1002ac50009fSMark Brown 		if (!region)
1003ac50009fSMark Brown 			return -ENOMEM;
1004ac50009fSMark Brown 		region->type = WMFW_ADSP2_ZM;
1005ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp2_id.fw.id);
1006ac50009fSMark Brown 		region->base = be32_to_cpu(adsp2_id.zm);
1007ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
1008ac50009fSMark Brown 
1009db40517cSMark Brown 		pos = sizeof(adsp2_id) / 2;
1010db40517cSMark Brown 		term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
1011db40517cSMark Brown 		break;
1012db40517cSMark Brown 
1013db40517cSMark Brown 	default:
10146c452bdaSTakashi Iwai 		WARN(1, "Unknown DSP type");
1015db40517cSMark Brown 		return -EINVAL;
1016db40517cSMark Brown 	}
1017db40517cSMark Brown 
1018db40517cSMark Brown 	if (algs == 0) {
1019db40517cSMark Brown 		adsp_err(dsp, "No algorithms\n");
1020db40517cSMark Brown 		return -EINVAL;
1021db40517cSMark Brown 	}
1022db40517cSMark Brown 
1023d62f4bc6SMark Brown 	if (algs > 1024) {
1024d62f4bc6SMark Brown 		adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
1025d62f4bc6SMark Brown 		print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
1026d62f4bc6SMark Brown 				     buf, buf_size);
1027d62f4bc6SMark Brown 		return -EINVAL;
1028d62f4bc6SMark Brown 	}
1029d62f4bc6SMark Brown 
1030db40517cSMark Brown 	/* Read the terminator first to validate the length */
1031db40517cSMark Brown 	ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
1032db40517cSMark Brown 	if (ret != 0) {
1033db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1034db40517cSMark Brown 			ret);
1035db40517cSMark Brown 		return ret;
1036db40517cSMark Brown 	}
1037db40517cSMark Brown 
1038db40517cSMark Brown 	if (be32_to_cpu(val) != 0xbedead)
1039db40517cSMark Brown 		adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
1040db40517cSMark Brown 			  term, be32_to_cpu(val));
1041db40517cSMark Brown 
1042f2a93e2aSMark Brown 	alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
1043db40517cSMark Brown 	if (!alg)
1044db40517cSMark Brown 		return -ENOMEM;
1045db40517cSMark Brown 
1046db40517cSMark Brown 	ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
1047db40517cSMark Brown 	if (ret != 0) {
1048db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm list: %d\n",
1049db40517cSMark Brown 			ret);
1050db40517cSMark Brown 		goto out;
1051db40517cSMark Brown 	}
1052db40517cSMark Brown 
1053db40517cSMark Brown 	adsp1_alg = alg;
1054db40517cSMark Brown 	adsp2_alg = alg;
1055db40517cSMark Brown 
1056db40517cSMark Brown 	for (i = 0; i < algs; i++) {
1057db40517cSMark Brown 		switch (dsp->type) {
1058db40517cSMark Brown 		case WMFW_ADSP1:
1059471f4885SMark Brown 			adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
1060db40517cSMark Brown 				  i, be32_to_cpu(adsp1_alg[i].alg.id),
1061db40517cSMark Brown 				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
1062db40517cSMark Brown 				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
1063471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
1064471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].dm),
1065471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].zm));
1066471f4885SMark Brown 
1067471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
1068471f4885SMark Brown 			if (!region)
1069471f4885SMark Brown 				return -ENOMEM;
1070471f4885SMark Brown 			region->type = WMFW_ADSP1_DM;
1071471f4885SMark Brown 			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
1072471f4885SMark Brown 			region->base = be32_to_cpu(adsp1_alg[i].dm);
10736ab2b7b4SDimitris Papastamos 			region->len = 0;
10747480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
10756ab2b7b4SDimitris Papastamos 			if (i + 1 < algs) {
10766ab2b7b4SDimitris Papastamos 				region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
10776ab2b7b4SDimitris Papastamos 				region->len -= be32_to_cpu(adsp1_alg[i].dm);
1078c01422a4SNariman Poushin 				region->len *= 4;
107992bb4c32SDimitris Papastamos 				wm_adsp_create_control(dsp, region);
10806ab2b7b4SDimitris Papastamos 			} else {
10816ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
10826ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp1_alg[i].alg.id));
10836ab2b7b4SDimitris Papastamos 			}
1084471f4885SMark Brown 
1085471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
1086471f4885SMark Brown 			if (!region)
1087471f4885SMark Brown 				return -ENOMEM;
1088471f4885SMark Brown 			region->type = WMFW_ADSP1_ZM;
1089471f4885SMark Brown 			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
1090471f4885SMark Brown 			region->base = be32_to_cpu(adsp1_alg[i].zm);
10916ab2b7b4SDimitris Papastamos 			region->len = 0;
10927480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
10936ab2b7b4SDimitris Papastamos 			if (i + 1 < algs) {
10946ab2b7b4SDimitris Papastamos 				region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
10956ab2b7b4SDimitris Papastamos 				region->len -= be32_to_cpu(adsp1_alg[i].zm);
1096c01422a4SNariman Poushin 				region->len *= 4;
109792bb4c32SDimitris Papastamos 				wm_adsp_create_control(dsp, region);
10986ab2b7b4SDimitris Papastamos 			} else {
10996ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
11006ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp1_alg[i].alg.id));
11016ab2b7b4SDimitris Papastamos 			}
1102db40517cSMark Brown 			break;
1103db40517cSMark Brown 
1104db40517cSMark Brown 		case WMFW_ADSP2:
1105471f4885SMark Brown 			adsp_info(dsp,
1106471f4885SMark Brown 				  "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1107db40517cSMark Brown 				  i, be32_to_cpu(adsp2_alg[i].alg.id),
1108db40517cSMark Brown 				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1109db40517cSMark Brown 				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1110471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1111471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].xm),
1112471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].ym),
1113471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].zm));
1114471f4885SMark Brown 
1115471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
1116471f4885SMark Brown 			if (!region)
1117471f4885SMark Brown 				return -ENOMEM;
1118471f4885SMark Brown 			region->type = WMFW_ADSP2_XM;
1119471f4885SMark Brown 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
1120471f4885SMark Brown 			region->base = be32_to_cpu(adsp2_alg[i].xm);
11216ab2b7b4SDimitris Papastamos 			region->len = 0;
11227480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
11236ab2b7b4SDimitris Papastamos 			if (i + 1 < algs) {
11246ab2b7b4SDimitris Papastamos 				region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
11256ab2b7b4SDimitris Papastamos 				region->len -= be32_to_cpu(adsp2_alg[i].xm);
1126c01422a4SNariman Poushin 				region->len *= 4;
112792bb4c32SDimitris Papastamos 				wm_adsp_create_control(dsp, region);
11286ab2b7b4SDimitris Papastamos 			} else {
11296ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
11306ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp2_alg[i].alg.id));
11316ab2b7b4SDimitris Papastamos 			}
1132471f4885SMark Brown 
1133471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
1134471f4885SMark Brown 			if (!region)
1135471f4885SMark Brown 				return -ENOMEM;
1136471f4885SMark Brown 			region->type = WMFW_ADSP2_YM;
1137471f4885SMark Brown 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
1138471f4885SMark Brown 			region->base = be32_to_cpu(adsp2_alg[i].ym);
11396ab2b7b4SDimitris Papastamos 			region->len = 0;
11407480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
11416ab2b7b4SDimitris Papastamos 			if (i + 1 < algs) {
11426ab2b7b4SDimitris Papastamos 				region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
11436ab2b7b4SDimitris Papastamos 				region->len -= be32_to_cpu(adsp2_alg[i].ym);
1144c01422a4SNariman Poushin 				region->len *= 4;
114592bb4c32SDimitris Papastamos 				wm_adsp_create_control(dsp, region);
11466ab2b7b4SDimitris Papastamos 			} else {
11476ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
11486ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp2_alg[i].alg.id));
11496ab2b7b4SDimitris Papastamos 			}
1150471f4885SMark Brown 
1151471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
1152471f4885SMark Brown 			if (!region)
1153471f4885SMark Brown 				return -ENOMEM;
1154471f4885SMark Brown 			region->type = WMFW_ADSP2_ZM;
1155471f4885SMark Brown 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
1156471f4885SMark Brown 			region->base = be32_to_cpu(adsp2_alg[i].zm);
11576ab2b7b4SDimitris Papastamos 			region->len = 0;
11587480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
11596ab2b7b4SDimitris Papastamos 			if (i + 1 < algs) {
11606ab2b7b4SDimitris Papastamos 				region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
11616ab2b7b4SDimitris Papastamos 				region->len -= be32_to_cpu(adsp2_alg[i].zm);
1162c01422a4SNariman Poushin 				region->len *= 4;
116392bb4c32SDimitris Papastamos 				wm_adsp_create_control(dsp, region);
11646ab2b7b4SDimitris Papastamos 			} else {
11656ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
11666ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp2_alg[i].alg.id));
11676ab2b7b4SDimitris Papastamos 			}
1168db40517cSMark Brown 			break;
1169db40517cSMark Brown 		}
1170db40517cSMark Brown 	}
1171db40517cSMark Brown 
1172db40517cSMark Brown out:
1173db40517cSMark Brown 	kfree(alg);
1174db40517cSMark Brown 	return ret;
1175db40517cSMark Brown }
1176db40517cSMark Brown 
11772159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp)
11782159ad93SMark Brown {
1179cf17c83cSMark Brown 	LIST_HEAD(buf_list);
11802159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
11812159ad93SMark Brown 	struct wmfw_coeff_hdr *hdr;
11822159ad93SMark Brown 	struct wmfw_coeff_item *blk;
11832159ad93SMark Brown 	const struct firmware *firmware;
1184471f4885SMark Brown 	const struct wm_adsp_region *mem;
1185471f4885SMark Brown 	struct wm_adsp_alg_region *alg_region;
11862159ad93SMark Brown 	const char *region_name;
11872159ad93SMark Brown 	int ret, pos, blocks, type, offset, reg;
11882159ad93SMark Brown 	char *file;
1189cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
1190bdaacea3SChris Rattray 	int tmp;
11912159ad93SMark Brown 
11922159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
11932159ad93SMark Brown 	if (file == NULL)
11942159ad93SMark Brown 		return -ENOMEM;
11952159ad93SMark Brown 
11961023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
11971023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
11982159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
11992159ad93SMark Brown 
12002159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
12012159ad93SMark Brown 	if (ret != 0) {
12022159ad93SMark Brown 		adsp_warn(dsp, "Failed to request '%s'\n", file);
12032159ad93SMark Brown 		ret = 0;
12042159ad93SMark Brown 		goto out;
12052159ad93SMark Brown 	}
12062159ad93SMark Brown 	ret = -EINVAL;
12072159ad93SMark Brown 
12082159ad93SMark Brown 	if (sizeof(*hdr) >= firmware->size) {
12092159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
12102159ad93SMark Brown 			file, firmware->size);
12112159ad93SMark Brown 		goto out_fw;
12122159ad93SMark Brown 	}
12132159ad93SMark Brown 
12142159ad93SMark Brown 	hdr = (void*)&firmware->data[0];
12152159ad93SMark Brown 	if (memcmp(hdr->magic, "WMDR", 4) != 0) {
12162159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
1217a4cdbec7SCharles Keepax 		goto out_fw;
12182159ad93SMark Brown 	}
12192159ad93SMark Brown 
1220c712326dSMark Brown 	switch (be32_to_cpu(hdr->rev) & 0xff) {
1221c712326dSMark Brown 	case 1:
1222c712326dSMark Brown 		break;
1223c712326dSMark Brown 	default:
1224c712326dSMark Brown 		adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
1225c712326dSMark Brown 			 file, be32_to_cpu(hdr->rev) & 0xff);
1226c712326dSMark Brown 		ret = -EINVAL;
1227c712326dSMark Brown 		goto out_fw;
1228c712326dSMark Brown 	}
1229c712326dSMark Brown 
12302159ad93SMark Brown 	adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
12312159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >> 16) & 0xff,
12322159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >>  8) & 0xff,
12332159ad93SMark Brown 		le32_to_cpu(hdr->ver) & 0xff);
12342159ad93SMark Brown 
12352159ad93SMark Brown 	pos = le32_to_cpu(hdr->len);
12362159ad93SMark Brown 
12372159ad93SMark Brown 	blocks = 0;
12382159ad93SMark Brown 	while (pos < firmware->size &&
12392159ad93SMark Brown 	       pos - firmware->size > sizeof(*blk)) {
12402159ad93SMark Brown 		blk = (void*)(&firmware->data[pos]);
12412159ad93SMark Brown 
1242c712326dSMark Brown 		type = le16_to_cpu(blk->type);
1243c712326dSMark Brown 		offset = le16_to_cpu(blk->offset);
12442159ad93SMark Brown 
12452159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
12462159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->id),
12472159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >> 16) & 0xff,
12482159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >>  8) & 0xff,
12492159ad93SMark Brown 			 le32_to_cpu(blk->ver) & 0xff);
12502159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
12512159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->len), offset, type);
12522159ad93SMark Brown 
12532159ad93SMark Brown 		reg = 0;
12542159ad93SMark Brown 		region_name = "Unknown";
12552159ad93SMark Brown 		switch (type) {
1256c712326dSMark Brown 		case (WMFW_NAME_TEXT << 8):
1257c712326dSMark Brown 		case (WMFW_INFO_TEXT << 8):
12582159ad93SMark Brown 			break;
1259c712326dSMark Brown 		case (WMFW_ABSOLUTE << 8):
1260f395a218SMark Brown 			/*
1261f395a218SMark Brown 			 * Old files may use this for global
1262f395a218SMark Brown 			 * coefficients.
1263f395a218SMark Brown 			 */
1264f395a218SMark Brown 			if (le32_to_cpu(blk->id) == dsp->fw_id &&
1265f395a218SMark Brown 			    offset == 0) {
1266f395a218SMark Brown 				region_name = "global coefficients";
1267f395a218SMark Brown 				mem = wm_adsp_find_region(dsp, type);
1268f395a218SMark Brown 				if (!mem) {
1269f395a218SMark Brown 					adsp_err(dsp, "No ZM\n");
1270f395a218SMark Brown 					break;
1271f395a218SMark Brown 				}
1272f395a218SMark Brown 				reg = wm_adsp_region_to_reg(mem, 0);
1273f395a218SMark Brown 
1274f395a218SMark Brown 			} else {
12752159ad93SMark Brown 				region_name = "register";
12762159ad93SMark Brown 				reg = offset;
1277f395a218SMark Brown 			}
12782159ad93SMark Brown 			break;
1279471f4885SMark Brown 
1280471f4885SMark Brown 		case WMFW_ADSP1_DM:
1281471f4885SMark Brown 		case WMFW_ADSP1_ZM:
1282471f4885SMark Brown 		case WMFW_ADSP2_XM:
1283471f4885SMark Brown 		case WMFW_ADSP2_YM:
1284471f4885SMark Brown 			adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
1285471f4885SMark Brown 				 file, blocks, le32_to_cpu(blk->len),
1286471f4885SMark Brown 				 type, le32_to_cpu(blk->id));
1287471f4885SMark Brown 
1288471f4885SMark Brown 			mem = wm_adsp_find_region(dsp, type);
1289471f4885SMark Brown 			if (!mem) {
1290471f4885SMark Brown 				adsp_err(dsp, "No base for region %x\n", type);
1291471f4885SMark Brown 				break;
1292471f4885SMark Brown 			}
1293471f4885SMark Brown 
1294471f4885SMark Brown 			reg = 0;
1295471f4885SMark Brown 			list_for_each_entry(alg_region,
1296471f4885SMark Brown 					    &dsp->alg_regions, list) {
1297471f4885SMark Brown 				if (le32_to_cpu(blk->id) == alg_region->alg &&
1298471f4885SMark Brown 				    type == alg_region->type) {
1299338c5188SMark Brown 					reg = alg_region->base;
1300471f4885SMark Brown 					reg = wm_adsp_region_to_reg(mem,
1301471f4885SMark Brown 								    reg);
1302338c5188SMark Brown 					reg += offset;
1303d733dc08SCharles Keepax 					break;
1304471f4885SMark Brown 				}
1305471f4885SMark Brown 			}
1306471f4885SMark Brown 
1307471f4885SMark Brown 			if (reg == 0)
1308471f4885SMark Brown 				adsp_err(dsp, "No %x for algorithm %x\n",
1309471f4885SMark Brown 					 type, le32_to_cpu(blk->id));
1310471f4885SMark Brown 			break;
1311471f4885SMark Brown 
13122159ad93SMark Brown 		default:
131325c62f7eSMark Brown 			adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
131425c62f7eSMark Brown 				 file, blocks, type, pos);
13152159ad93SMark Brown 			break;
13162159ad93SMark Brown 		}
13172159ad93SMark Brown 
13182159ad93SMark Brown 		if (reg) {
1319cf17c83cSMark Brown 			buf = wm_adsp_buf_alloc(blk->data,
1320cf17c83cSMark Brown 						le32_to_cpu(blk->len),
1321cf17c83cSMark Brown 						&buf_list);
1322a76fefabSMark Brown 			if (!buf) {
1323a76fefabSMark Brown 				adsp_err(dsp, "Out of memory\n");
1324f4b82812SWei Yongjun 				ret = -ENOMEM;
1325f4b82812SWei Yongjun 				goto out_fw;
1326a76fefabSMark Brown 			}
1327a76fefabSMark Brown 
132820da6d5aSMark Brown 			adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
132920da6d5aSMark Brown 				 file, blocks, le32_to_cpu(blk->len),
133020da6d5aSMark Brown 				 reg);
1331cf17c83cSMark Brown 			ret = regmap_raw_write_async(regmap, reg, buf->buf,
13322159ad93SMark Brown 						     le32_to_cpu(blk->len));
13332159ad93SMark Brown 			if (ret != 0) {
13342159ad93SMark Brown 				adsp_err(dsp,
133543bc3bf6SDimitris Papastamos 					"%s.%d: Failed to write to %x in %s: %d\n",
133643bc3bf6SDimitris Papastamos 					file, blocks, reg, region_name, ret);
13372159ad93SMark Brown 			}
13382159ad93SMark Brown 		}
13392159ad93SMark Brown 
1340bdaacea3SChris Rattray 		tmp = le32_to_cpu(blk->len) % 4;
1341bdaacea3SChris Rattray 		if (tmp)
1342bdaacea3SChris Rattray 			pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk);
1343bdaacea3SChris Rattray 		else
13442159ad93SMark Brown 			pos += le32_to_cpu(blk->len) + sizeof(*blk);
1345bdaacea3SChris Rattray 
13462159ad93SMark Brown 		blocks++;
13472159ad93SMark Brown 	}
13482159ad93SMark Brown 
1349cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
1350cf17c83cSMark Brown 	if (ret != 0)
1351cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1352cf17c83cSMark Brown 
13532159ad93SMark Brown 	if (pos > firmware->size)
13542159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
13552159ad93SMark Brown 			  file, blocks, pos - firmware->size);
13562159ad93SMark Brown 
13572159ad93SMark Brown out_fw:
13582159ad93SMark Brown 	release_firmware(firmware);
1359cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
13602159ad93SMark Brown out:
13612159ad93SMark Brown 	kfree(file);
1362f4b82812SWei Yongjun 	return ret;
13632159ad93SMark Brown }
13642159ad93SMark Brown 
13655e7a7a22SMark Brown int wm_adsp1_init(struct wm_adsp *adsp)
13665e7a7a22SMark Brown {
13675e7a7a22SMark Brown 	INIT_LIST_HEAD(&adsp->alg_regions);
13685e7a7a22SMark Brown 
13695e7a7a22SMark Brown 	return 0;
13705e7a7a22SMark Brown }
13715e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
13725e7a7a22SMark Brown 
13732159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w,
13742159ad93SMark Brown 		   struct snd_kcontrol *kcontrol,
13752159ad93SMark Brown 		   int event)
13762159ad93SMark Brown {
13772159ad93SMark Brown 	struct snd_soc_codec *codec = w->codec;
13782159ad93SMark Brown 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
13792159ad93SMark Brown 	struct wm_adsp *dsp = &dsps[w->shift];
1380b0101b4fSDimitris Papastamos 	struct wm_adsp_alg_region *alg_region;
13816ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
13822159ad93SMark Brown 	int ret;
138394e205bfSChris Rattray 	int val;
13842159ad93SMark Brown 
138592bb4c32SDimitris Papastamos 	dsp->card = codec->card;
138692bb4c32SDimitris Papastamos 
13872159ad93SMark Brown 	switch (event) {
13882159ad93SMark Brown 	case SND_SOC_DAPM_POST_PMU:
13892159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
13902159ad93SMark Brown 				   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
13912159ad93SMark Brown 
139294e205bfSChris Rattray 		/*
139394e205bfSChris Rattray 		 * For simplicity set the DSP clock rate to be the
139494e205bfSChris Rattray 		 * SYSCLK rate rather than making it configurable.
139594e205bfSChris Rattray 		 */
139694e205bfSChris Rattray 		if(dsp->sysclk_reg) {
139794e205bfSChris Rattray 			ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
139894e205bfSChris Rattray 			if (ret != 0) {
139994e205bfSChris Rattray 				adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
140094e205bfSChris Rattray 				ret);
140194e205bfSChris Rattray 				return ret;
140294e205bfSChris Rattray 			}
140394e205bfSChris Rattray 
140494e205bfSChris Rattray 			val = (val & dsp->sysclk_mask)
140594e205bfSChris Rattray 				>> dsp->sysclk_shift;
140694e205bfSChris Rattray 
140794e205bfSChris Rattray 			ret = regmap_update_bits(dsp->regmap,
140894e205bfSChris Rattray 						 dsp->base + ADSP1_CONTROL_31,
140994e205bfSChris Rattray 						 ADSP1_CLK_SEL_MASK, val);
141094e205bfSChris Rattray 			if (ret != 0) {
141194e205bfSChris Rattray 				adsp_err(dsp, "Failed to set clock rate: %d\n",
141294e205bfSChris Rattray 					 ret);
141394e205bfSChris Rattray 				return ret;
141494e205bfSChris Rattray 			}
141594e205bfSChris Rattray 		}
141694e205bfSChris Rattray 
14172159ad93SMark Brown 		ret = wm_adsp_load(dsp);
14182159ad93SMark Brown 		if (ret != 0)
14192159ad93SMark Brown 			goto err;
14202159ad93SMark Brown 
142192bb4c32SDimitris Papastamos 		ret = wm_adsp_setup_algs(dsp);
1422db40517cSMark Brown 		if (ret != 0)
1423db40517cSMark Brown 			goto err;
1424db40517cSMark Brown 
14252159ad93SMark Brown 		ret = wm_adsp_load_coeff(dsp);
14262159ad93SMark Brown 		if (ret != 0)
14272159ad93SMark Brown 			goto err;
14282159ad93SMark Brown 
14290c2e3f34SDimitris Papastamos 		/* Initialize caches for enabled and unset controls */
143081ad93ecSDimitris Papastamos 		ret = wm_coeff_init_control_caches(dsp);
14316ab2b7b4SDimitris Papastamos 		if (ret != 0)
14326ab2b7b4SDimitris Papastamos 			goto err;
14336ab2b7b4SDimitris Papastamos 
14340c2e3f34SDimitris Papastamos 		/* Sync set controls */
143581ad93ecSDimitris Papastamos 		ret = wm_coeff_sync_controls(dsp);
14366ab2b7b4SDimitris Papastamos 		if (ret != 0)
14376ab2b7b4SDimitris Papastamos 			goto err;
14386ab2b7b4SDimitris Papastamos 
14392159ad93SMark Brown 		/* Start the core running */
14402159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14412159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START,
14422159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START);
14432159ad93SMark Brown 		break;
14442159ad93SMark Brown 
14452159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
14462159ad93SMark Brown 		/* Halt the core */
14472159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14482159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START, 0);
14492159ad93SMark Brown 
14502159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
14512159ad93SMark Brown 				   ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
14522159ad93SMark Brown 
14532159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14542159ad93SMark Brown 				   ADSP1_SYS_ENA, 0);
14556ab2b7b4SDimitris Papastamos 
145681ad93ecSDimitris Papastamos 		list_for_each_entry(ctl, &dsp->ctl_list, list)
14576ab2b7b4SDimitris Papastamos 			ctl->enabled = 0;
1458b0101b4fSDimitris Papastamos 
1459b0101b4fSDimitris Papastamos 		while (!list_empty(&dsp->alg_regions)) {
1460b0101b4fSDimitris Papastamos 			alg_region = list_first_entry(&dsp->alg_regions,
1461b0101b4fSDimitris Papastamos 						      struct wm_adsp_alg_region,
1462b0101b4fSDimitris Papastamos 						      list);
1463b0101b4fSDimitris Papastamos 			list_del(&alg_region->list);
1464b0101b4fSDimitris Papastamos 			kfree(alg_region);
1465b0101b4fSDimitris Papastamos 		}
14662159ad93SMark Brown 		break;
14672159ad93SMark Brown 
14682159ad93SMark Brown 	default:
14692159ad93SMark Brown 		break;
14702159ad93SMark Brown 	}
14712159ad93SMark Brown 
14722159ad93SMark Brown 	return 0;
14732159ad93SMark Brown 
14742159ad93SMark Brown err:
14752159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14762159ad93SMark Brown 			   ADSP1_SYS_ENA, 0);
14772159ad93SMark Brown 	return ret;
14782159ad93SMark Brown }
14792159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
14802159ad93SMark Brown 
14812159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp)
14822159ad93SMark Brown {
14832159ad93SMark Brown 	unsigned int val;
14842159ad93SMark Brown 	int ret, count;
14852159ad93SMark Brown 
14861552c325SMark Brown 	ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
14872159ad93SMark Brown 				       ADSP2_SYS_ENA, ADSP2_SYS_ENA);
14882159ad93SMark Brown 	if (ret != 0)
14892159ad93SMark Brown 		return ret;
14902159ad93SMark Brown 
14912159ad93SMark Brown 	/* Wait for the RAM to start, should be near instantaneous */
1492939fd1e8SCharles Keepax 	for (count = 0; count < 10; ++count) {
14932159ad93SMark Brown 		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
14942159ad93SMark Brown 				  &val);
14952159ad93SMark Brown 		if (ret != 0)
14962159ad93SMark Brown 			return ret;
1497939fd1e8SCharles Keepax 
1498939fd1e8SCharles Keepax 		if (val & ADSP2_RAM_RDY)
1499939fd1e8SCharles Keepax 			break;
1500939fd1e8SCharles Keepax 
1501939fd1e8SCharles Keepax 		msleep(1);
1502939fd1e8SCharles Keepax 	}
15032159ad93SMark Brown 
15042159ad93SMark Brown 	if (!(val & ADSP2_RAM_RDY)) {
15052159ad93SMark Brown 		adsp_err(dsp, "Failed to start DSP RAM\n");
15062159ad93SMark Brown 		return -EBUSY;
15072159ad93SMark Brown 	}
15082159ad93SMark Brown 
15092159ad93SMark Brown 	adsp_dbg(dsp, "RAM ready after %d polls\n", count);
15102159ad93SMark Brown 
15112159ad93SMark Brown 	return 0;
15122159ad93SMark Brown }
15132159ad93SMark Brown 
151418b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work)
15152159ad93SMark Brown {
1516d8a64d6aSCharles Keepax 	struct wm_adsp *dsp = container_of(work,
1517d8a64d6aSCharles Keepax 					   struct wm_adsp,
1518d8a64d6aSCharles Keepax 					   boot_work);
15192159ad93SMark Brown 	int ret;
1520d8a64d6aSCharles Keepax 	unsigned int val;
15212159ad93SMark Brown 
1522dd49e2c8SMark Brown 	/*
1523dd49e2c8SMark Brown 	 * For simplicity set the DSP clock rate to be the
1524dd49e2c8SMark Brown 	 * SYSCLK rate rather than making it configurable.
1525dd49e2c8SMark Brown 	 */
1526dd49e2c8SMark Brown 	ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1527dd49e2c8SMark Brown 	if (ret != 0) {
1528d8a64d6aSCharles Keepax 		adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
1529d8a64d6aSCharles Keepax 		return;
1530dd49e2c8SMark Brown 	}
1531dd49e2c8SMark Brown 	val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1532dd49e2c8SMark Brown 		>> ARIZONA_SYSCLK_FREQ_SHIFT;
1533dd49e2c8SMark Brown 
15341552c325SMark Brown 	ret = regmap_update_bits_async(dsp->regmap,
1535dd49e2c8SMark Brown 				       dsp->base + ADSP2_CLOCKING,
1536dd49e2c8SMark Brown 				       ADSP2_CLK_SEL_MASK, val);
1537dd49e2c8SMark Brown 	if (ret != 0) {
1538d8a64d6aSCharles Keepax 		adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
1539d8a64d6aSCharles Keepax 		return;
1540dd49e2c8SMark Brown 	}
1541dd49e2c8SMark Brown 
1542973838a0SMark Brown 	if (dsp->dvfs) {
1543973838a0SMark Brown 		ret = regmap_read(dsp->regmap,
1544973838a0SMark Brown 				  dsp->base + ADSP2_CLOCKING, &val);
1545973838a0SMark Brown 		if (ret != 0) {
154662c35b3bSCharles Keepax 			adsp_err(dsp, "Failed to read clocking: %d\n", ret);
1547d8a64d6aSCharles Keepax 			return;
1548973838a0SMark Brown 		}
1549973838a0SMark Brown 
155025c6fdb0SMark Brown 		if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
1551973838a0SMark Brown 			ret = regulator_enable(dsp->dvfs);
1552973838a0SMark Brown 			if (ret != 0) {
155362c35b3bSCharles Keepax 				adsp_err(dsp,
1554973838a0SMark Brown 					 "Failed to enable supply: %d\n",
1555973838a0SMark Brown 					 ret);
1556d8a64d6aSCharles Keepax 				return;
1557973838a0SMark Brown 			}
1558973838a0SMark Brown 
1559973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs,
1560973838a0SMark Brown 						    1800000,
1561973838a0SMark Brown 						    1800000);
1562973838a0SMark Brown 			if (ret != 0) {
156362c35b3bSCharles Keepax 				adsp_err(dsp,
1564973838a0SMark Brown 					 "Failed to raise supply: %d\n",
1565973838a0SMark Brown 					 ret);
1566d8a64d6aSCharles Keepax 				return;
1567973838a0SMark Brown 			}
1568973838a0SMark Brown 		}
1569973838a0SMark Brown 	}
1570973838a0SMark Brown 
15712159ad93SMark Brown 	ret = wm_adsp2_ena(dsp);
15722159ad93SMark Brown 	if (ret != 0)
1573d8a64d6aSCharles Keepax 		return;
15742159ad93SMark Brown 
15752159ad93SMark Brown 	ret = wm_adsp_load(dsp);
15762159ad93SMark Brown 	if (ret != 0)
15772159ad93SMark Brown 		goto err;
15782159ad93SMark Brown 
157992bb4c32SDimitris Papastamos 	ret = wm_adsp_setup_algs(dsp);
1580db40517cSMark Brown 	if (ret != 0)
1581db40517cSMark Brown 		goto err;
1582db40517cSMark Brown 
15832159ad93SMark Brown 	ret = wm_adsp_load_coeff(dsp);
15842159ad93SMark Brown 	if (ret != 0)
15852159ad93SMark Brown 		goto err;
15862159ad93SMark Brown 
15870c2e3f34SDimitris Papastamos 	/* Initialize caches for enabled and unset controls */
158881ad93ecSDimitris Papastamos 	ret = wm_coeff_init_control_caches(dsp);
15896ab2b7b4SDimitris Papastamos 	if (ret != 0)
15906ab2b7b4SDimitris Papastamos 		goto err;
15916ab2b7b4SDimitris Papastamos 
15920c2e3f34SDimitris Papastamos 	/* Sync set controls */
159381ad93ecSDimitris Papastamos 	ret = wm_coeff_sync_controls(dsp);
15946ab2b7b4SDimitris Papastamos 	if (ret != 0)
15956ab2b7b4SDimitris Papastamos 		goto err;
15966ab2b7b4SDimitris Papastamos 
15971552c325SMark Brown 	ret = regmap_update_bits_async(dsp->regmap,
15982159ad93SMark Brown 				       dsp->base + ADSP2_CONTROL,
1599d8a64d6aSCharles Keepax 				       ADSP2_CORE_ENA,
1600d8a64d6aSCharles Keepax 				       ADSP2_CORE_ENA);
16012159ad93SMark Brown 	if (ret != 0)
16022159ad93SMark Brown 		goto err;
16031023dbd9SMark Brown 
16041023dbd9SMark Brown 	dsp->running = true;
1605d8a64d6aSCharles Keepax 
1606d8a64d6aSCharles Keepax 	return;
1607d8a64d6aSCharles Keepax 
1608d8a64d6aSCharles Keepax err:
1609d8a64d6aSCharles Keepax 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1610d8a64d6aSCharles Keepax 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
1611d8a64d6aSCharles Keepax }
1612d8a64d6aSCharles Keepax 
161312db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
161412db5eddSCharles Keepax 		   struct snd_kcontrol *kcontrol, int event)
161512db5eddSCharles Keepax {
161612db5eddSCharles Keepax 	struct snd_soc_codec *codec = w->codec;
161712db5eddSCharles Keepax 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
161812db5eddSCharles Keepax 	struct wm_adsp *dsp = &dsps[w->shift];
161912db5eddSCharles Keepax 
162012db5eddSCharles Keepax 	dsp->card = codec->card;
162112db5eddSCharles Keepax 
162212db5eddSCharles Keepax 	switch (event) {
162312db5eddSCharles Keepax 	case SND_SOC_DAPM_PRE_PMU:
162412db5eddSCharles Keepax 		queue_work(system_unbound_wq, &dsp->boot_work);
162512db5eddSCharles Keepax 		break;
162612db5eddSCharles Keepax 	default:
162712db5eddSCharles Keepax 		break;
1628cab27258SCharles Keepax 	}
162912db5eddSCharles Keepax 
163012db5eddSCharles Keepax 	return 0;
163112db5eddSCharles Keepax }
163212db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
163312db5eddSCharles Keepax 
1634d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1635d8a64d6aSCharles Keepax 		   struct snd_kcontrol *kcontrol, int event)
1636d8a64d6aSCharles Keepax {
1637d8a64d6aSCharles Keepax 	struct snd_soc_codec *codec = w->codec;
1638d8a64d6aSCharles Keepax 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1639d8a64d6aSCharles Keepax 	struct wm_adsp *dsp = &dsps[w->shift];
1640d8a64d6aSCharles Keepax 	struct wm_adsp_alg_region *alg_region;
1641d8a64d6aSCharles Keepax 	struct wm_coeff_ctl *ctl;
1642d8a64d6aSCharles Keepax 	int ret;
1643d8a64d6aSCharles Keepax 
1644d8a64d6aSCharles Keepax 	switch (event) {
1645d8a64d6aSCharles Keepax 	case SND_SOC_DAPM_POST_PMU:
1646d8a64d6aSCharles Keepax 		flush_work(&dsp->boot_work);
1647d8a64d6aSCharles Keepax 
1648d8a64d6aSCharles Keepax 		if (!dsp->running)
1649d8a64d6aSCharles Keepax 			return -EIO;
1650d8a64d6aSCharles Keepax 
1651d8a64d6aSCharles Keepax 		ret = regmap_update_bits(dsp->regmap,
1652d8a64d6aSCharles Keepax 					 dsp->base + ADSP2_CONTROL,
1653d8a64d6aSCharles Keepax 					 ADSP2_START,
1654d8a64d6aSCharles Keepax 					 ADSP2_START);
1655d8a64d6aSCharles Keepax 		if (ret != 0)
1656d8a64d6aSCharles Keepax 			goto err;
16572159ad93SMark Brown 		break;
16582159ad93SMark Brown 
16592159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
16601023dbd9SMark Brown 		dsp->running = false;
16611023dbd9SMark Brown 
16622159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1663a7f9be7eSMark Brown 				   ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1664a7f9be7eSMark Brown 				   ADSP2_START, 0);
1665973838a0SMark Brown 
16662d30b575SMark Brown 		/* Make sure DMAs are quiesced */
16672d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
16682d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
16692d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
16702d30b575SMark Brown 
1671973838a0SMark Brown 		if (dsp->dvfs) {
1672973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs, 1200000,
1673973838a0SMark Brown 						    1800000);
1674973838a0SMark Brown 			if (ret != 0)
167562c35b3bSCharles Keepax 				adsp_warn(dsp,
1676973838a0SMark Brown 					  "Failed to lower supply: %d\n",
1677973838a0SMark Brown 					  ret);
1678973838a0SMark Brown 
1679973838a0SMark Brown 			ret = regulator_disable(dsp->dvfs);
1680973838a0SMark Brown 			if (ret != 0)
168162c35b3bSCharles Keepax 				adsp_err(dsp,
1682973838a0SMark Brown 					 "Failed to enable supply: %d\n",
1683973838a0SMark Brown 					 ret);
1684973838a0SMark Brown 		}
1685471f4885SMark Brown 
168681ad93ecSDimitris Papastamos 		list_for_each_entry(ctl, &dsp->ctl_list, list)
16876ab2b7b4SDimitris Papastamos 			ctl->enabled = 0;
16886ab2b7b4SDimitris Papastamos 
1689471f4885SMark Brown 		while (!list_empty(&dsp->alg_regions)) {
1690471f4885SMark Brown 			alg_region = list_first_entry(&dsp->alg_regions,
1691471f4885SMark Brown 						      struct wm_adsp_alg_region,
1692471f4885SMark Brown 						      list);
1693471f4885SMark Brown 			list_del(&alg_region->list);
1694471f4885SMark Brown 			kfree(alg_region);
1695471f4885SMark Brown 		}
1696ddbc5efeSCharles Keepax 
1697ddbc5efeSCharles Keepax 		adsp_dbg(dsp, "Shutdown complete\n");
16982159ad93SMark Brown 		break;
16992159ad93SMark Brown 
17002159ad93SMark Brown 	default:
17012159ad93SMark Brown 		break;
17022159ad93SMark Brown 	}
17032159ad93SMark Brown 
17042159ad93SMark Brown 	return 0;
17052159ad93SMark Brown err:
17062159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1707a7f9be7eSMark Brown 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
17082159ad93SMark Brown 	return ret;
17092159ad93SMark Brown }
17102159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event);
1711973838a0SMark Brown 
1712973838a0SMark Brown int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1713973838a0SMark Brown {
1714973838a0SMark Brown 	int ret;
1715973838a0SMark Brown 
171610a2b662SMark Brown 	/*
171710a2b662SMark Brown 	 * Disable the DSP memory by default when in reset for a small
171810a2b662SMark Brown 	 * power saving.
171910a2b662SMark Brown 	 */
172010a2b662SMark Brown 	ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
172110a2b662SMark Brown 				 ADSP2_MEM_ENA, 0);
172210a2b662SMark Brown 	if (ret != 0) {
172310a2b662SMark Brown 		adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
172410a2b662SMark Brown 		return ret;
172510a2b662SMark Brown 	}
172610a2b662SMark Brown 
1727471f4885SMark Brown 	INIT_LIST_HEAD(&adsp->alg_regions);
172881ad93ecSDimitris Papastamos 	INIT_LIST_HEAD(&adsp->ctl_list);
1729d8a64d6aSCharles Keepax 	INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);
17306ab2b7b4SDimitris Papastamos 
1731973838a0SMark Brown 	if (dvfs) {
1732973838a0SMark Brown 		adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1733973838a0SMark Brown 		if (IS_ERR(adsp->dvfs)) {
1734973838a0SMark Brown 			ret = PTR_ERR(adsp->dvfs);
173562c35b3bSCharles Keepax 			adsp_err(adsp, "Failed to get DCVDD: %d\n", ret);
173681ad93ecSDimitris Papastamos 			return ret;
1737973838a0SMark Brown 		}
1738973838a0SMark Brown 
1739973838a0SMark Brown 		ret = regulator_enable(adsp->dvfs);
1740973838a0SMark Brown 		if (ret != 0) {
174162c35b3bSCharles Keepax 			adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret);
174281ad93ecSDimitris Papastamos 			return ret;
1743973838a0SMark Brown 		}
1744973838a0SMark Brown 
1745973838a0SMark Brown 		ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1746973838a0SMark Brown 		if (ret != 0) {
174762c35b3bSCharles Keepax 			adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret);
174881ad93ecSDimitris Papastamos 			return ret;
1749973838a0SMark Brown 		}
1750973838a0SMark Brown 
1751973838a0SMark Brown 		ret = regulator_disable(adsp->dvfs);
1752973838a0SMark Brown 		if (ret != 0) {
175362c35b3bSCharles Keepax 			adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret);
175481ad93ecSDimitris Papastamos 			return ret;
1755973838a0SMark Brown 		}
1756973838a0SMark Brown 	}
1757973838a0SMark Brown 
1758973838a0SMark Brown 	return 0;
1759973838a0SMark Brown }
1760973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
1761