xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision 5be9c5b4)
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>
242159ad93SMark Brown #include <sound/core.h>
252159ad93SMark Brown #include <sound/pcm.h>
262159ad93SMark Brown #include <sound/pcm_params.h>
272159ad93SMark Brown #include <sound/soc.h>
282159ad93SMark Brown #include <sound/jack.h>
292159ad93SMark Brown #include <sound/initval.h>
302159ad93SMark Brown #include <sound/tlv.h>
312159ad93SMark Brown 
322159ad93SMark Brown #include <linux/mfd/arizona/registers.h>
332159ad93SMark Brown 
34dc91428aSMark Brown #include "arizona.h"
352159ad93SMark Brown #include "wm_adsp.h"
362159ad93SMark Brown 
372159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \
382159ad93SMark Brown 	dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
392159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \
402159ad93SMark Brown 	dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
412159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \
422159ad93SMark Brown 	dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
432159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \
442159ad93SMark Brown 	dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
452159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \
462159ad93SMark Brown 	dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
472159ad93SMark Brown 
482159ad93SMark Brown #define ADSP1_CONTROL_1                   0x00
492159ad93SMark Brown #define ADSP1_CONTROL_2                   0x02
502159ad93SMark Brown #define ADSP1_CONTROL_3                   0x03
512159ad93SMark Brown #define ADSP1_CONTROL_4                   0x04
522159ad93SMark Brown #define ADSP1_CONTROL_5                   0x06
532159ad93SMark Brown #define ADSP1_CONTROL_6                   0x07
542159ad93SMark Brown #define ADSP1_CONTROL_7                   0x08
552159ad93SMark Brown #define ADSP1_CONTROL_8                   0x09
562159ad93SMark Brown #define ADSP1_CONTROL_9                   0x0A
572159ad93SMark Brown #define ADSP1_CONTROL_10                  0x0B
582159ad93SMark Brown #define ADSP1_CONTROL_11                  0x0C
592159ad93SMark Brown #define ADSP1_CONTROL_12                  0x0D
602159ad93SMark Brown #define ADSP1_CONTROL_13                  0x0F
612159ad93SMark Brown #define ADSP1_CONTROL_14                  0x10
622159ad93SMark Brown #define ADSP1_CONTROL_15                  0x11
632159ad93SMark Brown #define ADSP1_CONTROL_16                  0x12
642159ad93SMark Brown #define ADSP1_CONTROL_17                  0x13
652159ad93SMark Brown #define ADSP1_CONTROL_18                  0x14
662159ad93SMark Brown #define ADSP1_CONTROL_19                  0x16
672159ad93SMark Brown #define ADSP1_CONTROL_20                  0x17
682159ad93SMark Brown #define ADSP1_CONTROL_21                  0x18
692159ad93SMark Brown #define ADSP1_CONTROL_22                  0x1A
702159ad93SMark Brown #define ADSP1_CONTROL_23                  0x1B
712159ad93SMark Brown #define ADSP1_CONTROL_24                  0x1C
722159ad93SMark Brown #define ADSP1_CONTROL_25                  0x1E
732159ad93SMark Brown #define ADSP1_CONTROL_26                  0x20
742159ad93SMark Brown #define ADSP1_CONTROL_27                  0x21
752159ad93SMark Brown #define ADSP1_CONTROL_28                  0x22
762159ad93SMark Brown #define ADSP1_CONTROL_29                  0x23
772159ad93SMark Brown #define ADSP1_CONTROL_30                  0x24
782159ad93SMark Brown #define ADSP1_CONTROL_31                  0x26
792159ad93SMark Brown 
802159ad93SMark Brown /*
812159ad93SMark Brown  * ADSP1 Control 19
822159ad93SMark Brown  */
832159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
842159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
852159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
862159ad93SMark Brown 
872159ad93SMark Brown 
882159ad93SMark Brown /*
892159ad93SMark Brown  * ADSP1 Control 30
902159ad93SMark Brown  */
912159ad93SMark Brown #define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
932159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
952159ad93SMark Brown #define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
962159ad93SMark Brown #define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
972159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
982159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
992159ad93SMark Brown #define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
1002159ad93SMark Brown #define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
1012159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1022159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1032159ad93SMark Brown #define ADSP1_START                       0x0001  /* DSP1_START */
1042159ad93SMark Brown #define ADSP1_START_MASK                  0x0001  /* DSP1_START */
1052159ad93SMark Brown #define ADSP1_START_SHIFT                      0  /* DSP1_START */
1062159ad93SMark Brown #define ADSP1_START_WIDTH                      1  /* DSP1_START */
1072159ad93SMark Brown 
10894e205bfSChris Rattray /*
10994e205bfSChris Rattray  * ADSP1 Control 31
11094e205bfSChris Rattray  */
11194e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
11294e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
11394e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
11494e205bfSChris Rattray 
1152d30b575SMark Brown #define ADSP2_CONTROL        0x0
1162d30b575SMark Brown #define ADSP2_CLOCKING       0x1
1172d30b575SMark Brown #define ADSP2_STATUS1        0x4
1182d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30
1192d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31
1202d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34
1212159ad93SMark Brown 
1222159ad93SMark Brown /*
1232159ad93SMark Brown  * ADSP2 Control
1242159ad93SMark Brown  */
1252159ad93SMark Brown 
1262159ad93SMark Brown #define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
1272159ad93SMark Brown #define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
1282159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
1292159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
1302159ad93SMark Brown #define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
1312159ad93SMark Brown #define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
1322159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
1332159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
1342159ad93SMark Brown #define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
1352159ad93SMark Brown #define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
1362159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1372159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1382159ad93SMark Brown #define ADSP2_START                       0x0001  /* DSP1_START */
1392159ad93SMark Brown #define ADSP2_START_MASK                  0x0001  /* DSP1_START */
1402159ad93SMark Brown #define ADSP2_START_SHIFT                      0  /* DSP1_START */
1412159ad93SMark Brown #define ADSP2_START_WIDTH                      1  /* DSP1_START */
1422159ad93SMark Brown 
1432159ad93SMark Brown /*
144973838a0SMark Brown  * ADSP2 clocking
145973838a0SMark Brown  */
146973838a0SMark Brown #define ADSP2_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
147973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
148973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
149973838a0SMark Brown 
150973838a0SMark Brown /*
1512159ad93SMark Brown  * ADSP2 Status 1
1522159ad93SMark Brown  */
1532159ad93SMark Brown #define ADSP2_RAM_RDY                     0x0001
1542159ad93SMark Brown #define ADSP2_RAM_RDY_MASK                0x0001
1552159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT                    0
1562159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH                    1
1572159ad93SMark Brown 
158cf17c83cSMark Brown struct wm_adsp_buf {
159cf17c83cSMark Brown 	struct list_head list;
160cf17c83cSMark Brown 	void *buf;
161cf17c83cSMark Brown };
162cf17c83cSMark Brown 
163cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
164cf17c83cSMark Brown 					     struct list_head *list)
165cf17c83cSMark Brown {
166cf17c83cSMark Brown 	struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
167cf17c83cSMark Brown 
168cf17c83cSMark Brown 	if (buf == NULL)
169cf17c83cSMark Brown 		return NULL;
170cf17c83cSMark Brown 
171cf17c83cSMark Brown 	buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
172cf17c83cSMark Brown 	if (!buf->buf) {
173cf17c83cSMark Brown 		kfree(buf);
174cf17c83cSMark Brown 		return NULL;
175cf17c83cSMark Brown 	}
176cf17c83cSMark Brown 
177cf17c83cSMark Brown 	if (list)
178cf17c83cSMark Brown 		list_add_tail(&buf->list, list);
179cf17c83cSMark Brown 
180cf17c83cSMark Brown 	return buf;
181cf17c83cSMark Brown }
182cf17c83cSMark Brown 
183cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list)
184cf17c83cSMark Brown {
185cf17c83cSMark Brown 	while (!list_empty(list)) {
186cf17c83cSMark Brown 		struct wm_adsp_buf *buf = list_first_entry(list,
187cf17c83cSMark Brown 							   struct wm_adsp_buf,
188cf17c83cSMark Brown 							   list);
189cf17c83cSMark Brown 		list_del(&buf->list);
190cf17c83cSMark Brown 		kfree(buf->buf);
191cf17c83cSMark Brown 		kfree(buf);
192cf17c83cSMark Brown 	}
193cf17c83cSMark Brown }
194cf17c83cSMark Brown 
19536e8fe99SMark Brown #define WM_ADSP_NUM_FW 4
1961023dbd9SMark Brown 
197dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0
198dd84f925SMark Brown #define WM_ADSP_FW_TX      1
199dd84f925SMark Brown #define WM_ADSP_FW_TX_SPK  2
200dd84f925SMark Brown #define WM_ADSP_FW_RX_ANC  3
201dd84f925SMark Brown 
2021023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
203dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
204dd84f925SMark Brown 	[WM_ADSP_FW_TX] =      "Tx",
205dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =  "Tx Speaker",
206dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =  "Rx ANC",
2071023dbd9SMark Brown };
2081023dbd9SMark Brown 
2091023dbd9SMark Brown static struct {
2101023dbd9SMark Brown 	const char *file;
2111023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = {
212dd84f925SMark Brown 	[WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
213dd84f925SMark Brown 	[WM_ADSP_FW_TX] =      { .file = "tx" },
214dd84f925SMark Brown 	[WM_ADSP_FW_TX_SPK] =  { .file = "tx-spk" },
215dd84f925SMark Brown 	[WM_ADSP_FW_RX_ANC] =  { .file = "rx-anc" },
2161023dbd9SMark Brown };
2171023dbd9SMark Brown 
2181023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
2191023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2201023dbd9SMark Brown {
2211023dbd9SMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2221023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2231023dbd9SMark Brown 	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
2241023dbd9SMark Brown 
2251023dbd9SMark Brown 	ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
2261023dbd9SMark Brown 
2271023dbd9SMark Brown 	return 0;
2281023dbd9SMark Brown }
2291023dbd9SMark Brown 
2301023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
2311023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
2321023dbd9SMark Brown {
2331023dbd9SMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2341023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2351023dbd9SMark Brown 	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
2361023dbd9SMark Brown 
2371023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
2381023dbd9SMark Brown 		return 0;
2391023dbd9SMark Brown 
2401023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
2411023dbd9SMark Brown 		return -EINVAL;
2421023dbd9SMark Brown 
2431023dbd9SMark Brown 	if (adsp[e->shift_l].running)
2441023dbd9SMark Brown 		return -EBUSY;
2451023dbd9SMark Brown 
24631522764SMark Brown 	adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
2471023dbd9SMark Brown 
2481023dbd9SMark Brown 	return 0;
2491023dbd9SMark Brown }
2501023dbd9SMark Brown 
2511023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = {
2521023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2531023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2541023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2551023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2561023dbd9SMark Brown };
2571023dbd9SMark Brown 
258b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
2591023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
2601023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2611023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
2621023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2631023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
2641023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
265b6ed61cfSMark Brown };
266b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
267b6ed61cfSMark Brown 
268b6ed61cfSMark Brown #if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
269b6ed61cfSMark Brown static const struct soc_enum wm_adsp2_rate_enum[] = {
270dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
271dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
272dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
273dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
274dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
275dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
276dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
277dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
278dc91428aSMark Brown 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
279dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
280dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
281dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
2825be9c5b4SCharles Keepax 	SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
283dc91428aSMark Brown 			      ARIZONA_DSP1_RATE_SHIFT, 0xf,
284dc91428aSMark Brown 			      ARIZONA_RATE_ENUM_SIZE,
285dc91428aSMark Brown 			      arizona_rate_text, arizona_rate_val),
286dc91428aSMark Brown };
287dc91428aSMark Brown 
288b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
2891023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
2901023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
291b6ed61cfSMark Brown 	SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
2921023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
2931023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
294b6ed61cfSMark Brown 	SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
2951023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
2961023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
297b6ed61cfSMark Brown 	SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
2981023dbd9SMark Brown 	SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
2991023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
300b6ed61cfSMark Brown 	SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
3011023dbd9SMark Brown };
302b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
303b6ed61cfSMark Brown #endif
3042159ad93SMark Brown 
3052159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
3062159ad93SMark Brown 							int type)
3072159ad93SMark Brown {
3082159ad93SMark Brown 	int i;
3092159ad93SMark Brown 
3102159ad93SMark Brown 	for (i = 0; i < dsp->num_mems; i++)
3112159ad93SMark Brown 		if (dsp->mem[i].type == type)
3122159ad93SMark Brown 			return &dsp->mem[i];
3132159ad93SMark Brown 
3142159ad93SMark Brown 	return NULL;
3152159ad93SMark Brown }
3162159ad93SMark Brown 
31745b9ee72SMark Brown static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
31845b9ee72SMark Brown 					  unsigned int offset)
31945b9ee72SMark Brown {
32045b9ee72SMark Brown 	switch (region->type) {
32145b9ee72SMark Brown 	case WMFW_ADSP1_PM:
32245b9ee72SMark Brown 		return region->base + (offset * 3);
32345b9ee72SMark Brown 	case WMFW_ADSP1_DM:
32445b9ee72SMark Brown 		return region->base + (offset * 2);
32545b9ee72SMark Brown 	case WMFW_ADSP2_XM:
32645b9ee72SMark Brown 		return region->base + (offset * 2);
32745b9ee72SMark Brown 	case WMFW_ADSP2_YM:
32845b9ee72SMark Brown 		return region->base + (offset * 2);
32945b9ee72SMark Brown 	case WMFW_ADSP1_ZM:
33045b9ee72SMark Brown 		return region->base + (offset * 2);
33145b9ee72SMark Brown 	default:
33245b9ee72SMark Brown 		WARN_ON(NULL != "Unknown memory region type");
33345b9ee72SMark Brown 		return offset;
33445b9ee72SMark Brown 	}
33545b9ee72SMark Brown }
33645b9ee72SMark Brown 
3372159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp)
3382159ad93SMark Brown {
339cf17c83cSMark Brown 	LIST_HEAD(buf_list);
3402159ad93SMark Brown 	const struct firmware *firmware;
3412159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
3422159ad93SMark Brown 	unsigned int pos = 0;
3432159ad93SMark Brown 	const struct wmfw_header *header;
3442159ad93SMark Brown 	const struct wmfw_adsp1_sizes *adsp1_sizes;
3452159ad93SMark Brown 	const struct wmfw_adsp2_sizes *adsp2_sizes;
3462159ad93SMark Brown 	const struct wmfw_footer *footer;
3472159ad93SMark Brown 	const struct wmfw_region *region;
3482159ad93SMark Brown 	const struct wm_adsp_region *mem;
3492159ad93SMark Brown 	const char *region_name;
3502159ad93SMark Brown 	char *file, *text;
351cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
3522159ad93SMark Brown 	unsigned int reg;
3532159ad93SMark Brown 	int regions = 0;
3542159ad93SMark Brown 	int ret, offset, type, sizes;
3552159ad93SMark Brown 
3562159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
3572159ad93SMark Brown 	if (file == NULL)
3582159ad93SMark Brown 		return -ENOMEM;
3592159ad93SMark Brown 
3601023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
3611023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
3622159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
3632159ad93SMark Brown 
3642159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
3652159ad93SMark Brown 	if (ret != 0) {
3662159ad93SMark Brown 		adsp_err(dsp, "Failed to request '%s'\n", file);
3672159ad93SMark Brown 		goto out;
3682159ad93SMark Brown 	}
3692159ad93SMark Brown 	ret = -EINVAL;
3702159ad93SMark Brown 
3712159ad93SMark Brown 	pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
3722159ad93SMark Brown 	if (pos >= firmware->size) {
3732159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
3742159ad93SMark Brown 			 file, firmware->size);
3752159ad93SMark Brown 		goto out_fw;
3762159ad93SMark Brown 	}
3772159ad93SMark Brown 
3782159ad93SMark Brown 	header = (void*)&firmware->data[0];
3792159ad93SMark Brown 
3802159ad93SMark Brown 	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
3812159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
3822159ad93SMark Brown 		goto out_fw;
3832159ad93SMark Brown 	}
3842159ad93SMark Brown 
3852159ad93SMark Brown 	if (header->ver != 0) {
3862159ad93SMark Brown 		adsp_err(dsp, "%s: unknown file format %d\n",
3872159ad93SMark Brown 			 file, header->ver);
3882159ad93SMark Brown 		goto out_fw;
3892159ad93SMark Brown 	}
3902159ad93SMark Brown 
3912159ad93SMark Brown 	if (header->core != dsp->type) {
3922159ad93SMark Brown 		adsp_err(dsp, "%s: invalid core %d != %d\n",
3932159ad93SMark Brown 			 file, header->core, dsp->type);
3942159ad93SMark Brown 		goto out_fw;
3952159ad93SMark Brown 	}
3962159ad93SMark Brown 
3972159ad93SMark Brown 	switch (dsp->type) {
3982159ad93SMark Brown 	case WMFW_ADSP1:
3992159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
4002159ad93SMark Brown 		adsp1_sizes = (void *)&(header[1]);
4012159ad93SMark Brown 		footer = (void *)&(adsp1_sizes[1]);
4022159ad93SMark Brown 		sizes = sizeof(*adsp1_sizes);
4032159ad93SMark Brown 
4042159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
4052159ad93SMark Brown 			 file, le32_to_cpu(adsp1_sizes->dm),
4062159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->pm),
4072159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->zm));
4082159ad93SMark Brown 		break;
4092159ad93SMark Brown 
4102159ad93SMark Brown 	case WMFW_ADSP2:
4112159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
4122159ad93SMark Brown 		adsp2_sizes = (void *)&(header[1]);
4132159ad93SMark Brown 		footer = (void *)&(adsp2_sizes[1]);
4142159ad93SMark Brown 		sizes = sizeof(*adsp2_sizes);
4152159ad93SMark Brown 
4162159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
4172159ad93SMark Brown 			 file, le32_to_cpu(adsp2_sizes->xm),
4182159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->ym),
4192159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->pm),
4202159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->zm));
4212159ad93SMark Brown 		break;
4222159ad93SMark Brown 
4232159ad93SMark Brown 	default:
4242159ad93SMark Brown 		BUG_ON(NULL == "Unknown DSP type");
4252159ad93SMark Brown 		goto out_fw;
4262159ad93SMark Brown 	}
4272159ad93SMark Brown 
4282159ad93SMark Brown 	if (le32_to_cpu(header->len) != sizeof(*header) +
4292159ad93SMark Brown 	    sizes + sizeof(*footer)) {
4302159ad93SMark Brown 		adsp_err(dsp, "%s: unexpected header length %d\n",
4312159ad93SMark Brown 			 file, le32_to_cpu(header->len));
4322159ad93SMark Brown 		goto out_fw;
4332159ad93SMark Brown 	}
4342159ad93SMark Brown 
4352159ad93SMark Brown 	adsp_dbg(dsp, "%s: timestamp %llu\n", file,
4362159ad93SMark Brown 		 le64_to_cpu(footer->timestamp));
4372159ad93SMark Brown 
4382159ad93SMark Brown 	while (pos < firmware->size &&
4392159ad93SMark Brown 	       pos - firmware->size > sizeof(*region)) {
4402159ad93SMark Brown 		region = (void *)&(firmware->data[pos]);
4412159ad93SMark Brown 		region_name = "Unknown";
4422159ad93SMark Brown 		reg = 0;
4432159ad93SMark Brown 		text = NULL;
4442159ad93SMark Brown 		offset = le32_to_cpu(region->offset) & 0xffffff;
4452159ad93SMark Brown 		type = be32_to_cpu(region->type) & 0xff;
4462159ad93SMark Brown 		mem = wm_adsp_find_region(dsp, type);
4472159ad93SMark Brown 
4482159ad93SMark Brown 		switch (type) {
4492159ad93SMark Brown 		case WMFW_NAME_TEXT:
4502159ad93SMark Brown 			region_name = "Firmware name";
4512159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
4522159ad93SMark Brown 				       GFP_KERNEL);
4532159ad93SMark Brown 			break;
4542159ad93SMark Brown 		case WMFW_INFO_TEXT:
4552159ad93SMark Brown 			region_name = "Information";
4562159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
4572159ad93SMark Brown 				       GFP_KERNEL);
4582159ad93SMark Brown 			break;
4592159ad93SMark Brown 		case WMFW_ABSOLUTE:
4602159ad93SMark Brown 			region_name = "Absolute";
4612159ad93SMark Brown 			reg = offset;
4622159ad93SMark Brown 			break;
4632159ad93SMark Brown 		case WMFW_ADSP1_PM:
4642159ad93SMark Brown 			BUG_ON(!mem);
4652159ad93SMark Brown 			region_name = "PM";
46645b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
4672159ad93SMark Brown 			break;
4682159ad93SMark Brown 		case WMFW_ADSP1_DM:
4692159ad93SMark Brown 			BUG_ON(!mem);
4702159ad93SMark Brown 			region_name = "DM";
47145b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
4722159ad93SMark Brown 			break;
4732159ad93SMark Brown 		case WMFW_ADSP2_XM:
4742159ad93SMark Brown 			BUG_ON(!mem);
4752159ad93SMark Brown 			region_name = "XM";
47645b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
4772159ad93SMark Brown 			break;
4782159ad93SMark Brown 		case WMFW_ADSP2_YM:
4792159ad93SMark Brown 			BUG_ON(!mem);
4802159ad93SMark Brown 			region_name = "YM";
48145b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
4822159ad93SMark Brown 			break;
4832159ad93SMark Brown 		case WMFW_ADSP1_ZM:
4842159ad93SMark Brown 			BUG_ON(!mem);
4852159ad93SMark Brown 			region_name = "ZM";
48645b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
4872159ad93SMark Brown 			break;
4882159ad93SMark Brown 		default:
4892159ad93SMark Brown 			adsp_warn(dsp,
4902159ad93SMark Brown 				  "%s.%d: Unknown region type %x at %d(%x)\n",
4912159ad93SMark Brown 				  file, regions, type, pos, pos);
4922159ad93SMark Brown 			break;
4932159ad93SMark Brown 		}
4942159ad93SMark Brown 
4952159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
4962159ad93SMark Brown 			 regions, le32_to_cpu(region->len), offset,
4972159ad93SMark Brown 			 region_name);
4982159ad93SMark Brown 
4992159ad93SMark Brown 		if (text) {
5002159ad93SMark Brown 			memcpy(text, region->data, le32_to_cpu(region->len));
5012159ad93SMark Brown 			adsp_info(dsp, "%s: %s\n", file, text);
5022159ad93SMark Brown 			kfree(text);
5032159ad93SMark Brown 		}
5042159ad93SMark Brown 
5052159ad93SMark Brown 		if (reg) {
506cf17c83cSMark Brown 			buf = wm_adsp_buf_alloc(region->data,
507cf17c83cSMark Brown 						le32_to_cpu(region->len),
508cf17c83cSMark Brown 						&buf_list);
509a76fefabSMark Brown 			if (!buf) {
510a76fefabSMark Brown 				adsp_err(dsp, "Out of memory\n");
511a76fefabSMark Brown 				return -ENOMEM;
512a76fefabSMark Brown 			}
513a76fefabSMark Brown 
514cf17c83cSMark Brown 			ret = regmap_raw_write_async(regmap, reg, buf->buf,
5152159ad93SMark Brown 						     le32_to_cpu(region->len));
5162159ad93SMark Brown 			if (ret != 0) {
5172159ad93SMark Brown 				adsp_err(dsp,
5182159ad93SMark Brown 					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
5192159ad93SMark Brown 					file, regions,
5202159ad93SMark Brown 					le32_to_cpu(region->len), offset,
5212159ad93SMark Brown 					region_name, ret);
5222159ad93SMark Brown 				goto out_fw;
5232159ad93SMark Brown 			}
5242159ad93SMark Brown 		}
5252159ad93SMark Brown 
5262159ad93SMark Brown 		pos += le32_to_cpu(region->len) + sizeof(*region);
5272159ad93SMark Brown 		regions++;
5282159ad93SMark Brown 	}
5292159ad93SMark Brown 
530cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
531cf17c83cSMark Brown 	if (ret != 0) {
532cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
533cf17c83cSMark Brown 		goto out_fw;
534cf17c83cSMark Brown 	}
535cf17c83cSMark Brown 
5362159ad93SMark Brown 	if (pos > firmware->size)
5372159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
5382159ad93SMark Brown 			  file, regions, pos - firmware->size);
5392159ad93SMark Brown 
5402159ad93SMark Brown out_fw:
541cf17c83cSMark Brown 	regmap_async_complete(regmap);
542cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
5432159ad93SMark Brown 	release_firmware(firmware);
5442159ad93SMark Brown out:
5452159ad93SMark Brown 	kfree(file);
5462159ad93SMark Brown 
5472159ad93SMark Brown 	return ret;
5482159ad93SMark Brown }
5492159ad93SMark Brown 
550db40517cSMark Brown static int wm_adsp_setup_algs(struct wm_adsp *dsp)
551db40517cSMark Brown {
552db40517cSMark Brown 	struct regmap *regmap = dsp->regmap;
553db40517cSMark Brown 	struct wmfw_adsp1_id_hdr adsp1_id;
554db40517cSMark Brown 	struct wmfw_adsp2_id_hdr adsp2_id;
555db40517cSMark Brown 	struct wmfw_adsp1_alg_hdr *adsp1_alg;
556db40517cSMark Brown 	struct wmfw_adsp2_alg_hdr *adsp2_alg;
557d62f4bc6SMark Brown 	void *alg, *buf;
558471f4885SMark Brown 	struct wm_adsp_alg_region *region;
559db40517cSMark Brown 	const struct wm_adsp_region *mem;
560db40517cSMark Brown 	unsigned int pos, term;
561d62f4bc6SMark Brown 	size_t algs, buf_size;
562db40517cSMark Brown 	__be32 val;
563db40517cSMark Brown 	int i, ret;
564db40517cSMark Brown 
565db40517cSMark Brown 	switch (dsp->type) {
566db40517cSMark Brown 	case WMFW_ADSP1:
567db40517cSMark Brown 		mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
568db40517cSMark Brown 		break;
569db40517cSMark Brown 	case WMFW_ADSP2:
570db40517cSMark Brown 		mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
571db40517cSMark Brown 		break;
572db40517cSMark Brown 	default:
573db40517cSMark Brown 		mem = NULL;
574db40517cSMark Brown 		break;
575db40517cSMark Brown 	}
576db40517cSMark Brown 
577db40517cSMark Brown 	if (mem == NULL) {
578db40517cSMark Brown 		BUG_ON(mem != NULL);
579db40517cSMark Brown 		return -EINVAL;
580db40517cSMark Brown 	}
581db40517cSMark Brown 
582db40517cSMark Brown 	switch (dsp->type) {
583db40517cSMark Brown 	case WMFW_ADSP1:
584db40517cSMark Brown 		ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
585db40517cSMark Brown 				      sizeof(adsp1_id));
586db40517cSMark Brown 		if (ret != 0) {
587db40517cSMark Brown 			adsp_err(dsp, "Failed to read algorithm info: %d\n",
588db40517cSMark Brown 				 ret);
589db40517cSMark Brown 			return ret;
590db40517cSMark Brown 		}
591db40517cSMark Brown 
592d62f4bc6SMark Brown 		buf = &adsp1_id;
593d62f4bc6SMark Brown 		buf_size = sizeof(adsp1_id);
594d62f4bc6SMark Brown 
595db40517cSMark Brown 		algs = be32_to_cpu(adsp1_id.algs);
596f395a218SMark Brown 		dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
597db40517cSMark Brown 		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
598f395a218SMark Brown 			  dsp->fw_id,
599db40517cSMark Brown 			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
600db40517cSMark Brown 			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
601db40517cSMark Brown 			  be32_to_cpu(adsp1_id.fw.ver) & 0xff,
602db40517cSMark Brown 			  algs);
603db40517cSMark Brown 
604ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
605ac50009fSMark Brown 		if (!region)
606ac50009fSMark Brown 			return -ENOMEM;
607ac50009fSMark Brown 		region->type = WMFW_ADSP1_ZM;
608ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp1_id.fw.id);
609ac50009fSMark Brown 		region->base = be32_to_cpu(adsp1_id.zm);
610ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
611ac50009fSMark Brown 
612ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
613ac50009fSMark Brown 		if (!region)
614ac50009fSMark Brown 			return -ENOMEM;
615ac50009fSMark Brown 		region->type = WMFW_ADSP1_DM;
616ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp1_id.fw.id);
617ac50009fSMark Brown 		region->base = be32_to_cpu(adsp1_id.dm);
618ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
619ac50009fSMark Brown 
620db40517cSMark Brown 		pos = sizeof(adsp1_id) / 2;
621db40517cSMark Brown 		term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
622db40517cSMark Brown 		break;
623db40517cSMark Brown 
624db40517cSMark Brown 	case WMFW_ADSP2:
625db40517cSMark Brown 		ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
626db40517cSMark Brown 				      sizeof(adsp2_id));
627db40517cSMark Brown 		if (ret != 0) {
628db40517cSMark Brown 			adsp_err(dsp, "Failed to read algorithm info: %d\n",
629db40517cSMark Brown 				 ret);
630db40517cSMark Brown 			return ret;
631db40517cSMark Brown 		}
632db40517cSMark Brown 
633d62f4bc6SMark Brown 		buf = &adsp2_id;
634d62f4bc6SMark Brown 		buf_size = sizeof(adsp2_id);
635d62f4bc6SMark Brown 
636db40517cSMark Brown 		algs = be32_to_cpu(adsp2_id.algs);
637f395a218SMark Brown 		dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
638db40517cSMark Brown 		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
639f395a218SMark Brown 			  dsp->fw_id,
640db40517cSMark Brown 			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
641db40517cSMark Brown 			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
642db40517cSMark Brown 			  be32_to_cpu(adsp2_id.fw.ver) & 0xff,
643db40517cSMark Brown 			  algs);
644db40517cSMark Brown 
645ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
646ac50009fSMark Brown 		if (!region)
647ac50009fSMark Brown 			return -ENOMEM;
648ac50009fSMark Brown 		region->type = WMFW_ADSP2_XM;
649ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp2_id.fw.id);
650ac50009fSMark Brown 		region->base = be32_to_cpu(adsp2_id.xm);
651ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
652ac50009fSMark Brown 
653ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
654ac50009fSMark Brown 		if (!region)
655ac50009fSMark Brown 			return -ENOMEM;
656ac50009fSMark Brown 		region->type = WMFW_ADSP2_YM;
657ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp2_id.fw.id);
658ac50009fSMark Brown 		region->base = be32_to_cpu(adsp2_id.ym);
659ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
660ac50009fSMark Brown 
661ac50009fSMark Brown 		region = kzalloc(sizeof(*region), GFP_KERNEL);
662ac50009fSMark Brown 		if (!region)
663ac50009fSMark Brown 			return -ENOMEM;
664ac50009fSMark Brown 		region->type = WMFW_ADSP2_ZM;
665ac50009fSMark Brown 		region->alg = be32_to_cpu(adsp2_id.fw.id);
666ac50009fSMark Brown 		region->base = be32_to_cpu(adsp2_id.zm);
667ac50009fSMark Brown 		list_add_tail(&region->list, &dsp->alg_regions);
668ac50009fSMark Brown 
669db40517cSMark Brown 		pos = sizeof(adsp2_id) / 2;
670db40517cSMark Brown 		term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
671db40517cSMark Brown 		break;
672db40517cSMark Brown 
673db40517cSMark Brown 	default:
674db40517cSMark Brown 		BUG_ON(NULL == "Unknown DSP type");
675db40517cSMark Brown 		return -EINVAL;
676db40517cSMark Brown 	}
677db40517cSMark Brown 
678db40517cSMark Brown 	if (algs == 0) {
679db40517cSMark Brown 		adsp_err(dsp, "No algorithms\n");
680db40517cSMark Brown 		return -EINVAL;
681db40517cSMark Brown 	}
682db40517cSMark Brown 
683d62f4bc6SMark Brown 	if (algs > 1024) {
684d62f4bc6SMark Brown 		adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
685d62f4bc6SMark Brown 		print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
686d62f4bc6SMark Brown 				     buf, buf_size);
687d62f4bc6SMark Brown 		return -EINVAL;
688d62f4bc6SMark Brown 	}
689d62f4bc6SMark Brown 
690db40517cSMark Brown 	/* Read the terminator first to validate the length */
691db40517cSMark Brown 	ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
692db40517cSMark Brown 	if (ret != 0) {
693db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm list end: %d\n",
694db40517cSMark Brown 			ret);
695db40517cSMark Brown 		return ret;
696db40517cSMark Brown 	}
697db40517cSMark Brown 
698db40517cSMark Brown 	if (be32_to_cpu(val) != 0xbedead)
699db40517cSMark Brown 		adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
700db40517cSMark Brown 			  term, be32_to_cpu(val));
701db40517cSMark Brown 
702f2a93e2aSMark Brown 	alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
703db40517cSMark Brown 	if (!alg)
704db40517cSMark Brown 		return -ENOMEM;
705db40517cSMark Brown 
706db40517cSMark Brown 	ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
707db40517cSMark Brown 	if (ret != 0) {
708db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm list: %d\n",
709db40517cSMark Brown 			ret);
710db40517cSMark Brown 		goto out;
711db40517cSMark Brown 	}
712db40517cSMark Brown 
713db40517cSMark Brown 	adsp1_alg = alg;
714db40517cSMark Brown 	adsp2_alg = alg;
715db40517cSMark Brown 
716db40517cSMark Brown 	for (i = 0; i < algs; i++) {
717db40517cSMark Brown 		switch (dsp->type) {
718db40517cSMark Brown 		case WMFW_ADSP1:
719471f4885SMark Brown 			adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
720db40517cSMark Brown 				  i, be32_to_cpu(adsp1_alg[i].alg.id),
721db40517cSMark Brown 				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
722db40517cSMark Brown 				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
723471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
724471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].dm),
725471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].zm));
726471f4885SMark Brown 
727471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
728471f4885SMark Brown 			if (!region)
729471f4885SMark Brown 				return -ENOMEM;
730471f4885SMark Brown 			region->type = WMFW_ADSP1_DM;
731471f4885SMark Brown 			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
732471f4885SMark Brown 			region->base = be32_to_cpu(adsp1_alg[i].dm);
7337480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
734471f4885SMark Brown 
735471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
736471f4885SMark Brown 			if (!region)
737471f4885SMark Brown 				return -ENOMEM;
738471f4885SMark Brown 			region->type = WMFW_ADSP1_ZM;
739471f4885SMark Brown 			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
740471f4885SMark Brown 			region->base = be32_to_cpu(adsp1_alg[i].zm);
7417480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
742db40517cSMark Brown 			break;
743db40517cSMark Brown 
744db40517cSMark Brown 		case WMFW_ADSP2:
745471f4885SMark Brown 			adsp_info(dsp,
746471f4885SMark Brown 				  "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
747db40517cSMark Brown 				  i, be32_to_cpu(adsp2_alg[i].alg.id),
748db40517cSMark Brown 				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
749db40517cSMark Brown 				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
750471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
751471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].xm),
752471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].ym),
753471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].zm));
754471f4885SMark Brown 
755471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
756471f4885SMark Brown 			if (!region)
757471f4885SMark Brown 				return -ENOMEM;
758471f4885SMark Brown 			region->type = WMFW_ADSP2_XM;
759471f4885SMark Brown 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
760471f4885SMark Brown 			region->base = be32_to_cpu(adsp2_alg[i].xm);
7617480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
762471f4885SMark Brown 
763471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
764471f4885SMark Brown 			if (!region)
765471f4885SMark Brown 				return -ENOMEM;
766471f4885SMark Brown 			region->type = WMFW_ADSP2_YM;
767471f4885SMark Brown 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
768471f4885SMark Brown 			region->base = be32_to_cpu(adsp2_alg[i].ym);
7697480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
770471f4885SMark Brown 
771471f4885SMark Brown 			region = kzalloc(sizeof(*region), GFP_KERNEL);
772471f4885SMark Brown 			if (!region)
773471f4885SMark Brown 				return -ENOMEM;
774471f4885SMark Brown 			region->type = WMFW_ADSP2_ZM;
775471f4885SMark Brown 			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
776471f4885SMark Brown 			region->base = be32_to_cpu(adsp2_alg[i].zm);
7777480800eSMark Brown 			list_add_tail(&region->list, &dsp->alg_regions);
778db40517cSMark Brown 			break;
779db40517cSMark Brown 		}
780db40517cSMark Brown 	}
781db40517cSMark Brown 
782db40517cSMark Brown out:
783db40517cSMark Brown 	kfree(alg);
784db40517cSMark Brown 	return ret;
785db40517cSMark Brown }
786db40517cSMark Brown 
7872159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp)
7882159ad93SMark Brown {
789cf17c83cSMark Brown 	LIST_HEAD(buf_list);
7902159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
7912159ad93SMark Brown 	struct wmfw_coeff_hdr *hdr;
7922159ad93SMark Brown 	struct wmfw_coeff_item *blk;
7932159ad93SMark Brown 	const struct firmware *firmware;
794471f4885SMark Brown 	const struct wm_adsp_region *mem;
795471f4885SMark Brown 	struct wm_adsp_alg_region *alg_region;
7962159ad93SMark Brown 	const char *region_name;
7972159ad93SMark Brown 	int ret, pos, blocks, type, offset, reg;
7982159ad93SMark Brown 	char *file;
799cf17c83cSMark Brown 	struct wm_adsp_buf *buf;
800bdaacea3SChris Rattray 	int tmp;
8012159ad93SMark Brown 
8022159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
8032159ad93SMark Brown 	if (file == NULL)
8042159ad93SMark Brown 		return -ENOMEM;
8052159ad93SMark Brown 
8061023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
8071023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
8082159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
8092159ad93SMark Brown 
8102159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
8112159ad93SMark Brown 	if (ret != 0) {
8122159ad93SMark Brown 		adsp_warn(dsp, "Failed to request '%s'\n", file);
8132159ad93SMark Brown 		ret = 0;
8142159ad93SMark Brown 		goto out;
8152159ad93SMark Brown 	}
8162159ad93SMark Brown 	ret = -EINVAL;
8172159ad93SMark Brown 
8182159ad93SMark Brown 	if (sizeof(*hdr) >= firmware->size) {
8192159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
8202159ad93SMark Brown 			file, firmware->size);
8212159ad93SMark Brown 		goto out_fw;
8222159ad93SMark Brown 	}
8232159ad93SMark Brown 
8242159ad93SMark Brown 	hdr = (void*)&firmware->data[0];
8252159ad93SMark Brown 	if (memcmp(hdr->magic, "WMDR", 4) != 0) {
8262159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
827a4cdbec7SCharles Keepax 		goto out_fw;
8282159ad93SMark Brown 	}
8292159ad93SMark Brown 
830c712326dSMark Brown 	switch (be32_to_cpu(hdr->rev) & 0xff) {
831c712326dSMark Brown 	case 1:
832c712326dSMark Brown 		break;
833c712326dSMark Brown 	default:
834c712326dSMark Brown 		adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
835c712326dSMark Brown 			 file, be32_to_cpu(hdr->rev) & 0xff);
836c712326dSMark Brown 		ret = -EINVAL;
837c712326dSMark Brown 		goto out_fw;
838c712326dSMark Brown 	}
839c712326dSMark Brown 
8402159ad93SMark Brown 	adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
8412159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >> 16) & 0xff,
8422159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >>  8) & 0xff,
8432159ad93SMark Brown 		le32_to_cpu(hdr->ver) & 0xff);
8442159ad93SMark Brown 
8452159ad93SMark Brown 	pos = le32_to_cpu(hdr->len);
8462159ad93SMark Brown 
8472159ad93SMark Brown 	blocks = 0;
8482159ad93SMark Brown 	while (pos < firmware->size &&
8492159ad93SMark Brown 	       pos - firmware->size > sizeof(*blk)) {
8502159ad93SMark Brown 		blk = (void*)(&firmware->data[pos]);
8512159ad93SMark Brown 
852c712326dSMark Brown 		type = le16_to_cpu(blk->type);
853c712326dSMark Brown 		offset = le16_to_cpu(blk->offset);
8542159ad93SMark Brown 
8552159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
8562159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->id),
8572159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >> 16) & 0xff,
8582159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >>  8) & 0xff,
8592159ad93SMark Brown 			 le32_to_cpu(blk->ver) & 0xff);
8602159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
8612159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->len), offset, type);
8622159ad93SMark Brown 
8632159ad93SMark Brown 		reg = 0;
8642159ad93SMark Brown 		region_name = "Unknown";
8652159ad93SMark Brown 		switch (type) {
866c712326dSMark Brown 		case (WMFW_NAME_TEXT << 8):
867c712326dSMark Brown 		case (WMFW_INFO_TEXT << 8):
8682159ad93SMark Brown 			break;
869c712326dSMark Brown 		case (WMFW_ABSOLUTE << 8):
870f395a218SMark Brown 			/*
871f395a218SMark Brown 			 * Old files may use this for global
872f395a218SMark Brown 			 * coefficients.
873f395a218SMark Brown 			 */
874f395a218SMark Brown 			if (le32_to_cpu(blk->id) == dsp->fw_id &&
875f395a218SMark Brown 			    offset == 0) {
876f395a218SMark Brown 				region_name = "global coefficients";
877f395a218SMark Brown 				mem = wm_adsp_find_region(dsp, type);
878f395a218SMark Brown 				if (!mem) {
879f395a218SMark Brown 					adsp_err(dsp, "No ZM\n");
880f395a218SMark Brown 					break;
881f395a218SMark Brown 				}
882f395a218SMark Brown 				reg = wm_adsp_region_to_reg(mem, 0);
883f395a218SMark Brown 
884f395a218SMark Brown 			} else {
8852159ad93SMark Brown 				region_name = "register";
8862159ad93SMark Brown 				reg = offset;
887f395a218SMark Brown 			}
8882159ad93SMark Brown 			break;
889471f4885SMark Brown 
890471f4885SMark Brown 		case WMFW_ADSP1_DM:
891471f4885SMark Brown 		case WMFW_ADSP1_ZM:
892471f4885SMark Brown 		case WMFW_ADSP2_XM:
893471f4885SMark Brown 		case WMFW_ADSP2_YM:
894471f4885SMark Brown 			adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
895471f4885SMark Brown 				 file, blocks, le32_to_cpu(blk->len),
896471f4885SMark Brown 				 type, le32_to_cpu(blk->id));
897471f4885SMark Brown 
898471f4885SMark Brown 			mem = wm_adsp_find_region(dsp, type);
899471f4885SMark Brown 			if (!mem) {
900471f4885SMark Brown 				adsp_err(dsp, "No base for region %x\n", type);
901471f4885SMark Brown 				break;
902471f4885SMark Brown 			}
903471f4885SMark Brown 
904471f4885SMark Brown 			reg = 0;
905471f4885SMark Brown 			list_for_each_entry(alg_region,
906471f4885SMark Brown 					    &dsp->alg_regions, list) {
907471f4885SMark Brown 				if (le32_to_cpu(blk->id) == alg_region->alg &&
908471f4885SMark Brown 				    type == alg_region->type) {
909338c5188SMark Brown 					reg = alg_region->base;
910471f4885SMark Brown 					reg = wm_adsp_region_to_reg(mem,
911471f4885SMark Brown 								    reg);
912338c5188SMark Brown 					reg += offset;
913471f4885SMark Brown 				}
914471f4885SMark Brown 			}
915471f4885SMark Brown 
916471f4885SMark Brown 			if (reg == 0)
917471f4885SMark Brown 				adsp_err(dsp, "No %x for algorithm %x\n",
918471f4885SMark Brown 					 type, le32_to_cpu(blk->id));
919471f4885SMark Brown 			break;
920471f4885SMark Brown 
9212159ad93SMark Brown 		default:
92225c62f7eSMark Brown 			adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
92325c62f7eSMark Brown 				 file, blocks, type, pos);
9242159ad93SMark Brown 			break;
9252159ad93SMark Brown 		}
9262159ad93SMark Brown 
9272159ad93SMark Brown 		if (reg) {
928cf17c83cSMark Brown 			buf = wm_adsp_buf_alloc(blk->data,
929cf17c83cSMark Brown 						le32_to_cpu(blk->len),
930cf17c83cSMark Brown 						&buf_list);
931a76fefabSMark Brown 			if (!buf) {
932a76fefabSMark Brown 				adsp_err(dsp, "Out of memory\n");
933f4b82812SWei Yongjun 				ret = -ENOMEM;
934f4b82812SWei Yongjun 				goto out_fw;
935a76fefabSMark Brown 			}
936a76fefabSMark Brown 
93720da6d5aSMark Brown 			adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
93820da6d5aSMark Brown 				 file, blocks, le32_to_cpu(blk->len),
93920da6d5aSMark Brown 				 reg);
940cf17c83cSMark Brown 			ret = regmap_raw_write_async(regmap, reg, buf->buf,
9412159ad93SMark Brown 						     le32_to_cpu(blk->len));
9422159ad93SMark Brown 			if (ret != 0) {
9432159ad93SMark Brown 				adsp_err(dsp,
9442159ad93SMark Brown 					"%s.%d: Failed to write to %x in %s\n",
9452159ad93SMark Brown 					file, blocks, reg, region_name);
9462159ad93SMark Brown 			}
9472159ad93SMark Brown 		}
9482159ad93SMark Brown 
949bdaacea3SChris Rattray 		tmp = le32_to_cpu(blk->len) % 4;
950bdaacea3SChris Rattray 		if (tmp)
951bdaacea3SChris Rattray 			pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk);
952bdaacea3SChris Rattray 		else
9532159ad93SMark Brown 			pos += le32_to_cpu(blk->len) + sizeof(*blk);
954bdaacea3SChris Rattray 
9552159ad93SMark Brown 		blocks++;
9562159ad93SMark Brown 	}
9572159ad93SMark Brown 
958cf17c83cSMark Brown 	ret = regmap_async_complete(regmap);
959cf17c83cSMark Brown 	if (ret != 0)
960cf17c83cSMark Brown 		adsp_err(dsp, "Failed to complete async write: %d\n", ret);
961cf17c83cSMark Brown 
9622159ad93SMark Brown 	if (pos > firmware->size)
9632159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
9642159ad93SMark Brown 			  file, blocks, pos - firmware->size);
9652159ad93SMark Brown 
9662159ad93SMark Brown out_fw:
9672159ad93SMark Brown 	release_firmware(firmware);
968cf17c83cSMark Brown 	wm_adsp_buf_free(&buf_list);
9692159ad93SMark Brown out:
9702159ad93SMark Brown 	kfree(file);
971f4b82812SWei Yongjun 	return ret;
9722159ad93SMark Brown }
9732159ad93SMark Brown 
9745e7a7a22SMark Brown int wm_adsp1_init(struct wm_adsp *adsp)
9755e7a7a22SMark Brown {
9765e7a7a22SMark Brown 	INIT_LIST_HEAD(&adsp->alg_regions);
9775e7a7a22SMark Brown 
9785e7a7a22SMark Brown 	return 0;
9795e7a7a22SMark Brown }
9805e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
9815e7a7a22SMark Brown 
9822159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w,
9832159ad93SMark Brown 		   struct snd_kcontrol *kcontrol,
9842159ad93SMark Brown 		   int event)
9852159ad93SMark Brown {
9862159ad93SMark Brown 	struct snd_soc_codec *codec = w->codec;
9872159ad93SMark Brown 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
9882159ad93SMark Brown 	struct wm_adsp *dsp = &dsps[w->shift];
9892159ad93SMark Brown 	int ret;
99094e205bfSChris Rattray 	int val;
9912159ad93SMark Brown 
9922159ad93SMark Brown 	switch (event) {
9932159ad93SMark Brown 	case SND_SOC_DAPM_POST_PMU:
9942159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
9952159ad93SMark Brown 				   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
9962159ad93SMark Brown 
99794e205bfSChris Rattray 		/*
99894e205bfSChris Rattray 		 * For simplicity set the DSP clock rate to be the
99994e205bfSChris Rattray 		 * SYSCLK rate rather than making it configurable.
100094e205bfSChris Rattray 		 */
100194e205bfSChris Rattray 		if(dsp->sysclk_reg) {
100294e205bfSChris Rattray 			ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
100394e205bfSChris Rattray 			if (ret != 0) {
100494e205bfSChris Rattray 				adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
100594e205bfSChris Rattray 				ret);
100694e205bfSChris Rattray 				return ret;
100794e205bfSChris Rattray 			}
100894e205bfSChris Rattray 
100994e205bfSChris Rattray 			val = (val & dsp->sysclk_mask)
101094e205bfSChris Rattray 				>> dsp->sysclk_shift;
101194e205bfSChris Rattray 
101294e205bfSChris Rattray 			ret = regmap_update_bits(dsp->regmap,
101394e205bfSChris Rattray 						 dsp->base + ADSP1_CONTROL_31,
101494e205bfSChris Rattray 						 ADSP1_CLK_SEL_MASK, val);
101594e205bfSChris Rattray 			if (ret != 0) {
101694e205bfSChris Rattray 				adsp_err(dsp, "Failed to set clock rate: %d\n",
101794e205bfSChris Rattray 					 ret);
101894e205bfSChris Rattray 				return ret;
101994e205bfSChris Rattray 			}
102094e205bfSChris Rattray 		}
102194e205bfSChris Rattray 
10222159ad93SMark Brown 		ret = wm_adsp_load(dsp);
10232159ad93SMark Brown 		if (ret != 0)
10242159ad93SMark Brown 			goto err;
10252159ad93SMark Brown 
1026db40517cSMark Brown 		ret = wm_adsp_setup_algs(dsp);
1027db40517cSMark Brown 		if (ret != 0)
1028db40517cSMark Brown 			goto err;
1029db40517cSMark Brown 
10302159ad93SMark Brown 		ret = wm_adsp_load_coeff(dsp);
10312159ad93SMark Brown 		if (ret != 0)
10322159ad93SMark Brown 			goto err;
10332159ad93SMark Brown 
10342159ad93SMark Brown 		/* Start the core running */
10352159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
10362159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START,
10372159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START);
10382159ad93SMark Brown 		break;
10392159ad93SMark Brown 
10402159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
10412159ad93SMark Brown 		/* Halt the core */
10422159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
10432159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START, 0);
10442159ad93SMark Brown 
10452159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
10462159ad93SMark Brown 				   ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
10472159ad93SMark Brown 
10482159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
10492159ad93SMark Brown 				   ADSP1_SYS_ENA, 0);
10502159ad93SMark Brown 		break;
10512159ad93SMark Brown 
10522159ad93SMark Brown 	default:
10532159ad93SMark Brown 		break;
10542159ad93SMark Brown 	}
10552159ad93SMark Brown 
10562159ad93SMark Brown 	return 0;
10572159ad93SMark Brown 
10582159ad93SMark Brown err:
10592159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
10602159ad93SMark Brown 			   ADSP1_SYS_ENA, 0);
10612159ad93SMark Brown 	return ret;
10622159ad93SMark Brown }
10632159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
10642159ad93SMark Brown 
10652159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp)
10662159ad93SMark Brown {
10672159ad93SMark Brown 	unsigned int val;
10682159ad93SMark Brown 	int ret, count;
10692159ad93SMark Brown 
10702159ad93SMark Brown 	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
10712159ad93SMark Brown 				 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
10722159ad93SMark Brown 	if (ret != 0)
10732159ad93SMark Brown 		return ret;
10742159ad93SMark Brown 
10752159ad93SMark Brown 	/* Wait for the RAM to start, should be near instantaneous */
10762159ad93SMark Brown 	count = 0;
10772159ad93SMark Brown 	do {
10782159ad93SMark Brown 		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
10792159ad93SMark Brown 				  &val);
10802159ad93SMark Brown 		if (ret != 0)
10812159ad93SMark Brown 			return ret;
10822159ad93SMark Brown 	} while (!(val & ADSP2_RAM_RDY) && ++count < 10);
10832159ad93SMark Brown 
10842159ad93SMark Brown 	if (!(val & ADSP2_RAM_RDY)) {
10852159ad93SMark Brown 		adsp_err(dsp, "Failed to start DSP RAM\n");
10862159ad93SMark Brown 		return -EBUSY;
10872159ad93SMark Brown 	}
10882159ad93SMark Brown 
10892159ad93SMark Brown 	adsp_dbg(dsp, "RAM ready after %d polls\n", count);
10902159ad93SMark Brown 	adsp_info(dsp, "RAM ready after %d polls\n", count);
10912159ad93SMark Brown 
10922159ad93SMark Brown 	return 0;
10932159ad93SMark Brown }
10942159ad93SMark Brown 
10952159ad93SMark Brown int wm_adsp2_event(struct snd_soc_dapm_widget *w,
10962159ad93SMark Brown 		   struct snd_kcontrol *kcontrol, int event)
10972159ad93SMark Brown {
10982159ad93SMark Brown 	struct snd_soc_codec *codec = w->codec;
10992159ad93SMark Brown 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
11002159ad93SMark Brown 	struct wm_adsp *dsp = &dsps[w->shift];
1101471f4885SMark Brown 	struct wm_adsp_alg_region *alg_region;
1102973838a0SMark Brown 	unsigned int val;
11032159ad93SMark Brown 	int ret;
11042159ad93SMark Brown 
11052159ad93SMark Brown 	switch (event) {
11062159ad93SMark Brown 	case SND_SOC_DAPM_POST_PMU:
1107dd49e2c8SMark Brown 		/*
1108dd49e2c8SMark Brown 		 * For simplicity set the DSP clock rate to be the
1109dd49e2c8SMark Brown 		 * SYSCLK rate rather than making it configurable.
1110dd49e2c8SMark Brown 		 */
1111dd49e2c8SMark Brown 		ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
1112dd49e2c8SMark Brown 		if (ret != 0) {
1113dd49e2c8SMark Brown 			adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
1114dd49e2c8SMark Brown 				 ret);
1115dd49e2c8SMark Brown 			return ret;
1116dd49e2c8SMark Brown 		}
1117dd49e2c8SMark Brown 		val = (val & ARIZONA_SYSCLK_FREQ_MASK)
1118dd49e2c8SMark Brown 			>> ARIZONA_SYSCLK_FREQ_SHIFT;
1119dd49e2c8SMark Brown 
1120dd49e2c8SMark Brown 		ret = regmap_update_bits(dsp->regmap,
1121dd49e2c8SMark Brown 					 dsp->base + ADSP2_CLOCKING,
1122dd49e2c8SMark Brown 					 ADSP2_CLK_SEL_MASK, val);
1123dd49e2c8SMark Brown 		if (ret != 0) {
1124dd49e2c8SMark Brown 			adsp_err(dsp, "Failed to set clock rate: %d\n",
1125dd49e2c8SMark Brown 				 ret);
1126dd49e2c8SMark Brown 			return ret;
1127dd49e2c8SMark Brown 		}
1128dd49e2c8SMark Brown 
1129973838a0SMark Brown 		if (dsp->dvfs) {
1130973838a0SMark Brown 			ret = regmap_read(dsp->regmap,
1131973838a0SMark Brown 					  dsp->base + ADSP2_CLOCKING, &val);
1132973838a0SMark Brown 			if (ret != 0) {
1133973838a0SMark Brown 				dev_err(dsp->dev,
1134973838a0SMark Brown 					"Failed to read clocking: %d\n", ret);
1135973838a0SMark Brown 				return ret;
1136973838a0SMark Brown 			}
1137973838a0SMark Brown 
113825c6fdb0SMark Brown 			if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
1139973838a0SMark Brown 				ret = regulator_enable(dsp->dvfs);
1140973838a0SMark Brown 				if (ret != 0) {
1141973838a0SMark Brown 					dev_err(dsp->dev,
1142973838a0SMark Brown 						"Failed to enable supply: %d\n",
1143973838a0SMark Brown 						ret);
1144973838a0SMark Brown 					return ret;
1145973838a0SMark Brown 				}
1146973838a0SMark Brown 
1147973838a0SMark Brown 				ret = regulator_set_voltage(dsp->dvfs,
1148973838a0SMark Brown 							    1800000,
1149973838a0SMark Brown 							    1800000);
1150973838a0SMark Brown 				if (ret != 0) {
1151973838a0SMark Brown 					dev_err(dsp->dev,
1152973838a0SMark Brown 						"Failed to raise supply: %d\n",
1153973838a0SMark Brown 						ret);
1154973838a0SMark Brown 					return ret;
1155973838a0SMark Brown 				}
1156973838a0SMark Brown 			}
1157973838a0SMark Brown 		}
1158973838a0SMark Brown 
11592159ad93SMark Brown 		ret = wm_adsp2_ena(dsp);
11602159ad93SMark Brown 		if (ret != 0)
11612159ad93SMark Brown 			return ret;
11622159ad93SMark Brown 
11632159ad93SMark Brown 		ret = wm_adsp_load(dsp);
11642159ad93SMark Brown 		if (ret != 0)
11652159ad93SMark Brown 			goto err;
11662159ad93SMark Brown 
1167db40517cSMark Brown 		ret = wm_adsp_setup_algs(dsp);
1168db40517cSMark Brown 		if (ret != 0)
1169db40517cSMark Brown 			goto err;
1170db40517cSMark Brown 
11712159ad93SMark Brown 		ret = wm_adsp_load_coeff(dsp);
11722159ad93SMark Brown 		if (ret != 0)
11732159ad93SMark Brown 			goto err;
11742159ad93SMark Brown 
11752159ad93SMark Brown 		ret = regmap_update_bits(dsp->regmap,
11762159ad93SMark Brown 					 dsp->base + ADSP2_CONTROL,
1177a7f9be7eSMark Brown 					 ADSP2_CORE_ENA | ADSP2_START,
1178a7f9be7eSMark Brown 					 ADSP2_CORE_ENA | ADSP2_START);
11792159ad93SMark Brown 		if (ret != 0)
11802159ad93SMark Brown 			goto err;
11811023dbd9SMark Brown 
11821023dbd9SMark Brown 		dsp->running = true;
11832159ad93SMark Brown 		break;
11842159ad93SMark Brown 
11852159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
11861023dbd9SMark Brown 		dsp->running = false;
11871023dbd9SMark Brown 
11882159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1189a7f9be7eSMark Brown 				   ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1190a7f9be7eSMark Brown 				   ADSP2_START, 0);
1191973838a0SMark Brown 
11922d30b575SMark Brown 		/* Make sure DMAs are quiesced */
11932d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
11942d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
11952d30b575SMark Brown 		regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
11962d30b575SMark Brown 
1197973838a0SMark Brown 		if (dsp->dvfs) {
1198973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs, 1200000,
1199973838a0SMark Brown 						    1800000);
1200973838a0SMark Brown 			if (ret != 0)
1201973838a0SMark Brown 				dev_warn(dsp->dev,
1202973838a0SMark Brown 					 "Failed to lower supply: %d\n",
1203973838a0SMark Brown 					 ret);
1204973838a0SMark Brown 
1205973838a0SMark Brown 			ret = regulator_disable(dsp->dvfs);
1206973838a0SMark Brown 			if (ret != 0)
1207973838a0SMark Brown 				dev_err(dsp->dev,
1208973838a0SMark Brown 					"Failed to enable supply: %d\n",
1209973838a0SMark Brown 					ret);
1210973838a0SMark Brown 		}
1211471f4885SMark Brown 
1212471f4885SMark Brown 		while (!list_empty(&dsp->alg_regions)) {
1213471f4885SMark Brown 			alg_region = list_first_entry(&dsp->alg_regions,
1214471f4885SMark Brown 						      struct wm_adsp_alg_region,
1215471f4885SMark Brown 						      list);
1216471f4885SMark Brown 			list_del(&alg_region->list);
1217471f4885SMark Brown 			kfree(alg_region);
1218471f4885SMark Brown 		}
12192159ad93SMark Brown 		break;
12202159ad93SMark Brown 
12212159ad93SMark Brown 	default:
12222159ad93SMark Brown 		break;
12232159ad93SMark Brown 	}
12242159ad93SMark Brown 
12252159ad93SMark Brown 	return 0;
12262159ad93SMark Brown err:
12272159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1228a7f9be7eSMark Brown 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
12292159ad93SMark Brown 	return ret;
12302159ad93SMark Brown }
12312159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event);
1232973838a0SMark Brown 
1233973838a0SMark Brown int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1234973838a0SMark Brown {
1235973838a0SMark Brown 	int ret;
1236973838a0SMark Brown 
123710a2b662SMark Brown 	/*
123810a2b662SMark Brown 	 * Disable the DSP memory by default when in reset for a small
123910a2b662SMark Brown 	 * power saving.
124010a2b662SMark Brown 	 */
124110a2b662SMark Brown 	ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
124210a2b662SMark Brown 				 ADSP2_MEM_ENA, 0);
124310a2b662SMark Brown 	if (ret != 0) {
124410a2b662SMark Brown 		adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
124510a2b662SMark Brown 		return ret;
124610a2b662SMark Brown 	}
124710a2b662SMark Brown 
1248471f4885SMark Brown 	INIT_LIST_HEAD(&adsp->alg_regions);
1249471f4885SMark Brown 
1250973838a0SMark Brown 	if (dvfs) {
1251973838a0SMark Brown 		adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1252973838a0SMark Brown 		if (IS_ERR(adsp->dvfs)) {
1253973838a0SMark Brown 			ret = PTR_ERR(adsp->dvfs);
1254973838a0SMark Brown 			dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1255973838a0SMark Brown 			return ret;
1256973838a0SMark Brown 		}
1257973838a0SMark Brown 
1258973838a0SMark Brown 		ret = regulator_enable(adsp->dvfs);
1259973838a0SMark Brown 		if (ret != 0) {
1260973838a0SMark Brown 			dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1261973838a0SMark Brown 				ret);
1262973838a0SMark Brown 			return ret;
1263973838a0SMark Brown 		}
1264973838a0SMark Brown 
1265973838a0SMark Brown 		ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1266973838a0SMark Brown 		if (ret != 0) {
1267973838a0SMark Brown 			dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1268973838a0SMark Brown 				ret);
1269973838a0SMark Brown 			return ret;
1270973838a0SMark Brown 		}
1271973838a0SMark Brown 
1272973838a0SMark Brown 		ret = regulator_disable(adsp->dvfs);
1273973838a0SMark Brown 		if (ret != 0) {
1274973838a0SMark Brown 			dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1275973838a0SMark Brown 				ret);
1276973838a0SMark Brown 			return ret;
1277973838a0SMark Brown 		}
1278973838a0SMark Brown 	}
1279973838a0SMark Brown 
1280973838a0SMark Brown 	return 0;
1281973838a0SMark Brown }
1282973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
1283