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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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