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