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> 246ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 252159ad93SMark Brown #include <sound/core.h> 262159ad93SMark Brown #include <sound/pcm.h> 272159ad93SMark Brown #include <sound/pcm_params.h> 282159ad93SMark Brown #include <sound/soc.h> 292159ad93SMark Brown #include <sound/jack.h> 302159ad93SMark Brown #include <sound/initval.h> 312159ad93SMark Brown #include <sound/tlv.h> 322159ad93SMark Brown 332159ad93SMark Brown #include <linux/mfd/arizona/registers.h> 342159ad93SMark Brown 35dc91428aSMark Brown #include "arizona.h" 362159ad93SMark Brown #include "wm_adsp.h" 372159ad93SMark Brown 382159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 392159ad93SMark Brown dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 402159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 412159ad93SMark Brown dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 422159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 432159ad93SMark Brown dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 442159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 452159ad93SMark Brown dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 462159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 472159ad93SMark Brown dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 482159ad93SMark Brown 492159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 502159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 512159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 522159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 532159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 542159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 552159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 562159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 572159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 582159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 592159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 602159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 612159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 622159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 632159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 642159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 652159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 662159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 672159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 682159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 692159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 702159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 712159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 722159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 732159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 742159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 752159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 762159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 772159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 782159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 792159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 802159ad93SMark Brown 812159ad93SMark Brown /* 822159ad93SMark Brown * ADSP1 Control 19 832159ad93SMark Brown */ 842159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 852159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 862159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 872159ad93SMark Brown 882159ad93SMark Brown 892159ad93SMark Brown /* 902159ad93SMark Brown * ADSP1 Control 30 912159ad93SMark Brown */ 922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 932159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 952159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 962159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 972159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 982159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 992159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1002159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1012159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1022159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1032159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1042159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1052159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1062159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1072159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1082159ad93SMark Brown 10994e205bfSChris Rattray /* 11094e205bfSChris Rattray * ADSP1 Control 31 11194e205bfSChris Rattray */ 11294e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11394e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11494e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11594e205bfSChris Rattray 1162d30b575SMark Brown #define ADSP2_CONTROL 0x0 1172d30b575SMark Brown #define ADSP2_CLOCKING 0x1 1182d30b575SMark Brown #define ADSP2_STATUS1 0x4 1192d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1202d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 1212d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1222159ad93SMark Brown 1232159ad93SMark Brown /* 1242159ad93SMark Brown * ADSP2 Control 1252159ad93SMark Brown */ 1262159ad93SMark Brown 1272159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1282159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1292159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1302159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1312159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1322159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1332159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1342159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1352159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1362159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1372159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1382159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1392159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1402159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1412159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1422159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1432159ad93SMark Brown 1442159ad93SMark Brown /* 145973838a0SMark Brown * ADSP2 clocking 146973838a0SMark Brown */ 147973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 148973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 149973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 150973838a0SMark Brown 151973838a0SMark Brown /* 1522159ad93SMark Brown * ADSP2 Status 1 1532159ad93SMark Brown */ 1542159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1552159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1562159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1572159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1582159ad93SMark Brown 159cf17c83cSMark Brown struct wm_adsp_buf { 160cf17c83cSMark Brown struct list_head list; 161cf17c83cSMark Brown void *buf; 162cf17c83cSMark Brown }; 163cf17c83cSMark Brown 164cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 165cf17c83cSMark Brown struct list_head *list) 166cf17c83cSMark Brown { 167cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 168cf17c83cSMark Brown 169cf17c83cSMark Brown if (buf == NULL) 170cf17c83cSMark Brown return NULL; 171cf17c83cSMark Brown 172cf17c83cSMark Brown buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); 173cf17c83cSMark Brown if (!buf->buf) { 174cf17c83cSMark Brown kfree(buf); 175cf17c83cSMark Brown return NULL; 176cf17c83cSMark Brown } 177cf17c83cSMark Brown 178cf17c83cSMark Brown if (list) 179cf17c83cSMark Brown list_add_tail(&buf->list, list); 180cf17c83cSMark Brown 181cf17c83cSMark Brown return buf; 182cf17c83cSMark Brown } 183cf17c83cSMark Brown 184cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 185cf17c83cSMark Brown { 186cf17c83cSMark Brown while (!list_empty(list)) { 187cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 188cf17c83cSMark Brown struct wm_adsp_buf, 189cf17c83cSMark Brown list); 190cf17c83cSMark Brown list_del(&buf->list); 191cf17c83cSMark Brown kfree(buf->buf); 192cf17c83cSMark Brown kfree(buf); 193cf17c83cSMark Brown } 194cf17c83cSMark Brown } 195cf17c83cSMark Brown 19636e8fe99SMark Brown #define WM_ADSP_NUM_FW 4 1971023dbd9SMark Brown 198dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 199dd84f925SMark Brown #define WM_ADSP_FW_TX 1 200dd84f925SMark Brown #define WM_ADSP_FW_TX_SPK 2 201dd84f925SMark Brown #define WM_ADSP_FW_RX_ANC 3 202dd84f925SMark Brown 2031023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 204dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 205dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 206dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 207dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 2081023dbd9SMark Brown }; 2091023dbd9SMark Brown 2101023dbd9SMark Brown static struct { 2111023dbd9SMark Brown const char *file; 2121023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 213dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 214dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 215dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 216dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 2171023dbd9SMark Brown }; 2181023dbd9SMark Brown 2196ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 2206ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 2216ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 2226ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 2236ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 2246ab2b7b4SDimitris Papastamos int (*xinfo)(struct snd_kcontrol *kcontrol, 2256ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo); 2266ab2b7b4SDimitris Papastamos }; 2276ab2b7b4SDimitris Papastamos 2286ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 2296ab2b7b4SDimitris Papastamos const char *name; 2306ab2b7b4SDimitris Papastamos struct wm_adsp_alg_region region; 2316ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 2326ab2b7b4SDimitris Papastamos struct wm_adsp *adsp; 2336ab2b7b4SDimitris Papastamos void *private; 2346ab2b7b4SDimitris Papastamos unsigned int enabled:1; 2356ab2b7b4SDimitris Papastamos struct list_head list; 2366ab2b7b4SDimitris Papastamos void *cache; 2376ab2b7b4SDimitris Papastamos size_t len; 2380c2e3f34SDimitris Papastamos unsigned int set:1; 2396ab2b7b4SDimitris Papastamos struct snd_kcontrol *kcontrol; 2406ab2b7b4SDimitris Papastamos }; 2416ab2b7b4SDimitris Papastamos 2421023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 2431023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 2441023dbd9SMark Brown { 2451023dbd9SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 2461023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 2471023dbd9SMark Brown struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); 2481023dbd9SMark Brown 2491023dbd9SMark Brown ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; 2501023dbd9SMark Brown 2511023dbd9SMark Brown return 0; 2521023dbd9SMark Brown } 2531023dbd9SMark Brown 2541023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 2551023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 2561023dbd9SMark Brown { 2571023dbd9SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 2581023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 2591023dbd9SMark Brown struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); 2601023dbd9SMark Brown 2611023dbd9SMark Brown if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) 2621023dbd9SMark Brown return 0; 2631023dbd9SMark Brown 2641023dbd9SMark Brown if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) 2651023dbd9SMark Brown return -EINVAL; 2661023dbd9SMark Brown 2671023dbd9SMark Brown if (adsp[e->shift_l].running) 2681023dbd9SMark Brown return -EBUSY; 2691023dbd9SMark Brown 27031522764SMark Brown adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; 2711023dbd9SMark Brown 2721023dbd9SMark Brown return 0; 2731023dbd9SMark Brown } 2741023dbd9SMark Brown 2751023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 2761023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2771023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2781023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2791023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2801023dbd9SMark Brown }; 2811023dbd9SMark Brown 282b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp1_fw_controls[] = { 2831023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 2841023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2851023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 2861023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2871023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 2881023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 289b6ed61cfSMark Brown }; 290b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls); 291b6ed61cfSMark Brown 292b6ed61cfSMark Brown #if IS_ENABLED(CONFIG_SND_SOC_ARIZONA) 293b6ed61cfSMark Brown static const struct soc_enum wm_adsp2_rate_enum[] = { 294dc91428aSMark Brown SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1, 295dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 296dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 297dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 298dc91428aSMark Brown SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1, 299dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 300dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 301dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 302dc91428aSMark Brown SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1, 303dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 304dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 305dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 3065be9c5b4SCharles Keepax SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1, 307dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 308dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 309dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 310dc91428aSMark Brown }; 311dc91428aSMark Brown 312b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp2_fw_controls[] = { 3131023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 3141023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 315b6ed61cfSMark Brown SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]), 3161023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 3171023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 318b6ed61cfSMark Brown SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]), 3191023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 3201023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 321b6ed61cfSMark Brown SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]), 3221023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 3231023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 324b6ed61cfSMark Brown SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]), 3251023dbd9SMark Brown }; 326b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls); 327b6ed61cfSMark Brown #endif 3282159ad93SMark Brown 3292159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 3302159ad93SMark Brown int type) 3312159ad93SMark Brown { 3322159ad93SMark Brown int i; 3332159ad93SMark Brown 3342159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 3352159ad93SMark Brown if (dsp->mem[i].type == type) 3362159ad93SMark Brown return &dsp->mem[i]; 3372159ad93SMark Brown 3382159ad93SMark Brown return NULL; 3392159ad93SMark Brown } 3402159ad93SMark Brown 34145b9ee72SMark Brown static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, 34245b9ee72SMark Brown unsigned int offset) 34345b9ee72SMark Brown { 3446c452bdaSTakashi Iwai if (WARN_ON(!region)) 3456c452bdaSTakashi Iwai return offset; 34645b9ee72SMark Brown switch (region->type) { 34745b9ee72SMark Brown case WMFW_ADSP1_PM: 34845b9ee72SMark Brown return region->base + (offset * 3); 34945b9ee72SMark Brown case WMFW_ADSP1_DM: 35045b9ee72SMark Brown return region->base + (offset * 2); 35145b9ee72SMark Brown case WMFW_ADSP2_XM: 35245b9ee72SMark Brown return region->base + (offset * 2); 35345b9ee72SMark Brown case WMFW_ADSP2_YM: 35445b9ee72SMark Brown return region->base + (offset * 2); 35545b9ee72SMark Brown case WMFW_ADSP1_ZM: 35645b9ee72SMark Brown return region->base + (offset * 2); 35745b9ee72SMark Brown default: 3586c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 35945b9ee72SMark Brown return offset; 36045b9ee72SMark Brown } 36145b9ee72SMark Brown } 36245b9ee72SMark Brown 3636ab2b7b4SDimitris Papastamos static int wm_coeff_info(struct snd_kcontrol *kcontrol, 3646ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 3656ab2b7b4SDimitris Papastamos { 3666ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 3676ab2b7b4SDimitris Papastamos 3686ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 3696ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 3706ab2b7b4SDimitris Papastamos return 0; 3716ab2b7b4SDimitris Papastamos } 3726ab2b7b4SDimitris Papastamos 3736ab2b7b4SDimitris Papastamos static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, 3746ab2b7b4SDimitris Papastamos const void *buf, size_t len) 3756ab2b7b4SDimitris Papastamos { 3766ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 3776ab2b7b4SDimitris Papastamos struct wm_adsp_alg_region *region = &ctl->region; 3786ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 3796ab2b7b4SDimitris Papastamos struct wm_adsp *adsp = ctl->adsp; 3806ab2b7b4SDimitris Papastamos void *scratch; 3816ab2b7b4SDimitris Papastamos int ret; 3826ab2b7b4SDimitris Papastamos unsigned int reg; 3836ab2b7b4SDimitris Papastamos 3846ab2b7b4SDimitris Papastamos mem = wm_adsp_find_region(adsp, region->type); 3856ab2b7b4SDimitris Papastamos if (!mem) { 3866ab2b7b4SDimitris Papastamos adsp_err(adsp, "No base for region %x\n", 3876ab2b7b4SDimitris Papastamos region->type); 3886ab2b7b4SDimitris Papastamos return -EINVAL; 3896ab2b7b4SDimitris Papastamos } 3906ab2b7b4SDimitris Papastamos 3916ab2b7b4SDimitris Papastamos reg = ctl->region.base; 3926ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 3936ab2b7b4SDimitris Papastamos 3946ab2b7b4SDimitris Papastamos scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); 3956ab2b7b4SDimitris Papastamos if (!scratch) 3966ab2b7b4SDimitris Papastamos return -ENOMEM; 3976ab2b7b4SDimitris Papastamos 39881ad93ecSDimitris Papastamos ret = regmap_raw_write(adsp->regmap, reg, scratch, 3996ab2b7b4SDimitris Papastamos ctl->len); 4006ab2b7b4SDimitris Papastamos if (ret) { 40143bc3bf6SDimitris Papastamos adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n", 40243bc3bf6SDimitris Papastamos ctl->len, reg, ret); 4036ab2b7b4SDimitris Papastamos kfree(scratch); 4046ab2b7b4SDimitris Papastamos return ret; 4056ab2b7b4SDimitris Papastamos } 406562c5e6fSDimitris Papastamos adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg); 4076ab2b7b4SDimitris Papastamos 4086ab2b7b4SDimitris Papastamos kfree(scratch); 4096ab2b7b4SDimitris Papastamos 4106ab2b7b4SDimitris Papastamos return 0; 4116ab2b7b4SDimitris Papastamos } 4126ab2b7b4SDimitris Papastamos 4136ab2b7b4SDimitris Papastamos static int wm_coeff_put(struct snd_kcontrol *kcontrol, 4146ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 4156ab2b7b4SDimitris Papastamos { 4166ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 4176ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 4186ab2b7b4SDimitris Papastamos 4196ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 4206ab2b7b4SDimitris Papastamos 4216ab2b7b4SDimitris Papastamos if (!ctl->enabled) { 4220c2e3f34SDimitris Papastamos ctl->set = 1; 4236ab2b7b4SDimitris Papastamos return 0; 4246ab2b7b4SDimitris Papastamos } 4256ab2b7b4SDimitris Papastamos 4266ab2b7b4SDimitris Papastamos return wm_coeff_write_control(kcontrol, p, ctl->len); 4276ab2b7b4SDimitris Papastamos } 4286ab2b7b4SDimitris Papastamos 4296ab2b7b4SDimitris Papastamos static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, 4306ab2b7b4SDimitris Papastamos void *buf, size_t len) 4316ab2b7b4SDimitris Papastamos { 4326ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 4336ab2b7b4SDimitris Papastamos struct wm_adsp_alg_region *region = &ctl->region; 4346ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 4356ab2b7b4SDimitris Papastamos struct wm_adsp *adsp = ctl->adsp; 4366ab2b7b4SDimitris Papastamos void *scratch; 4376ab2b7b4SDimitris Papastamos int ret; 4386ab2b7b4SDimitris Papastamos unsigned int reg; 4396ab2b7b4SDimitris Papastamos 4406ab2b7b4SDimitris Papastamos mem = wm_adsp_find_region(adsp, region->type); 4416ab2b7b4SDimitris Papastamos if (!mem) { 4426ab2b7b4SDimitris Papastamos adsp_err(adsp, "No base for region %x\n", 4436ab2b7b4SDimitris Papastamos region->type); 4446ab2b7b4SDimitris Papastamos return -EINVAL; 4456ab2b7b4SDimitris Papastamos } 4466ab2b7b4SDimitris Papastamos 4476ab2b7b4SDimitris Papastamos reg = ctl->region.base; 4486ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 4496ab2b7b4SDimitris Papastamos 4506ab2b7b4SDimitris Papastamos scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); 4516ab2b7b4SDimitris Papastamos if (!scratch) 4526ab2b7b4SDimitris Papastamos return -ENOMEM; 4536ab2b7b4SDimitris Papastamos 45481ad93ecSDimitris Papastamos ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len); 4556ab2b7b4SDimitris Papastamos if (ret) { 45643bc3bf6SDimitris Papastamos adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n", 45743bc3bf6SDimitris Papastamos ctl->len, reg, ret); 4586ab2b7b4SDimitris Papastamos kfree(scratch); 4596ab2b7b4SDimitris Papastamos return ret; 4606ab2b7b4SDimitris Papastamos } 461562c5e6fSDimitris Papastamos adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg); 4626ab2b7b4SDimitris Papastamos 4636ab2b7b4SDimitris Papastamos memcpy(buf, scratch, ctl->len); 4646ab2b7b4SDimitris Papastamos kfree(scratch); 4656ab2b7b4SDimitris Papastamos 4666ab2b7b4SDimitris Papastamos return 0; 4676ab2b7b4SDimitris Papastamos } 4686ab2b7b4SDimitris Papastamos 4696ab2b7b4SDimitris Papastamos static int wm_coeff_get(struct snd_kcontrol *kcontrol, 4706ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 4716ab2b7b4SDimitris Papastamos { 4726ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 4736ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 4746ab2b7b4SDimitris Papastamos 4756ab2b7b4SDimitris Papastamos memcpy(p, ctl->cache, ctl->len); 4766ab2b7b4SDimitris Papastamos return 0; 4776ab2b7b4SDimitris Papastamos } 4786ab2b7b4SDimitris Papastamos 4796ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 48081ad93ecSDimitris Papastamos struct wm_adsp *adsp; 4816ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 4826ab2b7b4SDimitris Papastamos struct work_struct work; 4836ab2b7b4SDimitris Papastamos }; 4846ab2b7b4SDimitris Papastamos 48581ad93ecSDimitris Papastamos static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) 4866ab2b7b4SDimitris Papastamos { 4876ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 4886ab2b7b4SDimitris Papastamos int ret; 4896ab2b7b4SDimitris Papastamos 49092bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 4916ab2b7b4SDimitris Papastamos return -EINVAL; 4926ab2b7b4SDimitris Papastamos 4936ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 4946ab2b7b4SDimitris Papastamos if (!kcontrol) 4956ab2b7b4SDimitris Papastamos return -ENOMEM; 4966ab2b7b4SDimitris Papastamos kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 4976ab2b7b4SDimitris Papastamos 4986ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 4996ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 5006ab2b7b4SDimitris Papastamos kcontrol->get = wm_coeff_get; 5016ab2b7b4SDimitris Papastamos kcontrol->put = wm_coeff_put; 5026ab2b7b4SDimitris Papastamos kcontrol->private_value = (unsigned long)ctl; 5036ab2b7b4SDimitris Papastamos 50492bb4c32SDimitris Papastamos ret = snd_soc_add_card_controls(adsp->card, 50581ad93ecSDimitris Papastamos kcontrol, 1); 5066ab2b7b4SDimitris Papastamos if (ret < 0) 5076ab2b7b4SDimitris Papastamos goto err_kcontrol; 5086ab2b7b4SDimitris Papastamos 5096ab2b7b4SDimitris Papastamos kfree(kcontrol); 5106ab2b7b4SDimitris Papastamos 51192bb4c32SDimitris Papastamos ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card, 51281ad93ecSDimitris Papastamos ctl->name); 51381ad93ecSDimitris Papastamos 51481ad93ecSDimitris Papastamos list_add(&ctl->list, &adsp->ctl_list); 5156ab2b7b4SDimitris Papastamos return 0; 5166ab2b7b4SDimitris Papastamos 5176ab2b7b4SDimitris Papastamos err_kcontrol: 5186ab2b7b4SDimitris Papastamos kfree(kcontrol); 5196ab2b7b4SDimitris Papastamos return ret; 5206ab2b7b4SDimitris Papastamos } 5216ab2b7b4SDimitris Papastamos 5222159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 5232159ad93SMark Brown { 524cf17c83cSMark Brown LIST_HEAD(buf_list); 5252159ad93SMark Brown const struct firmware *firmware; 5262159ad93SMark Brown struct regmap *regmap = dsp->regmap; 5272159ad93SMark Brown unsigned int pos = 0; 5282159ad93SMark Brown const struct wmfw_header *header; 5292159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 5302159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 5312159ad93SMark Brown const struct wmfw_footer *footer; 5322159ad93SMark Brown const struct wmfw_region *region; 5332159ad93SMark Brown const struct wm_adsp_region *mem; 5342159ad93SMark Brown const char *region_name; 5352159ad93SMark Brown char *file, *text; 536cf17c83cSMark Brown struct wm_adsp_buf *buf; 5372159ad93SMark Brown unsigned int reg; 5382159ad93SMark Brown int regions = 0; 5392159ad93SMark Brown int ret, offset, type, sizes; 5402159ad93SMark Brown 5412159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 5422159ad93SMark Brown if (file == NULL) 5432159ad93SMark Brown return -ENOMEM; 5442159ad93SMark Brown 5451023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 5461023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 5472159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 5482159ad93SMark Brown 5492159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 5502159ad93SMark Brown if (ret != 0) { 5512159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 5522159ad93SMark Brown goto out; 5532159ad93SMark Brown } 5542159ad93SMark Brown ret = -EINVAL; 5552159ad93SMark Brown 5562159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 5572159ad93SMark Brown if (pos >= firmware->size) { 5582159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 5592159ad93SMark Brown file, firmware->size); 5602159ad93SMark Brown goto out_fw; 5612159ad93SMark Brown } 5622159ad93SMark Brown 5632159ad93SMark Brown header = (void*)&firmware->data[0]; 5642159ad93SMark Brown 5652159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 5662159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 5672159ad93SMark Brown goto out_fw; 5682159ad93SMark Brown } 5692159ad93SMark Brown 5702159ad93SMark Brown if (header->ver != 0) { 5712159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 5722159ad93SMark Brown file, header->ver); 5732159ad93SMark Brown goto out_fw; 5742159ad93SMark Brown } 5753626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 5762159ad93SMark Brown 5772159ad93SMark Brown if (header->core != dsp->type) { 5782159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 5792159ad93SMark Brown file, header->core, dsp->type); 5802159ad93SMark Brown goto out_fw; 5812159ad93SMark Brown } 5822159ad93SMark Brown 5832159ad93SMark Brown switch (dsp->type) { 5842159ad93SMark Brown case WMFW_ADSP1: 5852159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 5862159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 5872159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 5882159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 5892159ad93SMark Brown 5902159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 5912159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 5922159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 5932159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 5942159ad93SMark Brown break; 5952159ad93SMark Brown 5962159ad93SMark Brown case WMFW_ADSP2: 5972159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 5982159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 5992159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 6002159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 6012159ad93SMark Brown 6022159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 6032159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 6042159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 6052159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 6062159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 6072159ad93SMark Brown break; 6082159ad93SMark Brown 6092159ad93SMark Brown default: 6106c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 6112159ad93SMark Brown goto out_fw; 6122159ad93SMark Brown } 6132159ad93SMark Brown 6142159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 6152159ad93SMark Brown sizes + sizeof(*footer)) { 6162159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 6172159ad93SMark Brown file, le32_to_cpu(header->len)); 6182159ad93SMark Brown goto out_fw; 6192159ad93SMark Brown } 6202159ad93SMark Brown 6212159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 6222159ad93SMark Brown le64_to_cpu(footer->timestamp)); 6232159ad93SMark Brown 6242159ad93SMark Brown while (pos < firmware->size && 6252159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 6262159ad93SMark Brown region = (void *)&(firmware->data[pos]); 6272159ad93SMark Brown region_name = "Unknown"; 6282159ad93SMark Brown reg = 0; 6292159ad93SMark Brown text = NULL; 6302159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 6312159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 6322159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 6332159ad93SMark Brown 6342159ad93SMark Brown switch (type) { 6352159ad93SMark Brown case WMFW_NAME_TEXT: 6362159ad93SMark Brown region_name = "Firmware name"; 6372159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 6382159ad93SMark Brown GFP_KERNEL); 6392159ad93SMark Brown break; 6402159ad93SMark Brown case WMFW_INFO_TEXT: 6412159ad93SMark Brown region_name = "Information"; 6422159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 6432159ad93SMark Brown GFP_KERNEL); 6442159ad93SMark Brown break; 6452159ad93SMark Brown case WMFW_ABSOLUTE: 6462159ad93SMark Brown region_name = "Absolute"; 6472159ad93SMark Brown reg = offset; 6482159ad93SMark Brown break; 6492159ad93SMark Brown case WMFW_ADSP1_PM: 6502159ad93SMark Brown region_name = "PM"; 65145b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6522159ad93SMark Brown break; 6532159ad93SMark Brown case WMFW_ADSP1_DM: 6542159ad93SMark Brown region_name = "DM"; 65545b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6562159ad93SMark Brown break; 6572159ad93SMark Brown case WMFW_ADSP2_XM: 6582159ad93SMark Brown region_name = "XM"; 65945b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6602159ad93SMark Brown break; 6612159ad93SMark Brown case WMFW_ADSP2_YM: 6622159ad93SMark Brown region_name = "YM"; 66345b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6642159ad93SMark Brown break; 6652159ad93SMark Brown case WMFW_ADSP1_ZM: 6662159ad93SMark Brown region_name = "ZM"; 66745b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6682159ad93SMark Brown break; 6692159ad93SMark Brown default: 6702159ad93SMark Brown adsp_warn(dsp, 6712159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 6722159ad93SMark Brown file, regions, type, pos, pos); 6732159ad93SMark Brown break; 6742159ad93SMark Brown } 6752159ad93SMark Brown 6762159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 6772159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 6782159ad93SMark Brown region_name); 6792159ad93SMark Brown 6802159ad93SMark Brown if (text) { 6812159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 6822159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 6832159ad93SMark Brown kfree(text); 6842159ad93SMark Brown } 6852159ad93SMark Brown 6862159ad93SMark Brown if (reg) { 687c1a7898dSCharles Keepax size_t to_write = PAGE_SIZE; 688c1a7898dSCharles Keepax size_t remain = le32_to_cpu(region->len); 689c1a7898dSCharles Keepax const u8 *data = region->data; 690c1a7898dSCharles Keepax 691c1a7898dSCharles Keepax while (remain > 0) { 692c1a7898dSCharles Keepax if (remain < PAGE_SIZE) 693c1a7898dSCharles Keepax to_write = remain; 694c1a7898dSCharles Keepax 695c1a7898dSCharles Keepax buf = wm_adsp_buf_alloc(data, 696c1a7898dSCharles Keepax to_write, 697cf17c83cSMark Brown &buf_list); 698a76fefabSMark Brown if (!buf) { 699a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 7007328823dSDimitris Papastamos ret = -ENOMEM; 7017328823dSDimitris Papastamos goto out_fw; 702a76fefabSMark Brown } 703a76fefabSMark Brown 704c1a7898dSCharles Keepax ret = regmap_raw_write_async(regmap, reg, 705c1a7898dSCharles Keepax buf->buf, 706c1a7898dSCharles Keepax to_write); 7072159ad93SMark Brown if (ret != 0) { 7082159ad93SMark Brown adsp_err(dsp, 709fab800ccSCharles Keepax "%s.%d: Failed to write %zd bytes at %d in %s: %d\n", 7102159ad93SMark Brown file, regions, 711c1a7898dSCharles Keepax to_write, offset, 7122159ad93SMark Brown region_name, ret); 7132159ad93SMark Brown goto out_fw; 7142159ad93SMark Brown } 715c1a7898dSCharles Keepax 716c1a7898dSCharles Keepax data += to_write; 717c1a7898dSCharles Keepax reg += to_write / 2; 718c1a7898dSCharles Keepax remain -= to_write; 719c1a7898dSCharles Keepax } 7202159ad93SMark Brown } 7212159ad93SMark Brown 7222159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 7232159ad93SMark Brown regions++; 7242159ad93SMark Brown } 7252159ad93SMark Brown 726cf17c83cSMark Brown ret = regmap_async_complete(regmap); 727cf17c83cSMark Brown if (ret != 0) { 728cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 729cf17c83cSMark Brown goto out_fw; 730cf17c83cSMark Brown } 731cf17c83cSMark Brown 7322159ad93SMark Brown if (pos > firmware->size) 7332159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 7342159ad93SMark Brown file, regions, pos - firmware->size); 7352159ad93SMark Brown 7362159ad93SMark Brown out_fw: 737cf17c83cSMark Brown regmap_async_complete(regmap); 738cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 7392159ad93SMark Brown release_firmware(firmware); 7402159ad93SMark Brown out: 7412159ad93SMark Brown kfree(file); 7422159ad93SMark Brown 7432159ad93SMark Brown return ret; 7442159ad93SMark Brown } 7452159ad93SMark Brown 74681ad93ecSDimitris Papastamos static int wm_coeff_init_control_caches(struct wm_adsp *adsp) 7476ab2b7b4SDimitris Papastamos { 7486ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 7496ab2b7b4SDimitris Papastamos int ret; 7506ab2b7b4SDimitris Papastamos 75181ad93ecSDimitris Papastamos list_for_each_entry(ctl, &adsp->ctl_list, list) { 7520c2e3f34SDimitris Papastamos if (!ctl->enabled || ctl->set) 7536ab2b7b4SDimitris Papastamos continue; 7546ab2b7b4SDimitris Papastamos ret = wm_coeff_read_control(ctl->kcontrol, 7556ab2b7b4SDimitris Papastamos ctl->cache, 7566ab2b7b4SDimitris Papastamos ctl->len); 7576ab2b7b4SDimitris Papastamos if (ret < 0) 7586ab2b7b4SDimitris Papastamos return ret; 7596ab2b7b4SDimitris Papastamos } 7606ab2b7b4SDimitris Papastamos 7616ab2b7b4SDimitris Papastamos return 0; 7626ab2b7b4SDimitris Papastamos } 7636ab2b7b4SDimitris Papastamos 76481ad93ecSDimitris Papastamos static int wm_coeff_sync_controls(struct wm_adsp *adsp) 7656ab2b7b4SDimitris Papastamos { 7666ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 7676ab2b7b4SDimitris Papastamos int ret; 7686ab2b7b4SDimitris Papastamos 76981ad93ecSDimitris Papastamos list_for_each_entry(ctl, &adsp->ctl_list, list) { 7706ab2b7b4SDimitris Papastamos if (!ctl->enabled) 7716ab2b7b4SDimitris Papastamos continue; 7720c2e3f34SDimitris Papastamos if (ctl->set) { 7736ab2b7b4SDimitris Papastamos ret = wm_coeff_write_control(ctl->kcontrol, 7746ab2b7b4SDimitris Papastamos ctl->cache, 7756ab2b7b4SDimitris Papastamos ctl->len); 7766ab2b7b4SDimitris Papastamos if (ret < 0) 7776ab2b7b4SDimitris Papastamos return ret; 7786ab2b7b4SDimitris Papastamos } 7796ab2b7b4SDimitris Papastamos } 7806ab2b7b4SDimitris Papastamos 7816ab2b7b4SDimitris Papastamos return 0; 7826ab2b7b4SDimitris Papastamos } 7836ab2b7b4SDimitris Papastamos 7846ab2b7b4SDimitris Papastamos static void wm_adsp_ctl_work(struct work_struct *work) 7856ab2b7b4SDimitris Papastamos { 7866ab2b7b4SDimitris Papastamos struct wmfw_ctl_work *ctl_work = container_of(work, 7876ab2b7b4SDimitris Papastamos struct wmfw_ctl_work, 7886ab2b7b4SDimitris Papastamos work); 7896ab2b7b4SDimitris Papastamos 79081ad93ecSDimitris Papastamos wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl); 7916ab2b7b4SDimitris Papastamos kfree(ctl_work); 7926ab2b7b4SDimitris Papastamos } 7936ab2b7b4SDimitris Papastamos 79492bb4c32SDimitris Papastamos static int wm_adsp_create_control(struct wm_adsp *dsp, 7956ab2b7b4SDimitris Papastamos const struct wm_adsp_alg_region *region) 7966ab2b7b4SDimitris Papastamos 7976ab2b7b4SDimitris Papastamos { 7986ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 7996ab2b7b4SDimitris Papastamos struct wmfw_ctl_work *ctl_work; 8006ab2b7b4SDimitris Papastamos char *name; 8016ab2b7b4SDimitris Papastamos char *region_name; 8026ab2b7b4SDimitris Papastamos int ret; 8036ab2b7b4SDimitris Papastamos 8046ab2b7b4SDimitris Papastamos name = kmalloc(PAGE_SIZE, GFP_KERNEL); 8056ab2b7b4SDimitris Papastamos if (!name) 8066ab2b7b4SDimitris Papastamos return -ENOMEM; 8076ab2b7b4SDimitris Papastamos 8086ab2b7b4SDimitris Papastamos switch (region->type) { 8096ab2b7b4SDimitris Papastamos case WMFW_ADSP1_PM: 8106ab2b7b4SDimitris Papastamos region_name = "PM"; 8116ab2b7b4SDimitris Papastamos break; 8126ab2b7b4SDimitris Papastamos case WMFW_ADSP1_DM: 8136ab2b7b4SDimitris Papastamos region_name = "DM"; 8146ab2b7b4SDimitris Papastamos break; 8156ab2b7b4SDimitris Papastamos case WMFW_ADSP2_XM: 8166ab2b7b4SDimitris Papastamos region_name = "XM"; 8176ab2b7b4SDimitris Papastamos break; 8186ab2b7b4SDimitris Papastamos case WMFW_ADSP2_YM: 8196ab2b7b4SDimitris Papastamos region_name = "YM"; 8206ab2b7b4SDimitris Papastamos break; 8216ab2b7b4SDimitris Papastamos case WMFW_ADSP1_ZM: 8226ab2b7b4SDimitris Papastamos region_name = "ZM"; 8236ab2b7b4SDimitris Papastamos break; 8246ab2b7b4SDimitris Papastamos default: 8259dbce044SDan Carpenter ret = -EINVAL; 8269dbce044SDan Carpenter goto err_name; 8276ab2b7b4SDimitris Papastamos } 8286ab2b7b4SDimitris Papastamos 8296ab2b7b4SDimitris Papastamos snprintf(name, PAGE_SIZE, "DSP%d %s %x", 8306ab2b7b4SDimitris Papastamos dsp->num, region_name, region->alg); 8316ab2b7b4SDimitris Papastamos 83281ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, 8336ab2b7b4SDimitris Papastamos list) { 8346ab2b7b4SDimitris Papastamos if (!strcmp(ctl->name, name)) { 8356ab2b7b4SDimitris Papastamos if (!ctl->enabled) 8366ab2b7b4SDimitris Papastamos ctl->enabled = 1; 8379dbce044SDan Carpenter goto found; 8386ab2b7b4SDimitris Papastamos } 8396ab2b7b4SDimitris Papastamos } 8406ab2b7b4SDimitris Papastamos 8416ab2b7b4SDimitris Papastamos ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 8426ab2b7b4SDimitris Papastamos if (!ctl) { 8436ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8446ab2b7b4SDimitris Papastamos goto err_name; 8456ab2b7b4SDimitris Papastamos } 8466ab2b7b4SDimitris Papastamos ctl->region = *region; 8476ab2b7b4SDimitris Papastamos ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 8486ab2b7b4SDimitris Papastamos if (!ctl->name) { 8496ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8506ab2b7b4SDimitris Papastamos goto err_ctl; 8516ab2b7b4SDimitris Papastamos } 8526ab2b7b4SDimitris Papastamos ctl->enabled = 1; 8530c2e3f34SDimitris Papastamos ctl->set = 0; 8546ab2b7b4SDimitris Papastamos ctl->ops.xget = wm_coeff_get; 8556ab2b7b4SDimitris Papastamos ctl->ops.xput = wm_coeff_put; 8566ab2b7b4SDimitris Papastamos ctl->adsp = dsp; 8576ab2b7b4SDimitris Papastamos 8586ab2b7b4SDimitris Papastamos ctl->len = region->len; 8596ab2b7b4SDimitris Papastamos ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 8606ab2b7b4SDimitris Papastamos if (!ctl->cache) { 8616ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8626ab2b7b4SDimitris Papastamos goto err_ctl_name; 8636ab2b7b4SDimitris Papastamos } 8646ab2b7b4SDimitris Papastamos 8656ab2b7b4SDimitris Papastamos ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 8666ab2b7b4SDimitris Papastamos if (!ctl_work) { 8676ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8686ab2b7b4SDimitris Papastamos goto err_ctl_cache; 8696ab2b7b4SDimitris Papastamos } 8706ab2b7b4SDimitris Papastamos 87181ad93ecSDimitris Papastamos ctl_work->adsp = dsp; 8726ab2b7b4SDimitris Papastamos ctl_work->ctl = ctl; 8736ab2b7b4SDimitris Papastamos INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 8746ab2b7b4SDimitris Papastamos schedule_work(&ctl_work->work); 8756ab2b7b4SDimitris Papastamos 8769dbce044SDan Carpenter found: 8776ab2b7b4SDimitris Papastamos kfree(name); 8786ab2b7b4SDimitris Papastamos 8796ab2b7b4SDimitris Papastamos return 0; 8806ab2b7b4SDimitris Papastamos 8816ab2b7b4SDimitris Papastamos err_ctl_cache: 8826ab2b7b4SDimitris Papastamos kfree(ctl->cache); 8836ab2b7b4SDimitris Papastamos err_ctl_name: 8846ab2b7b4SDimitris Papastamos kfree(ctl->name); 8856ab2b7b4SDimitris Papastamos err_ctl: 8866ab2b7b4SDimitris Papastamos kfree(ctl); 8876ab2b7b4SDimitris Papastamos err_name: 8886ab2b7b4SDimitris Papastamos kfree(name); 8896ab2b7b4SDimitris Papastamos return ret; 8906ab2b7b4SDimitris Papastamos } 8916ab2b7b4SDimitris Papastamos 89292bb4c32SDimitris Papastamos static int wm_adsp_setup_algs(struct wm_adsp *dsp) 893db40517cSMark Brown { 894db40517cSMark Brown struct regmap *regmap = dsp->regmap; 895db40517cSMark Brown struct wmfw_adsp1_id_hdr adsp1_id; 896db40517cSMark Brown struct wmfw_adsp2_id_hdr adsp2_id; 897db40517cSMark Brown struct wmfw_adsp1_alg_hdr *adsp1_alg; 898db40517cSMark Brown struct wmfw_adsp2_alg_hdr *adsp2_alg; 899d62f4bc6SMark Brown void *alg, *buf; 900471f4885SMark Brown struct wm_adsp_alg_region *region; 901db40517cSMark Brown const struct wm_adsp_region *mem; 902db40517cSMark Brown unsigned int pos, term; 903d62f4bc6SMark Brown size_t algs, buf_size; 904db40517cSMark Brown __be32 val; 905db40517cSMark Brown int i, ret; 906db40517cSMark Brown 907db40517cSMark Brown switch (dsp->type) { 908db40517cSMark Brown case WMFW_ADSP1: 909db40517cSMark Brown mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 910db40517cSMark Brown break; 911db40517cSMark Brown case WMFW_ADSP2: 912db40517cSMark Brown mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 913db40517cSMark Brown break; 914db40517cSMark Brown default: 915db40517cSMark Brown mem = NULL; 916db40517cSMark Brown break; 917db40517cSMark Brown } 918db40517cSMark Brown 9196c452bdaSTakashi Iwai if (WARN_ON(!mem)) 920db40517cSMark Brown return -EINVAL; 921db40517cSMark Brown 922db40517cSMark Brown switch (dsp->type) { 923db40517cSMark Brown case WMFW_ADSP1: 924db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base, &adsp1_id, 925db40517cSMark Brown sizeof(adsp1_id)); 926db40517cSMark Brown if (ret != 0) { 927db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 928db40517cSMark Brown ret); 929db40517cSMark Brown return ret; 930db40517cSMark Brown } 931db40517cSMark Brown 932d62f4bc6SMark Brown buf = &adsp1_id; 933d62f4bc6SMark Brown buf_size = sizeof(adsp1_id); 934d62f4bc6SMark Brown 935db40517cSMark Brown algs = be32_to_cpu(adsp1_id.algs); 936f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 937db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 938f395a218SMark Brown dsp->fw_id, 939db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 940db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 941db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 942db40517cSMark Brown algs); 943db40517cSMark Brown 944ac50009fSMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 945ac50009fSMark Brown if (!region) 946ac50009fSMark Brown return -ENOMEM; 947ac50009fSMark Brown region->type = WMFW_ADSP1_ZM; 948ac50009fSMark Brown region->alg = be32_to_cpu(adsp1_id.fw.id); 949ac50009fSMark Brown region->base = be32_to_cpu(adsp1_id.zm); 950ac50009fSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 951ac50009fSMark Brown 952ac50009fSMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 953ac50009fSMark Brown if (!region) 954ac50009fSMark Brown return -ENOMEM; 955ac50009fSMark Brown region->type = WMFW_ADSP1_DM; 956ac50009fSMark Brown region->alg = be32_to_cpu(adsp1_id.fw.id); 957ac50009fSMark Brown region->base = be32_to_cpu(adsp1_id.dm); 958ac50009fSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 959ac50009fSMark Brown 960db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 961db40517cSMark Brown term = pos + ((sizeof(*adsp1_alg) * algs) / 2); 962db40517cSMark Brown break; 963db40517cSMark Brown 964db40517cSMark Brown case WMFW_ADSP2: 965db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base, &adsp2_id, 966db40517cSMark Brown sizeof(adsp2_id)); 967db40517cSMark Brown if (ret != 0) { 968db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 969db40517cSMark Brown ret); 970db40517cSMark Brown return ret; 971db40517cSMark Brown } 972db40517cSMark Brown 973d62f4bc6SMark Brown buf = &adsp2_id; 974d62f4bc6SMark Brown buf_size = sizeof(adsp2_id); 975d62f4bc6SMark Brown 976db40517cSMark Brown algs = be32_to_cpu(adsp2_id.algs); 977f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 978db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 979f395a218SMark Brown dsp->fw_id, 980db40517cSMark Brown (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, 981db40517cSMark Brown (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, 982db40517cSMark Brown be32_to_cpu(adsp2_id.fw.ver) & 0xff, 983db40517cSMark Brown algs); 984db40517cSMark Brown 985ac50009fSMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 986ac50009fSMark Brown if (!region) 987ac50009fSMark Brown return -ENOMEM; 988ac50009fSMark Brown region->type = WMFW_ADSP2_XM; 989ac50009fSMark Brown region->alg = be32_to_cpu(adsp2_id.fw.id); 990ac50009fSMark Brown region->base = be32_to_cpu(adsp2_id.xm); 991ac50009fSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 992ac50009fSMark Brown 993ac50009fSMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 994ac50009fSMark Brown if (!region) 995ac50009fSMark Brown return -ENOMEM; 996ac50009fSMark Brown region->type = WMFW_ADSP2_YM; 997ac50009fSMark Brown region->alg = be32_to_cpu(adsp2_id.fw.id); 998ac50009fSMark Brown region->base = be32_to_cpu(adsp2_id.ym); 999ac50009fSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 1000ac50009fSMark Brown 1001ac50009fSMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 1002ac50009fSMark Brown if (!region) 1003ac50009fSMark Brown return -ENOMEM; 1004ac50009fSMark Brown region->type = WMFW_ADSP2_ZM; 1005ac50009fSMark Brown region->alg = be32_to_cpu(adsp2_id.fw.id); 1006ac50009fSMark Brown region->base = be32_to_cpu(adsp2_id.zm); 1007ac50009fSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 1008ac50009fSMark Brown 1009db40517cSMark Brown pos = sizeof(adsp2_id) / 2; 1010db40517cSMark Brown term = pos + ((sizeof(*adsp2_alg) * algs) / 2); 1011db40517cSMark Brown break; 1012db40517cSMark Brown 1013db40517cSMark Brown default: 10146c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 1015db40517cSMark Brown return -EINVAL; 1016db40517cSMark Brown } 1017db40517cSMark Brown 1018db40517cSMark Brown if (algs == 0) { 1019db40517cSMark Brown adsp_err(dsp, "No algorithms\n"); 1020db40517cSMark Brown return -EINVAL; 1021db40517cSMark Brown } 1022db40517cSMark Brown 1023d62f4bc6SMark Brown if (algs > 1024) { 1024d62f4bc6SMark Brown adsp_err(dsp, "Algorithm count %zx excessive\n", algs); 1025d62f4bc6SMark Brown print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, 1026d62f4bc6SMark Brown buf, buf_size); 1027d62f4bc6SMark Brown return -EINVAL; 1028d62f4bc6SMark Brown } 1029d62f4bc6SMark Brown 1030db40517cSMark Brown /* Read the terminator first to validate the length */ 1031db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); 1032db40517cSMark Brown if (ret != 0) { 1033db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1034db40517cSMark Brown ret); 1035db40517cSMark Brown return ret; 1036db40517cSMark Brown } 1037db40517cSMark Brown 1038db40517cSMark Brown if (be32_to_cpu(val) != 0xbedead) 1039db40517cSMark Brown adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 1040db40517cSMark Brown term, be32_to_cpu(val)); 1041db40517cSMark Brown 1042f2a93e2aSMark Brown alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); 1043db40517cSMark Brown if (!alg) 1044db40517cSMark Brown return -ENOMEM; 1045db40517cSMark Brown 1046db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); 1047db40517cSMark Brown if (ret != 0) { 1048db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm list: %d\n", 1049db40517cSMark Brown ret); 1050db40517cSMark Brown goto out; 1051db40517cSMark Brown } 1052db40517cSMark Brown 1053db40517cSMark Brown adsp1_alg = alg; 1054db40517cSMark Brown adsp2_alg = alg; 1055db40517cSMark Brown 1056db40517cSMark Brown for (i = 0; i < algs; i++) { 1057db40517cSMark Brown switch (dsp->type) { 1058db40517cSMark Brown case WMFW_ADSP1: 1059471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1060db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 1061db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1062db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1063471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1064471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 1065471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 1066471f4885SMark Brown 1067471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 1068471f4885SMark Brown if (!region) 1069471f4885SMark Brown return -ENOMEM; 1070471f4885SMark Brown region->type = WMFW_ADSP1_DM; 1071471f4885SMark Brown region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 1072471f4885SMark Brown region->base = be32_to_cpu(adsp1_alg[i].dm); 10736ab2b7b4SDimitris Papastamos region->len = 0; 10747480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 10756ab2b7b4SDimitris Papastamos if (i + 1 < algs) { 10766ab2b7b4SDimitris Papastamos region->len = be32_to_cpu(adsp1_alg[i + 1].dm); 10776ab2b7b4SDimitris Papastamos region->len -= be32_to_cpu(adsp1_alg[i].dm); 1078c01422a4SNariman Poushin region->len *= 4; 107992bb4c32SDimitris Papastamos wm_adsp_create_control(dsp, region); 10806ab2b7b4SDimitris Papastamos } else { 10816ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 10826ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 10836ab2b7b4SDimitris Papastamos } 1084471f4885SMark Brown 1085471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 1086471f4885SMark Brown if (!region) 1087471f4885SMark Brown return -ENOMEM; 1088471f4885SMark Brown region->type = WMFW_ADSP1_ZM; 1089471f4885SMark Brown region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 1090471f4885SMark Brown region->base = be32_to_cpu(adsp1_alg[i].zm); 10916ab2b7b4SDimitris Papastamos region->len = 0; 10927480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 10936ab2b7b4SDimitris Papastamos if (i + 1 < algs) { 10946ab2b7b4SDimitris Papastamos region->len = be32_to_cpu(adsp1_alg[i + 1].zm); 10956ab2b7b4SDimitris Papastamos region->len -= be32_to_cpu(adsp1_alg[i].zm); 1096c01422a4SNariman Poushin region->len *= 4; 109792bb4c32SDimitris Papastamos wm_adsp_create_control(dsp, region); 10986ab2b7b4SDimitris Papastamos } else { 10996ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 11006ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 11016ab2b7b4SDimitris Papastamos } 1102db40517cSMark Brown break; 1103db40517cSMark Brown 1104db40517cSMark Brown case WMFW_ADSP2: 1105471f4885SMark Brown adsp_info(dsp, 1106471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1107db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 1108db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1109db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1110471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1111471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 1112471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 1113471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 1114471f4885SMark Brown 1115471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 1116471f4885SMark Brown if (!region) 1117471f4885SMark Brown return -ENOMEM; 1118471f4885SMark Brown region->type = WMFW_ADSP2_XM; 1119471f4885SMark Brown region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 1120471f4885SMark Brown region->base = be32_to_cpu(adsp2_alg[i].xm); 11216ab2b7b4SDimitris Papastamos region->len = 0; 11227480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 11236ab2b7b4SDimitris Papastamos if (i + 1 < algs) { 11246ab2b7b4SDimitris Papastamos region->len = be32_to_cpu(adsp2_alg[i + 1].xm); 11256ab2b7b4SDimitris Papastamos region->len -= be32_to_cpu(adsp2_alg[i].xm); 1126c01422a4SNariman Poushin region->len *= 4; 112792bb4c32SDimitris Papastamos wm_adsp_create_control(dsp, region); 11286ab2b7b4SDimitris Papastamos } else { 11296ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 11306ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 11316ab2b7b4SDimitris Papastamos } 1132471f4885SMark Brown 1133471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 1134471f4885SMark Brown if (!region) 1135471f4885SMark Brown return -ENOMEM; 1136471f4885SMark Brown region->type = WMFW_ADSP2_YM; 1137471f4885SMark Brown region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 1138471f4885SMark Brown region->base = be32_to_cpu(adsp2_alg[i].ym); 11396ab2b7b4SDimitris Papastamos region->len = 0; 11407480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 11416ab2b7b4SDimitris Papastamos if (i + 1 < algs) { 11426ab2b7b4SDimitris Papastamos region->len = be32_to_cpu(adsp2_alg[i + 1].ym); 11436ab2b7b4SDimitris Papastamos region->len -= be32_to_cpu(adsp2_alg[i].ym); 1144c01422a4SNariman Poushin region->len *= 4; 114592bb4c32SDimitris Papastamos wm_adsp_create_control(dsp, region); 11466ab2b7b4SDimitris Papastamos } else { 11476ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 11486ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 11496ab2b7b4SDimitris Papastamos } 1150471f4885SMark Brown 1151471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 1152471f4885SMark Brown if (!region) 1153471f4885SMark Brown return -ENOMEM; 1154471f4885SMark Brown region->type = WMFW_ADSP2_ZM; 1155471f4885SMark Brown region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 1156471f4885SMark Brown region->base = be32_to_cpu(adsp2_alg[i].zm); 11576ab2b7b4SDimitris Papastamos region->len = 0; 11587480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 11596ab2b7b4SDimitris Papastamos if (i + 1 < algs) { 11606ab2b7b4SDimitris Papastamos region->len = be32_to_cpu(adsp2_alg[i + 1].zm); 11616ab2b7b4SDimitris Papastamos region->len -= be32_to_cpu(adsp2_alg[i].zm); 1162c01422a4SNariman Poushin region->len *= 4; 116392bb4c32SDimitris Papastamos wm_adsp_create_control(dsp, region); 11646ab2b7b4SDimitris Papastamos } else { 11656ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 11666ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 11676ab2b7b4SDimitris Papastamos } 1168db40517cSMark Brown break; 1169db40517cSMark Brown } 1170db40517cSMark Brown } 1171db40517cSMark Brown 1172db40517cSMark Brown out: 1173db40517cSMark Brown kfree(alg); 1174db40517cSMark Brown return ret; 1175db40517cSMark Brown } 1176db40517cSMark Brown 11772159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 11782159ad93SMark Brown { 1179cf17c83cSMark Brown LIST_HEAD(buf_list); 11802159ad93SMark Brown struct regmap *regmap = dsp->regmap; 11812159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 11822159ad93SMark Brown struct wmfw_coeff_item *blk; 11832159ad93SMark Brown const struct firmware *firmware; 1184471f4885SMark Brown const struct wm_adsp_region *mem; 1185471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 11862159ad93SMark Brown const char *region_name; 11872159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 11882159ad93SMark Brown char *file; 1189cf17c83cSMark Brown struct wm_adsp_buf *buf; 1190bdaacea3SChris Rattray int tmp; 11912159ad93SMark Brown 11922159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 11932159ad93SMark Brown if (file == NULL) 11942159ad93SMark Brown return -ENOMEM; 11952159ad93SMark Brown 11961023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 11971023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 11982159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 11992159ad93SMark Brown 12002159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 12012159ad93SMark Brown if (ret != 0) { 12022159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 12032159ad93SMark Brown ret = 0; 12042159ad93SMark Brown goto out; 12052159ad93SMark Brown } 12062159ad93SMark Brown ret = -EINVAL; 12072159ad93SMark Brown 12082159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 12092159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 12102159ad93SMark Brown file, firmware->size); 12112159ad93SMark Brown goto out_fw; 12122159ad93SMark Brown } 12132159ad93SMark Brown 12142159ad93SMark Brown hdr = (void*)&firmware->data[0]; 12152159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 12162159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 1217a4cdbec7SCharles Keepax goto out_fw; 12182159ad93SMark Brown } 12192159ad93SMark Brown 1220c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 1221c712326dSMark Brown case 1: 1222c712326dSMark Brown break; 1223c712326dSMark Brown default: 1224c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 1225c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 1226c712326dSMark Brown ret = -EINVAL; 1227c712326dSMark Brown goto out_fw; 1228c712326dSMark Brown } 1229c712326dSMark Brown 12302159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 12312159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 12322159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 12332159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 12342159ad93SMark Brown 12352159ad93SMark Brown pos = le32_to_cpu(hdr->len); 12362159ad93SMark Brown 12372159ad93SMark Brown blocks = 0; 12382159ad93SMark Brown while (pos < firmware->size && 12392159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 12402159ad93SMark Brown blk = (void*)(&firmware->data[pos]); 12412159ad93SMark Brown 1242c712326dSMark Brown type = le16_to_cpu(blk->type); 1243c712326dSMark Brown offset = le16_to_cpu(blk->offset); 12442159ad93SMark Brown 12452159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 12462159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 12472159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 12482159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 12492159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 12502159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 12512159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 12522159ad93SMark Brown 12532159ad93SMark Brown reg = 0; 12542159ad93SMark Brown region_name = "Unknown"; 12552159ad93SMark Brown switch (type) { 1256c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 1257c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 12582159ad93SMark Brown break; 1259c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 1260f395a218SMark Brown /* 1261f395a218SMark Brown * Old files may use this for global 1262f395a218SMark Brown * coefficients. 1263f395a218SMark Brown */ 1264f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 1265f395a218SMark Brown offset == 0) { 1266f395a218SMark Brown region_name = "global coefficients"; 1267f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 1268f395a218SMark Brown if (!mem) { 1269f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 1270f395a218SMark Brown break; 1271f395a218SMark Brown } 1272f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 1273f395a218SMark Brown 1274f395a218SMark Brown } else { 12752159ad93SMark Brown region_name = "register"; 12762159ad93SMark Brown reg = offset; 1277f395a218SMark Brown } 12782159ad93SMark Brown break; 1279471f4885SMark Brown 1280471f4885SMark Brown case WMFW_ADSP1_DM: 1281471f4885SMark Brown case WMFW_ADSP1_ZM: 1282471f4885SMark Brown case WMFW_ADSP2_XM: 1283471f4885SMark Brown case WMFW_ADSP2_YM: 1284471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 1285471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 1286471f4885SMark Brown type, le32_to_cpu(blk->id)); 1287471f4885SMark Brown 1288471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 1289471f4885SMark Brown if (!mem) { 1290471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 1291471f4885SMark Brown break; 1292471f4885SMark Brown } 1293471f4885SMark Brown 1294471f4885SMark Brown reg = 0; 1295471f4885SMark Brown list_for_each_entry(alg_region, 1296471f4885SMark Brown &dsp->alg_regions, list) { 1297471f4885SMark Brown if (le32_to_cpu(blk->id) == alg_region->alg && 1298471f4885SMark Brown type == alg_region->type) { 1299338c5188SMark Brown reg = alg_region->base; 1300471f4885SMark Brown reg = wm_adsp_region_to_reg(mem, 1301471f4885SMark Brown reg); 1302338c5188SMark Brown reg += offset; 1303d733dc08SCharles Keepax break; 1304471f4885SMark Brown } 1305471f4885SMark Brown } 1306471f4885SMark Brown 1307471f4885SMark Brown if (reg == 0) 1308471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 1309471f4885SMark Brown type, le32_to_cpu(blk->id)); 1310471f4885SMark Brown break; 1311471f4885SMark Brown 13122159ad93SMark Brown default: 131325c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 131425c62f7eSMark Brown file, blocks, type, pos); 13152159ad93SMark Brown break; 13162159ad93SMark Brown } 13172159ad93SMark Brown 13182159ad93SMark Brown if (reg) { 1319cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 1320cf17c83cSMark Brown le32_to_cpu(blk->len), 1321cf17c83cSMark Brown &buf_list); 1322a76fefabSMark Brown if (!buf) { 1323a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 1324f4b82812SWei Yongjun ret = -ENOMEM; 1325f4b82812SWei Yongjun goto out_fw; 1326a76fefabSMark Brown } 1327a76fefabSMark Brown 132820da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 132920da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 133020da6d5aSMark Brown reg); 1331cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 13322159ad93SMark Brown le32_to_cpu(blk->len)); 13332159ad93SMark Brown if (ret != 0) { 13342159ad93SMark Brown adsp_err(dsp, 133543bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 133643bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 13372159ad93SMark Brown } 13382159ad93SMark Brown } 13392159ad93SMark Brown 1340bdaacea3SChris Rattray tmp = le32_to_cpu(blk->len) % 4; 1341bdaacea3SChris Rattray if (tmp) 1342bdaacea3SChris Rattray pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); 1343bdaacea3SChris Rattray else 13442159ad93SMark Brown pos += le32_to_cpu(blk->len) + sizeof(*blk); 1345bdaacea3SChris Rattray 13462159ad93SMark Brown blocks++; 13472159ad93SMark Brown } 13482159ad93SMark Brown 1349cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1350cf17c83cSMark Brown if (ret != 0) 1351cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1352cf17c83cSMark Brown 13532159ad93SMark Brown if (pos > firmware->size) 13542159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 13552159ad93SMark Brown file, blocks, pos - firmware->size); 13562159ad93SMark Brown 13572159ad93SMark Brown out_fw: 13582159ad93SMark Brown release_firmware(firmware); 1359cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 13602159ad93SMark Brown out: 13612159ad93SMark Brown kfree(file); 1362f4b82812SWei Yongjun return ret; 13632159ad93SMark Brown } 13642159ad93SMark Brown 13655e7a7a22SMark Brown int wm_adsp1_init(struct wm_adsp *adsp) 13665e7a7a22SMark Brown { 13675e7a7a22SMark Brown INIT_LIST_HEAD(&adsp->alg_regions); 13685e7a7a22SMark Brown 13695e7a7a22SMark Brown return 0; 13705e7a7a22SMark Brown } 13715e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 13725e7a7a22SMark Brown 13732159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 13742159ad93SMark Brown struct snd_kcontrol *kcontrol, 13752159ad93SMark Brown int event) 13762159ad93SMark Brown { 13772159ad93SMark Brown struct snd_soc_codec *codec = w->codec; 13782159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 13792159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 1380b0101b4fSDimitris Papastamos struct wm_adsp_alg_region *alg_region; 13816ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 13822159ad93SMark Brown int ret; 138394e205bfSChris Rattray int val; 13842159ad93SMark Brown 138592bb4c32SDimitris Papastamos dsp->card = codec->card; 138692bb4c32SDimitris Papastamos 13872159ad93SMark Brown switch (event) { 13882159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 13892159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 13902159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 13912159ad93SMark Brown 139294e205bfSChris Rattray /* 139394e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 139494e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 139594e205bfSChris Rattray */ 139694e205bfSChris Rattray if(dsp->sysclk_reg) { 139794e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 139894e205bfSChris Rattray if (ret != 0) { 139994e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 140094e205bfSChris Rattray ret); 140194e205bfSChris Rattray return ret; 140294e205bfSChris Rattray } 140394e205bfSChris Rattray 140494e205bfSChris Rattray val = (val & dsp->sysclk_mask) 140594e205bfSChris Rattray >> dsp->sysclk_shift; 140694e205bfSChris Rattray 140794e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 140894e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 140994e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 141094e205bfSChris Rattray if (ret != 0) { 141194e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 141294e205bfSChris Rattray ret); 141394e205bfSChris Rattray return ret; 141494e205bfSChris Rattray } 141594e205bfSChris Rattray } 141694e205bfSChris Rattray 14172159ad93SMark Brown ret = wm_adsp_load(dsp); 14182159ad93SMark Brown if (ret != 0) 14192159ad93SMark Brown goto err; 14202159ad93SMark Brown 142192bb4c32SDimitris Papastamos ret = wm_adsp_setup_algs(dsp); 1422db40517cSMark Brown if (ret != 0) 1423db40517cSMark Brown goto err; 1424db40517cSMark Brown 14252159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 14262159ad93SMark Brown if (ret != 0) 14272159ad93SMark Brown goto err; 14282159ad93SMark Brown 14290c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 143081ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 14316ab2b7b4SDimitris Papastamos if (ret != 0) 14326ab2b7b4SDimitris Papastamos goto err; 14336ab2b7b4SDimitris Papastamos 14340c2e3f34SDimitris Papastamos /* Sync set controls */ 143581ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 14366ab2b7b4SDimitris Papastamos if (ret != 0) 14376ab2b7b4SDimitris Papastamos goto err; 14386ab2b7b4SDimitris Papastamos 14392159ad93SMark Brown /* Start the core running */ 14402159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14412159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 14422159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 14432159ad93SMark Brown break; 14442159ad93SMark Brown 14452159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 14462159ad93SMark Brown /* Halt the core */ 14472159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14482159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 14492159ad93SMark Brown 14502159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 14512159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 14522159ad93SMark Brown 14532159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14542159ad93SMark Brown ADSP1_SYS_ENA, 0); 14556ab2b7b4SDimitris Papastamos 145681ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 14576ab2b7b4SDimitris Papastamos ctl->enabled = 0; 1458b0101b4fSDimitris Papastamos 1459b0101b4fSDimitris Papastamos while (!list_empty(&dsp->alg_regions)) { 1460b0101b4fSDimitris Papastamos alg_region = list_first_entry(&dsp->alg_regions, 1461b0101b4fSDimitris Papastamos struct wm_adsp_alg_region, 1462b0101b4fSDimitris Papastamos list); 1463b0101b4fSDimitris Papastamos list_del(&alg_region->list); 1464b0101b4fSDimitris Papastamos kfree(alg_region); 1465b0101b4fSDimitris Papastamos } 14662159ad93SMark Brown break; 14672159ad93SMark Brown 14682159ad93SMark Brown default: 14692159ad93SMark Brown break; 14702159ad93SMark Brown } 14712159ad93SMark Brown 14722159ad93SMark Brown return 0; 14732159ad93SMark Brown 14742159ad93SMark Brown err: 14752159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14762159ad93SMark Brown ADSP1_SYS_ENA, 0); 14772159ad93SMark Brown return ret; 14782159ad93SMark Brown } 14792159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 14802159ad93SMark Brown 14812159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 14822159ad93SMark Brown { 14832159ad93SMark Brown unsigned int val; 14842159ad93SMark Brown int ret, count; 14852159ad93SMark Brown 14861552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 14872159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 14882159ad93SMark Brown if (ret != 0) 14892159ad93SMark Brown return ret; 14902159ad93SMark Brown 14912159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 1492939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 14932159ad93SMark Brown ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, 14942159ad93SMark Brown &val); 14952159ad93SMark Brown if (ret != 0) 14962159ad93SMark Brown return ret; 1497939fd1e8SCharles Keepax 1498939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 1499939fd1e8SCharles Keepax break; 1500939fd1e8SCharles Keepax 1501939fd1e8SCharles Keepax msleep(1); 1502939fd1e8SCharles Keepax } 15032159ad93SMark Brown 15042159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 15052159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 15062159ad93SMark Brown return -EBUSY; 15072159ad93SMark Brown } 15082159ad93SMark Brown 15092159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 15102159ad93SMark Brown 15112159ad93SMark Brown return 0; 15122159ad93SMark Brown } 15132159ad93SMark Brown 151418b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 15152159ad93SMark Brown { 1516d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 1517d8a64d6aSCharles Keepax struct wm_adsp, 1518d8a64d6aSCharles Keepax boot_work); 15192159ad93SMark Brown int ret; 1520d8a64d6aSCharles Keepax unsigned int val; 15212159ad93SMark Brown 1522dd49e2c8SMark Brown /* 1523dd49e2c8SMark Brown * For simplicity set the DSP clock rate to be the 1524dd49e2c8SMark Brown * SYSCLK rate rather than making it configurable. 1525dd49e2c8SMark Brown */ 1526dd49e2c8SMark Brown ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); 1527dd49e2c8SMark Brown if (ret != 0) { 1528d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 1529d8a64d6aSCharles Keepax return; 1530dd49e2c8SMark Brown } 1531dd49e2c8SMark Brown val = (val & ARIZONA_SYSCLK_FREQ_MASK) 1532dd49e2c8SMark Brown >> ARIZONA_SYSCLK_FREQ_SHIFT; 1533dd49e2c8SMark Brown 15341552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, 1535dd49e2c8SMark Brown dsp->base + ADSP2_CLOCKING, 1536dd49e2c8SMark Brown ADSP2_CLK_SEL_MASK, val); 1537dd49e2c8SMark Brown if (ret != 0) { 1538d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 1539d8a64d6aSCharles Keepax return; 1540dd49e2c8SMark Brown } 1541dd49e2c8SMark Brown 1542973838a0SMark Brown if (dsp->dvfs) { 1543973838a0SMark Brown ret = regmap_read(dsp->regmap, 1544973838a0SMark Brown dsp->base + ADSP2_CLOCKING, &val); 1545973838a0SMark Brown if (ret != 0) { 1546d8a64d6aSCharles Keepax dev_err(dsp->dev, "Failed to read clocking: %d\n", ret); 1547d8a64d6aSCharles Keepax return; 1548973838a0SMark Brown } 1549973838a0SMark Brown 155025c6fdb0SMark Brown if ((val & ADSP2_CLK_SEL_MASK) >= 3) { 1551973838a0SMark Brown ret = regulator_enable(dsp->dvfs); 1552973838a0SMark Brown if (ret != 0) { 1553973838a0SMark Brown dev_err(dsp->dev, 1554973838a0SMark Brown "Failed to enable supply: %d\n", 1555973838a0SMark Brown ret); 1556d8a64d6aSCharles Keepax return; 1557973838a0SMark Brown } 1558973838a0SMark Brown 1559973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 1560973838a0SMark Brown 1800000, 1561973838a0SMark Brown 1800000); 1562973838a0SMark Brown if (ret != 0) { 1563973838a0SMark Brown dev_err(dsp->dev, 1564973838a0SMark Brown "Failed to raise supply: %d\n", 1565973838a0SMark Brown ret); 1566d8a64d6aSCharles Keepax return; 1567973838a0SMark Brown } 1568973838a0SMark Brown } 1569973838a0SMark Brown } 1570973838a0SMark Brown 15712159ad93SMark Brown ret = wm_adsp2_ena(dsp); 15722159ad93SMark Brown if (ret != 0) 1573d8a64d6aSCharles Keepax return; 15742159ad93SMark Brown 15752159ad93SMark Brown ret = wm_adsp_load(dsp); 15762159ad93SMark Brown if (ret != 0) 15772159ad93SMark Brown goto err; 15782159ad93SMark Brown 157992bb4c32SDimitris Papastamos ret = wm_adsp_setup_algs(dsp); 1580db40517cSMark Brown if (ret != 0) 1581db40517cSMark Brown goto err; 1582db40517cSMark Brown 15832159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 15842159ad93SMark Brown if (ret != 0) 15852159ad93SMark Brown goto err; 15862159ad93SMark Brown 15870c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 158881ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 15896ab2b7b4SDimitris Papastamos if (ret != 0) 15906ab2b7b4SDimitris Papastamos goto err; 15916ab2b7b4SDimitris Papastamos 15920c2e3f34SDimitris Papastamos /* Sync set controls */ 159381ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 15946ab2b7b4SDimitris Papastamos if (ret != 0) 15956ab2b7b4SDimitris Papastamos goto err; 15966ab2b7b4SDimitris Papastamos 15971552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, 15982159ad93SMark Brown dsp->base + ADSP2_CONTROL, 1599d8a64d6aSCharles Keepax ADSP2_CORE_ENA, 1600d8a64d6aSCharles Keepax ADSP2_CORE_ENA); 16012159ad93SMark Brown if (ret != 0) 16022159ad93SMark Brown goto err; 16031023dbd9SMark Brown 16041023dbd9SMark Brown dsp->running = true; 1605d8a64d6aSCharles Keepax 1606d8a64d6aSCharles Keepax return; 1607d8a64d6aSCharles Keepax 1608d8a64d6aSCharles Keepax err: 1609d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1610d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 1611d8a64d6aSCharles Keepax } 1612d8a64d6aSCharles Keepax 161312db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 161412db5eddSCharles Keepax struct snd_kcontrol *kcontrol, int event) 161512db5eddSCharles Keepax { 161612db5eddSCharles Keepax struct snd_soc_codec *codec = w->codec; 161712db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 161812db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 161912db5eddSCharles Keepax 162012db5eddSCharles Keepax dsp->card = codec->card; 162112db5eddSCharles Keepax 162212db5eddSCharles Keepax switch (event) { 162312db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 162412db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 162512db5eddSCharles Keepax break; 162612db5eddSCharles Keepax default: 162712db5eddSCharles Keepax break; 162812db5eddSCharles Keepax }; 162912db5eddSCharles Keepax 163012db5eddSCharles Keepax return 0; 163112db5eddSCharles Keepax } 163212db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 163312db5eddSCharles Keepax 1634d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 1635d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 1636d8a64d6aSCharles Keepax { 1637d8a64d6aSCharles Keepax struct snd_soc_codec *codec = w->codec; 1638d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 1639d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 1640d8a64d6aSCharles Keepax struct wm_adsp_alg_region *alg_region; 1641d8a64d6aSCharles Keepax struct wm_coeff_ctl *ctl; 1642d8a64d6aSCharles Keepax int ret; 1643d8a64d6aSCharles Keepax 1644d8a64d6aSCharles Keepax switch (event) { 1645d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 1646d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 1647d8a64d6aSCharles Keepax 1648d8a64d6aSCharles Keepax if (!dsp->running) 1649d8a64d6aSCharles Keepax return -EIO; 1650d8a64d6aSCharles Keepax 1651d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 1652d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 1653d8a64d6aSCharles Keepax ADSP2_START, 1654d8a64d6aSCharles Keepax ADSP2_START); 1655d8a64d6aSCharles Keepax if (ret != 0) 1656d8a64d6aSCharles Keepax goto err; 16572159ad93SMark Brown break; 16582159ad93SMark Brown 16592159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 16601023dbd9SMark Brown dsp->running = false; 16611023dbd9SMark Brown 16622159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1663a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | 1664a7f9be7eSMark Brown ADSP2_START, 0); 1665973838a0SMark Brown 16662d30b575SMark Brown /* Make sure DMAs are quiesced */ 16672d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 16682d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 16692d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 16702d30b575SMark Brown 1671973838a0SMark Brown if (dsp->dvfs) { 1672973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 1200000, 1673973838a0SMark Brown 1800000); 1674973838a0SMark Brown if (ret != 0) 1675973838a0SMark Brown dev_warn(dsp->dev, 1676973838a0SMark Brown "Failed to lower supply: %d\n", 1677973838a0SMark Brown ret); 1678973838a0SMark Brown 1679973838a0SMark Brown ret = regulator_disable(dsp->dvfs); 1680973838a0SMark Brown if (ret != 0) 1681973838a0SMark Brown dev_err(dsp->dev, 1682973838a0SMark Brown "Failed to enable supply: %d\n", 1683973838a0SMark Brown ret); 1684973838a0SMark Brown } 1685471f4885SMark Brown 168681ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 16876ab2b7b4SDimitris Papastamos ctl->enabled = 0; 16886ab2b7b4SDimitris Papastamos 1689471f4885SMark Brown while (!list_empty(&dsp->alg_regions)) { 1690471f4885SMark Brown alg_region = list_first_entry(&dsp->alg_regions, 1691471f4885SMark Brown struct wm_adsp_alg_region, 1692471f4885SMark Brown list); 1693471f4885SMark Brown list_del(&alg_region->list); 1694471f4885SMark Brown kfree(alg_region); 1695471f4885SMark Brown } 1696ddbc5efeSCharles Keepax 1697ddbc5efeSCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 16982159ad93SMark Brown break; 16992159ad93SMark Brown 17002159ad93SMark Brown default: 17012159ad93SMark Brown break; 17022159ad93SMark Brown } 17032159ad93SMark Brown 17042159ad93SMark Brown return 0; 17052159ad93SMark Brown err: 17062159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1707a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 17082159ad93SMark Brown return ret; 17092159ad93SMark Brown } 17102159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 1711973838a0SMark Brown 1712973838a0SMark Brown int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) 1713973838a0SMark Brown { 1714973838a0SMark Brown int ret; 1715973838a0SMark Brown 171610a2b662SMark Brown /* 171710a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 171810a2b662SMark Brown * power saving. 171910a2b662SMark Brown */ 172010a2b662SMark Brown ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL, 172110a2b662SMark Brown ADSP2_MEM_ENA, 0); 172210a2b662SMark Brown if (ret != 0) { 172310a2b662SMark Brown adsp_err(adsp, "Failed to clear memory retention: %d\n", ret); 172410a2b662SMark Brown return ret; 172510a2b662SMark Brown } 172610a2b662SMark Brown 1727471f4885SMark Brown INIT_LIST_HEAD(&adsp->alg_regions); 172881ad93ecSDimitris Papastamos INIT_LIST_HEAD(&adsp->ctl_list); 1729d8a64d6aSCharles Keepax INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work); 17306ab2b7b4SDimitris Papastamos 1731973838a0SMark Brown if (dvfs) { 1732973838a0SMark Brown adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); 1733973838a0SMark Brown if (IS_ERR(adsp->dvfs)) { 1734973838a0SMark Brown ret = PTR_ERR(adsp->dvfs); 1735973838a0SMark Brown dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); 173681ad93ecSDimitris Papastamos return ret; 1737973838a0SMark Brown } 1738973838a0SMark Brown 1739973838a0SMark Brown ret = regulator_enable(adsp->dvfs); 1740973838a0SMark Brown if (ret != 0) { 1741973838a0SMark Brown dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", 1742973838a0SMark Brown ret); 174381ad93ecSDimitris Papastamos return ret; 1744973838a0SMark Brown } 1745973838a0SMark Brown 1746973838a0SMark Brown ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); 1747973838a0SMark Brown if (ret != 0) { 1748973838a0SMark Brown dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", 1749973838a0SMark Brown ret); 175081ad93ecSDimitris Papastamos return ret; 1751973838a0SMark Brown } 1752973838a0SMark Brown 1753973838a0SMark Brown ret = regulator_disable(adsp->dvfs); 1754973838a0SMark Brown if (ret != 0) { 1755973838a0SMark Brown dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", 1756973838a0SMark Brown ret); 175781ad93ecSDimitris Papastamos return ret; 1758973838a0SMark Brown } 1759973838a0SMark Brown } 1760973838a0SMark Brown 1761973838a0SMark Brown return 0; 1762973838a0SMark Brown } 1763973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 1764