xref: /openbmc/linux/sound/soc/codecs/wm_adsp.c (revision 94e205bf)
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>
182159ad93SMark Brown #include <linux/pm.h>
192159ad93SMark Brown #include <linux/pm_runtime.h>
202159ad93SMark Brown #include <linux/regmap.h>
21973838a0SMark Brown #include <linux/regulator/consumer.h>
222159ad93SMark Brown #include <linux/slab.h>
232159ad93SMark Brown #include <sound/core.h>
242159ad93SMark Brown #include <sound/pcm.h>
252159ad93SMark Brown #include <sound/pcm_params.h>
262159ad93SMark Brown #include <sound/soc.h>
272159ad93SMark Brown #include <sound/jack.h>
282159ad93SMark Brown #include <sound/initval.h>
292159ad93SMark Brown #include <sound/tlv.h>
302159ad93SMark Brown 
312159ad93SMark Brown #include <linux/mfd/arizona/registers.h>
322159ad93SMark Brown 
332159ad93SMark Brown #include "wm_adsp.h"
342159ad93SMark Brown 
352159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \
362159ad93SMark Brown 	dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
372159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \
382159ad93SMark Brown 	dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
392159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \
402159ad93SMark Brown 	dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
412159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \
422159ad93SMark Brown 	dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
432159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \
442159ad93SMark Brown 	dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
452159ad93SMark Brown 
462159ad93SMark Brown #define ADSP1_CONTROL_1                   0x00
472159ad93SMark Brown #define ADSP1_CONTROL_2                   0x02
482159ad93SMark Brown #define ADSP1_CONTROL_3                   0x03
492159ad93SMark Brown #define ADSP1_CONTROL_4                   0x04
502159ad93SMark Brown #define ADSP1_CONTROL_5                   0x06
512159ad93SMark Brown #define ADSP1_CONTROL_6                   0x07
522159ad93SMark Brown #define ADSP1_CONTROL_7                   0x08
532159ad93SMark Brown #define ADSP1_CONTROL_8                   0x09
542159ad93SMark Brown #define ADSP1_CONTROL_9                   0x0A
552159ad93SMark Brown #define ADSP1_CONTROL_10                  0x0B
562159ad93SMark Brown #define ADSP1_CONTROL_11                  0x0C
572159ad93SMark Brown #define ADSP1_CONTROL_12                  0x0D
582159ad93SMark Brown #define ADSP1_CONTROL_13                  0x0F
592159ad93SMark Brown #define ADSP1_CONTROL_14                  0x10
602159ad93SMark Brown #define ADSP1_CONTROL_15                  0x11
612159ad93SMark Brown #define ADSP1_CONTROL_16                  0x12
622159ad93SMark Brown #define ADSP1_CONTROL_17                  0x13
632159ad93SMark Brown #define ADSP1_CONTROL_18                  0x14
642159ad93SMark Brown #define ADSP1_CONTROL_19                  0x16
652159ad93SMark Brown #define ADSP1_CONTROL_20                  0x17
662159ad93SMark Brown #define ADSP1_CONTROL_21                  0x18
672159ad93SMark Brown #define ADSP1_CONTROL_22                  0x1A
682159ad93SMark Brown #define ADSP1_CONTROL_23                  0x1B
692159ad93SMark Brown #define ADSP1_CONTROL_24                  0x1C
702159ad93SMark Brown #define ADSP1_CONTROL_25                  0x1E
712159ad93SMark Brown #define ADSP1_CONTROL_26                  0x20
722159ad93SMark Brown #define ADSP1_CONTROL_27                  0x21
732159ad93SMark Brown #define ADSP1_CONTROL_28                  0x22
742159ad93SMark Brown #define ADSP1_CONTROL_29                  0x23
752159ad93SMark Brown #define ADSP1_CONTROL_30                  0x24
762159ad93SMark Brown #define ADSP1_CONTROL_31                  0x26
772159ad93SMark Brown 
782159ad93SMark Brown /*
792159ad93SMark Brown  * ADSP1 Control 19
802159ad93SMark Brown  */
812159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
822159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
832159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
842159ad93SMark Brown 
852159ad93SMark Brown 
862159ad93SMark Brown /*
872159ad93SMark Brown  * ADSP1 Control 30
882159ad93SMark Brown  */
892159ad93SMark Brown #define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
902159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
912159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
932159ad93SMark Brown #define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
942159ad93SMark Brown #define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
952159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
962159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
972159ad93SMark Brown #define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
982159ad93SMark Brown #define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
992159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1002159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1012159ad93SMark Brown #define ADSP1_START                       0x0001  /* DSP1_START */
1022159ad93SMark Brown #define ADSP1_START_MASK                  0x0001  /* DSP1_START */
1032159ad93SMark Brown #define ADSP1_START_SHIFT                      0  /* DSP1_START */
1042159ad93SMark Brown #define ADSP1_START_WIDTH                      1  /* DSP1_START */
1052159ad93SMark Brown 
10694e205bfSChris Rattray /*
10794e205bfSChris Rattray  * ADSP1 Control 31
10894e205bfSChris Rattray  */
10994e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
11094e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
11194e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
11294e205bfSChris Rattray 
1132159ad93SMark Brown #define ADSP2_CONTROL  0
114973838a0SMark Brown #define ADSP2_CLOCKING 1
1152159ad93SMark Brown #define ADSP2_STATUS1  4
1162159ad93SMark Brown 
1172159ad93SMark Brown /*
1182159ad93SMark Brown  * ADSP2 Control
1192159ad93SMark Brown  */
1202159ad93SMark Brown 
1212159ad93SMark Brown #define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
1222159ad93SMark Brown #define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
1232159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
1242159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
1252159ad93SMark Brown #define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
1262159ad93SMark Brown #define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
1272159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
1282159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
1292159ad93SMark Brown #define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
1302159ad93SMark Brown #define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
1312159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
1322159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
1332159ad93SMark Brown #define ADSP2_START                       0x0001  /* DSP1_START */
1342159ad93SMark Brown #define ADSP2_START_MASK                  0x0001  /* DSP1_START */
1352159ad93SMark Brown #define ADSP2_START_SHIFT                      0  /* DSP1_START */
1362159ad93SMark Brown #define ADSP2_START_WIDTH                      1  /* DSP1_START */
1372159ad93SMark Brown 
1382159ad93SMark Brown /*
139973838a0SMark Brown  * ADSP2 clocking
140973838a0SMark Brown  */
141973838a0SMark Brown #define ADSP2_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
142973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
143973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
144973838a0SMark Brown 
145973838a0SMark Brown /*
1462159ad93SMark Brown  * ADSP2 Status 1
1472159ad93SMark Brown  */
1482159ad93SMark Brown #define ADSP2_RAM_RDY                     0x0001
1492159ad93SMark Brown #define ADSP2_RAM_RDY_MASK                0x0001
1502159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT                    0
1512159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH                    1
1522159ad93SMark Brown 
1531023dbd9SMark Brown #define WM_ADSP_NUM_FW 3
1541023dbd9SMark Brown 
1551023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
1561023dbd9SMark Brown 	"MBC/VSS", "Tx", "Rx ANC"
1571023dbd9SMark Brown };
1581023dbd9SMark Brown 
1591023dbd9SMark Brown static struct {
1601023dbd9SMark Brown 	const char *file;
1611023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = {
1621023dbd9SMark Brown 	{ .file = "mbc-vss" },
1631023dbd9SMark Brown 	{ .file = "tx" },
1641023dbd9SMark Brown 	{ .file = "rx-anc" },
1651023dbd9SMark Brown };
1661023dbd9SMark Brown 
1671023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
1681023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
1691023dbd9SMark Brown {
1701023dbd9SMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
1711023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1721023dbd9SMark Brown 	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
1731023dbd9SMark Brown 
1741023dbd9SMark Brown 	ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
1751023dbd9SMark Brown 
1761023dbd9SMark Brown 	return 0;
1771023dbd9SMark Brown }
1781023dbd9SMark Brown 
1791023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
1801023dbd9SMark Brown 			  struct snd_ctl_elem_value *ucontrol)
1811023dbd9SMark Brown {
1821023dbd9SMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
1831023dbd9SMark Brown 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1841023dbd9SMark Brown 	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
1851023dbd9SMark Brown 
1861023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
1871023dbd9SMark Brown 		return 0;
1881023dbd9SMark Brown 
1891023dbd9SMark Brown 	if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
1901023dbd9SMark Brown 		return -EINVAL;
1911023dbd9SMark Brown 
1921023dbd9SMark Brown 	if (adsp[e->shift_l].running)
1931023dbd9SMark Brown 		return -EBUSY;
1941023dbd9SMark Brown 
1951023dbd9SMark Brown 	adsp->fw = ucontrol->value.integer.value[0];
1961023dbd9SMark Brown 
1971023dbd9SMark Brown 	return 0;
1981023dbd9SMark Brown }
1991023dbd9SMark Brown 
2001023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = {
2011023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2021023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2031023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2041023dbd9SMark Brown 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
2051023dbd9SMark Brown };
2061023dbd9SMark Brown 
2071023dbd9SMark Brown const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
2081023dbd9SMark Brown 	SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
2091023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2101023dbd9SMark Brown 	SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
2111023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2121023dbd9SMark Brown 	SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
2131023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2141023dbd9SMark Brown 	SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
2151023dbd9SMark Brown 		     wm_adsp_fw_get, wm_adsp_fw_put),
2161023dbd9SMark Brown };
2171023dbd9SMark Brown EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
2182159ad93SMark Brown 
2192159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
2202159ad93SMark Brown 							int type)
2212159ad93SMark Brown {
2222159ad93SMark Brown 	int i;
2232159ad93SMark Brown 
2242159ad93SMark Brown 	for (i = 0; i < dsp->num_mems; i++)
2252159ad93SMark Brown 		if (dsp->mem[i].type == type)
2262159ad93SMark Brown 			return &dsp->mem[i];
2272159ad93SMark Brown 
2282159ad93SMark Brown 	return NULL;
2292159ad93SMark Brown }
2302159ad93SMark Brown 
23145b9ee72SMark Brown static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
23245b9ee72SMark Brown 					  unsigned int offset)
23345b9ee72SMark Brown {
23445b9ee72SMark Brown 	switch (region->type) {
23545b9ee72SMark Brown 	case WMFW_ADSP1_PM:
23645b9ee72SMark Brown 		return region->base + (offset * 3);
23745b9ee72SMark Brown 	case WMFW_ADSP1_DM:
23845b9ee72SMark Brown 		return region->base + (offset * 2);
23945b9ee72SMark Brown 	case WMFW_ADSP2_XM:
24045b9ee72SMark Brown 		return region->base + (offset * 2);
24145b9ee72SMark Brown 	case WMFW_ADSP2_YM:
24245b9ee72SMark Brown 		return region->base + (offset * 2);
24345b9ee72SMark Brown 	case WMFW_ADSP1_ZM:
24445b9ee72SMark Brown 		return region->base + (offset * 2);
24545b9ee72SMark Brown 	default:
24645b9ee72SMark Brown 		WARN_ON(NULL != "Unknown memory region type");
24745b9ee72SMark Brown 		return offset;
24845b9ee72SMark Brown 	}
24945b9ee72SMark Brown }
25045b9ee72SMark Brown 
2512159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp)
2522159ad93SMark Brown {
2532159ad93SMark Brown 	const struct firmware *firmware;
2542159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
2552159ad93SMark Brown 	unsigned int pos = 0;
2562159ad93SMark Brown 	const struct wmfw_header *header;
2572159ad93SMark Brown 	const struct wmfw_adsp1_sizes *adsp1_sizes;
2582159ad93SMark Brown 	const struct wmfw_adsp2_sizes *adsp2_sizes;
2592159ad93SMark Brown 	const struct wmfw_footer *footer;
2602159ad93SMark Brown 	const struct wmfw_region *region;
2612159ad93SMark Brown 	const struct wm_adsp_region *mem;
2622159ad93SMark Brown 	const char *region_name;
2632159ad93SMark Brown 	char *file, *text;
2642159ad93SMark Brown 	unsigned int reg;
2652159ad93SMark Brown 	int regions = 0;
2662159ad93SMark Brown 	int ret, offset, type, sizes;
2672159ad93SMark Brown 
2682159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
2692159ad93SMark Brown 	if (file == NULL)
2702159ad93SMark Brown 		return -ENOMEM;
2712159ad93SMark Brown 
2721023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
2731023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
2742159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
2752159ad93SMark Brown 
2762159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
2772159ad93SMark Brown 	if (ret != 0) {
2782159ad93SMark Brown 		adsp_err(dsp, "Failed to request '%s'\n", file);
2792159ad93SMark Brown 		goto out;
2802159ad93SMark Brown 	}
2812159ad93SMark Brown 	ret = -EINVAL;
2822159ad93SMark Brown 
2832159ad93SMark Brown 	pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
2842159ad93SMark Brown 	if (pos >= firmware->size) {
2852159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
2862159ad93SMark Brown 			 file, firmware->size);
2872159ad93SMark Brown 		goto out_fw;
2882159ad93SMark Brown 	}
2892159ad93SMark Brown 
2902159ad93SMark Brown 	header = (void*)&firmware->data[0];
2912159ad93SMark Brown 
2922159ad93SMark Brown 	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
2932159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
2942159ad93SMark Brown 		goto out_fw;
2952159ad93SMark Brown 	}
2962159ad93SMark Brown 
2972159ad93SMark Brown 	if (header->ver != 0) {
2982159ad93SMark Brown 		adsp_err(dsp, "%s: unknown file format %d\n",
2992159ad93SMark Brown 			 file, header->ver);
3002159ad93SMark Brown 		goto out_fw;
3012159ad93SMark Brown 	}
3022159ad93SMark Brown 
3032159ad93SMark Brown 	if (header->core != dsp->type) {
3042159ad93SMark Brown 		adsp_err(dsp, "%s: invalid core %d != %d\n",
3052159ad93SMark Brown 			 file, header->core, dsp->type);
3062159ad93SMark Brown 		goto out_fw;
3072159ad93SMark Brown 	}
3082159ad93SMark Brown 
3092159ad93SMark Brown 	switch (dsp->type) {
3102159ad93SMark Brown 	case WMFW_ADSP1:
3112159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
3122159ad93SMark Brown 		adsp1_sizes = (void *)&(header[1]);
3132159ad93SMark Brown 		footer = (void *)&(adsp1_sizes[1]);
3142159ad93SMark Brown 		sizes = sizeof(*adsp1_sizes);
3152159ad93SMark Brown 
3162159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
3172159ad93SMark Brown 			 file, le32_to_cpu(adsp1_sizes->dm),
3182159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->pm),
3192159ad93SMark Brown 			 le32_to_cpu(adsp1_sizes->zm));
3202159ad93SMark Brown 		break;
3212159ad93SMark Brown 
3222159ad93SMark Brown 	case WMFW_ADSP2:
3232159ad93SMark Brown 		pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
3242159ad93SMark Brown 		adsp2_sizes = (void *)&(header[1]);
3252159ad93SMark Brown 		footer = (void *)&(adsp2_sizes[1]);
3262159ad93SMark Brown 		sizes = sizeof(*adsp2_sizes);
3272159ad93SMark Brown 
3282159ad93SMark Brown 		adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
3292159ad93SMark Brown 			 file, le32_to_cpu(adsp2_sizes->xm),
3302159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->ym),
3312159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->pm),
3322159ad93SMark Brown 			 le32_to_cpu(adsp2_sizes->zm));
3332159ad93SMark Brown 		break;
3342159ad93SMark Brown 
3352159ad93SMark Brown 	default:
3362159ad93SMark Brown 		BUG_ON(NULL == "Unknown DSP type");
3372159ad93SMark Brown 		goto out_fw;
3382159ad93SMark Brown 	}
3392159ad93SMark Brown 
3402159ad93SMark Brown 	if (le32_to_cpu(header->len) != sizeof(*header) +
3412159ad93SMark Brown 	    sizes + sizeof(*footer)) {
3422159ad93SMark Brown 		adsp_err(dsp, "%s: unexpected header length %d\n",
3432159ad93SMark Brown 			 file, le32_to_cpu(header->len));
3442159ad93SMark Brown 		goto out_fw;
3452159ad93SMark Brown 	}
3462159ad93SMark Brown 
3472159ad93SMark Brown 	adsp_dbg(dsp, "%s: timestamp %llu\n", file,
3482159ad93SMark Brown 		 le64_to_cpu(footer->timestamp));
3492159ad93SMark Brown 
3502159ad93SMark Brown 	while (pos < firmware->size &&
3512159ad93SMark Brown 	       pos - firmware->size > sizeof(*region)) {
3522159ad93SMark Brown 		region = (void *)&(firmware->data[pos]);
3532159ad93SMark Brown 		region_name = "Unknown";
3542159ad93SMark Brown 		reg = 0;
3552159ad93SMark Brown 		text = NULL;
3562159ad93SMark Brown 		offset = le32_to_cpu(region->offset) & 0xffffff;
3572159ad93SMark Brown 		type = be32_to_cpu(region->type) & 0xff;
3582159ad93SMark Brown 		mem = wm_adsp_find_region(dsp, type);
3592159ad93SMark Brown 
3602159ad93SMark Brown 		switch (type) {
3612159ad93SMark Brown 		case WMFW_NAME_TEXT:
3622159ad93SMark Brown 			region_name = "Firmware name";
3632159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
3642159ad93SMark Brown 				       GFP_KERNEL);
3652159ad93SMark Brown 			break;
3662159ad93SMark Brown 		case WMFW_INFO_TEXT:
3672159ad93SMark Brown 			region_name = "Information";
3682159ad93SMark Brown 			text = kzalloc(le32_to_cpu(region->len) + 1,
3692159ad93SMark Brown 				       GFP_KERNEL);
3702159ad93SMark Brown 			break;
3712159ad93SMark Brown 		case WMFW_ABSOLUTE:
3722159ad93SMark Brown 			region_name = "Absolute";
3732159ad93SMark Brown 			reg = offset;
3742159ad93SMark Brown 			break;
3752159ad93SMark Brown 		case WMFW_ADSP1_PM:
3762159ad93SMark Brown 			BUG_ON(!mem);
3772159ad93SMark Brown 			region_name = "PM";
37845b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
3792159ad93SMark Brown 			break;
3802159ad93SMark Brown 		case WMFW_ADSP1_DM:
3812159ad93SMark Brown 			BUG_ON(!mem);
3822159ad93SMark Brown 			region_name = "DM";
38345b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
3842159ad93SMark Brown 			break;
3852159ad93SMark Brown 		case WMFW_ADSP2_XM:
3862159ad93SMark Brown 			BUG_ON(!mem);
3872159ad93SMark Brown 			region_name = "XM";
38845b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
3892159ad93SMark Brown 			break;
3902159ad93SMark Brown 		case WMFW_ADSP2_YM:
3912159ad93SMark Brown 			BUG_ON(!mem);
3922159ad93SMark Brown 			region_name = "YM";
39345b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
3942159ad93SMark Brown 			break;
3952159ad93SMark Brown 		case WMFW_ADSP1_ZM:
3962159ad93SMark Brown 			BUG_ON(!mem);
3972159ad93SMark Brown 			region_name = "ZM";
39845b9ee72SMark Brown 			reg = wm_adsp_region_to_reg(mem, offset);
3992159ad93SMark Brown 			break;
4002159ad93SMark Brown 		default:
4012159ad93SMark Brown 			adsp_warn(dsp,
4022159ad93SMark Brown 				  "%s.%d: Unknown region type %x at %d(%x)\n",
4032159ad93SMark Brown 				  file, regions, type, pos, pos);
4042159ad93SMark Brown 			break;
4052159ad93SMark Brown 		}
4062159ad93SMark Brown 
4072159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
4082159ad93SMark Brown 			 regions, le32_to_cpu(region->len), offset,
4092159ad93SMark Brown 			 region_name);
4102159ad93SMark Brown 
4112159ad93SMark Brown 		if (text) {
4122159ad93SMark Brown 			memcpy(text, region->data, le32_to_cpu(region->len));
4132159ad93SMark Brown 			adsp_info(dsp, "%s: %s\n", file, text);
4142159ad93SMark Brown 			kfree(text);
4152159ad93SMark Brown 		}
4162159ad93SMark Brown 
4172159ad93SMark Brown 		if (reg) {
4182159ad93SMark Brown 			ret = regmap_raw_write(regmap, reg, region->data,
4192159ad93SMark Brown 					       le32_to_cpu(region->len));
4202159ad93SMark Brown 			if (ret != 0) {
4212159ad93SMark Brown 				adsp_err(dsp,
4222159ad93SMark Brown 					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
4232159ad93SMark Brown 					file, regions,
4242159ad93SMark Brown 					le32_to_cpu(region->len), offset,
4252159ad93SMark Brown 					region_name, ret);
4262159ad93SMark Brown 				goto out_fw;
4272159ad93SMark Brown 			}
4282159ad93SMark Brown 		}
4292159ad93SMark Brown 
4302159ad93SMark Brown 		pos += le32_to_cpu(region->len) + sizeof(*region);
4312159ad93SMark Brown 		regions++;
4322159ad93SMark Brown 	}
4332159ad93SMark Brown 
4342159ad93SMark Brown 	if (pos > firmware->size)
4352159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
4362159ad93SMark Brown 			  file, regions, pos - firmware->size);
4372159ad93SMark Brown 
4382159ad93SMark Brown out_fw:
4392159ad93SMark Brown 	release_firmware(firmware);
4402159ad93SMark Brown out:
4412159ad93SMark Brown 	kfree(file);
4422159ad93SMark Brown 
4432159ad93SMark Brown 	return ret;
4442159ad93SMark Brown }
4452159ad93SMark Brown 
446db40517cSMark Brown static int wm_adsp_setup_algs(struct wm_adsp *dsp)
447db40517cSMark Brown {
448db40517cSMark Brown 	struct regmap *regmap = dsp->regmap;
449db40517cSMark Brown 	struct wmfw_adsp1_id_hdr adsp1_id;
450db40517cSMark Brown 	struct wmfw_adsp2_id_hdr adsp2_id;
451db40517cSMark Brown 	struct wmfw_adsp1_alg_hdr *adsp1_alg;
452db40517cSMark Brown 	struct wmfw_adsp2_alg_hdr *adsp2_alg;
453d62f4bc6SMark Brown 	void *alg, *buf;
454471f4885SMark Brown 	struct wm_adsp_alg_region *region;
455db40517cSMark Brown 	const struct wm_adsp_region *mem;
456db40517cSMark Brown 	unsigned int pos, term;
457d62f4bc6SMark Brown 	size_t algs, buf_size;
458db40517cSMark Brown 	__be32 val;
459db40517cSMark Brown 	int i, ret;
460db40517cSMark Brown 
461db40517cSMark Brown 	switch (dsp->type) {
462db40517cSMark Brown 	case WMFW_ADSP1:
463db40517cSMark Brown 		mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
464db40517cSMark Brown 		break;
465db40517cSMark Brown 	case WMFW_ADSP2:
466db40517cSMark Brown 		mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
467db40517cSMark Brown 		break;
468db40517cSMark Brown 	default:
469db40517cSMark Brown 		mem = NULL;
470db40517cSMark Brown 		break;
471db40517cSMark Brown 	}
472db40517cSMark Brown 
473db40517cSMark Brown 	if (mem == NULL) {
474db40517cSMark Brown 		BUG_ON(mem != NULL);
475db40517cSMark Brown 		return -EINVAL;
476db40517cSMark Brown 	}
477db40517cSMark Brown 
478db40517cSMark Brown 	switch (dsp->type) {
479db40517cSMark Brown 	case WMFW_ADSP1:
480db40517cSMark Brown 		ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
481db40517cSMark Brown 				      sizeof(adsp1_id));
482db40517cSMark Brown 		if (ret != 0) {
483db40517cSMark Brown 			adsp_err(dsp, "Failed to read algorithm info: %d\n",
484db40517cSMark Brown 				 ret);
485db40517cSMark Brown 			return ret;
486db40517cSMark Brown 		}
487db40517cSMark Brown 
488d62f4bc6SMark Brown 		buf = &adsp1_id;
489d62f4bc6SMark Brown 		buf_size = sizeof(adsp1_id);
490d62f4bc6SMark Brown 
491db40517cSMark Brown 		algs = be32_to_cpu(adsp1_id.algs);
492db40517cSMark Brown 		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
493db40517cSMark Brown 			  be32_to_cpu(adsp1_id.fw.id),
494db40517cSMark Brown 			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
495db40517cSMark Brown 			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
496db40517cSMark Brown 			  be32_to_cpu(adsp1_id.fw.ver) & 0xff,
497db40517cSMark Brown 			  algs);
498db40517cSMark Brown 
499db40517cSMark Brown 		pos = sizeof(adsp1_id) / 2;
500db40517cSMark Brown 		term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
501db40517cSMark Brown 		break;
502db40517cSMark Brown 
503db40517cSMark Brown 	case WMFW_ADSP2:
504db40517cSMark Brown 		ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
505db40517cSMark Brown 				      sizeof(adsp2_id));
506db40517cSMark Brown 		if (ret != 0) {
507db40517cSMark Brown 			adsp_err(dsp, "Failed to read algorithm info: %d\n",
508db40517cSMark Brown 				 ret);
509db40517cSMark Brown 			return ret;
510db40517cSMark Brown 		}
511db40517cSMark Brown 
512d62f4bc6SMark Brown 		buf = &adsp2_id;
513d62f4bc6SMark Brown 		buf_size = sizeof(adsp2_id);
514d62f4bc6SMark Brown 
515db40517cSMark Brown 		algs = be32_to_cpu(adsp2_id.algs);
516db40517cSMark Brown 		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
517db40517cSMark Brown 			  be32_to_cpu(adsp2_id.fw.id),
518db40517cSMark Brown 			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
519db40517cSMark Brown 			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
520db40517cSMark Brown 			  be32_to_cpu(adsp2_id.fw.ver) & 0xff,
521db40517cSMark Brown 			  algs);
522db40517cSMark Brown 
523db40517cSMark Brown 		pos = sizeof(adsp2_id) / 2;
524db40517cSMark Brown 		term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
525db40517cSMark Brown 		break;
526db40517cSMark Brown 
527db40517cSMark Brown 	default:
528db40517cSMark Brown 		BUG_ON(NULL == "Unknown DSP type");
529db40517cSMark Brown 		return -EINVAL;
530db40517cSMark Brown 	}
531db40517cSMark Brown 
532db40517cSMark Brown 	if (algs == 0) {
533db40517cSMark Brown 		adsp_err(dsp, "No algorithms\n");
534db40517cSMark Brown 		return -EINVAL;
535db40517cSMark Brown 	}
536db40517cSMark Brown 
537d62f4bc6SMark Brown 	if (algs > 1024) {
538d62f4bc6SMark Brown 		adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
539d62f4bc6SMark Brown 		print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
540d62f4bc6SMark Brown 				     buf, buf_size);
541d62f4bc6SMark Brown 		return -EINVAL;
542d62f4bc6SMark Brown 	}
543d62f4bc6SMark Brown 
544db40517cSMark Brown 	/* Read the terminator first to validate the length */
545db40517cSMark Brown 	ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
546db40517cSMark Brown 	if (ret != 0) {
547db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm list end: %d\n",
548db40517cSMark Brown 			ret);
549db40517cSMark Brown 		return ret;
550db40517cSMark Brown 	}
551db40517cSMark Brown 
552db40517cSMark Brown 	if (be32_to_cpu(val) != 0xbedead)
553db40517cSMark Brown 		adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
554db40517cSMark Brown 			  term, be32_to_cpu(val));
555db40517cSMark Brown 
556db40517cSMark Brown 	alg = kzalloc((term - pos) * 2, GFP_KERNEL);
557db40517cSMark Brown 	if (!alg)
558db40517cSMark Brown 		return -ENOMEM;
559db40517cSMark Brown 
560db40517cSMark Brown 	ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
561db40517cSMark Brown 	if (ret != 0) {
562db40517cSMark Brown 		adsp_err(dsp, "Failed to read algorithm list: %d\n",
563db40517cSMark Brown 			ret);
564db40517cSMark Brown 		goto out;
565db40517cSMark Brown 	}
566db40517cSMark Brown 
567db40517cSMark Brown 	adsp1_alg = alg;
568db40517cSMark Brown 	adsp2_alg = alg;
569db40517cSMark Brown 
570db40517cSMark Brown 	for (i = 0; i < algs; i++) {
571db40517cSMark Brown 		switch (dsp->type) {
572db40517cSMark Brown 		case WMFW_ADSP1:
573471f4885SMark Brown 			adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
574db40517cSMark Brown 				  i, be32_to_cpu(adsp1_alg[i].alg.id),
575db40517cSMark Brown 				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
576db40517cSMark Brown 				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
577471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
578471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].dm),
579471f4885SMark Brown 				  be32_to_cpu(adsp1_alg[i].zm));
580471f4885SMark Brown 
581471f4885SMark Brown 			if (adsp1_alg[i].dm) {
582471f4885SMark Brown 				region = kzalloc(sizeof(*region), GFP_KERNEL);
583471f4885SMark Brown 				if (!region)
584471f4885SMark Brown 					return -ENOMEM;
585471f4885SMark Brown 				region->type = WMFW_ADSP1_DM;
586471f4885SMark Brown 				region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
587471f4885SMark Brown 				region->base = be32_to_cpu(adsp1_alg[i].dm);
588471f4885SMark Brown 				list_add_tail(&region->list,
589471f4885SMark Brown 					      &dsp->alg_regions);
590471f4885SMark Brown 			}
591471f4885SMark Brown 
592471f4885SMark Brown 			if (adsp1_alg[i].zm) {
593471f4885SMark Brown 				region = kzalloc(sizeof(*region), GFP_KERNEL);
594471f4885SMark Brown 				if (!region)
595471f4885SMark Brown 					return -ENOMEM;
596471f4885SMark Brown 				region->type = WMFW_ADSP1_ZM;
597471f4885SMark Brown 				region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
598471f4885SMark Brown 				region->base = be32_to_cpu(adsp1_alg[i].zm);
599471f4885SMark Brown 				list_add_tail(&region->list,
600471f4885SMark Brown 					      &dsp->alg_regions);
601471f4885SMark Brown 			}
602db40517cSMark Brown 			break;
603db40517cSMark Brown 
604db40517cSMark Brown 		case WMFW_ADSP2:
605471f4885SMark Brown 			adsp_info(dsp,
606471f4885SMark Brown 				  "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
607db40517cSMark Brown 				  i, be32_to_cpu(adsp2_alg[i].alg.id),
608db40517cSMark Brown 				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
609db40517cSMark Brown 				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
610471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
611471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].xm),
612471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].ym),
613471f4885SMark Brown 				  be32_to_cpu(adsp2_alg[i].zm));
614471f4885SMark Brown 
615471f4885SMark Brown 			if (adsp2_alg[i].xm) {
616471f4885SMark Brown 				region = kzalloc(sizeof(*region), GFP_KERNEL);
617471f4885SMark Brown 				if (!region)
618471f4885SMark Brown 					return -ENOMEM;
619471f4885SMark Brown 				region->type = WMFW_ADSP2_XM;
620471f4885SMark Brown 				region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
621471f4885SMark Brown 				region->base = be32_to_cpu(adsp2_alg[i].xm);
622471f4885SMark Brown 				list_add_tail(&region->list,
623471f4885SMark Brown 					      &dsp->alg_regions);
624471f4885SMark Brown 			}
625471f4885SMark Brown 
626471f4885SMark Brown 			if (adsp2_alg[i].ym) {
627471f4885SMark Brown 				region = kzalloc(sizeof(*region), GFP_KERNEL);
628471f4885SMark Brown 				if (!region)
629471f4885SMark Brown 					return -ENOMEM;
630471f4885SMark Brown 				region->type = WMFW_ADSP2_YM;
631471f4885SMark Brown 				region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
632471f4885SMark Brown 				region->base = be32_to_cpu(adsp2_alg[i].ym);
633471f4885SMark Brown 				list_add_tail(&region->list,
634471f4885SMark Brown 					      &dsp->alg_regions);
635471f4885SMark Brown 			}
636471f4885SMark Brown 
637471f4885SMark Brown 			if (adsp2_alg[i].zm) {
638471f4885SMark Brown 				region = kzalloc(sizeof(*region), GFP_KERNEL);
639471f4885SMark Brown 				if (!region)
640471f4885SMark Brown 					return -ENOMEM;
641471f4885SMark Brown 				region->type = WMFW_ADSP2_ZM;
642471f4885SMark Brown 				region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
643471f4885SMark Brown 				region->base = be32_to_cpu(adsp2_alg[i].zm);
644471f4885SMark Brown 				list_add_tail(&region->list,
645471f4885SMark Brown 					      &dsp->alg_regions);
646471f4885SMark Brown 			}
647db40517cSMark Brown 			break;
648db40517cSMark Brown 		}
649db40517cSMark Brown 	}
650db40517cSMark Brown 
651db40517cSMark Brown out:
652db40517cSMark Brown 	kfree(alg);
653db40517cSMark Brown 	return ret;
654db40517cSMark Brown }
655db40517cSMark Brown 
6562159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp)
6572159ad93SMark Brown {
6582159ad93SMark Brown 	struct regmap *regmap = dsp->regmap;
6592159ad93SMark Brown 	struct wmfw_coeff_hdr *hdr;
6602159ad93SMark Brown 	struct wmfw_coeff_item *blk;
6612159ad93SMark Brown 	const struct firmware *firmware;
662471f4885SMark Brown 	const struct wm_adsp_region *mem;
663471f4885SMark Brown 	struct wm_adsp_alg_region *alg_region;
6642159ad93SMark Brown 	const char *region_name;
6652159ad93SMark Brown 	int ret, pos, blocks, type, offset, reg;
6662159ad93SMark Brown 	char *file;
6672159ad93SMark Brown 
6682159ad93SMark Brown 	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
6692159ad93SMark Brown 	if (file == NULL)
6702159ad93SMark Brown 		return -ENOMEM;
6712159ad93SMark Brown 
6721023dbd9SMark Brown 	snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
6731023dbd9SMark Brown 		 wm_adsp_fw[dsp->fw].file);
6742159ad93SMark Brown 	file[PAGE_SIZE - 1] = '\0';
6752159ad93SMark Brown 
6762159ad93SMark Brown 	ret = request_firmware(&firmware, file, dsp->dev);
6772159ad93SMark Brown 	if (ret != 0) {
6782159ad93SMark Brown 		adsp_warn(dsp, "Failed to request '%s'\n", file);
6792159ad93SMark Brown 		ret = 0;
6802159ad93SMark Brown 		goto out;
6812159ad93SMark Brown 	}
6822159ad93SMark Brown 	ret = -EINVAL;
6832159ad93SMark Brown 
6842159ad93SMark Brown 	if (sizeof(*hdr) >= firmware->size) {
6852159ad93SMark Brown 		adsp_err(dsp, "%s: file too short, %zu bytes\n",
6862159ad93SMark Brown 			file, firmware->size);
6872159ad93SMark Brown 		goto out_fw;
6882159ad93SMark Brown 	}
6892159ad93SMark Brown 
6902159ad93SMark Brown 	hdr = (void*)&firmware->data[0];
6912159ad93SMark Brown 	if (memcmp(hdr->magic, "WMDR", 4) != 0) {
6922159ad93SMark Brown 		adsp_err(dsp, "%s: invalid magic\n", file);
6932159ad93SMark Brown 		return -EINVAL;
6942159ad93SMark Brown 	}
6952159ad93SMark Brown 
696c712326dSMark Brown 	switch (be32_to_cpu(hdr->rev) & 0xff) {
697c712326dSMark Brown 	case 1:
698c712326dSMark Brown 		break;
699c712326dSMark Brown 	default:
700c712326dSMark Brown 		adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
701c712326dSMark Brown 			 file, be32_to_cpu(hdr->rev) & 0xff);
702c712326dSMark Brown 		ret = -EINVAL;
703c712326dSMark Brown 		goto out_fw;
704c712326dSMark Brown 	}
705c712326dSMark Brown 
7062159ad93SMark Brown 	adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
7072159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >> 16) & 0xff,
7082159ad93SMark Brown 		(le32_to_cpu(hdr->ver) >>  8) & 0xff,
7092159ad93SMark Brown 		le32_to_cpu(hdr->ver) & 0xff);
7102159ad93SMark Brown 
7112159ad93SMark Brown 	pos = le32_to_cpu(hdr->len);
7122159ad93SMark Brown 
7132159ad93SMark Brown 	blocks = 0;
7142159ad93SMark Brown 	while (pos < firmware->size &&
7152159ad93SMark Brown 	       pos - firmware->size > sizeof(*blk)) {
7162159ad93SMark Brown 		blk = (void*)(&firmware->data[pos]);
7172159ad93SMark Brown 
718c712326dSMark Brown 		type = le16_to_cpu(blk->type);
719c712326dSMark Brown 		offset = le16_to_cpu(blk->offset);
7202159ad93SMark Brown 
7212159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
7222159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->id),
7232159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >> 16) & 0xff,
7242159ad93SMark Brown 			 (le32_to_cpu(blk->ver) >>  8) & 0xff,
7252159ad93SMark Brown 			 le32_to_cpu(blk->ver) & 0xff);
7262159ad93SMark Brown 		adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
7272159ad93SMark Brown 			 file, blocks, le32_to_cpu(blk->len), offset, type);
7282159ad93SMark Brown 
7292159ad93SMark Brown 		reg = 0;
7302159ad93SMark Brown 		region_name = "Unknown";
7312159ad93SMark Brown 		switch (type) {
732c712326dSMark Brown 		case (WMFW_NAME_TEXT << 8):
733c712326dSMark Brown 		case (WMFW_INFO_TEXT << 8):
7342159ad93SMark Brown 			break;
735c712326dSMark Brown 		case (WMFW_ABSOLUTE << 8):
7362159ad93SMark Brown 			region_name = "register";
7372159ad93SMark Brown 			reg = offset;
7382159ad93SMark Brown 			break;
739471f4885SMark Brown 
740471f4885SMark Brown 		case WMFW_ADSP1_DM:
741471f4885SMark Brown 		case WMFW_ADSP1_ZM:
742471f4885SMark Brown 		case WMFW_ADSP2_XM:
743471f4885SMark Brown 		case WMFW_ADSP2_YM:
744471f4885SMark Brown 			adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
745471f4885SMark Brown 				 file, blocks, le32_to_cpu(blk->len),
746471f4885SMark Brown 				 type, le32_to_cpu(blk->id));
747471f4885SMark Brown 
748471f4885SMark Brown 			mem = wm_adsp_find_region(dsp, type);
749471f4885SMark Brown 			if (!mem) {
750471f4885SMark Brown 				adsp_err(dsp, "No base for region %x\n", type);
751471f4885SMark Brown 				break;
752471f4885SMark Brown 			}
753471f4885SMark Brown 
754471f4885SMark Brown 			reg = 0;
755471f4885SMark Brown 			list_for_each_entry(alg_region,
756471f4885SMark Brown 					    &dsp->alg_regions, list) {
757471f4885SMark Brown 				if (le32_to_cpu(blk->id) == alg_region->alg &&
758471f4885SMark Brown 				    type == alg_region->type) {
759471f4885SMark Brown 					reg = alg_region->base + offset;
760471f4885SMark Brown 					reg = wm_adsp_region_to_reg(mem,
761471f4885SMark Brown 								    reg);
762471f4885SMark Brown 				}
763471f4885SMark Brown 			}
764471f4885SMark Brown 
765471f4885SMark Brown 			if (reg == 0)
766471f4885SMark Brown 				adsp_err(dsp, "No %x for algorithm %x\n",
767471f4885SMark Brown 					 type, le32_to_cpu(blk->id));
768471f4885SMark Brown 			break;
769471f4885SMark Brown 
7702159ad93SMark Brown 		default:
7712159ad93SMark Brown 			adsp_err(dsp, "Unknown region type %x\n", type);
7722159ad93SMark Brown 			break;
7732159ad93SMark Brown 		}
7742159ad93SMark Brown 
7752159ad93SMark Brown 		if (reg) {
7762159ad93SMark Brown 			ret = regmap_raw_write(regmap, reg, blk->data,
7772159ad93SMark Brown 					       le32_to_cpu(blk->len));
7782159ad93SMark Brown 			if (ret != 0) {
7792159ad93SMark Brown 				adsp_err(dsp,
7802159ad93SMark Brown 					"%s.%d: Failed to write to %x in %s\n",
7812159ad93SMark Brown 					file, blocks, reg, region_name);
7822159ad93SMark Brown 			}
7832159ad93SMark Brown 		}
7842159ad93SMark Brown 
7852159ad93SMark Brown 		pos += le32_to_cpu(blk->len) + sizeof(*blk);
7862159ad93SMark Brown 		blocks++;
7872159ad93SMark Brown 	}
7882159ad93SMark Brown 
7892159ad93SMark Brown 	if (pos > firmware->size)
7902159ad93SMark Brown 		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
7912159ad93SMark Brown 			  file, blocks, pos - firmware->size);
7922159ad93SMark Brown 
7932159ad93SMark Brown out_fw:
7942159ad93SMark Brown 	release_firmware(firmware);
7952159ad93SMark Brown out:
7962159ad93SMark Brown 	kfree(file);
7972159ad93SMark Brown 	return 0;
7982159ad93SMark Brown }
7992159ad93SMark Brown 
8005e7a7a22SMark Brown int wm_adsp1_init(struct wm_adsp *adsp)
8015e7a7a22SMark Brown {
8025e7a7a22SMark Brown 	INIT_LIST_HEAD(&adsp->alg_regions);
8035e7a7a22SMark Brown 
8045e7a7a22SMark Brown 	return 0;
8055e7a7a22SMark Brown }
8065e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init);
8075e7a7a22SMark Brown 
8082159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w,
8092159ad93SMark Brown 		   struct snd_kcontrol *kcontrol,
8102159ad93SMark Brown 		   int event)
8112159ad93SMark Brown {
8122159ad93SMark Brown 	struct snd_soc_codec *codec = w->codec;
8132159ad93SMark Brown 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
8142159ad93SMark Brown 	struct wm_adsp *dsp = &dsps[w->shift];
8152159ad93SMark Brown 	int ret;
81694e205bfSChris Rattray 	int val;
8172159ad93SMark Brown 
8182159ad93SMark Brown 	switch (event) {
8192159ad93SMark Brown 	case SND_SOC_DAPM_POST_PMU:
8202159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
8212159ad93SMark Brown 				   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
8222159ad93SMark Brown 
82394e205bfSChris Rattray 		/*
82494e205bfSChris Rattray 		 * For simplicity set the DSP clock rate to be the
82594e205bfSChris Rattray 		 * SYSCLK rate rather than making it configurable.
82694e205bfSChris Rattray 		 */
82794e205bfSChris Rattray 		if(dsp->sysclk_reg) {
82894e205bfSChris Rattray 			ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
82994e205bfSChris Rattray 			if (ret != 0) {
83094e205bfSChris Rattray 				adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
83194e205bfSChris Rattray 				ret);
83294e205bfSChris Rattray 				return ret;
83394e205bfSChris Rattray 			}
83494e205bfSChris Rattray 
83594e205bfSChris Rattray 			val = (val & dsp->sysclk_mask)
83694e205bfSChris Rattray 				>> dsp->sysclk_shift;
83794e205bfSChris Rattray 
83894e205bfSChris Rattray 			ret = regmap_update_bits(dsp->regmap,
83994e205bfSChris Rattray 						 dsp->base + ADSP1_CONTROL_31,
84094e205bfSChris Rattray 						 ADSP1_CLK_SEL_MASK, val);
84194e205bfSChris Rattray 			if (ret != 0) {
84294e205bfSChris Rattray 				adsp_err(dsp, "Failed to set clock rate: %d\n",
84394e205bfSChris Rattray 					 ret);
84494e205bfSChris Rattray 				return ret;
84594e205bfSChris Rattray 			}
84694e205bfSChris Rattray 		}
84794e205bfSChris Rattray 
8482159ad93SMark Brown 		ret = wm_adsp_load(dsp);
8492159ad93SMark Brown 		if (ret != 0)
8502159ad93SMark Brown 			goto err;
8512159ad93SMark Brown 
852db40517cSMark Brown 		ret = wm_adsp_setup_algs(dsp);
853db40517cSMark Brown 		if (ret != 0)
854db40517cSMark Brown 			goto err;
855db40517cSMark Brown 
8562159ad93SMark Brown 		ret = wm_adsp_load_coeff(dsp);
8572159ad93SMark Brown 		if (ret != 0)
8582159ad93SMark Brown 			goto err;
8592159ad93SMark Brown 
8602159ad93SMark Brown 		/* Start the core running */
8612159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
8622159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START,
8632159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START);
8642159ad93SMark Brown 		break;
8652159ad93SMark Brown 
8662159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
8672159ad93SMark Brown 		/* Halt the core */
8682159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
8692159ad93SMark Brown 				   ADSP1_CORE_ENA | ADSP1_START, 0);
8702159ad93SMark Brown 
8712159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
8722159ad93SMark Brown 				   ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
8732159ad93SMark Brown 
8742159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
8752159ad93SMark Brown 				   ADSP1_SYS_ENA, 0);
8762159ad93SMark Brown 		break;
8772159ad93SMark Brown 
8782159ad93SMark Brown 	default:
8792159ad93SMark Brown 		break;
8802159ad93SMark Brown 	}
8812159ad93SMark Brown 
8822159ad93SMark Brown 	return 0;
8832159ad93SMark Brown 
8842159ad93SMark Brown err:
8852159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
8862159ad93SMark Brown 			   ADSP1_SYS_ENA, 0);
8872159ad93SMark Brown 	return ret;
8882159ad93SMark Brown }
8892159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event);
8902159ad93SMark Brown 
8912159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp)
8922159ad93SMark Brown {
8932159ad93SMark Brown 	unsigned int val;
8942159ad93SMark Brown 	int ret, count;
8952159ad93SMark Brown 
8962159ad93SMark Brown 	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
8972159ad93SMark Brown 				 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
8982159ad93SMark Brown 	if (ret != 0)
8992159ad93SMark Brown 		return ret;
9002159ad93SMark Brown 
9012159ad93SMark Brown 	/* Wait for the RAM to start, should be near instantaneous */
9022159ad93SMark Brown 	count = 0;
9032159ad93SMark Brown 	do {
9042159ad93SMark Brown 		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
9052159ad93SMark Brown 				  &val);
9062159ad93SMark Brown 		if (ret != 0)
9072159ad93SMark Brown 			return ret;
9082159ad93SMark Brown 	} while (!(val & ADSP2_RAM_RDY) && ++count < 10);
9092159ad93SMark Brown 
9102159ad93SMark Brown 	if (!(val & ADSP2_RAM_RDY)) {
9112159ad93SMark Brown 		adsp_err(dsp, "Failed to start DSP RAM\n");
9122159ad93SMark Brown 		return -EBUSY;
9132159ad93SMark Brown 	}
9142159ad93SMark Brown 
9152159ad93SMark Brown 	adsp_dbg(dsp, "RAM ready after %d polls\n", count);
9162159ad93SMark Brown 	adsp_info(dsp, "RAM ready after %d polls\n", count);
9172159ad93SMark Brown 
9182159ad93SMark Brown 	return 0;
9192159ad93SMark Brown }
9202159ad93SMark Brown 
9212159ad93SMark Brown int wm_adsp2_event(struct snd_soc_dapm_widget *w,
9222159ad93SMark Brown 		   struct snd_kcontrol *kcontrol, int event)
9232159ad93SMark Brown {
9242159ad93SMark Brown 	struct snd_soc_codec *codec = w->codec;
9252159ad93SMark Brown 	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
9262159ad93SMark Brown 	struct wm_adsp *dsp = &dsps[w->shift];
927471f4885SMark Brown 	struct wm_adsp_alg_region *alg_region;
928973838a0SMark Brown 	unsigned int val;
9292159ad93SMark Brown 	int ret;
9302159ad93SMark Brown 
9312159ad93SMark Brown 	switch (event) {
9322159ad93SMark Brown 	case SND_SOC_DAPM_POST_PMU:
933dd49e2c8SMark Brown 		/*
934dd49e2c8SMark Brown 		 * For simplicity set the DSP clock rate to be the
935dd49e2c8SMark Brown 		 * SYSCLK rate rather than making it configurable.
936dd49e2c8SMark Brown 		 */
937dd49e2c8SMark Brown 		ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
938dd49e2c8SMark Brown 		if (ret != 0) {
939dd49e2c8SMark Brown 			adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
940dd49e2c8SMark Brown 				 ret);
941dd49e2c8SMark Brown 			return ret;
942dd49e2c8SMark Brown 		}
943dd49e2c8SMark Brown 		val = (val & ARIZONA_SYSCLK_FREQ_MASK)
944dd49e2c8SMark Brown 			>> ARIZONA_SYSCLK_FREQ_SHIFT;
945dd49e2c8SMark Brown 
946dd49e2c8SMark Brown 		ret = regmap_update_bits(dsp->regmap,
947dd49e2c8SMark Brown 					 dsp->base + ADSP2_CLOCKING,
948dd49e2c8SMark Brown 					 ADSP2_CLK_SEL_MASK, val);
949dd49e2c8SMark Brown 		if (ret != 0) {
950dd49e2c8SMark Brown 			adsp_err(dsp, "Failed to set clock rate: %d\n",
951dd49e2c8SMark Brown 				 ret);
952dd49e2c8SMark Brown 			return ret;
953dd49e2c8SMark Brown 		}
954dd49e2c8SMark Brown 
955973838a0SMark Brown 		if (dsp->dvfs) {
956973838a0SMark Brown 			ret = regmap_read(dsp->regmap,
957973838a0SMark Brown 					  dsp->base + ADSP2_CLOCKING, &val);
958973838a0SMark Brown 			if (ret != 0) {
959973838a0SMark Brown 				dev_err(dsp->dev,
960973838a0SMark Brown 					"Failed to read clocking: %d\n", ret);
961973838a0SMark Brown 				return ret;
962973838a0SMark Brown 			}
963973838a0SMark Brown 
96425c6fdb0SMark Brown 			if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
965973838a0SMark Brown 				ret = regulator_enable(dsp->dvfs);
966973838a0SMark Brown 				if (ret != 0) {
967973838a0SMark Brown 					dev_err(dsp->dev,
968973838a0SMark Brown 						"Failed to enable supply: %d\n",
969973838a0SMark Brown 						ret);
970973838a0SMark Brown 					return ret;
971973838a0SMark Brown 				}
972973838a0SMark Brown 
973973838a0SMark Brown 				ret = regulator_set_voltage(dsp->dvfs,
974973838a0SMark Brown 							    1800000,
975973838a0SMark Brown 							    1800000);
976973838a0SMark Brown 				if (ret != 0) {
977973838a0SMark Brown 					dev_err(dsp->dev,
978973838a0SMark Brown 						"Failed to raise supply: %d\n",
979973838a0SMark Brown 						ret);
980973838a0SMark Brown 					return ret;
981973838a0SMark Brown 				}
982973838a0SMark Brown 			}
983973838a0SMark Brown 		}
984973838a0SMark Brown 
9852159ad93SMark Brown 		ret = wm_adsp2_ena(dsp);
9862159ad93SMark Brown 		if (ret != 0)
9872159ad93SMark Brown 			return ret;
9882159ad93SMark Brown 
9892159ad93SMark Brown 		ret = wm_adsp_load(dsp);
9902159ad93SMark Brown 		if (ret != 0)
9912159ad93SMark Brown 			goto err;
9922159ad93SMark Brown 
993db40517cSMark Brown 		ret = wm_adsp_setup_algs(dsp);
994db40517cSMark Brown 		if (ret != 0)
995db40517cSMark Brown 			goto err;
996db40517cSMark Brown 
9972159ad93SMark Brown 		ret = wm_adsp_load_coeff(dsp);
9982159ad93SMark Brown 		if (ret != 0)
9992159ad93SMark Brown 			goto err;
10002159ad93SMark Brown 
10012159ad93SMark Brown 		ret = regmap_update_bits(dsp->regmap,
10022159ad93SMark Brown 					 dsp->base + ADSP2_CONTROL,
1003a7f9be7eSMark Brown 					 ADSP2_CORE_ENA | ADSP2_START,
1004a7f9be7eSMark Brown 					 ADSP2_CORE_ENA | ADSP2_START);
10052159ad93SMark Brown 		if (ret != 0)
10062159ad93SMark Brown 			goto err;
10071023dbd9SMark Brown 
10081023dbd9SMark Brown 		dsp->running = true;
10092159ad93SMark Brown 		break;
10102159ad93SMark Brown 
10112159ad93SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
10121023dbd9SMark Brown 		dsp->running = false;
10131023dbd9SMark Brown 
10142159ad93SMark Brown 		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1015a7f9be7eSMark Brown 				   ADSP2_SYS_ENA | ADSP2_CORE_ENA |
1016a7f9be7eSMark Brown 				   ADSP2_START, 0);
1017973838a0SMark Brown 
1018973838a0SMark Brown 		if (dsp->dvfs) {
1019973838a0SMark Brown 			ret = regulator_set_voltage(dsp->dvfs, 1200000,
1020973838a0SMark Brown 						    1800000);
1021973838a0SMark Brown 			if (ret != 0)
1022973838a0SMark Brown 				dev_warn(dsp->dev,
1023973838a0SMark Brown 					 "Failed to lower supply: %d\n",
1024973838a0SMark Brown 					 ret);
1025973838a0SMark Brown 
1026973838a0SMark Brown 			ret = regulator_disable(dsp->dvfs);
1027973838a0SMark Brown 			if (ret != 0)
1028973838a0SMark Brown 				dev_err(dsp->dev,
1029973838a0SMark Brown 					"Failed to enable supply: %d\n",
1030973838a0SMark Brown 					ret);
1031973838a0SMark Brown 		}
1032471f4885SMark Brown 
1033471f4885SMark Brown 		while (!list_empty(&dsp->alg_regions)) {
1034471f4885SMark Brown 			alg_region = list_first_entry(&dsp->alg_regions,
1035471f4885SMark Brown 						      struct wm_adsp_alg_region,
1036471f4885SMark Brown 						      list);
1037471f4885SMark Brown 			list_del(&alg_region->list);
1038471f4885SMark Brown 			kfree(alg_region);
1039471f4885SMark Brown 		}
10402159ad93SMark Brown 		break;
10412159ad93SMark Brown 
10422159ad93SMark Brown 	default:
10432159ad93SMark Brown 		break;
10442159ad93SMark Brown 	}
10452159ad93SMark Brown 
10462159ad93SMark Brown 	return 0;
10472159ad93SMark Brown err:
10482159ad93SMark Brown 	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
1049a7f9be7eSMark Brown 			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
10502159ad93SMark Brown 	return ret;
10512159ad93SMark Brown }
10522159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event);
1053973838a0SMark Brown 
1054973838a0SMark Brown int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1055973838a0SMark Brown {
1056973838a0SMark Brown 	int ret;
1057973838a0SMark Brown 
105810a2b662SMark Brown 	/*
105910a2b662SMark Brown 	 * Disable the DSP memory by default when in reset for a small
106010a2b662SMark Brown 	 * power saving.
106110a2b662SMark Brown 	 */
106210a2b662SMark Brown 	ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
106310a2b662SMark Brown 				 ADSP2_MEM_ENA, 0);
106410a2b662SMark Brown 	if (ret != 0) {
106510a2b662SMark Brown 		adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
106610a2b662SMark Brown 		return ret;
106710a2b662SMark Brown 	}
106810a2b662SMark Brown 
1069471f4885SMark Brown 	INIT_LIST_HEAD(&adsp->alg_regions);
1070471f4885SMark Brown 
1071973838a0SMark Brown 	if (dvfs) {
1072973838a0SMark Brown 		adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1073973838a0SMark Brown 		if (IS_ERR(adsp->dvfs)) {
1074973838a0SMark Brown 			ret = PTR_ERR(adsp->dvfs);
1075973838a0SMark Brown 			dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1076973838a0SMark Brown 			return ret;
1077973838a0SMark Brown 		}
1078973838a0SMark Brown 
1079973838a0SMark Brown 		ret = regulator_enable(adsp->dvfs);
1080973838a0SMark Brown 		if (ret != 0) {
1081973838a0SMark Brown 			dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1082973838a0SMark Brown 				ret);
1083973838a0SMark Brown 			return ret;
1084973838a0SMark Brown 		}
1085973838a0SMark Brown 
1086973838a0SMark Brown 		ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1087973838a0SMark Brown 		if (ret != 0) {
1088973838a0SMark Brown 			dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1089973838a0SMark Brown 				ret);
1090973838a0SMark Brown 			return ret;
1091973838a0SMark Brown 		}
1092973838a0SMark Brown 
1093973838a0SMark Brown 		ret = regulator_disable(adsp->dvfs);
1094973838a0SMark Brown 		if (ret != 0) {
1095973838a0SMark Brown 			dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1096973838a0SMark Brown 				ret);
1097973838a0SMark Brown 			return ret;
1098973838a0SMark Brown 		}
1099973838a0SMark Brown 	}
1100973838a0SMark Brown 
1101973838a0SMark Brown 	return 0;
1102973838a0SMark Brown }
1103973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init);
1104