xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision 6958eb2a)
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>
24cdcd7f72SCharles Keepax #include <linux/vmalloc.h>
256ab2b7b4SDimitris Papastamos #include <linux/workqueue.h>
262159ad93SMark Brown #include <sound/core.h>
272159ad93SMark Brown #include <sound/pcm.h>
282159ad93SMark Brown #include <sound/pcm_params.h>
292159ad93SMark Brown #include <sound/soc.h>
302159ad93SMark Brown #include <sound/jack.h>
312159ad93SMark Brown #include <sound/initval.h>
322159ad93SMark Brown #include <sound/tlv.h>
332159ad93SMark Brown 
342159ad93SMark Brown #include <linux/mfd/arizona/registers.h>
352159ad93SMark Brown 
36dc91428aSMark Brown #include "arizona.h"
372159ad93SMark Brown #include "wm_adsp.h"
382159ad93SMark Brown 
392159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \
402159ad93SMark Brown 	dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
412159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \
422159ad93SMark Brown 	dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
432159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \
442159ad93SMark Brown 	dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
452159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \
462159ad93SMark Brown 	dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
472159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \
482159ad93SMark Brown 	dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
492159ad93SMark Brown 
502159ad93SMark Brown #define ADSP1_CONTROL_1                   0x00
512159ad93SMark Brown #define ADSP1_CONTROL_2                   0x02
522159ad93SMark Brown #define ADSP1_CONTROL_3                   0x03
532159ad93SMark Brown #define ADSP1_CONTROL_4                   0x04
542159ad93SMark Brown #define ADSP1_CONTROL_5                   0x06
552159ad93SMark Brown #define ADSP1_CONTROL_6                   0x07
562159ad93SMark Brown #define ADSP1_CONTROL_7                   0x08
572159ad93SMark Brown #define ADSP1_CONTROL_8                   0x09
582159ad93SMark Brown #define ADSP1_CONTROL_9                   0x0A
592159ad93SMark Brown #define ADSP1_CONTROL_10                  0x0B
602159ad93SMark Brown #define ADSP1_CONTROL_11                  0x0C
612159ad93SMark Brown #define ADSP1_CONTROL_12                  0x0D
622159ad93SMark Brown #define ADSP1_CONTROL_13                  0x0F
632159ad93SMark Brown #define ADSP1_CONTROL_14                  0x10
642159ad93SMark Brown #define ADSP1_CONTROL_15                  0x11
652159ad93SMark Brown #define ADSP1_CONTROL_16                  0x12
662159ad93SMark Brown #define ADSP1_CONTROL_17                  0x13
672159ad93SMark Brown #define ADSP1_CONTROL_18                  0x14
682159ad93SMark Brown #define ADSP1_CONTROL_19                  0x16
692159ad93SMark Brown #define ADSP1_CONTROL_20                  0x17
702159ad93SMark Brown #define ADSP1_CONTROL_21                  0x18
712159ad93SMark Brown #define ADSP1_CONTROL_22                  0x1A
722159ad93SMark Brown #define ADSP1_CONTROL_23                  0x1B
732159ad93SMark Brown #define ADSP1_CONTROL_24                  0x1C
742159ad93SMark Brown #define ADSP1_CONTROL_25                  0x1E
752159ad93SMark Brown #define ADSP1_CONTROL_26                  0x20
762159ad93SMark Brown #define ADSP1_CONTROL_27                  0x21
772159ad93SMark Brown #define ADSP1_CONTROL_28                  0x22
782159ad93SMark Brown #define ADSP1_CONTROL_29                  0x23
792159ad93SMark Brown #define ADSP1_CONTROL_30                  0x24
802159ad93SMark Brown #define ADSP1_CONTROL_31                  0x26
812159ad93SMark Brown 
822159ad93SMark Brown /*
832159ad93SMark Brown  * ADSP1 Control 19
842159ad93SMark Brown  */
852159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
862159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
872159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
882159ad93SMark Brown 
892159ad93SMark Brown 
902159ad93SMark Brown /*
912159ad93SMark Brown  * ADSP1 Control 30
922159ad93SMark Brown  */
932159ad93SMark Brown #define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
952159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
962159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
972159ad93SMark Brown #define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
982159ad93SMark Brown #define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
992159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
1002159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
1012159ad93SMark Brown #define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
1022159ad93SMark Brown #define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
1032159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1042159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1052159ad93SMark Brown #define ADSP1_START                       0x0001  /* DSP1_START */
1062159ad93SMark Brown #define ADSP1_START_MASK                  0x0001  /* DSP1_START */
1072159ad93SMark Brown #define ADSP1_START_SHIFT                      0  /* DSP1_START */
1082159ad93SMark Brown #define ADSP1_START_WIDTH                      1  /* DSP1_START */
1092159ad93SMark Brown 
11094e205bfSChris Rattray /*
11194e205bfSChris Rattray  * ADSP1 Control 31
11294e205bfSChris Rattray  */
11394e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
11494e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
11594e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
11694e205bfSChris Rattray 
1172d30b575SMark Brown #define ADSP2_CONTROL        0x0
1182d30b575SMark Brown #define ADSP2_CLOCKING       0x1
1192d30b575SMark Brown #define ADSP2_STATUS1        0x4
1202d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30
1212d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31
1222d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34
1232159ad93SMark Brown 
1242159ad93SMark Brown /*
1252159ad93SMark Brown  * ADSP2 Control
1262159ad93SMark Brown  */
1272159ad93SMark Brown 
1282159ad93SMark Brown #define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
1292159ad93SMark Brown #define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
1302159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
1312159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
1322159ad93SMark Brown #define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
1332159ad93SMark Brown #define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
1342159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
1352159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
1362159ad93SMark Brown #define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
1372159ad93SMark Brown #define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
1382159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1392159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1402159ad93SMark Brown #define ADSP2_START                       0x0001  /* DSP1_START */
1412159ad93SMark Brown #define ADSP2_START_MASK                  0x0001  /* DSP1_START */
1422159ad93SMark Brown #define ADSP2_START_SHIFT                      0  /* DSP1_START */
1432159ad93SMark Brown #define ADSP2_START_WIDTH                      1  /* DSP1_START */
1442159ad93SMark Brown 
1452159ad93SMark Brown /*
146973838a0SMark Brown  * ADSP2 clocking
147973838a0SMark Brown  */
148973838a0SMark Brown #define ADSP2_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
149973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
150973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
151973838a0SMark Brown 
152973838a0SMark Brown /*
1532159ad93SMark Brown  * ADSP2 Status 1
1542159ad93SMark Brown  */
1552159ad93SMark Brown #define ADSP2_RAM_RDY                     0x0001
1562159ad93SMark Brown #define ADSP2_RAM_RDY_MASK                0x0001
1572159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT                    0
1582159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH                    1
1592159ad93SMark Brown 
160cf17c83cSMark Brown struct wm_adsp_buf {
161cf17c83cSMark Brown 	struct list_head list;
162cf17c83cSMark Brown 	void *buf;
163cf17c83cSMark Brown };
164cf17c83cSMark Brown 
165cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
166cf17c83cSMark Brown 					     struct list_head *list)
167cf17c83cSMark Brown {
168cf17c83cSMark Brown 	struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
169cf17c83cSMark Brown 
170cf17c83cSMark Brown 	if (buf == NULL)
171cf17c83cSMark Brown 		return NULL;
172cf17c83cSMark Brown 
173cdcd7f72SCharles Keepax 	buf->buf = vmalloc(len);
174cf17c83cSMark Brown 	if (!buf->buf) {
175cdcd7f72SCharles Keepax 		vfree(buf);
176cf17c83cSMark Brown 		return NULL;
177cf17c83cSMark Brown 	}
178cdcd7f72SCharles Keepax 	memcpy(buf->buf, src, len);
179cf17c83cSMark Brown 
180cf17c83cSMark Brown 	if (list)
181cf17c83cSMark Brown 		list_add_tail(&buf->list, list);
182cf17c83cSMark Brown 
183cf17c83cSMark Brown 	return buf;
184cf17c83cSMark Brown }
185cf17c83cSMark Brown 
186cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list)
187cf17c83cSMark Brown {
188cf17c83cSMark Brown 	while (!list_empty(list)) {
189cf17c83cSMark Brown 		struct wm_adsp_buf *buf = list_first_entry(list,
190cf17c83cSMark Brown 							   struct wm_adsp_buf,
191cf17c83cSMark Brown 							   list);
192cf17c83cSMark Brown 		list_del(&buf->list);
193cdcd7f72SCharles Keepax 		vfree(buf->buf);
194cf17c83cSMark Brown 		kfree(buf);
195cf17c83cSMark Brown 	}
196cf17c83cSMark Brown }
197cf17c83cSMark Brown 
19836e8fe99SMark Brown #define WM_ADSP_NUM_FW 4
1991023dbd9SMark Brown 
200dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0
201dd84f925SMark Brown #define WM_ADSP_FW_TX      1
202dd84f925SMark Brown #define WM_ADSP_FW_TX_SPK  2
203dd84f925SMark Brown #define WM_ADSP_FW_RX_ANC  3
204dd84f925SMark Brown 
2051023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
206dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
207dd84f925SMark Brown 	[WM_ADSP_FW_TX] =      "Tx",
208dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =  "Tx Speaker",
209dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =  "Rx ANC",
2101023dbd9SMark Brown };
2111023dbd9SMark Brown 
2121023dbd9SMark Brown static struct {
2131023dbd9SMark Brown 	const char *file;
2141023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = {
215dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
216dd84f925SMark Brown 	[WM_ADSP_FW_TX] =      { .file = "tx" },
217dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =  { .file = "tx-spk" },
218dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =  { .file = "rx-anc" },
2191023dbd9SMark Brown };
2201023dbd9SMark Brown 
2216ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops {
2226ab2b7b4SDimitris Papastamos 	int (*xget)(struct snd_kcontrol *kcontrol,
2236ab2b7b4SDimitris Papastamos 		    struct snd_ctl_elem_value *ucontrol);
2246ab2b7b4SDimitris Papastamos 	int (*xput)(struct snd_kcontrol *kcontrol,
2256ab2b7b4SDimitris Papastamos 		    struct snd_ctl_elem_value *ucontrol);
2266ab2b7b4SDimitris Papastamos 	int (*xinfo)(struct snd_kcontrol *kcontrol,
2276ab2b7b4SDimitris Papastamos 		     struct snd_ctl_elem_info *uinfo);
2286ab2b7b4SDimitris Papastamos };
2296ab2b7b4SDimitris Papastamos 
2306ab2b7b4SDimitris Papastamos struct wm_coeff_ctl {
2316ab2b7b4SDimitris Papastamos 	const char *name;
2323809f001SCharles Keepax 	struct wm_adsp_alg_region alg_region;
2336ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl_ops ops;
2343809f001SCharles Keepax 	struct wm_adsp *dsp;
2356ab2b7b4SDimitris Papastamos 	void *private;
2366ab2b7b4SDimitris Papastamos 	unsigned int enabled:1;
2376ab2b7b4SDimitris Papastamos 	struct list_head list;
2386ab2b7b4SDimitris Papastamos 	void *cache;
2396ab2b7b4SDimitris Papastamos 	size_t len;
2400c2e3f34SDimitris Papastamos 	unsigned int set:1;
2416ab2b7b4SDimitris Papastamos 	struct snd_kcontrol *kcontrol;
2426ab2b7b4SDimitris Papastamos };
2436ab2b7b4SDimitris Papastamos 
2441023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
2451023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2461023dbd9SMark Brown {
247ea53bf77SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
2481023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2493809f001SCharles Keepax 	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
2501023dbd9SMark Brown 
2513809f001SCharles Keepax 	ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
2521023dbd9SMark Brown 
2531023dbd9SMark Brown 	return 0;
2541023dbd9SMark Brown }
2551023dbd9SMark Brown 
2561023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
2571023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2581023dbd9SMark Brown {
259ea53bf77SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
2601023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2613809f001SCharles Keepax 	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
2621023dbd9SMark Brown 
2633809f001SCharles Keepax 	if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
2641023dbd9SMark Brown 		return 0;
2651023dbd9SMark Brown 
2661023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
2671023dbd9SMark Brown 		return -EINVAL;
2681023dbd9SMark Brown 
2693809f001SCharles Keepax 	if (dsp[e->shift_l].running)
2701023dbd9SMark Brown 		return -EBUSY;
2711023dbd9SMark Brown 
2723809f001SCharles Keepax 	dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
2731023dbd9SMark Brown 
2741023dbd9SMark Brown 	return 0;
2751023dbd9SMark Brown }
2761023dbd9SMark Brown 
2771023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = {
2781023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2791023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2801023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2811023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2821023dbd9SMark Brown };
2831023dbd9SMark Brown 
284b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
2851023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
2861023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2871023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
2881023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2891023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
2901023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
291b6ed61cfSMark Brown };
292b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
293b6ed61cfSMark Brown 
294b6ed61cfSMark Brown #if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
295b6ed61cfSMark Brown static const struct soc_enum wm_adsp2_rate_enum[] = {
296dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
297dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
298dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
299dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
300dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
301dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
302dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
303dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
304dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
305dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
306dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
307dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
3085be9c5b4SCharles Keepax 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
309dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
310dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
311dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
312dc91428aSMark Brown };
313dc91428aSMark Brown 
314b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
3151023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
3161023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
317b6ed61cfSMark Brown 	SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
3181023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
3191023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
320b6ed61cfSMark Brown 	SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
3211023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
3221023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
323b6ed61cfSMark Brown 	SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
3241023dbd9SMark Brown 	SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
3251023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
326b6ed61cfSMark Brown 	SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
3271023dbd9SMark Brown };
328b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
329b6ed61cfSMark Brown #endif
3302159ad93SMark Brown 
3312159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
3322159ad93SMark Brown 							int type)
3332159ad93SMark Brown {
3342159ad93SMark Brown 	int i;
3352159ad93SMark Brown 
3362159ad93SMark Brown 	for (i = 0; i < dsp->num_mems; i++)
3372159ad93SMark Brown 		if (dsp->mem[i].type == type)
3382159ad93SMark Brown 			return &dsp->mem[i];
3392159ad93SMark Brown 
3402159ad93SMark Brown 	return NULL;
3412159ad93SMark Brown }
3422159ad93SMark Brown 
3433809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
34445b9ee72SMark Brown 					  unsigned int offset)
34545b9ee72SMark Brown {
3463809f001SCharles Keepax 	if (WARN_ON(!mem))
3476c452bdaSTakashi Iwai 		return offset;
3483809f001SCharles Keepax 	switch (mem->type) {
34945b9ee72SMark Brown 	case WMFW_ADSP1_PM:
3503809f001SCharles Keepax 		return mem->base + (offset * 3);
35145b9ee72SMark Brown 	case WMFW_ADSP1_DM:
3523809f001SCharles Keepax 		return mem->base + (offset * 2);
35345b9ee72SMark Brown 	case WMFW_ADSP2_XM:
3543809f001SCharles Keepax 		return mem->base + (offset * 2);
35545b9ee72SMark Brown 	case WMFW_ADSP2_YM:
3563809f001SCharles Keepax 		return mem->base + (offset * 2);
35745b9ee72SMark Brown 	case WMFW_ADSP1_ZM:
3583809f001SCharles Keepax 		return mem->base + (offset * 2);
35945b9ee72SMark Brown 	default:
3606c452bdaSTakashi Iwai 		WARN(1, "Unknown memory region type");
36145b9ee72SMark Brown 		return offset;
36245b9ee72SMark Brown 	}
36345b9ee72SMark Brown }
36445b9ee72SMark Brown 
3656ab2b7b4SDimitris Papastamos static int wm_coeff_info(struct snd_kcontrol *kcontrol,
3666ab2b7b4SDimitris Papastamos 			 struct snd_ctl_elem_info *uinfo)
3676ab2b7b4SDimitris Papastamos {
3686ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
3696ab2b7b4SDimitris Papastamos 
3706ab2b7b4SDimitris Papastamos 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
3716ab2b7b4SDimitris Papastamos 	uinfo->count = ctl->len;
3726ab2b7b4SDimitris Papastamos 	return 0;
3736ab2b7b4SDimitris Papastamos }
3746ab2b7b4SDimitris Papastamos 
3756ab2b7b4SDimitris Papastamos static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
3766ab2b7b4SDimitris Papastamos 				  const void *buf, size_t len)
3776ab2b7b4SDimitris Papastamos {
3786ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
3793809f001SCharles Keepax 	struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
3806ab2b7b4SDimitris Papastamos 	const struct wm_adsp_region *mem;
3813809f001SCharles Keepax 	struct wm_adsp *dsp = ctl->dsp;
3826ab2b7b4SDimitris Papastamos 	void *scratch;
3836ab2b7b4SDimitris Papastamos 	int ret;
3846ab2b7b4SDimitris Papastamos 	unsigned int reg;
3856ab2b7b4SDimitris Papastamos 
3863809f001SCharles Keepax 	mem = wm_adsp_find_region(dsp, alg_region->type);
3876ab2b7b4SDimitris Papastamos 	if (!mem) {
3883809f001SCharles Keepax 		adsp_err(dsp, "No base for region %x\n",
3893809f001SCharles Keepax 			 alg_region->type);
3906ab2b7b4SDimitris Papastamos 		return -EINVAL;
3916ab2b7b4SDimitris Papastamos 	}
3926ab2b7b4SDimitris Papastamos 
3933809f001SCharles Keepax 	reg = ctl->alg_region.base;
3946ab2b7b4SDimitris Papastamos 	reg = wm_adsp_region_to_reg(mem, reg);
3956ab2b7b4SDimitris Papastamos 
3966ab2b7b4SDimitris Papastamos 	scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
3976ab2b7b4SDimitris Papastamos 	if (!scratch)
3986ab2b7b4SDimitris Papastamos 		return -ENOMEM;
3996ab2b7b4SDimitris Papastamos 
4003809f001SCharles Keepax 	ret = regmap_raw_write(dsp->regmap, reg, scratch,
4016ab2b7b4SDimitris Papastamos 			       ctl->len);
4026ab2b7b4SDimitris Papastamos 	if (ret) {
4033809f001SCharles Keepax 		adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
40443bc3bf6SDimitris Papastamos 			 ctl->len, reg, ret);
4056ab2b7b4SDimitris Papastamos 		kfree(scratch);
4066ab2b7b4SDimitris Papastamos 		return ret;
4076ab2b7b4SDimitris Papastamos 	}
4083809f001SCharles Keepax 	adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
4096ab2b7b4SDimitris Papastamos 
4106ab2b7b4SDimitris Papastamos 	kfree(scratch);
4116ab2b7b4SDimitris Papastamos 
4126ab2b7b4SDimitris Papastamos 	return 0;
4136ab2b7b4SDimitris Papastamos }
4146ab2b7b4SDimitris Papastamos 
4156ab2b7b4SDimitris Papastamos static int wm_coeff_put(struct snd_kcontrol *kcontrol,
4166ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4176ab2b7b4SDimitris Papastamos {
4186ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
4196ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
4206ab2b7b4SDimitris Papastamos 
4216ab2b7b4SDimitris Papastamos 	memcpy(ctl->cache, p, ctl->len);
4226ab2b7b4SDimitris Papastamos 
4230c2e3f34SDimitris Papastamos 	ctl->set = 1;
42465d17a9cSNikesh Oswal 	if (!ctl->enabled)
4256ab2b7b4SDimitris Papastamos 		return 0;
4266ab2b7b4SDimitris Papastamos 
4276ab2b7b4SDimitris Papastamos 	return wm_coeff_write_control(kcontrol, p, ctl->len);
4286ab2b7b4SDimitris Papastamos }
4296ab2b7b4SDimitris Papastamos 
4306ab2b7b4SDimitris Papastamos static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
4316ab2b7b4SDimitris Papastamos 				 void *buf, size_t len)
4326ab2b7b4SDimitris Papastamos {
4336ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
4343809f001SCharles Keepax 	struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
4356ab2b7b4SDimitris Papastamos 	const struct wm_adsp_region *mem;
4363809f001SCharles Keepax 	struct wm_adsp *dsp = ctl->dsp;
4376ab2b7b4SDimitris Papastamos 	void *scratch;
4386ab2b7b4SDimitris Papastamos 	int ret;
4396ab2b7b4SDimitris Papastamos 	unsigned int reg;
4406ab2b7b4SDimitris Papastamos 
4413809f001SCharles Keepax 	mem = wm_adsp_find_region(dsp, alg_region->type);
4426ab2b7b4SDimitris Papastamos 	if (!mem) {
4433809f001SCharles Keepax 		adsp_err(dsp, "No base for region %x\n",
4443809f001SCharles Keepax 			 alg_region->type);
4456ab2b7b4SDimitris Papastamos 		return -EINVAL;
4466ab2b7b4SDimitris Papastamos 	}
4476ab2b7b4SDimitris Papastamos 
4483809f001SCharles Keepax 	reg = ctl->alg_region.base;
4496ab2b7b4SDimitris Papastamos 	reg = wm_adsp_region_to_reg(mem, reg);
4506ab2b7b4SDimitris Papastamos 
4516ab2b7b4SDimitris Papastamos 	scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
4526ab2b7b4SDimitris Papastamos 	if (!scratch)
4536ab2b7b4SDimitris Papastamos 		return -ENOMEM;
4546ab2b7b4SDimitris Papastamos 
4553809f001SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
4566ab2b7b4SDimitris Papastamos 	if (ret) {
4573809f001SCharles Keepax 		adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
45843bc3bf6SDimitris Papastamos 			 ctl->len, reg, ret);
4596ab2b7b4SDimitris Papastamos 		kfree(scratch);
4606ab2b7b4SDimitris Papastamos 		return ret;
4616ab2b7b4SDimitris Papastamos 	}
4623809f001SCharles Keepax 	adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
4636ab2b7b4SDimitris Papastamos 
4646ab2b7b4SDimitris Papastamos 	memcpy(buf, scratch, ctl->len);
4656ab2b7b4SDimitris Papastamos 	kfree(scratch);
4666ab2b7b4SDimitris Papastamos 
4676ab2b7b4SDimitris Papastamos 	return 0;
4686ab2b7b4SDimitris Papastamos }
4696ab2b7b4SDimitris Papastamos 
4706ab2b7b4SDimitris Papastamos static int wm_coeff_get(struct snd_kcontrol *kcontrol,
4716ab2b7b4SDimitris Papastamos 			struct snd_ctl_elem_value *ucontrol)
4726ab2b7b4SDimitris Papastamos {
4736ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
4746ab2b7b4SDimitris Papastamos 	char *p = ucontrol->value.bytes.data;
4756ab2b7b4SDimitris Papastamos 
4766ab2b7b4SDimitris Papastamos 	memcpy(p, ctl->cache, ctl->len);
4776ab2b7b4SDimitris Papastamos 	return 0;
4786ab2b7b4SDimitris Papastamos }
4796ab2b7b4SDimitris Papastamos 
4806ab2b7b4SDimitris Papastamos struct wmfw_ctl_work {
4813809f001SCharles Keepax 	struct wm_adsp *dsp;
4826ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
4836ab2b7b4SDimitris Papastamos 	struct work_struct work;
4846ab2b7b4SDimitris Papastamos };
4856ab2b7b4SDimitris Papastamos 
4863809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
4876ab2b7b4SDimitris Papastamos {
4886ab2b7b4SDimitris Papastamos 	struct snd_kcontrol_new *kcontrol;
4896ab2b7b4SDimitris Papastamos 	int ret;
4906ab2b7b4SDimitris Papastamos 
49192bb4c32SDimitris Papastamos 	if (!ctl || !ctl->name)
4926ab2b7b4SDimitris Papastamos 		return -EINVAL;
4936ab2b7b4SDimitris Papastamos 
4946ab2b7b4SDimitris Papastamos 	kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
4956ab2b7b4SDimitris Papastamos 	if (!kcontrol)
4966ab2b7b4SDimitris Papastamos 		return -ENOMEM;
4976ab2b7b4SDimitris Papastamos 	kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
4986ab2b7b4SDimitris Papastamos 
4996ab2b7b4SDimitris Papastamos 	kcontrol->name = ctl->name;
5006ab2b7b4SDimitris Papastamos 	kcontrol->info = wm_coeff_info;
5016ab2b7b4SDimitris Papastamos 	kcontrol->get = wm_coeff_get;
5026ab2b7b4SDimitris Papastamos 	kcontrol->put = wm_coeff_put;
5036ab2b7b4SDimitris Papastamos 	kcontrol->private_value = (unsigned long)ctl;
5046ab2b7b4SDimitris Papastamos 
5053809f001SCharles Keepax 	ret = snd_soc_add_card_controls(dsp->card,
50681ad93ecSDimitris Papastamos 					kcontrol, 1);
5076ab2b7b4SDimitris Papastamos 	if (ret < 0)
5086ab2b7b4SDimitris Papastamos 		goto err_kcontrol;
5096ab2b7b4SDimitris Papastamos 
5106ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
5116ab2b7b4SDimitris Papastamos 
5123809f001SCharles Keepax 	ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
51381ad93ecSDimitris Papastamos 						  ctl->name);
51481ad93ecSDimitris Papastamos 
5153809f001SCharles Keepax 	list_add(&ctl->list, &dsp->ctl_list);
5163809f001SCharles Keepax 
5176ab2b7b4SDimitris Papastamos 	return 0;
5186ab2b7b4SDimitris Papastamos 
5196ab2b7b4SDimitris Papastamos err_kcontrol:
5206ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
5216ab2b7b4SDimitris Papastamos 	return ret;
5226ab2b7b4SDimitris Papastamos }
5236ab2b7b4SDimitris Papastamos 
5242159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp)
5252159ad93SMark Brown {
526cf17c83cSMark Brown 	LIST_HEAD(buf_list);
5272159ad93SMark Brown 	const struct firmware *firmware;
5282159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
5292159ad93SMark Brown 	unsigned int pos = 0;
5302159ad93SMark Brown 	const struct wmfw_header *header;
5312159ad93SMark Brown 	const struct wmfw_adsp1_sizes *adsp1_sizes;
5322159ad93SMark Brown 	const struct wmfw_adsp2_sizes *adsp2_sizes;
5332159ad93SMark Brown 	const struct wmfw_footer *footer;
5342159ad93SMark Brown 	const struct wmfw_region *region;
5352159ad93SMark Brown 	const struct wm_adsp_region *mem;
5362159ad93SMark Brown 	const char *region_name;
5372159ad93SMark Brown 	char *file, *text;
538cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
5392159ad93SMark Brown 	unsigned int reg;
5402159ad93SMark Brown 	int regions = 0;
5412159ad93SMark Brown 	int ret, offset, type, sizes;
5422159ad93SMark Brown 
5432159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
5442159ad93SMark Brown 	if (file == NULL)
5452159ad93SMark Brown 		return -ENOMEM;
5462159ad93SMark Brown 
5471023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
5481023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
5492159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
5502159ad93SMark Brown 
5512159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
5522159ad93SMark Brown 	if (ret != 0) {
5532159ad93SMark Brown 		adsp_err(dsp, "Failed to request '%s'\n", file);
5542159ad93SMark Brown 		goto out;
5552159ad93SMark Brown 	}
5562159ad93SMark Brown 	ret = -EINVAL;
5572159ad93SMark Brown 
5582159ad93SMark Brown 	pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
5592159ad93SMark Brown 	if (pos >= firmware->size) {
5602159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
5612159ad93SMark Brown 			 file, firmware->size);
5622159ad93SMark Brown 		goto out_fw;
5632159ad93SMark Brown 	}
5642159ad93SMark Brown 
5652159ad93SMark Brown 	header = (void*)&firmware->data[0];
5662159ad93SMark Brown 
5672159ad93SMark Brown 	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
5682159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
5692159ad93SMark Brown 		goto out_fw;
5702159ad93SMark Brown 	}
5712159ad93SMark Brown 
5722159ad93SMark Brown 	if (header->ver != 0) {
5732159ad93SMark Brown 		adsp_err(dsp, "%s: unknown file format %d\n",
5742159ad93SMark Brown 			 file, header->ver);
5752159ad93SMark Brown 		goto out_fw;
5762159ad93SMark Brown 	}
5773626992aSDimitris Papastamos 	adsp_info(dsp, "Firmware version: %d\n", header->ver);
5782159ad93SMark Brown 
5792159ad93SMark Brown 	if (header->core != dsp->type) {
5802159ad93SMark Brown 		adsp_err(dsp, "%s: invalid core %d != %d\n",
5812159ad93SMark Brown 			 file, header->core, dsp->type);
5822159ad93SMark Brown 		goto out_fw;
5832159ad93SMark Brown 	}
5842159ad93SMark Brown 
5852159ad93SMark Brown 	switch (dsp->type) {
5862159ad93SMark Brown 	case WMFW_ADSP1:
5872159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
5882159ad93SMark Brown 		adsp1_sizes = (void *)&(header[1]);
5892159ad93SMark Brown 		footer = (void *)&(adsp1_sizes[1]);
5902159ad93SMark Brown 		sizes = sizeof(*adsp1_sizes);
5912159ad93SMark Brown 
5922159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
5932159ad93SMark Brown 			 file, le32_to_cpu(adsp1_sizes->dm),
5942159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->pm),
5952159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->zm));
5962159ad93SMark Brown 		break;
5972159ad93SMark Brown 
5982159ad93SMark Brown 	case WMFW_ADSP2:
5992159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
6002159ad93SMark Brown 		adsp2_sizes = (void *)&(header[1]);
6012159ad93SMark Brown 		footer = (void *)&(adsp2_sizes[1]);
6022159ad93SMark Brown 		sizes = sizeof(*adsp2_sizes);
6032159ad93SMark Brown 
6042159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
6052159ad93SMark Brown 			 file, le32_to_cpu(adsp2_sizes->xm),
6062159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->ym),
6072159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->pm),
6082159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->zm));
6092159ad93SMark Brown 		break;
6102159ad93SMark Brown 
6112159ad93SMark Brown 	default:
6126c452bdaSTakashi Iwai 		WARN(1, "Unknown DSP type");
6132159ad93SMark Brown 		goto out_fw;
6142159ad93SMark Brown 	}
6152159ad93SMark Brown 
6162159ad93SMark Brown 	if (le32_to_cpu(header->len) != sizeof(*header) +
6172159ad93SMark Brown 	    sizes + sizeof(*footer)) {
6182159ad93SMark Brown 		adsp_err(dsp, "%s: unexpected header length %d\n",
6192159ad93SMark Brown 			 file, le32_to_cpu(header->len));
6202159ad93SMark Brown 		goto out_fw;
6212159ad93SMark Brown 	}
6222159ad93SMark Brown 
6232159ad93SMark Brown 	adsp_dbg(dsp, "%s: timestamp %llu\n", file,
6242159ad93SMark Brown 		 le64_to_cpu(footer->timestamp));
6252159ad93SMark Brown 
6262159ad93SMark Brown 	while (pos < firmware->size &&
6272159ad93SMark Brown 	       pos - firmware->size > sizeof(*region)) {
6282159ad93SMark Brown 		region = (void *)&(firmware->data[pos]);
6292159ad93SMark Brown 		region_name = "Unknown";
6302159ad93SMark Brown 		reg = 0;
6312159ad93SMark Brown 		text = NULL;
6322159ad93SMark Brown 		offset = le32_to_cpu(region->offset) & 0xffffff;
6332159ad93SMark Brown 		type = be32_to_cpu(region->type) & 0xff;
6342159ad93SMark Brown 		mem = wm_adsp_find_region(dsp, type);
6352159ad93SMark Brown 
6362159ad93SMark Brown 		switch (type) {
6372159ad93SMark Brown 		case WMFW_NAME_TEXT:
6382159ad93SMark Brown 			region_name = "Firmware name";
6392159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
6402159ad93SMark Brown 				       GFP_KERNEL);
6412159ad93SMark Brown 			break;
6422159ad93SMark Brown 		case WMFW_INFO_TEXT:
6432159ad93SMark Brown 			region_name = "Information";
6442159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
6452159ad93SMark Brown 				       GFP_KERNEL);
6462159ad93SMark Brown 			break;
6472159ad93SMark Brown 		case WMFW_ABSOLUTE:
6482159ad93SMark Brown 			region_name = "Absolute";
6492159ad93SMark Brown 			reg = offset;
6502159ad93SMark Brown 			break;
6512159ad93SMark Brown 		case WMFW_ADSP1_PM:
6522159ad93SMark Brown 			region_name = "PM";
65345b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6542159ad93SMark Brown 			break;
6552159ad93SMark Brown 		case WMFW_ADSP1_DM:
6562159ad93SMark Brown 			region_name = "DM";
65745b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6582159ad93SMark Brown 			break;
6592159ad93SMark Brown 		case WMFW_ADSP2_XM:
6602159ad93SMark Brown 			region_name = "XM";
66145b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6622159ad93SMark Brown 			break;
6632159ad93SMark Brown 		case WMFW_ADSP2_YM:
6642159ad93SMark Brown 			region_name = "YM";
66545b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6662159ad93SMark Brown 			break;
6672159ad93SMark Brown 		case WMFW_ADSP1_ZM:
6682159ad93SMark Brown 			region_name = "ZM";
66945b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
6702159ad93SMark Brown 			break;
6712159ad93SMark Brown 		default:
6722159ad93SMark Brown 			adsp_warn(dsp,
6732159ad93SMark Brown 				  "%s.%d: Unknown region type %x at %d(%x)\n",
6742159ad93SMark Brown 				  file, regions, type, pos, pos);
6752159ad93SMark Brown 			break;
6762159ad93SMark Brown 		}
6772159ad93SMark Brown 
6782159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
6792159ad93SMark Brown 			 regions, le32_to_cpu(region->len), offset,
6802159ad93SMark Brown 			 region_name);
6812159ad93SMark Brown 
6822159ad93SMark Brown 		if (text) {
6832159ad93SMark Brown 			memcpy(text, region->data, le32_to_cpu(region->len));
6842159ad93SMark Brown 			adsp_info(dsp, "%s: %s\n", file, text);
6852159ad93SMark Brown 			kfree(text);
6862159ad93SMark Brown 		}
6872159ad93SMark Brown 
6882159ad93SMark Brown 		if (reg) {
689cdcd7f72SCharles Keepax 			buf = wm_adsp_buf_alloc(region->data,
690cdcd7f72SCharles Keepax 						le32_to_cpu(region->len),
691cf17c83cSMark Brown 						&buf_list);
692a76fefabSMark Brown 			if (!buf) {
693a76fefabSMark Brown 				adsp_err(dsp, "Out of memory\n");
6947328823dSDimitris Papastamos 				ret = -ENOMEM;
6957328823dSDimitris Papastamos 				goto out_fw;
696a76fefabSMark Brown 			}
697a76fefabSMark Brown 
698cdcd7f72SCharles Keepax 			ret = regmap_raw_write_async(regmap, reg, buf->buf,
699cdcd7f72SCharles Keepax 						     le32_to_cpu(region->len));
7002159ad93SMark Brown 			if (ret != 0) {
7012159ad93SMark Brown 				adsp_err(dsp,
702cdcd7f72SCharles Keepax 					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
7032159ad93SMark Brown 					file, regions,
704cdcd7f72SCharles Keepax 					le32_to_cpu(region->len), offset,
7052159ad93SMark Brown 					region_name, ret);
7062159ad93SMark Brown 				goto out_fw;
7072159ad93SMark Brown 			}
7082159ad93SMark Brown 		}
7092159ad93SMark Brown 
7102159ad93SMark Brown 		pos += le32_to_cpu(region->len) + sizeof(*region);
7112159ad93SMark Brown 		regions++;
7122159ad93SMark Brown 	}
7132159ad93SMark Brown 
714cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
715cf17c83cSMark Brown 	if (ret != 0) {
716cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
717cf17c83cSMark Brown 		goto out_fw;
718cf17c83cSMark Brown 	}
719cf17c83cSMark Brown 
7202159ad93SMark Brown 	if (pos > firmware->size)
7212159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
7222159ad93SMark Brown 			  file, regions, pos - firmware->size);
7232159ad93SMark Brown 
7242159ad93SMark Brown out_fw:
725cf17c83cSMark Brown 	regmap_async_complete(regmap);
726cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
7272159ad93SMark Brown 	release_firmware(firmware);
7282159ad93SMark Brown out:
7292159ad93SMark Brown 	kfree(file);
7302159ad93SMark Brown 
7312159ad93SMark Brown 	return ret;
7322159ad93SMark Brown }
7332159ad93SMark Brown 
7343809f001SCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
7356ab2b7b4SDimitris Papastamos {
7366ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
7376ab2b7b4SDimitris Papastamos 	int ret;
7386ab2b7b4SDimitris Papastamos 
7393809f001SCharles Keepax 	list_for_each_entry(ctl, &dsp->ctl_list, list) {
7400c2e3f34SDimitris Papastamos 		if (!ctl->enabled || ctl->set)
7416ab2b7b4SDimitris Papastamos 			continue;
7426ab2b7b4SDimitris Papastamos 		ret = wm_coeff_read_control(ctl->kcontrol,
7436ab2b7b4SDimitris Papastamos 					    ctl->cache,
7446ab2b7b4SDimitris Papastamos 					    ctl->len);
7456ab2b7b4SDimitris Papastamos 		if (ret < 0)
7466ab2b7b4SDimitris Papastamos 			return ret;
7476ab2b7b4SDimitris Papastamos 	}
7486ab2b7b4SDimitris Papastamos 
7496ab2b7b4SDimitris Papastamos 	return 0;
7506ab2b7b4SDimitris Papastamos }
7516ab2b7b4SDimitris Papastamos 
7523809f001SCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp)
7536ab2b7b4SDimitris Papastamos {
7546ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
7556ab2b7b4SDimitris Papastamos 	int ret;
7566ab2b7b4SDimitris Papastamos 
7573809f001SCharles Keepax 	list_for_each_entry(ctl, &dsp->ctl_list, list) {
7586ab2b7b4SDimitris Papastamos 		if (!ctl->enabled)
7596ab2b7b4SDimitris Papastamos 			continue;
7600c2e3f34SDimitris Papastamos 		if (ctl->set) {
7616ab2b7b4SDimitris Papastamos 			ret = wm_coeff_write_control(ctl->kcontrol,
7626ab2b7b4SDimitris Papastamos 						     ctl->cache,
7636ab2b7b4SDimitris Papastamos 						     ctl->len);
7646ab2b7b4SDimitris Papastamos 			if (ret < 0)
7656ab2b7b4SDimitris Papastamos 				return ret;
7666ab2b7b4SDimitris Papastamos 		}
7676ab2b7b4SDimitris Papastamos 	}
7686ab2b7b4SDimitris Papastamos 
7696ab2b7b4SDimitris Papastamos 	return 0;
7706ab2b7b4SDimitris Papastamos }
7716ab2b7b4SDimitris Papastamos 
7726ab2b7b4SDimitris Papastamos static void wm_adsp_ctl_work(struct work_struct *work)
7736ab2b7b4SDimitris Papastamos {
7746ab2b7b4SDimitris Papastamos 	struct wmfw_ctl_work *ctl_work = container_of(work,
7756ab2b7b4SDimitris Papastamos 						      struct wmfw_ctl_work,
7766ab2b7b4SDimitris Papastamos 						      work);
7776ab2b7b4SDimitris Papastamos 
7783809f001SCharles Keepax 	wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
7796ab2b7b4SDimitris Papastamos 	kfree(ctl_work);
7806ab2b7b4SDimitris Papastamos }
7816ab2b7b4SDimitris Papastamos 
78292bb4c32SDimitris Papastamos static int wm_adsp_create_control(struct wm_adsp *dsp,
7836958eb2aSCharles Keepax 				  const struct wm_adsp_alg_region *alg_region,
7846958eb2aSCharles Keepax 				  unsigned int len)
7856ab2b7b4SDimitris Papastamos {
7866ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
7876ab2b7b4SDimitris Papastamos 	struct wmfw_ctl_work *ctl_work;
7886ab2b7b4SDimitris Papastamos 	char *name;
7896ab2b7b4SDimitris Papastamos 	char *region_name;
7906ab2b7b4SDimitris Papastamos 	int ret;
7916ab2b7b4SDimitris Papastamos 
7926ab2b7b4SDimitris Papastamos 	name = kmalloc(PAGE_SIZE, GFP_KERNEL);
7936ab2b7b4SDimitris Papastamos 	if (!name)
7946ab2b7b4SDimitris Papastamos 		return -ENOMEM;
7956ab2b7b4SDimitris Papastamos 
7963809f001SCharles Keepax 	switch (alg_region->type) {
7976ab2b7b4SDimitris Papastamos 	case WMFW_ADSP1_PM:
7986ab2b7b4SDimitris Papastamos 		region_name = "PM";
7996ab2b7b4SDimitris Papastamos 		break;
8006ab2b7b4SDimitris Papastamos 	case WMFW_ADSP1_DM:
8016ab2b7b4SDimitris Papastamos 		region_name = "DM";
8026ab2b7b4SDimitris Papastamos 		break;
8036ab2b7b4SDimitris Papastamos 	case WMFW_ADSP2_XM:
8046ab2b7b4SDimitris Papastamos 		region_name = "XM";
8056ab2b7b4SDimitris Papastamos 		break;
8066ab2b7b4SDimitris Papastamos 	case WMFW_ADSP2_YM:
8076ab2b7b4SDimitris Papastamos 		region_name = "YM";
8086ab2b7b4SDimitris Papastamos 		break;
8096ab2b7b4SDimitris Papastamos 	case WMFW_ADSP1_ZM:
8106ab2b7b4SDimitris Papastamos 		region_name = "ZM";
8116ab2b7b4SDimitris Papastamos 		break;
8126ab2b7b4SDimitris Papastamos 	default:
8139dbce044SDan Carpenter 		ret = -EINVAL;
8149dbce044SDan Carpenter 		goto err_name;
8156ab2b7b4SDimitris Papastamos 	}
8166ab2b7b4SDimitris Papastamos 
8176ab2b7b4SDimitris Papastamos 	snprintf(name, PAGE_SIZE, "DSP%d %s %x",
8183809f001SCharles Keepax 		 dsp->num, region_name, alg_region->alg);
8196ab2b7b4SDimitris Papastamos 
82081ad93ecSDimitris Papastamos 	list_for_each_entry(ctl, &dsp->ctl_list,
8216ab2b7b4SDimitris Papastamos 			    list) {
8226ab2b7b4SDimitris Papastamos 		if (!strcmp(ctl->name, name)) {
8236ab2b7b4SDimitris Papastamos 			if (!ctl->enabled)
8246ab2b7b4SDimitris Papastamos 				ctl->enabled = 1;
8259dbce044SDan Carpenter 			goto found;
8266ab2b7b4SDimitris Papastamos 		}
8276ab2b7b4SDimitris Papastamos 	}
8286ab2b7b4SDimitris Papastamos 
8296ab2b7b4SDimitris Papastamos 	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
8306ab2b7b4SDimitris Papastamos 	if (!ctl) {
8316ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8326ab2b7b4SDimitris Papastamos 		goto err_name;
8336ab2b7b4SDimitris Papastamos 	}
8343809f001SCharles Keepax 	ctl->alg_region = *alg_region;
8356ab2b7b4SDimitris Papastamos 	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
8366ab2b7b4SDimitris Papastamos 	if (!ctl->name) {
8376ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8386ab2b7b4SDimitris Papastamos 		goto err_ctl;
8396ab2b7b4SDimitris Papastamos 	}
8406ab2b7b4SDimitris Papastamos 	ctl->enabled = 1;
8410c2e3f34SDimitris Papastamos 	ctl->set = 0;
8426ab2b7b4SDimitris Papastamos 	ctl->ops.xget = wm_coeff_get;
8436ab2b7b4SDimitris Papastamos 	ctl->ops.xput = wm_coeff_put;
8443809f001SCharles Keepax 	ctl->dsp = dsp;
8456ab2b7b4SDimitris Papastamos 
8466958eb2aSCharles Keepax 	if (len > 512) {
8476958eb2aSCharles Keepax 		adsp_warn(dsp, "Truncating control %s from %d\n",
8486958eb2aSCharles Keepax 			  ctl->name, len);
8496958eb2aSCharles Keepax 		len = 512;
8506958eb2aSCharles Keepax 	}
8516958eb2aSCharles Keepax 	ctl->len = len;
8526ab2b7b4SDimitris Papastamos 	ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
8536ab2b7b4SDimitris Papastamos 	if (!ctl->cache) {
8546ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8556ab2b7b4SDimitris Papastamos 		goto err_ctl_name;
8566ab2b7b4SDimitris Papastamos 	}
8576ab2b7b4SDimitris Papastamos 
8586ab2b7b4SDimitris Papastamos 	ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
8596ab2b7b4SDimitris Papastamos 	if (!ctl_work) {
8606ab2b7b4SDimitris Papastamos 		ret = -ENOMEM;
8616ab2b7b4SDimitris Papastamos 		goto err_ctl_cache;
8626ab2b7b4SDimitris Papastamos 	}
8636ab2b7b4SDimitris Papastamos 
8643809f001SCharles Keepax 	ctl_work->dsp = dsp;
8656ab2b7b4SDimitris Papastamos 	ctl_work->ctl = ctl;
8666ab2b7b4SDimitris Papastamos 	INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
8676ab2b7b4SDimitris Papastamos 	schedule_work(&ctl_work->work);
8686ab2b7b4SDimitris Papastamos 
8699dbce044SDan Carpenter found:
8706ab2b7b4SDimitris Papastamos 	kfree(name);
8716ab2b7b4SDimitris Papastamos 
8726ab2b7b4SDimitris Papastamos 	return 0;
8736ab2b7b4SDimitris Papastamos 
8746ab2b7b4SDimitris Papastamos err_ctl_cache:
8756ab2b7b4SDimitris Papastamos 	kfree(ctl->cache);
8766ab2b7b4SDimitris Papastamos err_ctl_name:
8776ab2b7b4SDimitris Papastamos 	kfree(ctl->name);
8786ab2b7b4SDimitris Papastamos err_ctl:
8796ab2b7b4SDimitris Papastamos 	kfree(ctl);
8806ab2b7b4SDimitris Papastamos err_name:
8816ab2b7b4SDimitris Papastamos 	kfree(name);
8826ab2b7b4SDimitris Papastamos 	return ret;
8836ab2b7b4SDimitris Papastamos }
8846ab2b7b4SDimitris Papastamos 
8853809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
886b618a185SCharles Keepax 			       unsigned int pos, unsigned int len)
887db40517cSMark Brown {
888b618a185SCharles Keepax 	void *alg;
889b618a185SCharles Keepax 	int ret;
890db40517cSMark Brown 	__be32 val;
891db40517cSMark Brown 
8923809f001SCharles Keepax 	if (n_algs == 0) {
893b618a185SCharles Keepax 		adsp_err(dsp, "No algorithms\n");
894b618a185SCharles Keepax 		return ERR_PTR(-EINVAL);
895db40517cSMark Brown 	}
896db40517cSMark Brown 
8973809f001SCharles Keepax 	if (n_algs > 1024) {
8983809f001SCharles Keepax 		adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
899b618a185SCharles Keepax 		return ERR_PTR(-EINVAL);
900b618a185SCharles Keepax 	}
901b618a185SCharles Keepax 
902b618a185SCharles Keepax 	/* Read the terminator first to validate the length */
903b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
904b618a185SCharles Keepax 	if (ret != 0) {
905b618a185SCharles Keepax 		adsp_err(dsp, "Failed to read algorithm list end: %d\n",
906b618a185SCharles Keepax 			ret);
907b618a185SCharles Keepax 		return ERR_PTR(ret);
908b618a185SCharles Keepax 	}
909b618a185SCharles Keepax 
910b618a185SCharles Keepax 	if (be32_to_cpu(val) != 0xbedead)
911b618a185SCharles Keepax 		adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
912b618a185SCharles Keepax 			  pos + len, be32_to_cpu(val));
913b618a185SCharles Keepax 
914b618a185SCharles Keepax 	alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
915b618a185SCharles Keepax 	if (!alg)
916b618a185SCharles Keepax 		return ERR_PTR(-ENOMEM);
917b618a185SCharles Keepax 
918b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
919b618a185SCharles Keepax 	if (ret != 0) {
920b618a185SCharles Keepax 		adsp_err(dsp, "Failed to read algorithm list: %d\n",
921b618a185SCharles Keepax 			ret);
922b618a185SCharles Keepax 		kfree(alg);
923b618a185SCharles Keepax 		return ERR_PTR(ret);
924b618a185SCharles Keepax 	}
925b618a185SCharles Keepax 
926b618a185SCharles Keepax 	return alg;
927b618a185SCharles Keepax }
928b618a185SCharles Keepax 
929b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
930b618a185SCharles Keepax {
931b618a185SCharles Keepax 	struct wmfw_adsp1_id_hdr adsp1_id;
932b618a185SCharles Keepax 	struct wmfw_adsp1_alg_hdr *adsp1_alg;
9333809f001SCharles Keepax 	struct wm_adsp_alg_region *alg_region;
934b618a185SCharles Keepax 	const struct wm_adsp_region *mem;
935b618a185SCharles Keepax 	unsigned int pos, len;
9363809f001SCharles Keepax 	size_t n_algs;
937b618a185SCharles Keepax 	int i, ret;
938b618a185SCharles Keepax 
939b618a185SCharles Keepax 	mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
9406c452bdaSTakashi Iwai 	if (WARN_ON(!mem))
941db40517cSMark Brown 		return -EINVAL;
942db40517cSMark Brown 
943b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
944db40517cSMark Brown 			      sizeof(adsp1_id));
945db40517cSMark Brown 	if (ret != 0) {
946db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm info: %d\n",
947db40517cSMark Brown 			 ret);
948db40517cSMark Brown 		return ret;
949db40517cSMark Brown 	}
950db40517cSMark Brown 
9513809f001SCharles Keepax 	n_algs = be32_to_cpu(adsp1_id.n_algs);
952f395a218SMark Brown 	dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
953db40517cSMark Brown 	adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
954f395a218SMark Brown 		  dsp->fw_id,
955db40517cSMark Brown 		  (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
956db40517cSMark Brown 		  (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
957db40517cSMark Brown 		  be32_to_cpu(adsp1_id.fw.ver) & 0xff,
9583809f001SCharles Keepax 		  n_algs);
959db40517cSMark Brown 
9603809f001SCharles Keepax 	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
9613809f001SCharles Keepax 	if (!alg_region)
962ac50009fSMark Brown 		return -ENOMEM;
9633809f001SCharles Keepax 	alg_region->type = WMFW_ADSP1_ZM;
9643809f001SCharles Keepax 	alg_region->alg = be32_to_cpu(adsp1_id.fw.id);
9653809f001SCharles Keepax 	alg_region->base = be32_to_cpu(adsp1_id.zm);
9663809f001SCharles Keepax 	list_add_tail(&alg_region->list, &dsp->alg_regions);
967ac50009fSMark Brown 
9683809f001SCharles Keepax 	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
9693809f001SCharles Keepax 	if (!alg_region)
970ac50009fSMark Brown 		return -ENOMEM;
9713809f001SCharles Keepax 	alg_region->type = WMFW_ADSP1_DM;
9723809f001SCharles Keepax 	alg_region->alg = be32_to_cpu(adsp1_id.fw.id);
9733809f001SCharles Keepax 	alg_region->base = be32_to_cpu(adsp1_id.dm);
9743809f001SCharles Keepax 	list_add_tail(&alg_region->list, &dsp->alg_regions);
975ac50009fSMark Brown 
976db40517cSMark Brown 	pos = sizeof(adsp1_id) / 2;
9773809f001SCharles Keepax 	len = (sizeof(*adsp1_alg) * n_algs) / 2;
978db40517cSMark Brown 
9793809f001SCharles Keepax 	adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
980b618a185SCharles Keepax 	if (IS_ERR(adsp1_alg))
981b618a185SCharles Keepax 		return PTR_ERR(adsp1_alg);
982db40517cSMark Brown 
9833809f001SCharles Keepax 	for (i = 0; i < n_algs; i++) {
984471f4885SMark Brown 		adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
985db40517cSMark Brown 			  i, be32_to_cpu(adsp1_alg[i].alg.id),
986db40517cSMark Brown 			  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
987db40517cSMark Brown 			  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
988471f4885SMark Brown 			  be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
989471f4885SMark Brown 			  be32_to_cpu(adsp1_alg[i].dm),
990471f4885SMark Brown 			  be32_to_cpu(adsp1_alg[i].zm));
991471f4885SMark Brown 
9923809f001SCharles Keepax 		alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
9933809f001SCharles Keepax 		if (!alg_region) {
994d6d52179SJS Park 			ret = -ENOMEM;
995d6d52179SJS Park 			goto out;
996d6d52179SJS Park 		}
9973809f001SCharles Keepax 		alg_region->type = WMFW_ADSP1_DM;
9983809f001SCharles Keepax 		alg_region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
9993809f001SCharles Keepax 		alg_region->base = be32_to_cpu(adsp1_alg[i].dm);
10003809f001SCharles Keepax 		list_add_tail(&alg_region->list, &dsp->alg_regions);
10013809f001SCharles Keepax 		if (i + 1 < n_algs) {
10026958eb2aSCharles Keepax 			len = be32_to_cpu(adsp1_alg[i + 1].dm);
10036958eb2aSCharles Keepax 			len -= be32_to_cpu(adsp1_alg[i].dm);
10046958eb2aSCharles Keepax 			len *= 4;
10056958eb2aSCharles Keepax 			wm_adsp_create_control(dsp, alg_region, len);
10066ab2b7b4SDimitris Papastamos 		} else {
10076ab2b7b4SDimitris Papastamos 			adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
10086ab2b7b4SDimitris Papastamos 				  be32_to_cpu(adsp1_alg[i].alg.id));
10096ab2b7b4SDimitris Papastamos 		}
1010471f4885SMark Brown 
10113809f001SCharles Keepax 		alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
10123809f001SCharles Keepax 		if (!alg_region) {
1013d6d52179SJS Park 			ret = -ENOMEM;
1014d6d52179SJS Park 			goto out;
1015d6d52179SJS Park 		}
10163809f001SCharles Keepax 		alg_region->type = WMFW_ADSP1_ZM;
10173809f001SCharles Keepax 		alg_region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
10183809f001SCharles Keepax 		alg_region->base = be32_to_cpu(adsp1_alg[i].zm);
10193809f001SCharles Keepax 		list_add_tail(&alg_region->list, &dsp->alg_regions);
10203809f001SCharles Keepax 		if (i + 1 < n_algs) {
10216958eb2aSCharles Keepax 			len = be32_to_cpu(adsp1_alg[i + 1].zm);
10226958eb2aSCharles Keepax 			len -= be32_to_cpu(adsp1_alg[i].zm);
10236958eb2aSCharles Keepax 			len *= 4;
10246958eb2aSCharles Keepax 			wm_adsp_create_control(dsp, alg_region, len);
10256ab2b7b4SDimitris Papastamos 		} else {
10266ab2b7b4SDimitris Papastamos 			adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
10276ab2b7b4SDimitris Papastamos 				  be32_to_cpu(adsp1_alg[i].alg.id));
10286ab2b7b4SDimitris Papastamos 		}
1029b618a185SCharles Keepax 	}
1030db40517cSMark Brown 
1031b618a185SCharles Keepax out:
1032b618a185SCharles Keepax 	kfree(adsp1_alg);
1033b618a185SCharles Keepax 	return ret;
1034b618a185SCharles Keepax }
1035b618a185SCharles Keepax 
1036b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
1037b618a185SCharles Keepax {
1038b618a185SCharles Keepax 	struct wmfw_adsp2_id_hdr adsp2_id;
1039b618a185SCharles Keepax 	struct wmfw_adsp2_alg_hdr *adsp2_alg;
10403809f001SCharles Keepax 	struct wm_adsp_alg_region *alg_region;
1041b618a185SCharles Keepax 	const struct wm_adsp_region *mem;
1042b618a185SCharles Keepax 	unsigned int pos, len;
10433809f001SCharles Keepax 	size_t n_algs;
1044b618a185SCharles Keepax 	int i, ret;
1045b618a185SCharles Keepax 
1046b618a185SCharles Keepax 	mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
1047b618a185SCharles Keepax 	if (WARN_ON(!mem))
1048b618a185SCharles Keepax 		return -EINVAL;
1049b618a185SCharles Keepax 
1050b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
1051b618a185SCharles Keepax 			      sizeof(adsp2_id));
1052b618a185SCharles Keepax 	if (ret != 0) {
1053b618a185SCharles Keepax 		adsp_err(dsp, "Failed to read algorithm info: %d\n",
1054b618a185SCharles Keepax 			 ret);
1055b618a185SCharles Keepax 		return ret;
1056b618a185SCharles Keepax 	}
1057b618a185SCharles Keepax 
10583809f001SCharles Keepax 	n_algs = be32_to_cpu(adsp2_id.n_algs);
1059b618a185SCharles Keepax 	dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
1060b618a185SCharles Keepax 	adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1061b618a185SCharles Keepax 		  dsp->fw_id,
1062b618a185SCharles Keepax 		  (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
1063b618a185SCharles Keepax 		  (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
1064b618a185SCharles Keepax 		  be32_to_cpu(adsp2_id.fw.ver) & 0xff,
10653809f001SCharles Keepax 		  n_algs);
1066b618a185SCharles Keepax 
10673809f001SCharles Keepax 	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
10683809f001SCharles Keepax 	if (!alg_region)
1069b618a185SCharles Keepax 		return -ENOMEM;
10703809f001SCharles Keepax 	alg_region->type = WMFW_ADSP2_XM;
10713809f001SCharles Keepax 	alg_region->alg = be32_to_cpu(adsp2_id.fw.id);
10723809f001SCharles Keepax 	alg_region->base = be32_to_cpu(adsp2_id.xm);
10733809f001SCharles Keepax 	list_add_tail(&alg_region->list, &dsp->alg_regions);
1074b618a185SCharles Keepax 
10753809f001SCharles Keepax 	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
10763809f001SCharles Keepax 	if (!alg_region)
1077b618a185SCharles Keepax 		return -ENOMEM;
10783809f001SCharles Keepax 	alg_region->type = WMFW_ADSP2_YM;
10793809f001SCharles Keepax 	alg_region->alg = be32_to_cpu(adsp2_id.fw.id);
10803809f001SCharles Keepax 	alg_region->base = be32_to_cpu(adsp2_id.ym);
10813809f001SCharles Keepax 	list_add_tail(&alg_region->list, &dsp->alg_regions);
1082b618a185SCharles Keepax 
10833809f001SCharles Keepax 	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
10843809f001SCharles Keepax 	if (!alg_region)
1085b618a185SCharles Keepax 		return -ENOMEM;
10863809f001SCharles Keepax 	alg_region->type = WMFW_ADSP2_ZM;
10873809f001SCharles Keepax 	alg_region->alg = be32_to_cpu(adsp2_id.fw.id);
10883809f001SCharles Keepax 	alg_region->base = be32_to_cpu(adsp2_id.zm);
10893809f001SCharles Keepax 	list_add_tail(&alg_region->list, &dsp->alg_regions);
1090b618a185SCharles Keepax 
1091b618a185SCharles Keepax 	pos = sizeof(adsp2_id) / 2;
10923809f001SCharles Keepax 	len = (sizeof(*adsp2_alg) * n_algs) / 2;
1093b618a185SCharles Keepax 
10943809f001SCharles Keepax 	adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
1095b618a185SCharles Keepax 	if (IS_ERR(adsp2_alg))
1096b618a185SCharles Keepax 		return PTR_ERR(adsp2_alg);
1097b618a185SCharles Keepax 
10983809f001SCharles Keepax 	for (i = 0; i < n_algs; i++) {
1099471f4885SMark Brown 		adsp_info(dsp,
1100471f4885SMark Brown 			  "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1101db40517cSMark Brown 			  i, be32_to_cpu(adsp2_alg[i].alg.id),
1102db40517cSMark Brown 			  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1103db40517cSMark Brown 			  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1104471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1105471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].xm),
1106471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].ym),
1107471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].zm));
1108471f4885SMark Brown 
11093809f001SCharles Keepax 		alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
11103809f001SCharles Keepax 		if (!alg_region) {
1111d6d52179SJS Park 			ret = -ENOMEM;
1112d6d52179SJS Park 			goto out;
1113d6d52179SJS Park 		}
11143809f001SCharles Keepax 		alg_region->type = WMFW_ADSP2_XM;
11153809f001SCharles Keepax 		alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
11163809f001SCharles Keepax 		alg_region->base = be32_to_cpu(adsp2_alg[i].xm);
11173809f001SCharles Keepax 		list_add_tail(&alg_region->list, &dsp->alg_regions);
11183809f001SCharles Keepax 		if (i + 1 < n_algs) {
11196958eb2aSCharles Keepax 			len = be32_to_cpu(adsp2_alg[i + 1].xm);
11206958eb2aSCharles Keepax 			len -= be32_to_cpu(adsp2_alg[i].xm);
11216958eb2aSCharles Keepax 			len *= 4;
11226958eb2aSCharles Keepax 			wm_adsp_create_control(dsp, alg_region, len);
11236ab2b7b4SDimitris Papastamos 		} else {
11246ab2b7b4SDimitris Papastamos 			adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
11256ab2b7b4SDimitris Papastamos 				  be32_to_cpu(adsp2_alg[i].alg.id));
11266ab2b7b4SDimitris Papastamos 		}
1127471f4885SMark Brown 
11283809f001SCharles Keepax 		alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
11293809f001SCharles Keepax 		if (!alg_region) {
1130d6d52179SJS Park 			ret = -ENOMEM;
1131d6d52179SJS Park 			goto out;
1132d6d52179SJS Park 		}
11333809f001SCharles Keepax 		alg_region->type = WMFW_ADSP2_YM;
11343809f001SCharles Keepax 		alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
11353809f001SCharles Keepax 		alg_region->base = be32_to_cpu(adsp2_alg[i].ym);
11363809f001SCharles Keepax 		list_add_tail(&alg_region->list, &dsp->alg_regions);
11373809f001SCharles Keepax 		if (i + 1 < n_algs) {
11386958eb2aSCharles Keepax 			len = be32_to_cpu(adsp2_alg[i + 1].ym);
11396958eb2aSCharles Keepax 			len -= be32_to_cpu(adsp2_alg[i].ym);
11406958eb2aSCharles Keepax 			len *= 4;
11416958eb2aSCharles Keepax 			wm_adsp_create_control(dsp, alg_region, len);
11426ab2b7b4SDimitris Papastamos 		} else {
11436ab2b7b4SDimitris Papastamos 			adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
11446ab2b7b4SDimitris Papastamos 				  be32_to_cpu(adsp2_alg[i].alg.id));
11456ab2b7b4SDimitris Papastamos 		}
1146471f4885SMark Brown 
11473809f001SCharles Keepax 		alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
11483809f001SCharles Keepax 		if (!alg_region) {
1149d6d52179SJS Park 			ret = -ENOMEM;
1150d6d52179SJS Park 			goto out;
1151d6d52179SJS Park 		}
11523809f001SCharles Keepax 		alg_region->type = WMFW_ADSP2_ZM;
11533809f001SCharles Keepax 		alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
11543809f001SCharles Keepax 		alg_region->base = be32_to_cpu(adsp2_alg[i].zm);
11553809f001SCharles Keepax 		list_add_tail(&alg_region->list, &dsp->alg_regions);
11563809f001SCharles Keepax 		if (i + 1 < n_algs) {
11576958eb2aSCharles Keepax 			len = be32_to_cpu(adsp2_alg[i + 1].zm);
11586958eb2aSCharles Keepax 			len -= be32_to_cpu(adsp2_alg[i].zm);
11596958eb2aSCharles Keepax 			len *= 4;
11606958eb2aSCharles Keepax 			wm_adsp_create_control(dsp, alg_region, len);
11616ab2b7b4SDimitris Papastamos 		} else {
11626ab2b7b4SDimitris Papastamos 			adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
11636ab2b7b4SDimitris Papastamos 				  be32_to_cpu(adsp2_alg[i].alg.id));
11646ab2b7b4SDimitris Papastamos 		}
1165db40517cSMark Brown 	}
1166db40517cSMark Brown 
1167db40517cSMark Brown out:
1168b618a185SCharles Keepax 	kfree(adsp2_alg);
1169db40517cSMark Brown 	return ret;
1170db40517cSMark Brown }
1171db40517cSMark Brown 
11722159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp)
11732159ad93SMark Brown {
1174cf17c83cSMark Brown 	LIST_HEAD(buf_list);
11752159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
11762159ad93SMark Brown 	struct wmfw_coeff_hdr *hdr;
11772159ad93SMark Brown 	struct wmfw_coeff_item *blk;
11782159ad93SMark Brown 	const struct firmware *firmware;
1179471f4885SMark Brown 	const struct wm_adsp_region *mem;
1180471f4885SMark Brown 	struct wm_adsp_alg_region *alg_region;
11812159ad93SMark Brown 	const char *region_name;
11822159ad93SMark Brown 	int ret, pos, blocks, type, offset, reg;
11832159ad93SMark Brown 	char *file;
1184cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
11852159ad93SMark Brown 
11862159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
11872159ad93SMark Brown 	if (file == NULL)
11882159ad93SMark Brown 		return -ENOMEM;
11892159ad93SMark Brown 
11901023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
11911023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
11922159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
11932159ad93SMark Brown 
11942159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
11952159ad93SMark Brown 	if (ret != 0) {
11962159ad93SMark Brown 		adsp_warn(dsp, "Failed to request '%s'\n", file);
11972159ad93SMark Brown 		ret = 0;
11982159ad93SMark Brown 		goto out;
11992159ad93SMark Brown 	}
12002159ad93SMark Brown 	ret = -EINVAL;
12012159ad93SMark Brown 
12022159ad93SMark Brown 	if (sizeof(*hdr) >= firmware->size) {
12032159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
12042159ad93SMark Brown 			file, firmware->size);
12052159ad93SMark Brown 		goto out_fw;
12062159ad93SMark Brown 	}
12072159ad93SMark Brown 
12082159ad93SMark Brown 	hdr = (void*)&firmware->data[0];
12092159ad93SMark Brown 	if (memcmp(hdr->magic, "WMDR", 4) != 0) {
12102159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
1211a4cdbec7SCharles Keepax 		goto out_fw;
12122159ad93SMark Brown 	}
12132159ad93SMark Brown 
1214c712326dSMark Brown 	switch (be32_to_cpu(hdr->rev) & 0xff) {
1215c712326dSMark Brown 	case 1:
1216c712326dSMark Brown 		break;
1217c712326dSMark Brown 	default:
1218c712326dSMark Brown 		adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
1219c712326dSMark Brown 			 file, be32_to_cpu(hdr->rev) & 0xff);
1220c712326dSMark Brown 		ret = -EINVAL;
1221c712326dSMark Brown 		goto out_fw;
1222c712326dSMark Brown 	}
1223c712326dSMark Brown 
12242159ad93SMark Brown 	adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
12252159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >> 16) & 0xff,
12262159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >>  8) & 0xff,
12272159ad93SMark Brown 		le32_to_cpu(hdr->ver) & 0xff);
12282159ad93SMark Brown 
12292159ad93SMark Brown 	pos = le32_to_cpu(hdr->len);
12302159ad93SMark Brown 
12312159ad93SMark Brown 	blocks = 0;
12322159ad93SMark Brown 	while (pos < firmware->size &&
12332159ad93SMark Brown 	       pos - firmware->size > sizeof(*blk)) {
12342159ad93SMark Brown 		blk = (void*)(&firmware->data[pos]);
12352159ad93SMark Brown 
1236c712326dSMark Brown 		type = le16_to_cpu(blk->type);
1237c712326dSMark Brown 		offset = le16_to_cpu(blk->offset);
12382159ad93SMark Brown 
12392159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
12402159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->id),
12412159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >> 16) & 0xff,
12422159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >>  8) & 0xff,
12432159ad93SMark Brown 			 le32_to_cpu(blk->ver) & 0xff);
12442159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
12452159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->len), offset, type);
12462159ad93SMark Brown 
12472159ad93SMark Brown 		reg = 0;
12482159ad93SMark Brown 		region_name = "Unknown";
12492159ad93SMark Brown 		switch (type) {
1250c712326dSMark Brown 		case (WMFW_NAME_TEXT << 8):
1251c712326dSMark Brown 		case (WMFW_INFO_TEXT << 8):
12522159ad93SMark Brown 			break;
1253c712326dSMark Brown 		case (WMFW_ABSOLUTE << 8):
1254f395a218SMark Brown 			/*
1255f395a218SMark Brown 			 * Old files may use this for global
1256f395a218SMark Brown 			 * coefficients.
1257f395a218SMark Brown 			 */
1258f395a218SMark Brown 			if (le32_to_cpu(blk->id) == dsp->fw_id &&
1259f395a218SMark Brown 			    offset == 0) {
1260f395a218SMark Brown 				region_name = "global coefficients";
1261f395a218SMark Brown 				mem = wm_adsp_find_region(dsp, type);
1262f395a218SMark Brown 				if (!mem) {
1263f395a218SMark Brown 					adsp_err(dsp, "No ZM\n");
1264f395a218SMark Brown 					break;
1265f395a218SMark Brown 				}
1266f395a218SMark Brown 				reg = wm_adsp_region_to_reg(mem, 0);
1267f395a218SMark Brown 
1268f395a218SMark Brown 			} else {
12692159ad93SMark Brown 				region_name = "register";
12702159ad93SMark Brown 				reg = offset;
1271f395a218SMark Brown 			}
12722159ad93SMark Brown 			break;
1273471f4885SMark Brown 
1274471f4885SMark Brown 		case WMFW_ADSP1_DM:
1275471f4885SMark Brown 		case WMFW_ADSP1_ZM:
1276471f4885SMark Brown 		case WMFW_ADSP2_XM:
1277471f4885SMark Brown 		case WMFW_ADSP2_YM:
1278471f4885SMark Brown 			adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
1279471f4885SMark Brown 				 file, blocks, le32_to_cpu(blk->len),
1280471f4885SMark Brown 				 type, le32_to_cpu(blk->id));
1281471f4885SMark Brown 
1282471f4885SMark Brown 			mem = wm_adsp_find_region(dsp, type);
1283471f4885SMark Brown 			if (!mem) {
1284471f4885SMark Brown 				adsp_err(dsp, "No base for region %x\n", type);
1285471f4885SMark Brown 				break;
1286471f4885SMark Brown 			}
1287471f4885SMark Brown 
1288471f4885SMark Brown 			reg = 0;
1289471f4885SMark Brown 			list_for_each_entry(alg_region,
1290471f4885SMark Brown 					    &dsp->alg_regions, list) {
1291471f4885SMark Brown 				if (le32_to_cpu(blk->id) == alg_region->alg &&
1292471f4885SMark Brown 				    type == alg_region->type) {
1293338c5188SMark Brown 					reg = alg_region->base;
1294471f4885SMark Brown 					reg = wm_adsp_region_to_reg(mem,
1295471f4885SMark Brown 								    reg);
1296338c5188SMark Brown 					reg += offset;
1297d733dc08SCharles Keepax 					break;
1298471f4885SMark Brown 				}
1299471f4885SMark Brown 			}
1300471f4885SMark Brown 
1301471f4885SMark Brown 			if (reg == 0)
1302471f4885SMark Brown 				adsp_err(dsp, "No %x for algorithm %x\n",
1303471f4885SMark Brown 					 type, le32_to_cpu(blk->id));
1304471f4885SMark Brown 			break;
1305471f4885SMark Brown 
13062159ad93SMark Brown 		default:
130725c62f7eSMark Brown 			adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
130825c62f7eSMark Brown 				 file, blocks, type, pos);
13092159ad93SMark Brown 			break;
13102159ad93SMark Brown 		}
13112159ad93SMark Brown 
13122159ad93SMark Brown 		if (reg) {
1313cf17c83cSMark Brown 			buf = wm_adsp_buf_alloc(blk->data,
1314cf17c83cSMark Brown 						le32_to_cpu(blk->len),
1315cf17c83cSMark Brown 						&buf_list);
1316a76fefabSMark Brown 			if (!buf) {
1317a76fefabSMark Brown 				adsp_err(dsp, "Out of memory\n");
1318f4b82812SWei Yongjun 				ret = -ENOMEM;
1319f4b82812SWei Yongjun 				goto out_fw;
1320a76fefabSMark Brown 			}
1321a76fefabSMark Brown 
132220da6d5aSMark Brown 			adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
132320da6d5aSMark Brown 				 file, blocks, le32_to_cpu(blk->len),
132420da6d5aSMark Brown 				 reg);
1325cf17c83cSMark Brown 			ret = regmap_raw_write_async(regmap, reg, buf->buf,
13262159ad93SMark Brown 						     le32_to_cpu(blk->len));
13272159ad93SMark Brown 			if (ret != 0) {
13282159ad93SMark Brown 				adsp_err(dsp,
132943bc3bf6SDimitris Papastamos 					"%s.%d: Failed to write to %x in %s: %d\n",
133043bc3bf6SDimitris Papastamos 					file, blocks, reg, region_name, ret);
13312159ad93SMark Brown 			}
13322159ad93SMark Brown 		}
13332159ad93SMark Brown 
1334be951017SCharles Keepax 		pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
13352159ad93SMark Brown 		blocks++;
13362159ad93SMark Brown 	}
13372159ad93SMark Brown 
1338cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
1339cf17c83cSMark Brown 	if (ret != 0)
1340cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1341cf17c83cSMark Brown 
13422159ad93SMark Brown 	if (pos > firmware->size)
13432159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
13442159ad93SMark Brown 			  file, blocks, pos - firmware->size);
13452159ad93SMark Brown 
13462159ad93SMark Brown out_fw:
13479da7a5a9SCharles Keepax 	regmap_async_complete(regmap);
13482159ad93SMark Brown 	release_firmware(firmware);
1349cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
13502159ad93SMark Brown out:
13512159ad93SMark Brown 	kfree(file);
1352f4b82812SWei Yongjun 	return ret;
13532159ad93SMark Brown }
13542159ad93SMark Brown 
13553809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp)
13565e7a7a22SMark Brown {
13573809f001SCharles Keepax 	INIT_LIST_HEAD(&dsp->alg_regions);
13585e7a7a22SMark Brown 
13595e7a7a22SMark Brown 	return 0;
13605e7a7a22SMark Brown }
13615e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
13625e7a7a22SMark Brown 
13632159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w,
13642159ad93SMark Brown 		   struct snd_kcontrol *kcontrol,
13652159ad93SMark Brown 		   int event)
13662159ad93SMark Brown {
136772718517SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
13682159ad93SMark Brown 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
13692159ad93SMark Brown 	struct wm_adsp *dsp = &dsps[w->shift];
1370b0101b4fSDimitris Papastamos 	struct wm_adsp_alg_region *alg_region;
13716ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
13722159ad93SMark Brown 	int ret;
137394e205bfSChris Rattray 	int val;
13742159ad93SMark Brown 
137500200107SLars-Peter Clausen 	dsp->card = codec->component.card;
137692bb4c32SDimitris Papastamos 
13772159ad93SMark Brown 	switch (event) {
13782159ad93SMark Brown 	case SND_SOC_DAPM_POST_PMU:
13792159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
13802159ad93SMark Brown 				   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
13812159ad93SMark Brown 
138294e205bfSChris Rattray 		/*
138394e205bfSChris Rattray 		 * For simplicity set the DSP clock rate to be the
138494e205bfSChris Rattray 		 * SYSCLK rate rather than making it configurable.
138594e205bfSChris Rattray 		 */
138694e205bfSChris Rattray 		if(dsp->sysclk_reg) {
138794e205bfSChris Rattray 			ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
138894e205bfSChris Rattray 			if (ret != 0) {
138994e205bfSChris Rattray 				adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
139094e205bfSChris Rattray 				ret);
139194e205bfSChris Rattray 				return ret;
139294e205bfSChris Rattray 			}
139394e205bfSChris Rattray 
139494e205bfSChris Rattray 			val = (val & dsp->sysclk_mask)
139594e205bfSChris Rattray 				>> dsp->sysclk_shift;
139694e205bfSChris Rattray 
139794e205bfSChris Rattray 			ret = regmap_update_bits(dsp->regmap,
139894e205bfSChris Rattray 						 dsp->base + ADSP1_CONTROL_31,
139994e205bfSChris Rattray 						 ADSP1_CLK_SEL_MASK, val);
140094e205bfSChris Rattray 			if (ret != 0) {
140194e205bfSChris Rattray 				adsp_err(dsp, "Failed to set clock rate: %d\n",
140294e205bfSChris Rattray 					 ret);
140394e205bfSChris Rattray 				return ret;
140494e205bfSChris Rattray 			}
140594e205bfSChris Rattray 		}
140694e205bfSChris Rattray 
14072159ad93SMark Brown 		ret = wm_adsp_load(dsp);
14082159ad93SMark Brown 		if (ret != 0)
14092159ad93SMark Brown 			goto err;
14102159ad93SMark Brown 
1411b618a185SCharles Keepax 		ret = wm_adsp1_setup_algs(dsp);
1412db40517cSMark Brown 		if (ret != 0)
1413db40517cSMark Brown 			goto err;
1414db40517cSMark Brown 
14152159ad93SMark Brown 		ret = wm_adsp_load_coeff(dsp);
14162159ad93SMark Brown 		if (ret != 0)
14172159ad93SMark Brown 			goto err;
14182159ad93SMark Brown 
14190c2e3f34SDimitris Papastamos 		/* Initialize caches for enabled and unset controls */
142081ad93ecSDimitris Papastamos 		ret = wm_coeff_init_control_caches(dsp);
14216ab2b7b4SDimitris Papastamos 		if (ret != 0)
14226ab2b7b4SDimitris Papastamos 			goto err;
14236ab2b7b4SDimitris Papastamos 
14240c2e3f34SDimitris Papastamos 		/* Sync set controls */
142581ad93ecSDimitris Papastamos 		ret = wm_coeff_sync_controls(dsp);
14266ab2b7b4SDimitris Papastamos 		if (ret != 0)
14276ab2b7b4SDimitris Papastamos 			goto err;
14286ab2b7b4SDimitris Papastamos 
14292159ad93SMark Brown 		/* Start the core running */
14302159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14312159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START,
14322159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START);
14332159ad93SMark Brown 		break;
14342159ad93SMark Brown 
14352159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
14362159ad93SMark Brown 		/* Halt the core */
14372159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14382159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START, 0);
14392159ad93SMark Brown 
14402159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
14412159ad93SMark Brown 				   ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
14422159ad93SMark Brown 
14432159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14442159ad93SMark Brown 				   ADSP1_SYS_ENA, 0);
14456ab2b7b4SDimitris Papastamos 
144681ad93ecSDimitris Papastamos 		list_for_each_entry(ctl, &dsp->ctl_list, list)
14476ab2b7b4SDimitris Papastamos 			ctl->enabled = 0;
1448b0101b4fSDimitris Papastamos 
1449b0101b4fSDimitris Papastamos 		while (!list_empty(&dsp->alg_regions)) {
1450b0101b4fSDimitris Papastamos 			alg_region = list_first_entry(&dsp->alg_regions,
1451b0101b4fSDimitris Papastamos 						      struct wm_adsp_alg_region,
1452b0101b4fSDimitris Papastamos 						      list);
1453b0101b4fSDimitris Papastamos 			list_del(&alg_region->list);
1454b0101b4fSDimitris Papastamos 			kfree(alg_region);
1455b0101b4fSDimitris Papastamos 		}
14562159ad93SMark Brown 		break;
14572159ad93SMark Brown 
14582159ad93SMark Brown 	default:
14592159ad93SMark Brown 		break;
14602159ad93SMark Brown 	}
14612159ad93SMark Brown 
14622159ad93SMark Brown 	return 0;
14632159ad93SMark Brown 
14642159ad93SMark Brown err:
14652159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
14662159ad93SMark Brown 			   ADSP1_SYS_ENA, 0);
14672159ad93SMark Brown 	return ret;
14682159ad93SMark Brown }
14692159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
14702159ad93SMark Brown 
14712159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp)
14722159ad93SMark Brown {
14732159ad93SMark Brown 	unsigned int val;
14742159ad93SMark Brown 	int ret, count;
14752159ad93SMark Brown 
14761552c325SMark Brown 	ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
14772159ad93SMark Brown 				       ADSP2_SYS_ENA, ADSP2_SYS_ENA);
14782159ad93SMark Brown 	if (ret != 0)
14792159ad93SMark Brown 		return ret;
14802159ad93SMark Brown 
14812159ad93SMark Brown 	/* Wait for the RAM to start, should be near instantaneous */
1482939fd1e8SCharles Keepax 	for (count = 0; count < 10; ++count) {
14832159ad93SMark Brown 		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
14842159ad93SMark Brown 				  &val);
14852159ad93SMark Brown 		if (ret != 0)
14862159ad93SMark Brown 			return ret;
1487939fd1e8SCharles Keepax 
1488939fd1e8SCharles Keepax 		if (val & ADSP2_RAM_RDY)
1489939fd1e8SCharles Keepax 			break;
1490939fd1e8SCharles Keepax 
1491939fd1e8SCharles Keepax 		msleep(1);
1492939fd1e8SCharles Keepax 	}
14932159ad93SMark Brown 
14942159ad93SMark Brown 	if (!(val & ADSP2_RAM_RDY)) {
14952159ad93SMark Brown 		adsp_err(dsp, "Failed to start DSP RAM\n");
14962159ad93SMark Brown 		return -EBUSY;
14972159ad93SMark Brown 	}
14982159ad93SMark Brown 
14992159ad93SMark Brown 	adsp_dbg(dsp, "RAM ready after %d polls\n", count);
15002159ad93SMark Brown 
15012159ad93SMark Brown 	return 0;
15022159ad93SMark Brown }
15032159ad93SMark Brown 
150418b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work)
15052159ad93SMark Brown {
1506d8a64d6aSCharles Keepax 	struct wm_adsp *dsp = container_of(work,
1507d8a64d6aSCharles Keepax 					   struct wm_adsp,
1508d8a64d6aSCharles Keepax 					   boot_work);
15092159ad93SMark Brown 	int ret;
1510d8a64d6aSCharles Keepax 	unsigned int val;
15112159ad93SMark Brown 
1512dd49e2c8SMark Brown 	/*
1513dd49e2c8SMark Brown 	 * For simplicity set the DSP clock rate to be the
1514dd49e2c8SMark Brown 	 * SYSCLK rate rather than making it configurable.
1515dd49e2c8SMark Brown 	 */
1516dd49e2c8SMark Brown 	ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1517dd49e2c8SMark Brown 	if (ret != 0) {
1518d8a64d6aSCharles Keepax 		adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
1519d8a64d6aSCharles Keepax 		return;
1520dd49e2c8SMark Brown 	}
1521dd49e2c8SMark Brown 	val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1522dd49e2c8SMark Brown 		>> ARIZONA_SYSCLK_FREQ_SHIFT;
1523dd49e2c8SMark Brown 
15241552c325SMark Brown 	ret = regmap_update_bits_async(dsp->regmap,
1525dd49e2c8SMark Brown 				       dsp->base + ADSP2_CLOCKING,
1526dd49e2c8SMark Brown 				       ADSP2_CLK_SEL_MASK, val);
1527dd49e2c8SMark Brown 	if (ret != 0) {
1528d8a64d6aSCharles Keepax 		adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
1529d8a64d6aSCharles Keepax 		return;
1530dd49e2c8SMark Brown 	}
1531dd49e2c8SMark Brown 
1532973838a0SMark Brown 	if (dsp->dvfs) {
1533973838a0SMark Brown 		ret = regmap_read(dsp->regmap,
1534973838a0SMark Brown 				  dsp->base + ADSP2_CLOCKING, &val);
1535973838a0SMark Brown 		if (ret != 0) {
153662c35b3bSCharles Keepax 			adsp_err(dsp, "Failed to read clocking: %d\n", ret);
1537d8a64d6aSCharles Keepax 			return;
1538973838a0SMark Brown 		}
1539973838a0SMark Brown 
154025c6fdb0SMark Brown 		if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
1541973838a0SMark Brown 			ret = regulator_enable(dsp->dvfs);
1542973838a0SMark Brown 			if (ret != 0) {
154362c35b3bSCharles Keepax 				adsp_err(dsp,
1544973838a0SMark Brown 					 "Failed to enable supply: %d\n",
1545973838a0SMark Brown 					 ret);
1546d8a64d6aSCharles Keepax 				return;
1547973838a0SMark Brown 			}
1548973838a0SMark Brown 
1549973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs,
1550973838a0SMark Brown 						    1800000,
1551973838a0SMark Brown 						    1800000);
1552973838a0SMark Brown 			if (ret != 0) {
155362c35b3bSCharles Keepax 				adsp_err(dsp,
1554973838a0SMark Brown 					 "Failed to raise supply: %d\n",
1555973838a0SMark Brown 					 ret);
1556d8a64d6aSCharles Keepax 				return;
1557973838a0SMark Brown 			}
1558973838a0SMark Brown 		}
1559973838a0SMark Brown 	}
1560973838a0SMark Brown 
15612159ad93SMark Brown 	ret = wm_adsp2_ena(dsp);
15622159ad93SMark Brown 	if (ret != 0)
1563d8a64d6aSCharles Keepax 		return;
15642159ad93SMark Brown 
15652159ad93SMark Brown 	ret = wm_adsp_load(dsp);
15662159ad93SMark Brown 	if (ret != 0)
15672159ad93SMark Brown 		goto err;
15682159ad93SMark Brown 
1569b618a185SCharles Keepax 	ret = wm_adsp2_setup_algs(dsp);
1570db40517cSMark Brown 	if (ret != 0)
1571db40517cSMark Brown 		goto err;
1572db40517cSMark Brown 
15732159ad93SMark Brown 	ret = wm_adsp_load_coeff(dsp);
15742159ad93SMark Brown 	if (ret != 0)
15752159ad93SMark Brown 		goto err;
15762159ad93SMark Brown 
15770c2e3f34SDimitris Papastamos 	/* Initialize caches for enabled and unset controls */
157881ad93ecSDimitris Papastamos 	ret = wm_coeff_init_control_caches(dsp);
15796ab2b7b4SDimitris Papastamos 	if (ret != 0)
15806ab2b7b4SDimitris Papastamos 		goto err;
15816ab2b7b4SDimitris Papastamos 
15820c2e3f34SDimitris Papastamos 	/* Sync set controls */
158381ad93ecSDimitris Papastamos 	ret = wm_coeff_sync_controls(dsp);
15846ab2b7b4SDimitris Papastamos 	if (ret != 0)
15856ab2b7b4SDimitris Papastamos 		goto err;
15866ab2b7b4SDimitris Papastamos 
15871023dbd9SMark Brown 	dsp->running = true;
1588d8a64d6aSCharles Keepax 
1589d8a64d6aSCharles Keepax 	return;
1590d8a64d6aSCharles Keepax 
1591d8a64d6aSCharles Keepax err:
1592d8a64d6aSCharles Keepax 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1593d8a64d6aSCharles Keepax 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
1594d8a64d6aSCharles Keepax }
1595d8a64d6aSCharles Keepax 
159612db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
159712db5eddSCharles Keepax 		   struct snd_kcontrol *kcontrol, int event)
159812db5eddSCharles Keepax {
159972718517SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
160012db5eddSCharles Keepax 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
160112db5eddSCharles Keepax 	struct wm_adsp *dsp = &dsps[w->shift];
160212db5eddSCharles Keepax 
160300200107SLars-Peter Clausen 	dsp->card = codec->component.card;
160412db5eddSCharles Keepax 
160512db5eddSCharles Keepax 	switch (event) {
160612db5eddSCharles Keepax 	case SND_SOC_DAPM_PRE_PMU:
160712db5eddSCharles Keepax 		queue_work(system_unbound_wq, &dsp->boot_work);
160812db5eddSCharles Keepax 		break;
160912db5eddSCharles Keepax 	default:
161012db5eddSCharles Keepax 		break;
1611cab27258SCharles Keepax 	}
161212db5eddSCharles Keepax 
161312db5eddSCharles Keepax 	return 0;
161412db5eddSCharles Keepax }
161512db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
161612db5eddSCharles Keepax 
1617d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1618d8a64d6aSCharles Keepax 		   struct snd_kcontrol *kcontrol, int event)
1619d8a64d6aSCharles Keepax {
162072718517SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
1621d8a64d6aSCharles Keepax 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1622d8a64d6aSCharles Keepax 	struct wm_adsp *dsp = &dsps[w->shift];
1623d8a64d6aSCharles Keepax 	struct wm_adsp_alg_region *alg_region;
1624d8a64d6aSCharles Keepax 	struct wm_coeff_ctl *ctl;
1625d8a64d6aSCharles Keepax 	int ret;
1626d8a64d6aSCharles Keepax 
1627d8a64d6aSCharles Keepax 	switch (event) {
1628d8a64d6aSCharles Keepax 	case SND_SOC_DAPM_POST_PMU:
1629d8a64d6aSCharles Keepax 		flush_work(&dsp->boot_work);
1630d8a64d6aSCharles Keepax 
1631d8a64d6aSCharles Keepax 		if (!dsp->running)
1632d8a64d6aSCharles Keepax 			return -EIO;
1633d8a64d6aSCharles Keepax 
1634d8a64d6aSCharles Keepax 		ret = regmap_update_bits(dsp->regmap,
1635d8a64d6aSCharles Keepax 					 dsp->base + ADSP2_CONTROL,
163600e4c3b6SCharles Keepax 					 ADSP2_CORE_ENA | ADSP2_START,
163700e4c3b6SCharles Keepax 					 ADSP2_CORE_ENA | ADSP2_START);
1638d8a64d6aSCharles Keepax 		if (ret != 0)
1639d8a64d6aSCharles Keepax 			goto err;
16402159ad93SMark Brown 		break;
16412159ad93SMark Brown 
16422159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
16431023dbd9SMark Brown 		dsp->running = false;
16441023dbd9SMark Brown 
16452159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1646a7f9be7eSMark Brown 				   ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1647a7f9be7eSMark Brown 				   ADSP2_START, 0);
1648973838a0SMark Brown 
16492d30b575SMark Brown 		/* Make sure DMAs are quiesced */
16502d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
16512d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
16522d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
16532d30b575SMark Brown 
1654973838a0SMark Brown 		if (dsp->dvfs) {
1655973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs, 1200000,
1656973838a0SMark Brown 						    1800000);
1657973838a0SMark Brown 			if (ret != 0)
165862c35b3bSCharles Keepax 				adsp_warn(dsp,
1659973838a0SMark Brown 					  "Failed to lower supply: %d\n",
1660973838a0SMark Brown 					  ret);
1661973838a0SMark Brown 
1662973838a0SMark Brown 			ret = regulator_disable(dsp->dvfs);
1663973838a0SMark Brown 			if (ret != 0)
166462c35b3bSCharles Keepax 				adsp_err(dsp,
1665973838a0SMark Brown 					 "Failed to enable supply: %d\n",
1666973838a0SMark Brown 					 ret);
1667973838a0SMark Brown 		}
1668471f4885SMark Brown 
166981ad93ecSDimitris Papastamos 		list_for_each_entry(ctl, &dsp->ctl_list, list)
16706ab2b7b4SDimitris Papastamos 			ctl->enabled = 0;
16716ab2b7b4SDimitris Papastamos 
1672471f4885SMark Brown 		while (!list_empty(&dsp->alg_regions)) {
1673471f4885SMark Brown 			alg_region = list_first_entry(&dsp->alg_regions,
1674471f4885SMark Brown 						      struct wm_adsp_alg_region,
1675471f4885SMark Brown 						      list);
1676471f4885SMark Brown 			list_del(&alg_region->list);
1677471f4885SMark Brown 			kfree(alg_region);
1678471f4885SMark Brown 		}
1679ddbc5efeSCharles Keepax 
1680ddbc5efeSCharles Keepax 		adsp_dbg(dsp, "Shutdown complete\n");
16812159ad93SMark Brown 		break;
16822159ad93SMark Brown 
16832159ad93SMark Brown 	default:
16842159ad93SMark Brown 		break;
16852159ad93SMark Brown 	}
16862159ad93SMark Brown 
16872159ad93SMark Brown 	return 0;
16882159ad93SMark Brown err:
16892159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1690a7f9be7eSMark Brown 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
16912159ad93SMark Brown 	return ret;
16922159ad93SMark Brown }
16932159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event);
1694973838a0SMark Brown 
16953809f001SCharles Keepax int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs)
1696973838a0SMark Brown {
1697973838a0SMark Brown 	int ret;
1698973838a0SMark Brown 
169910a2b662SMark Brown 	/*
170010a2b662SMark Brown 	 * Disable the DSP memory by default when in reset for a small
170110a2b662SMark Brown 	 * power saving.
170210a2b662SMark Brown 	 */
17033809f001SCharles Keepax 	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
170410a2b662SMark Brown 				 ADSP2_MEM_ENA, 0);
170510a2b662SMark Brown 	if (ret != 0) {
17063809f001SCharles Keepax 		adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
170710a2b662SMark Brown 		return ret;
170810a2b662SMark Brown 	}
170910a2b662SMark Brown 
17103809f001SCharles Keepax 	INIT_LIST_HEAD(&dsp->alg_regions);
17113809f001SCharles Keepax 	INIT_LIST_HEAD(&dsp->ctl_list);
17123809f001SCharles Keepax 	INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
17136ab2b7b4SDimitris Papastamos 
1714973838a0SMark Brown 	if (dvfs) {
17153809f001SCharles Keepax 		dsp->dvfs = devm_regulator_get(dsp->dev, "DCVDD");
17163809f001SCharles Keepax 		if (IS_ERR(dsp->dvfs)) {
17173809f001SCharles Keepax 			ret = PTR_ERR(dsp->dvfs);
17183809f001SCharles Keepax 			adsp_err(dsp, "Failed to get DCVDD: %d\n", ret);
171981ad93ecSDimitris Papastamos 			return ret;
1720973838a0SMark Brown 		}
1721973838a0SMark Brown 
17223809f001SCharles Keepax 		ret = regulator_enable(dsp->dvfs);
1723973838a0SMark Brown 		if (ret != 0) {
17243809f001SCharles Keepax 			adsp_err(dsp, "Failed to enable DCVDD: %d\n", ret);
172581ad93ecSDimitris Papastamos 			return ret;
1726973838a0SMark Brown 		}
1727973838a0SMark Brown 
17283809f001SCharles Keepax 		ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000);
1729973838a0SMark Brown 		if (ret != 0) {
17303809f001SCharles Keepax 			adsp_err(dsp, "Failed to initialise DVFS: %d\n", ret);
173181ad93ecSDimitris Papastamos 			return ret;
1732973838a0SMark Brown 		}
1733973838a0SMark Brown 
17343809f001SCharles Keepax 		ret = regulator_disable(dsp->dvfs);
1735973838a0SMark Brown 		if (ret != 0) {
17363809f001SCharles Keepax 			adsp_err(dsp, "Failed to disable DCVDD: %d\n", ret);
173781ad93ecSDimitris Papastamos 			return ret;
1738973838a0SMark Brown 		}
1739973838a0SMark Brown 	}
1740973838a0SMark Brown 
1741973838a0SMark Brown 	return 0;
1742973838a0SMark Brown }
1743973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
17440a37c6efSPraveen Diwakar 
17450a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2");
1746