xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision cb5b57a9)
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;
2322323736dSCharles Keepax 	const char *fw_name;
2333809f001SCharles Keepax 	struct wm_adsp_alg_region alg_region;
2346ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl_ops ops;
2353809f001SCharles Keepax 	struct wm_adsp *dsp;
2366ab2b7b4SDimitris Papastamos 	unsigned int enabled:1;
2376ab2b7b4SDimitris Papastamos 	struct list_head list;
2386ab2b7b4SDimitris Papastamos 	void *cache;
2392323736dSCharles Keepax 	unsigned int offset;
2406ab2b7b4SDimitris Papastamos 	size_t len;
2410c2e3f34SDimitris Papastamos 	unsigned int set:1;
2426ab2b7b4SDimitris Papastamos 	struct snd_kcontrol *kcontrol;
2436ab2b7b4SDimitris Papastamos };
2446ab2b7b4SDimitris Papastamos 
2451023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
2461023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2471023dbd9SMark Brown {
248ea53bf77SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
2491023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2503809f001SCharles Keepax 	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
2511023dbd9SMark Brown 
2523809f001SCharles Keepax 	ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
2531023dbd9SMark Brown 
2541023dbd9SMark Brown 	return 0;
2551023dbd9SMark Brown }
2561023dbd9SMark Brown 
2571023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
2581023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2591023dbd9SMark Brown {
260ea53bf77SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
2611023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2623809f001SCharles Keepax 	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
2631023dbd9SMark Brown 
2643809f001SCharles Keepax 	if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
2651023dbd9SMark Brown 		return 0;
2661023dbd9SMark Brown 
2671023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
2681023dbd9SMark Brown 		return -EINVAL;
2691023dbd9SMark Brown 
2703809f001SCharles Keepax 	if (dsp[e->shift_l].running)
2711023dbd9SMark Brown 		return -EBUSY;
2721023dbd9SMark Brown 
2733809f001SCharles Keepax 	dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
2741023dbd9SMark Brown 
2751023dbd9SMark Brown 	return 0;
2761023dbd9SMark Brown }
2771023dbd9SMark Brown 
2781023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = {
2791023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2801023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2811023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2821023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2831023dbd9SMark Brown };
2841023dbd9SMark Brown 
285b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
2861023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
2871023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2881023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
2891023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2901023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
2911023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
292b6ed61cfSMark Brown };
293b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
294b6ed61cfSMark Brown 
295b6ed61cfSMark Brown #if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
296b6ed61cfSMark Brown static const struct soc_enum wm_adsp2_rate_enum[] = {
297dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
298dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
299dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
300dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
301dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
302dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
303dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
304dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
305dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
306dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
307dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
308dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
3095be9c5b4SCharles Keepax 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
310dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
311dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
312dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
313dc91428aSMark Brown };
314dc91428aSMark Brown 
315b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
3161023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
3171023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
318b6ed61cfSMark Brown 	SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
3191023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
3201023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
321b6ed61cfSMark Brown 	SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
3221023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
3231023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
324b6ed61cfSMark Brown 	SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
3251023dbd9SMark Brown 	SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
3261023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
327b6ed61cfSMark Brown 	SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
3281023dbd9SMark Brown };
329b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
330b6ed61cfSMark Brown #endif
3312159ad93SMark Brown 
3322159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
3332159ad93SMark Brown 							int type)
3342159ad93SMark Brown {
3352159ad93SMark Brown 	int i;
3362159ad93SMark Brown 
3372159ad93SMark Brown 	for (i = 0; i < dsp->num_mems; i++)
3382159ad93SMark Brown 		if (dsp->mem[i].type == type)
3392159ad93SMark Brown 			return &dsp->mem[i];
3402159ad93SMark Brown 
3412159ad93SMark Brown 	return NULL;
3422159ad93SMark Brown }
3432159ad93SMark Brown 
3443809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
34545b9ee72SMark Brown 					  unsigned int offset)
34645b9ee72SMark Brown {
3473809f001SCharles Keepax 	if (WARN_ON(!mem))
3486c452bdaSTakashi Iwai 		return offset;
3493809f001SCharles Keepax 	switch (mem->type) {
35045b9ee72SMark Brown 	case WMFW_ADSP1_PM:
3513809f001SCharles Keepax 		return mem->base + (offset * 3);
35245b9ee72SMark Brown 	case WMFW_ADSP1_DM:
3533809f001SCharles Keepax 		return mem->base + (offset * 2);
35445b9ee72SMark Brown 	case WMFW_ADSP2_XM:
3553809f001SCharles Keepax 		return mem->base + (offset * 2);
35645b9ee72SMark Brown 	case WMFW_ADSP2_YM:
3573809f001SCharles Keepax 		return mem->base + (offset * 2);
35845b9ee72SMark Brown 	case WMFW_ADSP1_ZM:
3593809f001SCharles Keepax 		return mem->base + (offset * 2);
36045b9ee72SMark Brown 	default:
3616c452bdaSTakashi Iwai 		WARN(1, "Unknown memory region type");
36245b9ee72SMark Brown 		return offset;
36345b9ee72SMark Brown 	}
36445b9ee72SMark Brown }
36545b9ee72SMark Brown 
3666ab2b7b4SDimitris Papastamos static int wm_coeff_info(struct snd_kcontrol *kcontrol,
3676ab2b7b4SDimitris Papastamos 			 struct snd_ctl_elem_info *uinfo)
3686ab2b7b4SDimitris Papastamos {
3696ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
3706ab2b7b4SDimitris Papastamos 
3716ab2b7b4SDimitris Papastamos 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
3726ab2b7b4SDimitris Papastamos 	uinfo->count = ctl->len;
3736ab2b7b4SDimitris Papastamos 	return 0;
3746ab2b7b4SDimitris Papastamos }
3756ab2b7b4SDimitris Papastamos 
376c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
3776ab2b7b4SDimitris Papastamos 				  const void *buf, size_t len)
3786ab2b7b4SDimitris Papastamos {
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 
3932323736dSCharles Keepax 	reg = ctl->alg_region.base + ctl->offset;
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 
427c9f8dd71SCharles Keepax 	return wm_coeff_write_control(ctl, p, ctl->len);
4286ab2b7b4SDimitris Papastamos }
4296ab2b7b4SDimitris Papastamos 
430c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
4316ab2b7b4SDimitris Papastamos 				 void *buf, size_t len)
4326ab2b7b4SDimitris Papastamos {
4333809f001SCharles Keepax 	struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
4346ab2b7b4SDimitris Papastamos 	const struct wm_adsp_region *mem;
4353809f001SCharles Keepax 	struct wm_adsp *dsp = ctl->dsp;
4366ab2b7b4SDimitris Papastamos 	void *scratch;
4376ab2b7b4SDimitris Papastamos 	int ret;
4386ab2b7b4SDimitris Papastamos 	unsigned int reg;
4396ab2b7b4SDimitris Papastamos 
4403809f001SCharles Keepax 	mem = wm_adsp_find_region(dsp, alg_region->type);
4416ab2b7b4SDimitris Papastamos 	if (!mem) {
4423809f001SCharles Keepax 		adsp_err(dsp, "No base for region %x\n",
4433809f001SCharles Keepax 			 alg_region->type);
4446ab2b7b4SDimitris Papastamos 		return -EINVAL;
4456ab2b7b4SDimitris Papastamos 	}
4466ab2b7b4SDimitris Papastamos 
4472323736dSCharles Keepax 	reg = ctl->alg_region.base + ctl->offset;
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 
4543809f001SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
4556ab2b7b4SDimitris Papastamos 	if (ret) {
4563809f001SCharles Keepax 		adsp_err(dsp, "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 	}
4613809f001SCharles Keepax 	adsp_dbg(dsp, "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 {
4803809f001SCharles Keepax 	struct wm_adsp *dsp;
4816ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
4826ab2b7b4SDimitris Papastamos 	struct work_struct work;
4836ab2b7b4SDimitris Papastamos };
4846ab2b7b4SDimitris Papastamos 
4853809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, 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 
5043809f001SCharles Keepax 	ret = snd_soc_add_card_controls(dsp->card,
50581ad93ecSDimitris Papastamos 					kcontrol, 1);
5066ab2b7b4SDimitris Papastamos 	if (ret < 0)
5076ab2b7b4SDimitris Papastamos 		goto err_kcontrol;
5086ab2b7b4SDimitris Papastamos 
5096ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
5106ab2b7b4SDimitris Papastamos 
5113809f001SCharles Keepax 	ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
51281ad93ecSDimitris Papastamos 						  ctl->name);
51381ad93ecSDimitris Papastamos 
5146ab2b7b4SDimitris Papastamos 	return 0;
5156ab2b7b4SDimitris Papastamos 
5166ab2b7b4SDimitris Papastamos err_kcontrol:
5176ab2b7b4SDimitris Papastamos 	kfree(kcontrol);
5186ab2b7b4SDimitris Papastamos 	return ret;
5196ab2b7b4SDimitris Papastamos }
5206ab2b7b4SDimitris Papastamos 
521b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
522b21acc1cSCharles Keepax {
523b21acc1cSCharles Keepax 	struct wm_coeff_ctl *ctl;
524b21acc1cSCharles Keepax 	int ret;
525b21acc1cSCharles Keepax 
526b21acc1cSCharles Keepax 	list_for_each_entry(ctl, &dsp->ctl_list, list) {
527b21acc1cSCharles Keepax 		if (!ctl->enabled || ctl->set)
528b21acc1cSCharles Keepax 			continue;
529b21acc1cSCharles Keepax 		ret = wm_coeff_read_control(ctl,
530b21acc1cSCharles Keepax 					    ctl->cache,
531b21acc1cSCharles Keepax 					    ctl->len);
532b21acc1cSCharles Keepax 		if (ret < 0)
533b21acc1cSCharles Keepax 			return ret;
534b21acc1cSCharles Keepax 	}
535b21acc1cSCharles Keepax 
536b21acc1cSCharles Keepax 	return 0;
537b21acc1cSCharles Keepax }
538b21acc1cSCharles Keepax 
539b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp)
540b21acc1cSCharles Keepax {
541b21acc1cSCharles Keepax 	struct wm_coeff_ctl *ctl;
542b21acc1cSCharles Keepax 	int ret;
543b21acc1cSCharles Keepax 
544b21acc1cSCharles Keepax 	list_for_each_entry(ctl, &dsp->ctl_list, list) {
545b21acc1cSCharles Keepax 		if (!ctl->enabled)
546b21acc1cSCharles Keepax 			continue;
547b21acc1cSCharles Keepax 		if (ctl->set) {
548b21acc1cSCharles Keepax 			ret = wm_coeff_write_control(ctl,
549b21acc1cSCharles Keepax 						     ctl->cache,
550b21acc1cSCharles Keepax 						     ctl->len);
551b21acc1cSCharles Keepax 			if (ret < 0)
552b21acc1cSCharles Keepax 				return ret;
553b21acc1cSCharles Keepax 		}
554b21acc1cSCharles Keepax 	}
555b21acc1cSCharles Keepax 
556b21acc1cSCharles Keepax 	return 0;
557b21acc1cSCharles Keepax }
558b21acc1cSCharles Keepax 
559b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work)
560b21acc1cSCharles Keepax {
561b21acc1cSCharles Keepax 	struct wmfw_ctl_work *ctl_work = container_of(work,
562b21acc1cSCharles Keepax 						      struct wmfw_ctl_work,
563b21acc1cSCharles Keepax 						      work);
564b21acc1cSCharles Keepax 
565b21acc1cSCharles Keepax 	wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
566b21acc1cSCharles Keepax 	kfree(ctl_work);
567b21acc1cSCharles Keepax }
568b21acc1cSCharles Keepax 
569b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp,
570b21acc1cSCharles Keepax 				  const struct wm_adsp_alg_region *alg_region,
5712323736dSCharles Keepax 				  unsigned int offset, unsigned int len,
5722323736dSCharles Keepax 				  const char *subname, unsigned int subname_len)
573b21acc1cSCharles Keepax {
574b21acc1cSCharles Keepax 	struct wm_coeff_ctl *ctl;
575b21acc1cSCharles Keepax 	struct wmfw_ctl_work *ctl_work;
576b21acc1cSCharles Keepax 	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
577b21acc1cSCharles Keepax 	char *region_name;
578b21acc1cSCharles Keepax 	int ret;
579b21acc1cSCharles Keepax 
580b21acc1cSCharles Keepax 	switch (alg_region->type) {
581b21acc1cSCharles Keepax 	case WMFW_ADSP1_PM:
582b21acc1cSCharles Keepax 		region_name = "PM";
583b21acc1cSCharles Keepax 		break;
584b21acc1cSCharles Keepax 	case WMFW_ADSP1_DM:
585b21acc1cSCharles Keepax 		region_name = "DM";
586b21acc1cSCharles Keepax 		break;
587b21acc1cSCharles Keepax 	case WMFW_ADSP2_XM:
588b21acc1cSCharles Keepax 		region_name = "XM";
589b21acc1cSCharles Keepax 		break;
590b21acc1cSCharles Keepax 	case WMFW_ADSP2_YM:
591b21acc1cSCharles Keepax 		region_name = "YM";
592b21acc1cSCharles Keepax 		break;
593b21acc1cSCharles Keepax 	case WMFW_ADSP1_ZM:
594b21acc1cSCharles Keepax 		region_name = "ZM";
595b21acc1cSCharles Keepax 		break;
596b21acc1cSCharles Keepax 	default:
5972323736dSCharles Keepax 		adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
598b21acc1cSCharles Keepax 		return -EINVAL;
599b21acc1cSCharles Keepax 	}
600b21acc1cSCharles Keepax 
601cb5b57a9SCharles Keepax 	switch (dsp->fw_ver) {
602cb5b57a9SCharles Keepax 	case 0:
603cb5b57a9SCharles Keepax 	case 1:
604b21acc1cSCharles Keepax 		snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
605b21acc1cSCharles Keepax 			 dsp->num, region_name, alg_region->alg);
606cb5b57a9SCharles Keepax 		break;
607cb5b57a9SCharles Keepax 	default:
608cb5b57a9SCharles Keepax 		ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
609cb5b57a9SCharles Keepax 				"DSP%d%c %.12s %x", dsp->num, *region_name,
610cb5b57a9SCharles Keepax 				wm_adsp_fw_text[dsp->fw], alg_region->alg);
611cb5b57a9SCharles Keepax 
612cb5b57a9SCharles Keepax 		/* Truncate the subname from the start if it is too long */
613cb5b57a9SCharles Keepax 		if (subname) {
614cb5b57a9SCharles Keepax 			int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
615cb5b57a9SCharles Keepax 			int skip = 0;
616cb5b57a9SCharles Keepax 
617cb5b57a9SCharles Keepax 			if (subname_len > avail)
618cb5b57a9SCharles Keepax 				skip = subname_len - avail;
619cb5b57a9SCharles Keepax 
620cb5b57a9SCharles Keepax 			snprintf(name + ret,
621cb5b57a9SCharles Keepax 				 SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
622cb5b57a9SCharles Keepax 				 subname_len - skip, subname + skip);
623cb5b57a9SCharles Keepax 		}
624cb5b57a9SCharles Keepax 		break;
625cb5b57a9SCharles Keepax 	}
626b21acc1cSCharles Keepax 
627b21acc1cSCharles Keepax 	list_for_each_entry(ctl, &dsp->ctl_list,
628b21acc1cSCharles Keepax 			    list) {
629b21acc1cSCharles Keepax 		if (!strcmp(ctl->name, name)) {
630b21acc1cSCharles Keepax 			if (!ctl->enabled)
631b21acc1cSCharles Keepax 				ctl->enabled = 1;
632b21acc1cSCharles Keepax 			return 0;
633b21acc1cSCharles Keepax 		}
634b21acc1cSCharles Keepax 	}
635b21acc1cSCharles Keepax 
636b21acc1cSCharles Keepax 	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
637b21acc1cSCharles Keepax 	if (!ctl)
638b21acc1cSCharles Keepax 		return -ENOMEM;
6392323736dSCharles Keepax 	ctl->fw_name = wm_adsp_fw_text[dsp->fw];
640b21acc1cSCharles Keepax 	ctl->alg_region = *alg_region;
641b21acc1cSCharles Keepax 	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
642b21acc1cSCharles Keepax 	if (!ctl->name) {
643b21acc1cSCharles Keepax 		ret = -ENOMEM;
644b21acc1cSCharles Keepax 		goto err_ctl;
645b21acc1cSCharles Keepax 	}
646b21acc1cSCharles Keepax 	ctl->enabled = 1;
647b21acc1cSCharles Keepax 	ctl->set = 0;
648b21acc1cSCharles Keepax 	ctl->ops.xget = wm_coeff_get;
649b21acc1cSCharles Keepax 	ctl->ops.xput = wm_coeff_put;
650b21acc1cSCharles Keepax 	ctl->dsp = dsp;
651b21acc1cSCharles Keepax 
6522323736dSCharles Keepax 	ctl->offset = offset;
653b21acc1cSCharles Keepax 	if (len > 512) {
654b21acc1cSCharles Keepax 		adsp_warn(dsp, "Truncating control %s from %d\n",
655b21acc1cSCharles Keepax 			  ctl->name, len);
656b21acc1cSCharles Keepax 		len = 512;
657b21acc1cSCharles Keepax 	}
658b21acc1cSCharles Keepax 	ctl->len = len;
659b21acc1cSCharles Keepax 	ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
660b21acc1cSCharles Keepax 	if (!ctl->cache) {
661b21acc1cSCharles Keepax 		ret = -ENOMEM;
662b21acc1cSCharles Keepax 		goto err_ctl_name;
663b21acc1cSCharles Keepax 	}
664b21acc1cSCharles Keepax 
6652323736dSCharles Keepax 	list_add(&ctl->list, &dsp->ctl_list);
6662323736dSCharles Keepax 
667b21acc1cSCharles Keepax 	ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
668b21acc1cSCharles Keepax 	if (!ctl_work) {
669b21acc1cSCharles Keepax 		ret = -ENOMEM;
670b21acc1cSCharles Keepax 		goto err_ctl_cache;
671b21acc1cSCharles Keepax 	}
672b21acc1cSCharles Keepax 
673b21acc1cSCharles Keepax 	ctl_work->dsp = dsp;
674b21acc1cSCharles Keepax 	ctl_work->ctl = ctl;
675b21acc1cSCharles Keepax 	INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
676b21acc1cSCharles Keepax 	schedule_work(&ctl_work->work);
677b21acc1cSCharles Keepax 
678b21acc1cSCharles Keepax 	return 0;
679b21acc1cSCharles Keepax 
680b21acc1cSCharles Keepax err_ctl_cache:
681b21acc1cSCharles Keepax 	kfree(ctl->cache);
682b21acc1cSCharles Keepax err_ctl_name:
683b21acc1cSCharles Keepax 	kfree(ctl->name);
684b21acc1cSCharles Keepax err_ctl:
685b21acc1cSCharles Keepax 	kfree(ctl);
686b21acc1cSCharles Keepax 
687b21acc1cSCharles Keepax 	return ret;
688b21acc1cSCharles Keepax }
689b21acc1cSCharles Keepax 
6902323736dSCharles Keepax struct wm_coeff_parsed_alg {
6912323736dSCharles Keepax 	int id;
6922323736dSCharles Keepax 	const u8 *name;
6932323736dSCharles Keepax 	int name_len;
6942323736dSCharles Keepax 	int ncoeff;
6952323736dSCharles Keepax };
6962323736dSCharles Keepax 
6972323736dSCharles Keepax struct wm_coeff_parsed_coeff {
6982323736dSCharles Keepax 	int offset;
6992323736dSCharles Keepax 	int mem_type;
7002323736dSCharles Keepax 	const u8 *name;
7012323736dSCharles Keepax 	int name_len;
7022323736dSCharles Keepax 	int ctl_type;
7032323736dSCharles Keepax 	int flags;
7042323736dSCharles Keepax 	int len;
7052323736dSCharles Keepax };
7062323736dSCharles Keepax 
707cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
708cb5b57a9SCharles Keepax {
709cb5b57a9SCharles Keepax 	int length;
710cb5b57a9SCharles Keepax 
711cb5b57a9SCharles Keepax 	switch (bytes) {
712cb5b57a9SCharles Keepax 	case 1:
713cb5b57a9SCharles Keepax 		length = **pos;
714cb5b57a9SCharles Keepax 		break;
715cb5b57a9SCharles Keepax 	case 2:
716cb5b57a9SCharles Keepax 		length = le16_to_cpu(*((u16 *)*pos));
717cb5b57a9SCharles Keepax 		break;
718cb5b57a9SCharles Keepax 	default:
719cb5b57a9SCharles Keepax 		return 0;
720cb5b57a9SCharles Keepax 	}
721cb5b57a9SCharles Keepax 
722cb5b57a9SCharles Keepax 	if (str)
723cb5b57a9SCharles Keepax 		*str = *pos + bytes;
724cb5b57a9SCharles Keepax 
725cb5b57a9SCharles Keepax 	*pos += ((length + bytes) + 3) & ~0x03;
726cb5b57a9SCharles Keepax 
727cb5b57a9SCharles Keepax 	return length;
728cb5b57a9SCharles Keepax }
729cb5b57a9SCharles Keepax 
730cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos)
731cb5b57a9SCharles Keepax {
732cb5b57a9SCharles Keepax 	int val = 0;
733cb5b57a9SCharles Keepax 
734cb5b57a9SCharles Keepax 	switch (bytes) {
735cb5b57a9SCharles Keepax 	case 2:
736cb5b57a9SCharles Keepax 		val = le16_to_cpu(*((u16 *)*pos));
737cb5b57a9SCharles Keepax 		break;
738cb5b57a9SCharles Keepax 	case 4:
739cb5b57a9SCharles Keepax 		val = le32_to_cpu(*((u32 *)*pos));
740cb5b57a9SCharles Keepax 		break;
741cb5b57a9SCharles Keepax 	default:
742cb5b57a9SCharles Keepax 		break;
743cb5b57a9SCharles Keepax 	}
744cb5b57a9SCharles Keepax 
745cb5b57a9SCharles Keepax 	*pos += bytes;
746cb5b57a9SCharles Keepax 
747cb5b57a9SCharles Keepax 	return val;
748cb5b57a9SCharles Keepax }
749cb5b57a9SCharles Keepax 
7502323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
7512323736dSCharles Keepax 				      struct wm_coeff_parsed_alg *blk)
7522323736dSCharles Keepax {
7532323736dSCharles Keepax 	const struct wmfw_adsp_alg_data *raw;
7542323736dSCharles Keepax 
755cb5b57a9SCharles Keepax 	switch (dsp->fw_ver) {
756cb5b57a9SCharles Keepax 	case 0:
757cb5b57a9SCharles Keepax 	case 1:
7582323736dSCharles Keepax 		raw = (const struct wmfw_adsp_alg_data *)*data;
7592323736dSCharles Keepax 		*data = raw->data;
7602323736dSCharles Keepax 
7612323736dSCharles Keepax 		blk->id = le32_to_cpu(raw->id);
7622323736dSCharles Keepax 		blk->name = raw->name;
7632323736dSCharles Keepax 		blk->name_len = strlen(raw->name);
7642323736dSCharles Keepax 		blk->ncoeff = le32_to_cpu(raw->ncoeff);
765cb5b57a9SCharles Keepax 		break;
766cb5b57a9SCharles Keepax 	default:
767cb5b57a9SCharles Keepax 		blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
768cb5b57a9SCharles Keepax 		blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
769cb5b57a9SCharles Keepax 						      &blk->name);
770cb5b57a9SCharles Keepax 		wm_coeff_parse_string(sizeof(u16), data, NULL);
771cb5b57a9SCharles Keepax 		blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
772cb5b57a9SCharles Keepax 		break;
773cb5b57a9SCharles Keepax 	}
7742323736dSCharles Keepax 
7752323736dSCharles Keepax 	adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
7762323736dSCharles Keepax 	adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
7772323736dSCharles Keepax 	adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
7782323736dSCharles Keepax }
7792323736dSCharles Keepax 
7802323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
7812323736dSCharles Keepax 					struct wm_coeff_parsed_coeff *blk)
7822323736dSCharles Keepax {
7832323736dSCharles Keepax 	const struct wmfw_adsp_coeff_data *raw;
784cb5b57a9SCharles Keepax 	const u8 *tmp;
785cb5b57a9SCharles Keepax 	int length;
7862323736dSCharles Keepax 
787cb5b57a9SCharles Keepax 	switch (dsp->fw_ver) {
788cb5b57a9SCharles Keepax 	case 0:
789cb5b57a9SCharles Keepax 	case 1:
7902323736dSCharles Keepax 		raw = (const struct wmfw_adsp_coeff_data *)*data;
7912323736dSCharles Keepax 		*data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
7922323736dSCharles Keepax 
7932323736dSCharles Keepax 		blk->offset = le16_to_cpu(raw->hdr.offset);
7942323736dSCharles Keepax 		blk->mem_type = le16_to_cpu(raw->hdr.type);
7952323736dSCharles Keepax 		blk->name = raw->name;
7962323736dSCharles Keepax 		blk->name_len = strlen(raw->name);
7972323736dSCharles Keepax 		blk->ctl_type = le16_to_cpu(raw->ctl_type);
7982323736dSCharles Keepax 		blk->flags = le16_to_cpu(raw->flags);
7992323736dSCharles Keepax 		blk->len = le32_to_cpu(raw->len);
800cb5b57a9SCharles Keepax 		break;
801cb5b57a9SCharles Keepax 	default:
802cb5b57a9SCharles Keepax 		tmp = *data;
803cb5b57a9SCharles Keepax 		blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
804cb5b57a9SCharles Keepax 		blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
805cb5b57a9SCharles Keepax 		length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
806cb5b57a9SCharles Keepax 		blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
807cb5b57a9SCharles Keepax 						      &blk->name);
808cb5b57a9SCharles Keepax 		wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
809cb5b57a9SCharles Keepax 		wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
810cb5b57a9SCharles Keepax 		blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
811cb5b57a9SCharles Keepax 		blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
812cb5b57a9SCharles Keepax 		blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
813cb5b57a9SCharles Keepax 
814cb5b57a9SCharles Keepax 		*data = *data + sizeof(raw->hdr) + length;
815cb5b57a9SCharles Keepax 		break;
816cb5b57a9SCharles Keepax 	}
8172323736dSCharles Keepax 
8182323736dSCharles Keepax 	adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
8192323736dSCharles Keepax 	adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
8202323736dSCharles Keepax 	adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
8212323736dSCharles Keepax 	adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
8222323736dSCharles Keepax 	adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
8232323736dSCharles Keepax 	adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
8242323736dSCharles Keepax }
8252323736dSCharles Keepax 
8262323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
8272323736dSCharles Keepax 			       const struct wmfw_region *region)
8282323736dSCharles Keepax {
8292323736dSCharles Keepax 	struct wm_adsp_alg_region alg_region = {};
8302323736dSCharles Keepax 	struct wm_coeff_parsed_alg alg_blk;
8312323736dSCharles Keepax 	struct wm_coeff_parsed_coeff coeff_blk;
8322323736dSCharles Keepax 	const u8 *data = region->data;
8332323736dSCharles Keepax 	int i, ret;
8342323736dSCharles Keepax 
8352323736dSCharles Keepax 	wm_coeff_parse_alg(dsp, &data, &alg_blk);
8362323736dSCharles Keepax 	for (i = 0; i < alg_blk.ncoeff; i++) {
8372323736dSCharles Keepax 		wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
8382323736dSCharles Keepax 
8392323736dSCharles Keepax 		switch (coeff_blk.ctl_type) {
8402323736dSCharles Keepax 		case SNDRV_CTL_ELEM_TYPE_BYTES:
8412323736dSCharles Keepax 			break;
8422323736dSCharles Keepax 		default:
8432323736dSCharles Keepax 			adsp_err(dsp, "Unknown control type: %d\n",
8442323736dSCharles Keepax 				 coeff_blk.ctl_type);
8452323736dSCharles Keepax 			return -EINVAL;
8462323736dSCharles Keepax 		}
8472323736dSCharles Keepax 
8482323736dSCharles Keepax 		alg_region.type = coeff_blk.mem_type;
8492323736dSCharles Keepax 		alg_region.alg = alg_blk.id;
8502323736dSCharles Keepax 
8512323736dSCharles Keepax 		ret = wm_adsp_create_control(dsp, &alg_region,
8522323736dSCharles Keepax 					     coeff_blk.offset,
8532323736dSCharles Keepax 					     coeff_blk.len,
8542323736dSCharles Keepax 					     coeff_blk.name,
8552323736dSCharles Keepax 					     coeff_blk.name_len);
8562323736dSCharles Keepax 		if (ret < 0)
8572323736dSCharles Keepax 			adsp_err(dsp, "Failed to create control: %.*s, %d\n",
8582323736dSCharles Keepax 				 coeff_blk.name_len, coeff_blk.name, ret);
8592323736dSCharles Keepax 	}
8602323736dSCharles Keepax 
8612323736dSCharles Keepax 	return 0;
8622323736dSCharles Keepax }
8632323736dSCharles Keepax 
8642159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp)
8652159ad93SMark Brown {
866cf17c83cSMark Brown 	LIST_HEAD(buf_list);
8672159ad93SMark Brown 	const struct firmware *firmware;
8682159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
8692159ad93SMark Brown 	unsigned int pos = 0;
8702159ad93SMark Brown 	const struct wmfw_header *header;
8712159ad93SMark Brown 	const struct wmfw_adsp1_sizes *adsp1_sizes;
8722159ad93SMark Brown 	const struct wmfw_adsp2_sizes *adsp2_sizes;
8732159ad93SMark Brown 	const struct wmfw_footer *footer;
8742159ad93SMark Brown 	const struct wmfw_region *region;
8752159ad93SMark Brown 	const struct wm_adsp_region *mem;
8762159ad93SMark Brown 	const char *region_name;
8772159ad93SMark Brown 	char *file, *text;
878cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
8792159ad93SMark Brown 	unsigned int reg;
8802159ad93SMark Brown 	int regions = 0;
8812159ad93SMark Brown 	int ret, offset, type, sizes;
8822159ad93SMark Brown 
8832159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
8842159ad93SMark Brown 	if (file == NULL)
8852159ad93SMark Brown 		return -ENOMEM;
8862159ad93SMark Brown 
8871023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
8881023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
8892159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
8902159ad93SMark Brown 
8912159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
8922159ad93SMark Brown 	if (ret != 0) {
8932159ad93SMark Brown 		adsp_err(dsp, "Failed to request '%s'\n", file);
8942159ad93SMark Brown 		goto out;
8952159ad93SMark Brown 	}
8962159ad93SMark Brown 	ret = -EINVAL;
8972159ad93SMark Brown 
8982159ad93SMark Brown 	pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
8992159ad93SMark Brown 	if (pos >= firmware->size) {
9002159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
9012159ad93SMark Brown 			 file, firmware->size);
9022159ad93SMark Brown 		goto out_fw;
9032159ad93SMark Brown 	}
9042159ad93SMark Brown 
9052159ad93SMark Brown 	header = (void*)&firmware->data[0];
9062159ad93SMark Brown 
9072159ad93SMark Brown 	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
9082159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
9092159ad93SMark Brown 		goto out_fw;
9102159ad93SMark Brown 	}
9112159ad93SMark Brown 
9122323736dSCharles Keepax 	switch (header->ver) {
9132323736dSCharles Keepax 	case 0:
9142323736dSCharles Keepax 	case 1:
915cb5b57a9SCharles Keepax 	case 2:
9162323736dSCharles Keepax 		break;
9172323736dSCharles Keepax 	default:
9182159ad93SMark Brown 		adsp_err(dsp, "%s: unknown file format %d\n",
9192159ad93SMark Brown 			 file, header->ver);
9202159ad93SMark Brown 		goto out_fw;
9212159ad93SMark Brown 	}
9222323736dSCharles Keepax 
9233626992aSDimitris Papastamos 	adsp_info(dsp, "Firmware version: %d\n", header->ver);
9242323736dSCharles Keepax 	dsp->fw_ver = header->ver;
9252159ad93SMark Brown 
9262159ad93SMark Brown 	if (header->core != dsp->type) {
9272159ad93SMark Brown 		adsp_err(dsp, "%s: invalid core %d != %d\n",
9282159ad93SMark Brown 			 file, header->core, dsp->type);
9292159ad93SMark Brown 		goto out_fw;
9302159ad93SMark Brown 	}
9312159ad93SMark Brown 
9322159ad93SMark Brown 	switch (dsp->type) {
9332159ad93SMark Brown 	case WMFW_ADSP1:
9342159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
9352159ad93SMark Brown 		adsp1_sizes = (void *)&(header[1]);
9362159ad93SMark Brown 		footer = (void *)&(adsp1_sizes[1]);
9372159ad93SMark Brown 		sizes = sizeof(*adsp1_sizes);
9382159ad93SMark Brown 
9392159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
9402159ad93SMark Brown 			 file, le32_to_cpu(adsp1_sizes->dm),
9412159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->pm),
9422159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->zm));
9432159ad93SMark Brown 		break;
9442159ad93SMark Brown 
9452159ad93SMark Brown 	case WMFW_ADSP2:
9462159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
9472159ad93SMark Brown 		adsp2_sizes = (void *)&(header[1]);
9482159ad93SMark Brown 		footer = (void *)&(adsp2_sizes[1]);
9492159ad93SMark Brown 		sizes = sizeof(*adsp2_sizes);
9502159ad93SMark Brown 
9512159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
9522159ad93SMark Brown 			 file, le32_to_cpu(adsp2_sizes->xm),
9532159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->ym),
9542159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->pm),
9552159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->zm));
9562159ad93SMark Brown 		break;
9572159ad93SMark Brown 
9582159ad93SMark Brown 	default:
9596c452bdaSTakashi Iwai 		WARN(1, "Unknown DSP type");
9602159ad93SMark Brown 		goto out_fw;
9612159ad93SMark Brown 	}
9622159ad93SMark Brown 
9632159ad93SMark Brown 	if (le32_to_cpu(header->len) != sizeof(*header) +
9642159ad93SMark Brown 	    sizes + sizeof(*footer)) {
9652159ad93SMark Brown 		adsp_err(dsp, "%s: unexpected header length %d\n",
9662159ad93SMark Brown 			 file, le32_to_cpu(header->len));
9672159ad93SMark Brown 		goto out_fw;
9682159ad93SMark Brown 	}
9692159ad93SMark Brown 
9702159ad93SMark Brown 	adsp_dbg(dsp, "%s: timestamp %llu\n", file,
9712159ad93SMark Brown 		 le64_to_cpu(footer->timestamp));
9722159ad93SMark Brown 
9732159ad93SMark Brown 	while (pos < firmware->size &&
9742159ad93SMark Brown 	       pos - firmware->size > sizeof(*region)) {
9752159ad93SMark Brown 		region = (void *)&(firmware->data[pos]);
9762159ad93SMark Brown 		region_name = "Unknown";
9772159ad93SMark Brown 		reg = 0;
9782159ad93SMark Brown 		text = NULL;
9792159ad93SMark Brown 		offset = le32_to_cpu(region->offset) & 0xffffff;
9802159ad93SMark Brown 		type = be32_to_cpu(region->type) & 0xff;
9812159ad93SMark Brown 		mem = wm_adsp_find_region(dsp, type);
9822159ad93SMark Brown 
9832159ad93SMark Brown 		switch (type) {
9842159ad93SMark Brown 		case WMFW_NAME_TEXT:
9852159ad93SMark Brown 			region_name = "Firmware name";
9862159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
9872159ad93SMark Brown 				       GFP_KERNEL);
9882159ad93SMark Brown 			break;
9892323736dSCharles Keepax 		case WMFW_ALGORITHM_DATA:
9902323736dSCharles Keepax 			region_name = "Algorithm";
9912323736dSCharles Keepax 			ret = wm_adsp_parse_coeff(dsp, region);
9922323736dSCharles Keepax 			if (ret != 0)
9932323736dSCharles Keepax 				goto out_fw;
9942323736dSCharles Keepax 			break;
9952159ad93SMark Brown 		case WMFW_INFO_TEXT:
9962159ad93SMark Brown 			region_name = "Information";
9972159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
9982159ad93SMark Brown 				       GFP_KERNEL);
9992159ad93SMark Brown 			break;
10002159ad93SMark Brown 		case WMFW_ABSOLUTE:
10012159ad93SMark Brown 			region_name = "Absolute";
10022159ad93SMark Brown 			reg = offset;
10032159ad93SMark Brown 			break;
10042159ad93SMark Brown 		case WMFW_ADSP1_PM:
10052159ad93SMark Brown 			region_name = "PM";
100645b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
10072159ad93SMark Brown 			break;
10082159ad93SMark Brown 		case WMFW_ADSP1_DM:
10092159ad93SMark Brown 			region_name = "DM";
101045b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
10112159ad93SMark Brown 			break;
10122159ad93SMark Brown 		case WMFW_ADSP2_XM:
10132159ad93SMark Brown 			region_name = "XM";
101445b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
10152159ad93SMark Brown 			break;
10162159ad93SMark Brown 		case WMFW_ADSP2_YM:
10172159ad93SMark Brown 			region_name = "YM";
101845b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
10192159ad93SMark Brown 			break;
10202159ad93SMark Brown 		case WMFW_ADSP1_ZM:
10212159ad93SMark Brown 			region_name = "ZM";
102245b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
10232159ad93SMark Brown 			break;
10242159ad93SMark Brown 		default:
10252159ad93SMark Brown 			adsp_warn(dsp,
10262159ad93SMark Brown 				  "%s.%d: Unknown region type %x at %d(%x)\n",
10272159ad93SMark Brown 				  file, regions, type, pos, pos);
10282159ad93SMark Brown 			break;
10292159ad93SMark Brown 		}
10302159ad93SMark Brown 
10312159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
10322159ad93SMark Brown 			 regions, le32_to_cpu(region->len), offset,
10332159ad93SMark Brown 			 region_name);
10342159ad93SMark Brown 
10352159ad93SMark Brown 		if (text) {
10362159ad93SMark Brown 			memcpy(text, region->data, le32_to_cpu(region->len));
10372159ad93SMark Brown 			adsp_info(dsp, "%s: %s\n", file, text);
10382159ad93SMark Brown 			kfree(text);
10392159ad93SMark Brown 		}
10402159ad93SMark Brown 
10412159ad93SMark Brown 		if (reg) {
1042cdcd7f72SCharles Keepax 			buf = wm_adsp_buf_alloc(region->data,
1043cdcd7f72SCharles Keepax 						le32_to_cpu(region->len),
1044cf17c83cSMark Brown 						&buf_list);
1045a76fefabSMark Brown 			if (!buf) {
1046a76fefabSMark Brown 				adsp_err(dsp, "Out of memory\n");
10477328823dSDimitris Papastamos 				ret = -ENOMEM;
10487328823dSDimitris Papastamos 				goto out_fw;
1049a76fefabSMark Brown 			}
1050a76fefabSMark Brown 
1051cdcd7f72SCharles Keepax 			ret = regmap_raw_write_async(regmap, reg, buf->buf,
1052cdcd7f72SCharles Keepax 						     le32_to_cpu(region->len));
10532159ad93SMark Brown 			if (ret != 0) {
10542159ad93SMark Brown 				adsp_err(dsp,
1055cdcd7f72SCharles Keepax 					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
10562159ad93SMark Brown 					file, regions,
1057cdcd7f72SCharles Keepax 					le32_to_cpu(region->len), offset,
10582159ad93SMark Brown 					region_name, ret);
10592159ad93SMark Brown 				goto out_fw;
10602159ad93SMark Brown 			}
10612159ad93SMark Brown 		}
10622159ad93SMark Brown 
10632159ad93SMark Brown 		pos += le32_to_cpu(region->len) + sizeof(*region);
10642159ad93SMark Brown 		regions++;
10652159ad93SMark Brown 	}
10662159ad93SMark Brown 
1067cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
1068cf17c83cSMark Brown 	if (ret != 0) {
1069cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1070cf17c83cSMark Brown 		goto out_fw;
1071cf17c83cSMark Brown 	}
1072cf17c83cSMark Brown 
10732159ad93SMark Brown 	if (pos > firmware->size)
10742159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
10752159ad93SMark Brown 			  file, regions, pos - firmware->size);
10762159ad93SMark Brown 
10772159ad93SMark Brown out_fw:
1078cf17c83cSMark Brown 	regmap_async_complete(regmap);
1079cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
10802159ad93SMark Brown 	release_firmware(firmware);
10812159ad93SMark Brown out:
10822159ad93SMark Brown 	kfree(file);
10832159ad93SMark Brown 
10842159ad93SMark Brown 	return ret;
10852159ad93SMark Brown }
10862159ad93SMark Brown 
10872323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
10882323736dSCharles Keepax 				  const struct wm_adsp_alg_region *alg_region)
10892323736dSCharles Keepax {
10902323736dSCharles Keepax 	struct wm_coeff_ctl *ctl;
10912323736dSCharles Keepax 
10922323736dSCharles Keepax 	list_for_each_entry(ctl, &dsp->ctl_list, list) {
10932323736dSCharles Keepax 		if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
10942323736dSCharles Keepax 		    alg_region->alg == ctl->alg_region.alg &&
10952323736dSCharles Keepax 		    alg_region->type == ctl->alg_region.type) {
10962323736dSCharles Keepax 			ctl->alg_region.base = alg_region->base;
10972323736dSCharles Keepax 		}
10982323736dSCharles Keepax 	}
10992323736dSCharles Keepax }
11002323736dSCharles Keepax 
11013809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
1102b618a185SCharles Keepax 			       unsigned int pos, unsigned int len)
1103db40517cSMark Brown {
1104b618a185SCharles Keepax 	void *alg;
1105b618a185SCharles Keepax 	int ret;
1106db40517cSMark Brown 	__be32 val;
1107db40517cSMark Brown 
11083809f001SCharles Keepax 	if (n_algs == 0) {
1109b618a185SCharles Keepax 		adsp_err(dsp, "No algorithms\n");
1110b618a185SCharles Keepax 		return ERR_PTR(-EINVAL);
1111db40517cSMark Brown 	}
1112db40517cSMark Brown 
11133809f001SCharles Keepax 	if (n_algs > 1024) {
11143809f001SCharles Keepax 		adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
1115b618a185SCharles Keepax 		return ERR_PTR(-EINVAL);
1116b618a185SCharles Keepax 	}
1117b618a185SCharles Keepax 
1118b618a185SCharles Keepax 	/* Read the terminator first to validate the length */
1119b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
1120b618a185SCharles Keepax 	if (ret != 0) {
1121b618a185SCharles Keepax 		adsp_err(dsp, "Failed to read algorithm list end: %d\n",
1122b618a185SCharles Keepax 			ret);
1123b618a185SCharles Keepax 		return ERR_PTR(ret);
1124b618a185SCharles Keepax 	}
1125b618a185SCharles Keepax 
1126b618a185SCharles Keepax 	if (be32_to_cpu(val) != 0xbedead)
1127b618a185SCharles Keepax 		adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
1128b618a185SCharles Keepax 			  pos + len, be32_to_cpu(val));
1129b618a185SCharles Keepax 
1130b618a185SCharles Keepax 	alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
1131b618a185SCharles Keepax 	if (!alg)
1132b618a185SCharles Keepax 		return ERR_PTR(-ENOMEM);
1133b618a185SCharles Keepax 
1134b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
1135b618a185SCharles Keepax 	if (ret != 0) {
1136b618a185SCharles Keepax 		adsp_err(dsp, "Failed to read algorithm list: %d\n",
1137b618a185SCharles Keepax 			ret);
1138b618a185SCharles Keepax 		kfree(alg);
1139b618a185SCharles Keepax 		return ERR_PTR(ret);
1140b618a185SCharles Keepax 	}
1141b618a185SCharles Keepax 
1142b618a185SCharles Keepax 	return alg;
1143b618a185SCharles Keepax }
1144b618a185SCharles Keepax 
1145d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
1146d9d20e17SCharles Keepax 							int type, __be32 id,
1147d9d20e17SCharles Keepax 							__be32 base)
1148d9d20e17SCharles Keepax {
1149d9d20e17SCharles Keepax 	struct wm_adsp_alg_region *alg_region;
1150d9d20e17SCharles Keepax 
1151d9d20e17SCharles Keepax 	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
1152d9d20e17SCharles Keepax 	if (!alg_region)
1153d9d20e17SCharles Keepax 		return ERR_PTR(-ENOMEM);
1154d9d20e17SCharles Keepax 
1155d9d20e17SCharles Keepax 	alg_region->type = type;
1156d9d20e17SCharles Keepax 	alg_region->alg = be32_to_cpu(id);
1157d9d20e17SCharles Keepax 	alg_region->base = be32_to_cpu(base);
1158d9d20e17SCharles Keepax 
1159d9d20e17SCharles Keepax 	list_add_tail(&alg_region->list, &dsp->alg_regions);
1160d9d20e17SCharles Keepax 
11612323736dSCharles Keepax 	if (dsp->fw_ver > 0)
11622323736dSCharles Keepax 		wm_adsp_ctl_fixup_base(dsp, alg_region);
11632323736dSCharles Keepax 
1164d9d20e17SCharles Keepax 	return alg_region;
1165d9d20e17SCharles Keepax }
1166d9d20e17SCharles Keepax 
1167b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
1168b618a185SCharles Keepax {
1169b618a185SCharles Keepax 	struct wmfw_adsp1_id_hdr adsp1_id;
1170b618a185SCharles Keepax 	struct wmfw_adsp1_alg_hdr *adsp1_alg;
11713809f001SCharles Keepax 	struct wm_adsp_alg_region *alg_region;
1172b618a185SCharles Keepax 	const struct wm_adsp_region *mem;
1173b618a185SCharles Keepax 	unsigned int pos, len;
11743809f001SCharles Keepax 	size_t n_algs;
1175b618a185SCharles Keepax 	int i, ret;
1176b618a185SCharles Keepax 
1177b618a185SCharles Keepax 	mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
11786c452bdaSTakashi Iwai 	if (WARN_ON(!mem))
1179db40517cSMark Brown 		return -EINVAL;
1180db40517cSMark Brown 
1181b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
1182db40517cSMark Brown 			      sizeof(adsp1_id));
1183db40517cSMark Brown 	if (ret != 0) {
1184db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm info: %d\n",
1185db40517cSMark Brown 			 ret);
1186db40517cSMark Brown 		return ret;
1187db40517cSMark Brown 	}
1188db40517cSMark Brown 
11893809f001SCharles Keepax 	n_algs = be32_to_cpu(adsp1_id.n_algs);
1190f395a218SMark Brown 	dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
1191db40517cSMark Brown 	adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1192f395a218SMark Brown 		  dsp->fw_id,
1193db40517cSMark Brown 		  (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
1194db40517cSMark Brown 		  (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
1195db40517cSMark Brown 		  be32_to_cpu(adsp1_id.fw.ver) & 0xff,
11963809f001SCharles Keepax 		  n_algs);
1197db40517cSMark Brown 
1198d9d20e17SCharles Keepax 	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1199d9d20e17SCharles Keepax 					   adsp1_id.fw.id, adsp1_id.zm);
1200d9d20e17SCharles Keepax 	if (IS_ERR(alg_region))
1201d9d20e17SCharles Keepax 		return PTR_ERR(alg_region);
1202ac50009fSMark Brown 
1203d9d20e17SCharles Keepax 	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1204d9d20e17SCharles Keepax 					   adsp1_id.fw.id, adsp1_id.dm);
1205d9d20e17SCharles Keepax 	if (IS_ERR(alg_region))
1206d9d20e17SCharles Keepax 		return PTR_ERR(alg_region);
1207ac50009fSMark Brown 
1208db40517cSMark Brown 	pos = sizeof(adsp1_id) / 2;
12093809f001SCharles Keepax 	len = (sizeof(*adsp1_alg) * n_algs) / 2;
1210db40517cSMark Brown 
12113809f001SCharles Keepax 	adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
1212b618a185SCharles Keepax 	if (IS_ERR(adsp1_alg))
1213b618a185SCharles Keepax 		return PTR_ERR(adsp1_alg);
1214db40517cSMark Brown 
12153809f001SCharles Keepax 	for (i = 0; i < n_algs; i++) {
1216471f4885SMark Brown 		adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
1217db40517cSMark Brown 			  i, be32_to_cpu(adsp1_alg[i].alg.id),
1218db40517cSMark Brown 			  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
1219db40517cSMark Brown 			  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
1220471f4885SMark Brown 			  be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
1221471f4885SMark Brown 			  be32_to_cpu(adsp1_alg[i].dm),
1222471f4885SMark Brown 			  be32_to_cpu(adsp1_alg[i].zm));
1223471f4885SMark Brown 
1224d9d20e17SCharles Keepax 		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
1225d9d20e17SCharles Keepax 						   adsp1_alg[i].alg.id,
1226d9d20e17SCharles Keepax 						   adsp1_alg[i].dm);
1227d9d20e17SCharles Keepax 		if (IS_ERR(alg_region)) {
1228d9d20e17SCharles Keepax 			ret = PTR_ERR(alg_region);
1229d6d52179SJS Park 			goto out;
1230d6d52179SJS Park 		}
12312323736dSCharles Keepax 		if (dsp->fw_ver == 0) {
12323809f001SCharles Keepax 			if (i + 1 < n_algs) {
12336958eb2aSCharles Keepax 				len = be32_to_cpu(adsp1_alg[i + 1].dm);
12346958eb2aSCharles Keepax 				len -= be32_to_cpu(adsp1_alg[i].dm);
12356958eb2aSCharles Keepax 				len *= 4;
12362323736dSCharles Keepax 				wm_adsp_create_control(dsp, alg_region, 0,
12372323736dSCharles Keepax 						       len, NULL, 0);
12386ab2b7b4SDimitris Papastamos 			} else {
12396ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
12406ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp1_alg[i].alg.id));
12416ab2b7b4SDimitris Papastamos 			}
12422323736dSCharles Keepax 		}
1243471f4885SMark Brown 
1244d9d20e17SCharles Keepax 		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
1245d9d20e17SCharles Keepax 						   adsp1_alg[i].alg.id,
1246d9d20e17SCharles Keepax 						   adsp1_alg[i].zm);
1247d9d20e17SCharles Keepax 		if (IS_ERR(alg_region)) {
1248d9d20e17SCharles Keepax 			ret = PTR_ERR(alg_region);
1249d6d52179SJS Park 			goto out;
1250d6d52179SJS Park 		}
12512323736dSCharles Keepax 		if (dsp->fw_ver == 0) {
12523809f001SCharles Keepax 			if (i + 1 < n_algs) {
12536958eb2aSCharles Keepax 				len = be32_to_cpu(adsp1_alg[i + 1].zm);
12546958eb2aSCharles Keepax 				len -= be32_to_cpu(adsp1_alg[i].zm);
12556958eb2aSCharles Keepax 				len *= 4;
12562323736dSCharles Keepax 				wm_adsp_create_control(dsp, alg_region, 0,
12572323736dSCharles Keepax 						       len, NULL, 0);
12586ab2b7b4SDimitris Papastamos 			} else {
12596ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
12606ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp1_alg[i].alg.id));
12616ab2b7b4SDimitris Papastamos 			}
1262b618a185SCharles Keepax 		}
12632323736dSCharles Keepax 	}
1264db40517cSMark Brown 
1265b618a185SCharles Keepax out:
1266b618a185SCharles Keepax 	kfree(adsp1_alg);
1267b618a185SCharles Keepax 	return ret;
1268b618a185SCharles Keepax }
1269b618a185SCharles Keepax 
1270b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
1271b618a185SCharles Keepax {
1272b618a185SCharles Keepax 	struct wmfw_adsp2_id_hdr adsp2_id;
1273b618a185SCharles Keepax 	struct wmfw_adsp2_alg_hdr *adsp2_alg;
12743809f001SCharles Keepax 	struct wm_adsp_alg_region *alg_region;
1275b618a185SCharles Keepax 	const struct wm_adsp_region *mem;
1276b618a185SCharles Keepax 	unsigned int pos, len;
12773809f001SCharles Keepax 	size_t n_algs;
1278b618a185SCharles Keepax 	int i, ret;
1279b618a185SCharles Keepax 
1280b618a185SCharles Keepax 	mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
1281b618a185SCharles Keepax 	if (WARN_ON(!mem))
1282b618a185SCharles Keepax 		return -EINVAL;
1283b618a185SCharles Keepax 
1284b618a185SCharles Keepax 	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
1285b618a185SCharles Keepax 			      sizeof(adsp2_id));
1286b618a185SCharles Keepax 	if (ret != 0) {
1287b618a185SCharles Keepax 		adsp_err(dsp, "Failed to read algorithm info: %d\n",
1288b618a185SCharles Keepax 			 ret);
1289b618a185SCharles Keepax 		return ret;
1290b618a185SCharles Keepax 	}
1291b618a185SCharles Keepax 
12923809f001SCharles Keepax 	n_algs = be32_to_cpu(adsp2_id.n_algs);
1293b618a185SCharles Keepax 	dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
1294b618a185SCharles Keepax 	adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
1295b618a185SCharles Keepax 		  dsp->fw_id,
1296b618a185SCharles Keepax 		  (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
1297b618a185SCharles Keepax 		  (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
1298b618a185SCharles Keepax 		  be32_to_cpu(adsp2_id.fw.ver) & 0xff,
12993809f001SCharles Keepax 		  n_algs);
1300b618a185SCharles Keepax 
1301d9d20e17SCharles Keepax 	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1302d9d20e17SCharles Keepax 					   adsp2_id.fw.id, adsp2_id.xm);
1303d9d20e17SCharles Keepax 	if (IS_ERR(alg_region))
1304d9d20e17SCharles Keepax 		return PTR_ERR(alg_region);
1305b618a185SCharles Keepax 
1306d9d20e17SCharles Keepax 	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1307d9d20e17SCharles Keepax 					   adsp2_id.fw.id, adsp2_id.ym);
1308d9d20e17SCharles Keepax 	if (IS_ERR(alg_region))
1309d9d20e17SCharles Keepax 		return PTR_ERR(alg_region);
1310b618a185SCharles Keepax 
1311d9d20e17SCharles Keepax 	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1312d9d20e17SCharles Keepax 					   adsp2_id.fw.id, adsp2_id.zm);
1313d9d20e17SCharles Keepax 	if (IS_ERR(alg_region))
1314d9d20e17SCharles Keepax 		return PTR_ERR(alg_region);
1315b618a185SCharles Keepax 
1316b618a185SCharles Keepax 	pos = sizeof(adsp2_id) / 2;
13173809f001SCharles Keepax 	len = (sizeof(*adsp2_alg) * n_algs) / 2;
1318b618a185SCharles Keepax 
13193809f001SCharles Keepax 	adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
1320b618a185SCharles Keepax 	if (IS_ERR(adsp2_alg))
1321b618a185SCharles Keepax 		return PTR_ERR(adsp2_alg);
1322b618a185SCharles Keepax 
13233809f001SCharles Keepax 	for (i = 0; i < n_algs; i++) {
1324471f4885SMark Brown 		adsp_info(dsp,
1325471f4885SMark Brown 			  "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
1326db40517cSMark Brown 			  i, be32_to_cpu(adsp2_alg[i].alg.id),
1327db40517cSMark Brown 			  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
1328db40517cSMark Brown 			  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
1329471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
1330471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].xm),
1331471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].ym),
1332471f4885SMark Brown 			  be32_to_cpu(adsp2_alg[i].zm));
1333471f4885SMark Brown 
1334d9d20e17SCharles Keepax 		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
1335d9d20e17SCharles Keepax 						   adsp2_alg[i].alg.id,
1336d9d20e17SCharles Keepax 						   adsp2_alg[i].xm);
1337d9d20e17SCharles Keepax 		if (IS_ERR(alg_region)) {
1338d9d20e17SCharles Keepax 			ret = PTR_ERR(alg_region);
1339d6d52179SJS Park 			goto out;
1340d6d52179SJS Park 		}
13412323736dSCharles Keepax 		if (dsp->fw_ver == 0) {
13423809f001SCharles Keepax 			if (i + 1 < n_algs) {
13436958eb2aSCharles Keepax 				len = be32_to_cpu(adsp2_alg[i + 1].xm);
13446958eb2aSCharles Keepax 				len -= be32_to_cpu(adsp2_alg[i].xm);
13456958eb2aSCharles Keepax 				len *= 4;
13462323736dSCharles Keepax 				wm_adsp_create_control(dsp, alg_region, 0,
13472323736dSCharles Keepax 						       len, NULL, 0);
13486ab2b7b4SDimitris Papastamos 			} else {
13496ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
13506ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp2_alg[i].alg.id));
13516ab2b7b4SDimitris Papastamos 			}
13522323736dSCharles Keepax 		}
1353471f4885SMark Brown 
1354d9d20e17SCharles Keepax 		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
1355d9d20e17SCharles Keepax 						   adsp2_alg[i].alg.id,
1356d9d20e17SCharles Keepax 						   adsp2_alg[i].ym);
1357d9d20e17SCharles Keepax 		if (IS_ERR(alg_region)) {
1358d9d20e17SCharles Keepax 			ret = PTR_ERR(alg_region);
1359d6d52179SJS Park 			goto out;
1360d6d52179SJS Park 		}
13612323736dSCharles Keepax 		if (dsp->fw_ver == 0) {
13623809f001SCharles Keepax 			if (i + 1 < n_algs) {
13636958eb2aSCharles Keepax 				len = be32_to_cpu(adsp2_alg[i + 1].ym);
13646958eb2aSCharles Keepax 				len -= be32_to_cpu(adsp2_alg[i].ym);
13656958eb2aSCharles Keepax 				len *= 4;
13662323736dSCharles Keepax 				wm_adsp_create_control(dsp, alg_region, 0,
13672323736dSCharles Keepax 						       len, NULL, 0);
13686ab2b7b4SDimitris Papastamos 			} else {
13696ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
13706ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp2_alg[i].alg.id));
13716ab2b7b4SDimitris Papastamos 			}
13722323736dSCharles Keepax 		}
1373471f4885SMark Brown 
1374d9d20e17SCharles Keepax 		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
1375d9d20e17SCharles Keepax 						   adsp2_alg[i].alg.id,
1376d9d20e17SCharles Keepax 						   adsp2_alg[i].zm);
1377d9d20e17SCharles Keepax 		if (IS_ERR(alg_region)) {
1378d9d20e17SCharles Keepax 			ret = PTR_ERR(alg_region);
1379d6d52179SJS Park 			goto out;
1380d6d52179SJS Park 		}
13812323736dSCharles Keepax 		if (dsp->fw_ver == 0) {
13823809f001SCharles Keepax 			if (i + 1 < n_algs) {
13836958eb2aSCharles Keepax 				len = be32_to_cpu(adsp2_alg[i + 1].zm);
13846958eb2aSCharles Keepax 				len -= be32_to_cpu(adsp2_alg[i].zm);
13856958eb2aSCharles Keepax 				len *= 4;
13862323736dSCharles Keepax 				wm_adsp_create_control(dsp, alg_region, 0,
13872323736dSCharles Keepax 						       len, NULL, 0);
13886ab2b7b4SDimitris Papastamos 			} else {
13896ab2b7b4SDimitris Papastamos 				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
13906ab2b7b4SDimitris Papastamos 					  be32_to_cpu(adsp2_alg[i].alg.id));
13916ab2b7b4SDimitris Papastamos 			}
1392db40517cSMark Brown 		}
13932323736dSCharles Keepax 	}
1394db40517cSMark Brown 
1395db40517cSMark Brown out:
1396b618a185SCharles Keepax 	kfree(adsp2_alg);
1397db40517cSMark Brown 	return ret;
1398db40517cSMark Brown }
1399db40517cSMark Brown 
14002159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp)
14012159ad93SMark Brown {
1402cf17c83cSMark Brown 	LIST_HEAD(buf_list);
14032159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
14042159ad93SMark Brown 	struct wmfw_coeff_hdr *hdr;
14052159ad93SMark Brown 	struct wmfw_coeff_item *blk;
14062159ad93SMark Brown 	const struct firmware *firmware;
1407471f4885SMark Brown 	const struct wm_adsp_region *mem;
1408471f4885SMark Brown 	struct wm_adsp_alg_region *alg_region;
14092159ad93SMark Brown 	const char *region_name;
14102159ad93SMark Brown 	int ret, pos, blocks, type, offset, reg;
14112159ad93SMark Brown 	char *file;
1412cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
14132159ad93SMark Brown 
14142159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
14152159ad93SMark Brown 	if (file == NULL)
14162159ad93SMark Brown 		return -ENOMEM;
14172159ad93SMark Brown 
14181023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
14191023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
14202159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
14212159ad93SMark Brown 
14222159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
14232159ad93SMark Brown 	if (ret != 0) {
14242159ad93SMark Brown 		adsp_warn(dsp, "Failed to request '%s'\n", file);
14252159ad93SMark Brown 		ret = 0;
14262159ad93SMark Brown 		goto out;
14272159ad93SMark Brown 	}
14282159ad93SMark Brown 	ret = -EINVAL;
14292159ad93SMark Brown 
14302159ad93SMark Brown 	if (sizeof(*hdr) >= firmware->size) {
14312159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
14322159ad93SMark Brown 			file, firmware->size);
14332159ad93SMark Brown 		goto out_fw;
14342159ad93SMark Brown 	}
14352159ad93SMark Brown 
14362159ad93SMark Brown 	hdr = (void*)&firmware->data[0];
14372159ad93SMark Brown 	if (memcmp(hdr->magic, "WMDR", 4) != 0) {
14382159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
1439a4cdbec7SCharles Keepax 		goto out_fw;
14402159ad93SMark Brown 	}
14412159ad93SMark Brown 
1442c712326dSMark Brown 	switch (be32_to_cpu(hdr->rev) & 0xff) {
1443c712326dSMark Brown 	case 1:
1444c712326dSMark Brown 		break;
1445c712326dSMark Brown 	default:
1446c712326dSMark Brown 		adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
1447c712326dSMark Brown 			 file, be32_to_cpu(hdr->rev) & 0xff);
1448c712326dSMark Brown 		ret = -EINVAL;
1449c712326dSMark Brown 		goto out_fw;
1450c712326dSMark Brown 	}
1451c712326dSMark Brown 
14522159ad93SMark Brown 	adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
14532159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >> 16) & 0xff,
14542159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >>  8) & 0xff,
14552159ad93SMark Brown 		le32_to_cpu(hdr->ver) & 0xff);
14562159ad93SMark Brown 
14572159ad93SMark Brown 	pos = le32_to_cpu(hdr->len);
14582159ad93SMark Brown 
14592159ad93SMark Brown 	blocks = 0;
14602159ad93SMark Brown 	while (pos < firmware->size &&
14612159ad93SMark Brown 	       pos - firmware->size > sizeof(*blk)) {
14622159ad93SMark Brown 		blk = (void*)(&firmware->data[pos]);
14632159ad93SMark Brown 
1464c712326dSMark Brown 		type = le16_to_cpu(blk->type);
1465c712326dSMark Brown 		offset = le16_to_cpu(blk->offset);
14662159ad93SMark Brown 
14672159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
14682159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->id),
14692159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >> 16) & 0xff,
14702159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >>  8) & 0xff,
14712159ad93SMark Brown 			 le32_to_cpu(blk->ver) & 0xff);
14722159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
14732159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->len), offset, type);
14742159ad93SMark Brown 
14752159ad93SMark Brown 		reg = 0;
14762159ad93SMark Brown 		region_name = "Unknown";
14772159ad93SMark Brown 		switch (type) {
1478c712326dSMark Brown 		case (WMFW_NAME_TEXT << 8):
1479c712326dSMark Brown 		case (WMFW_INFO_TEXT << 8):
14802159ad93SMark Brown 			break;
1481c712326dSMark Brown 		case (WMFW_ABSOLUTE << 8):
1482f395a218SMark Brown 			/*
1483f395a218SMark Brown 			 * Old files may use this for global
1484f395a218SMark Brown 			 * coefficients.
1485f395a218SMark Brown 			 */
1486f395a218SMark Brown 			if (le32_to_cpu(blk->id) == dsp->fw_id &&
1487f395a218SMark Brown 			    offset == 0) {
1488f395a218SMark Brown 				region_name = "global coefficients";
1489f395a218SMark Brown 				mem = wm_adsp_find_region(dsp, type);
1490f395a218SMark Brown 				if (!mem) {
1491f395a218SMark Brown 					adsp_err(dsp, "No ZM\n");
1492f395a218SMark Brown 					break;
1493f395a218SMark Brown 				}
1494f395a218SMark Brown 				reg = wm_adsp_region_to_reg(mem, 0);
1495f395a218SMark Brown 
1496f395a218SMark Brown 			} else {
14972159ad93SMark Brown 				region_name = "register";
14982159ad93SMark Brown 				reg = offset;
1499f395a218SMark Brown 			}
15002159ad93SMark Brown 			break;
1501471f4885SMark Brown 
1502471f4885SMark Brown 		case WMFW_ADSP1_DM:
1503471f4885SMark Brown 		case WMFW_ADSP1_ZM:
1504471f4885SMark Brown 		case WMFW_ADSP2_XM:
1505471f4885SMark Brown 		case WMFW_ADSP2_YM:
1506471f4885SMark Brown 			adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
1507471f4885SMark Brown 				 file, blocks, le32_to_cpu(blk->len),
1508471f4885SMark Brown 				 type, le32_to_cpu(blk->id));
1509471f4885SMark Brown 
1510471f4885SMark Brown 			mem = wm_adsp_find_region(dsp, type);
1511471f4885SMark Brown 			if (!mem) {
1512471f4885SMark Brown 				adsp_err(dsp, "No base for region %x\n", type);
1513471f4885SMark Brown 				break;
1514471f4885SMark Brown 			}
1515471f4885SMark Brown 
1516471f4885SMark Brown 			reg = 0;
1517471f4885SMark Brown 			list_for_each_entry(alg_region,
1518471f4885SMark Brown 					    &dsp->alg_regions, list) {
1519471f4885SMark Brown 				if (le32_to_cpu(blk->id) == alg_region->alg &&
1520471f4885SMark Brown 				    type == alg_region->type) {
1521338c5188SMark Brown 					reg = alg_region->base;
1522471f4885SMark Brown 					reg = wm_adsp_region_to_reg(mem,
1523471f4885SMark Brown 								    reg);
1524338c5188SMark Brown 					reg += offset;
1525d733dc08SCharles Keepax 					break;
1526471f4885SMark Brown 				}
1527471f4885SMark Brown 			}
1528471f4885SMark Brown 
1529471f4885SMark Brown 			if (reg == 0)
1530471f4885SMark Brown 				adsp_err(dsp, "No %x for algorithm %x\n",
1531471f4885SMark Brown 					 type, le32_to_cpu(blk->id));
1532471f4885SMark Brown 			break;
1533471f4885SMark Brown 
15342159ad93SMark Brown 		default:
153525c62f7eSMark Brown 			adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
153625c62f7eSMark Brown 				 file, blocks, type, pos);
15372159ad93SMark Brown 			break;
15382159ad93SMark Brown 		}
15392159ad93SMark Brown 
15402159ad93SMark Brown 		if (reg) {
1541cf17c83cSMark Brown 			buf = wm_adsp_buf_alloc(blk->data,
1542cf17c83cSMark Brown 						le32_to_cpu(blk->len),
1543cf17c83cSMark Brown 						&buf_list);
1544a76fefabSMark Brown 			if (!buf) {
1545a76fefabSMark Brown 				adsp_err(dsp, "Out of memory\n");
1546f4b82812SWei Yongjun 				ret = -ENOMEM;
1547f4b82812SWei Yongjun 				goto out_fw;
1548a76fefabSMark Brown 			}
1549a76fefabSMark Brown 
155020da6d5aSMark Brown 			adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
155120da6d5aSMark Brown 				 file, blocks, le32_to_cpu(blk->len),
155220da6d5aSMark Brown 				 reg);
1553cf17c83cSMark Brown 			ret = regmap_raw_write_async(regmap, reg, buf->buf,
15542159ad93SMark Brown 						     le32_to_cpu(blk->len));
15552159ad93SMark Brown 			if (ret != 0) {
15562159ad93SMark Brown 				adsp_err(dsp,
155743bc3bf6SDimitris Papastamos 					"%s.%d: Failed to write to %x in %s: %d\n",
155843bc3bf6SDimitris Papastamos 					file, blocks, reg, region_name, ret);
15592159ad93SMark Brown 			}
15602159ad93SMark Brown 		}
15612159ad93SMark Brown 
1562be951017SCharles Keepax 		pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
15632159ad93SMark Brown 		blocks++;
15642159ad93SMark Brown 	}
15652159ad93SMark Brown 
1566cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
1567cf17c83cSMark Brown 	if (ret != 0)
1568cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
1569cf17c83cSMark Brown 
15702159ad93SMark Brown 	if (pos > firmware->size)
15712159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
15722159ad93SMark Brown 			  file, blocks, pos - firmware->size);
15732159ad93SMark Brown 
15742159ad93SMark Brown out_fw:
15759da7a5a9SCharles Keepax 	regmap_async_complete(regmap);
15762159ad93SMark Brown 	release_firmware(firmware);
1577cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
15782159ad93SMark Brown out:
15792159ad93SMark Brown 	kfree(file);
1580f4b82812SWei Yongjun 	return ret;
15812159ad93SMark Brown }
15822159ad93SMark Brown 
15833809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp)
15845e7a7a22SMark Brown {
15853809f001SCharles Keepax 	INIT_LIST_HEAD(&dsp->alg_regions);
15865e7a7a22SMark Brown 
15875e7a7a22SMark Brown 	return 0;
15885e7a7a22SMark Brown }
15895e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
15905e7a7a22SMark Brown 
15912159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w,
15922159ad93SMark Brown 		   struct snd_kcontrol *kcontrol,
15932159ad93SMark Brown 		   int event)
15942159ad93SMark Brown {
159572718517SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
15962159ad93SMark Brown 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
15972159ad93SMark Brown 	struct wm_adsp *dsp = &dsps[w->shift];
1598b0101b4fSDimitris Papastamos 	struct wm_adsp_alg_region *alg_region;
15996ab2b7b4SDimitris Papastamos 	struct wm_coeff_ctl *ctl;
16002159ad93SMark Brown 	int ret;
160194e205bfSChris Rattray 	int val;
16022159ad93SMark Brown 
160300200107SLars-Peter Clausen 	dsp->card = codec->component.card;
160492bb4c32SDimitris Papastamos 
16052159ad93SMark Brown 	switch (event) {
16062159ad93SMark Brown 	case SND_SOC_DAPM_POST_PMU:
16072159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
16082159ad93SMark Brown 				   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
16092159ad93SMark Brown 
161094e205bfSChris Rattray 		/*
161194e205bfSChris Rattray 		 * For simplicity set the DSP clock rate to be the
161294e205bfSChris Rattray 		 * SYSCLK rate rather than making it configurable.
161394e205bfSChris Rattray 		 */
161494e205bfSChris Rattray 		if(dsp->sysclk_reg) {
161594e205bfSChris Rattray 			ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
161694e205bfSChris Rattray 			if (ret != 0) {
161794e205bfSChris Rattray 				adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
161894e205bfSChris Rattray 				ret);
161994e205bfSChris Rattray 				return ret;
162094e205bfSChris Rattray 			}
162194e205bfSChris Rattray 
162294e205bfSChris Rattray 			val = (val & dsp->sysclk_mask)
162394e205bfSChris Rattray 				>> dsp->sysclk_shift;
162494e205bfSChris Rattray 
162594e205bfSChris Rattray 			ret = regmap_update_bits(dsp->regmap,
162694e205bfSChris Rattray 						 dsp->base + ADSP1_CONTROL_31,
162794e205bfSChris Rattray 						 ADSP1_CLK_SEL_MASK, val);
162894e205bfSChris Rattray 			if (ret != 0) {
162994e205bfSChris Rattray 				adsp_err(dsp, "Failed to set clock rate: %d\n",
163094e205bfSChris Rattray 					 ret);
163194e205bfSChris Rattray 				return ret;
163294e205bfSChris Rattray 			}
163394e205bfSChris Rattray 		}
163494e205bfSChris Rattray 
16352159ad93SMark Brown 		ret = wm_adsp_load(dsp);
16362159ad93SMark Brown 		if (ret != 0)
16372159ad93SMark Brown 			goto err;
16382159ad93SMark Brown 
1639b618a185SCharles Keepax 		ret = wm_adsp1_setup_algs(dsp);
1640db40517cSMark Brown 		if (ret != 0)
1641db40517cSMark Brown 			goto err;
1642db40517cSMark Brown 
16432159ad93SMark Brown 		ret = wm_adsp_load_coeff(dsp);
16442159ad93SMark Brown 		if (ret != 0)
16452159ad93SMark Brown 			goto err;
16462159ad93SMark Brown 
16470c2e3f34SDimitris Papastamos 		/* Initialize caches for enabled and unset controls */
164881ad93ecSDimitris Papastamos 		ret = wm_coeff_init_control_caches(dsp);
16496ab2b7b4SDimitris Papastamos 		if (ret != 0)
16506ab2b7b4SDimitris Papastamos 			goto err;
16516ab2b7b4SDimitris Papastamos 
16520c2e3f34SDimitris Papastamos 		/* Sync set controls */
165381ad93ecSDimitris Papastamos 		ret = wm_coeff_sync_controls(dsp);
16546ab2b7b4SDimitris Papastamos 		if (ret != 0)
16556ab2b7b4SDimitris Papastamos 			goto err;
16566ab2b7b4SDimitris Papastamos 
16572159ad93SMark Brown 		/* Start the core running */
16582159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
16592159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START,
16602159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START);
16612159ad93SMark Brown 		break;
16622159ad93SMark Brown 
16632159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
16642159ad93SMark Brown 		/* Halt the core */
16652159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
16662159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START, 0);
16672159ad93SMark Brown 
16682159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
16692159ad93SMark Brown 				   ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
16702159ad93SMark Brown 
16712159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
16722159ad93SMark Brown 				   ADSP1_SYS_ENA, 0);
16736ab2b7b4SDimitris Papastamos 
167481ad93ecSDimitris Papastamos 		list_for_each_entry(ctl, &dsp->ctl_list, list)
16756ab2b7b4SDimitris Papastamos 			ctl->enabled = 0;
1676b0101b4fSDimitris Papastamos 
1677b0101b4fSDimitris Papastamos 		while (!list_empty(&dsp->alg_regions)) {
1678b0101b4fSDimitris Papastamos 			alg_region = list_first_entry(&dsp->alg_regions,
1679b0101b4fSDimitris Papastamos 						      struct wm_adsp_alg_region,
1680b0101b4fSDimitris Papastamos 						      list);
1681b0101b4fSDimitris Papastamos 			list_del(&alg_region->list);
1682b0101b4fSDimitris Papastamos 			kfree(alg_region);
1683b0101b4fSDimitris Papastamos 		}
16842159ad93SMark Brown 		break;
16852159ad93SMark Brown 
16862159ad93SMark Brown 	default:
16872159ad93SMark Brown 		break;
16882159ad93SMark Brown 	}
16892159ad93SMark Brown 
16902159ad93SMark Brown 	return 0;
16912159ad93SMark Brown 
16922159ad93SMark Brown err:
16932159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
16942159ad93SMark Brown 			   ADSP1_SYS_ENA, 0);
16952159ad93SMark Brown 	return ret;
16962159ad93SMark Brown }
16972159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
16982159ad93SMark Brown 
16992159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp)
17002159ad93SMark Brown {
17012159ad93SMark Brown 	unsigned int val;
17022159ad93SMark Brown 	int ret, count;
17032159ad93SMark Brown 
17041552c325SMark Brown 	ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
17052159ad93SMark Brown 				       ADSP2_SYS_ENA, ADSP2_SYS_ENA);
17062159ad93SMark Brown 	if (ret != 0)
17072159ad93SMark Brown 		return ret;
17082159ad93SMark Brown 
17092159ad93SMark Brown 	/* Wait for the RAM to start, should be near instantaneous */
1710939fd1e8SCharles Keepax 	for (count = 0; count < 10; ++count) {
17112159ad93SMark Brown 		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
17122159ad93SMark Brown 				  &val);
17132159ad93SMark Brown 		if (ret != 0)
17142159ad93SMark Brown 			return ret;
1715939fd1e8SCharles Keepax 
1716939fd1e8SCharles Keepax 		if (val & ADSP2_RAM_RDY)
1717939fd1e8SCharles Keepax 			break;
1718939fd1e8SCharles Keepax 
1719939fd1e8SCharles Keepax 		msleep(1);
1720939fd1e8SCharles Keepax 	}
17212159ad93SMark Brown 
17222159ad93SMark Brown 	if (!(val & ADSP2_RAM_RDY)) {
17232159ad93SMark Brown 		adsp_err(dsp, "Failed to start DSP RAM\n");
17242159ad93SMark Brown 		return -EBUSY;
17252159ad93SMark Brown 	}
17262159ad93SMark Brown 
17272159ad93SMark Brown 	adsp_dbg(dsp, "RAM ready after %d polls\n", count);
17282159ad93SMark Brown 
17292159ad93SMark Brown 	return 0;
17302159ad93SMark Brown }
17312159ad93SMark Brown 
173218b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work)
17332159ad93SMark Brown {
1734d8a64d6aSCharles Keepax 	struct wm_adsp *dsp = container_of(work,
1735d8a64d6aSCharles Keepax 					   struct wm_adsp,
1736d8a64d6aSCharles Keepax 					   boot_work);
17372159ad93SMark Brown 	int ret;
1738d8a64d6aSCharles Keepax 	unsigned int val;
17392159ad93SMark Brown 
1740dd49e2c8SMark Brown 	/*
1741dd49e2c8SMark Brown 	 * For simplicity set the DSP clock rate to be the
1742dd49e2c8SMark Brown 	 * SYSCLK rate rather than making it configurable.
1743dd49e2c8SMark Brown 	 */
1744dd49e2c8SMark Brown 	ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1745dd49e2c8SMark Brown 	if (ret != 0) {
1746d8a64d6aSCharles Keepax 		adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
1747d8a64d6aSCharles Keepax 		return;
1748dd49e2c8SMark Brown 	}
1749dd49e2c8SMark Brown 	val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1750dd49e2c8SMark Brown 		>> ARIZONA_SYSCLK_FREQ_SHIFT;
1751dd49e2c8SMark Brown 
17521552c325SMark Brown 	ret = regmap_update_bits_async(dsp->regmap,
1753dd49e2c8SMark Brown 				       dsp->base + ADSP2_CLOCKING,
1754dd49e2c8SMark Brown 				       ADSP2_CLK_SEL_MASK, val);
1755dd49e2c8SMark Brown 	if (ret != 0) {
1756d8a64d6aSCharles Keepax 		adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
1757d8a64d6aSCharles Keepax 		return;
1758dd49e2c8SMark Brown 	}
1759dd49e2c8SMark Brown 
1760973838a0SMark Brown 	if (dsp->dvfs) {
1761973838a0SMark Brown 		ret = regmap_read(dsp->regmap,
1762973838a0SMark Brown 				  dsp->base + ADSP2_CLOCKING, &val);
1763973838a0SMark Brown 		if (ret != 0) {
176462c35b3bSCharles Keepax 			adsp_err(dsp, "Failed to read clocking: %d\n", ret);
1765d8a64d6aSCharles Keepax 			return;
1766973838a0SMark Brown 		}
1767973838a0SMark Brown 
176825c6fdb0SMark Brown 		if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
1769973838a0SMark Brown 			ret = regulator_enable(dsp->dvfs);
1770973838a0SMark Brown 			if (ret != 0) {
177162c35b3bSCharles Keepax 				adsp_err(dsp,
1772973838a0SMark Brown 					 "Failed to enable supply: %d\n",
1773973838a0SMark Brown 					 ret);
1774d8a64d6aSCharles Keepax 				return;
1775973838a0SMark Brown 			}
1776973838a0SMark Brown 
1777973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs,
1778973838a0SMark Brown 						    1800000,
1779973838a0SMark Brown 						    1800000);
1780973838a0SMark Brown 			if (ret != 0) {
178162c35b3bSCharles Keepax 				adsp_err(dsp,
1782973838a0SMark Brown 					 "Failed to raise supply: %d\n",
1783973838a0SMark Brown 					 ret);
1784d8a64d6aSCharles Keepax 				return;
1785973838a0SMark Brown 			}
1786973838a0SMark Brown 		}
1787973838a0SMark Brown 	}
1788973838a0SMark Brown 
17892159ad93SMark Brown 	ret = wm_adsp2_ena(dsp);
17902159ad93SMark Brown 	if (ret != 0)
1791d8a64d6aSCharles Keepax 		return;
17922159ad93SMark Brown 
17932159ad93SMark Brown 	ret = wm_adsp_load(dsp);
17942159ad93SMark Brown 	if (ret != 0)
17952159ad93SMark Brown 		goto err;
17962159ad93SMark Brown 
1797b618a185SCharles Keepax 	ret = wm_adsp2_setup_algs(dsp);
1798db40517cSMark Brown 	if (ret != 0)
1799db40517cSMark Brown 		goto err;
1800db40517cSMark Brown 
18012159ad93SMark Brown 	ret = wm_adsp_load_coeff(dsp);
18022159ad93SMark Brown 	if (ret != 0)
18032159ad93SMark Brown 		goto err;
18042159ad93SMark Brown 
18050c2e3f34SDimitris Papastamos 	/* Initialize caches for enabled and unset controls */
180681ad93ecSDimitris Papastamos 	ret = wm_coeff_init_control_caches(dsp);
18076ab2b7b4SDimitris Papastamos 	if (ret != 0)
18086ab2b7b4SDimitris Papastamos 		goto err;
18096ab2b7b4SDimitris Papastamos 
18100c2e3f34SDimitris Papastamos 	/* Sync set controls */
181181ad93ecSDimitris Papastamos 	ret = wm_coeff_sync_controls(dsp);
18126ab2b7b4SDimitris Papastamos 	if (ret != 0)
18136ab2b7b4SDimitris Papastamos 		goto err;
18146ab2b7b4SDimitris Papastamos 
18151023dbd9SMark Brown 	dsp->running = true;
1816d8a64d6aSCharles Keepax 
1817d8a64d6aSCharles Keepax 	return;
1818d8a64d6aSCharles Keepax 
1819d8a64d6aSCharles Keepax err:
1820d8a64d6aSCharles Keepax 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1821d8a64d6aSCharles Keepax 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
1822d8a64d6aSCharles Keepax }
1823d8a64d6aSCharles Keepax 
182412db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
182512db5eddSCharles Keepax 		   struct snd_kcontrol *kcontrol, int event)
182612db5eddSCharles Keepax {
182772718517SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
182812db5eddSCharles Keepax 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
182912db5eddSCharles Keepax 	struct wm_adsp *dsp = &dsps[w->shift];
183012db5eddSCharles Keepax 
183100200107SLars-Peter Clausen 	dsp->card = codec->component.card;
183212db5eddSCharles Keepax 
183312db5eddSCharles Keepax 	switch (event) {
183412db5eddSCharles Keepax 	case SND_SOC_DAPM_PRE_PMU:
183512db5eddSCharles Keepax 		queue_work(system_unbound_wq, &dsp->boot_work);
183612db5eddSCharles Keepax 		break;
183712db5eddSCharles Keepax 	default:
183812db5eddSCharles Keepax 		break;
1839cab27258SCharles Keepax 	}
184012db5eddSCharles Keepax 
184112db5eddSCharles Keepax 	return 0;
184212db5eddSCharles Keepax }
184312db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
184412db5eddSCharles Keepax 
1845d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1846d8a64d6aSCharles Keepax 		   struct snd_kcontrol *kcontrol, int event)
1847d8a64d6aSCharles Keepax {
184872718517SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
1849d8a64d6aSCharles Keepax 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1850d8a64d6aSCharles Keepax 	struct wm_adsp *dsp = &dsps[w->shift];
1851d8a64d6aSCharles Keepax 	struct wm_adsp_alg_region *alg_region;
1852d8a64d6aSCharles Keepax 	struct wm_coeff_ctl *ctl;
1853d8a64d6aSCharles Keepax 	int ret;
1854d8a64d6aSCharles Keepax 
1855d8a64d6aSCharles Keepax 	switch (event) {
1856d8a64d6aSCharles Keepax 	case SND_SOC_DAPM_POST_PMU:
1857d8a64d6aSCharles Keepax 		flush_work(&dsp->boot_work);
1858d8a64d6aSCharles Keepax 
1859d8a64d6aSCharles Keepax 		if (!dsp->running)
1860d8a64d6aSCharles Keepax 			return -EIO;
1861d8a64d6aSCharles Keepax 
1862d8a64d6aSCharles Keepax 		ret = regmap_update_bits(dsp->regmap,
1863d8a64d6aSCharles Keepax 					 dsp->base + ADSP2_CONTROL,
186400e4c3b6SCharles Keepax 					 ADSP2_CORE_ENA | ADSP2_START,
186500e4c3b6SCharles Keepax 					 ADSP2_CORE_ENA | ADSP2_START);
1866d8a64d6aSCharles Keepax 		if (ret != 0)
1867d8a64d6aSCharles Keepax 			goto err;
18682159ad93SMark Brown 		break;
18692159ad93SMark Brown 
18702159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
18711023dbd9SMark Brown 		dsp->running = false;
18721023dbd9SMark Brown 
18732159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1874a7f9be7eSMark Brown 				   ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1875a7f9be7eSMark Brown 				   ADSP2_START, 0);
1876973838a0SMark Brown 
18772d30b575SMark Brown 		/* Make sure DMAs are quiesced */
18782d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
18792d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
18802d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
18812d30b575SMark Brown 
1882973838a0SMark Brown 		if (dsp->dvfs) {
1883973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs, 1200000,
1884973838a0SMark Brown 						    1800000);
1885973838a0SMark Brown 			if (ret != 0)
188662c35b3bSCharles Keepax 				adsp_warn(dsp,
1887973838a0SMark Brown 					  "Failed to lower supply: %d\n",
1888973838a0SMark Brown 					  ret);
1889973838a0SMark Brown 
1890973838a0SMark Brown 			ret = regulator_disable(dsp->dvfs);
1891973838a0SMark Brown 			if (ret != 0)
189262c35b3bSCharles Keepax 				adsp_err(dsp,
1893973838a0SMark Brown 					 "Failed to enable supply: %d\n",
1894973838a0SMark Brown 					 ret);
1895973838a0SMark Brown 		}
1896471f4885SMark Brown 
189781ad93ecSDimitris Papastamos 		list_for_each_entry(ctl, &dsp->ctl_list, list)
18986ab2b7b4SDimitris Papastamos 			ctl->enabled = 0;
18996ab2b7b4SDimitris Papastamos 
1900471f4885SMark Brown 		while (!list_empty(&dsp->alg_regions)) {
1901471f4885SMark Brown 			alg_region = list_first_entry(&dsp->alg_regions,
1902471f4885SMark Brown 						      struct wm_adsp_alg_region,
1903471f4885SMark Brown 						      list);
1904471f4885SMark Brown 			list_del(&alg_region->list);
1905471f4885SMark Brown 			kfree(alg_region);
1906471f4885SMark Brown 		}
1907ddbc5efeSCharles Keepax 
1908ddbc5efeSCharles Keepax 		adsp_dbg(dsp, "Shutdown complete\n");
19092159ad93SMark Brown 		break;
19102159ad93SMark Brown 
19112159ad93SMark Brown 	default:
19122159ad93SMark Brown 		break;
19132159ad93SMark Brown 	}
19142159ad93SMark Brown 
19152159ad93SMark Brown 	return 0;
19162159ad93SMark Brown err:
19172159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1918a7f9be7eSMark Brown 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
19192159ad93SMark Brown 	return ret;
19202159ad93SMark Brown }
19212159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event);
1922973838a0SMark Brown 
19233809f001SCharles Keepax int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs)
1924973838a0SMark Brown {
1925973838a0SMark Brown 	int ret;
1926973838a0SMark Brown 
192710a2b662SMark Brown 	/*
192810a2b662SMark Brown 	 * Disable the DSP memory by default when in reset for a small
192910a2b662SMark Brown 	 * power saving.
193010a2b662SMark Brown 	 */
19313809f001SCharles Keepax 	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
193210a2b662SMark Brown 				 ADSP2_MEM_ENA, 0);
193310a2b662SMark Brown 	if (ret != 0) {
19343809f001SCharles Keepax 		adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
193510a2b662SMark Brown 		return ret;
193610a2b662SMark Brown 	}
193710a2b662SMark Brown 
19383809f001SCharles Keepax 	INIT_LIST_HEAD(&dsp->alg_regions);
19393809f001SCharles Keepax 	INIT_LIST_HEAD(&dsp->ctl_list);
19403809f001SCharles Keepax 	INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
19416ab2b7b4SDimitris Papastamos 
1942973838a0SMark Brown 	if (dvfs) {
19433809f001SCharles Keepax 		dsp->dvfs = devm_regulator_get(dsp->dev, "DCVDD");
19443809f001SCharles Keepax 		if (IS_ERR(dsp->dvfs)) {
19453809f001SCharles Keepax 			ret = PTR_ERR(dsp->dvfs);
19463809f001SCharles Keepax 			adsp_err(dsp, "Failed to get DCVDD: %d\n", ret);
194781ad93ecSDimitris Papastamos 			return ret;
1948973838a0SMark Brown 		}
1949973838a0SMark Brown 
19503809f001SCharles Keepax 		ret = regulator_enable(dsp->dvfs);
1951973838a0SMark Brown 		if (ret != 0) {
19523809f001SCharles Keepax 			adsp_err(dsp, "Failed to enable DCVDD: %d\n", ret);
195381ad93ecSDimitris Papastamos 			return ret;
1954973838a0SMark Brown 		}
1955973838a0SMark Brown 
19563809f001SCharles Keepax 		ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000);
1957973838a0SMark Brown 		if (ret != 0) {
19583809f001SCharles Keepax 			adsp_err(dsp, "Failed to initialise DVFS: %d\n", ret);
195981ad93ecSDimitris Papastamos 			return ret;
1960973838a0SMark Brown 		}
1961973838a0SMark Brown 
19623809f001SCharles Keepax 		ret = regulator_disable(dsp->dvfs);
1963973838a0SMark Brown 		if (ret != 0) {
19643809f001SCharles Keepax 			adsp_err(dsp, "Failed to disable DCVDD: %d\n", ret);
196581ad93ecSDimitris Papastamos 			return ret;
1966973838a0SMark Brown 		}
1967973838a0SMark Brown 	}
1968973838a0SMark Brown 
1969973838a0SMark Brown 	return 0;
1970973838a0SMark Brown }
1971973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
19720a37c6efSPraveen Diwakar 
19730a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2");
1974