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; 2323809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 2336ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 2343809f001SCharles Keepax struct wm_adsp *dsp; 2356ab2b7b4SDimitris Papastamos void *private; 2366ab2b7b4SDimitris Papastamos unsigned int enabled:1; 2376ab2b7b4SDimitris Papastamos struct list_head list; 2386ab2b7b4SDimitris Papastamos void *cache; 2396ab2b7b4SDimitris Papastamos size_t len; 2400c2e3f34SDimitris Papastamos unsigned int set:1; 2416ab2b7b4SDimitris Papastamos struct snd_kcontrol *kcontrol; 2426ab2b7b4SDimitris Papastamos }; 2436ab2b7b4SDimitris Papastamos 2441023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 2451023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 2461023dbd9SMark Brown { 247ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2481023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 2493809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 2501023dbd9SMark Brown 2513809f001SCharles Keepax ucontrol->value.integer.value[0] = dsp[e->shift_l].fw; 2521023dbd9SMark Brown 2531023dbd9SMark Brown return 0; 2541023dbd9SMark Brown } 2551023dbd9SMark Brown 2561023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 2571023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 2581023dbd9SMark Brown { 259ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2601023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 2613809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 2621023dbd9SMark Brown 2633809f001SCharles Keepax if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw) 2641023dbd9SMark Brown return 0; 2651023dbd9SMark Brown 2661023dbd9SMark Brown if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) 2671023dbd9SMark Brown return -EINVAL; 2681023dbd9SMark Brown 2693809f001SCharles Keepax if (dsp[e->shift_l].running) 2701023dbd9SMark Brown return -EBUSY; 2711023dbd9SMark Brown 2723809f001SCharles Keepax dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; 2731023dbd9SMark Brown 2741023dbd9SMark Brown return 0; 2751023dbd9SMark Brown } 2761023dbd9SMark Brown 2771023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 2781023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2791023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2801023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2811023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2821023dbd9SMark Brown }; 2831023dbd9SMark Brown 284b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp1_fw_controls[] = { 2851023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 2861023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2871023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 2881023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2891023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 2901023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 291b6ed61cfSMark Brown }; 292b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls); 293b6ed61cfSMark Brown 294b6ed61cfSMark Brown #if IS_ENABLED(CONFIG_SND_SOC_ARIZONA) 295b6ed61cfSMark Brown static const struct soc_enum wm_adsp2_rate_enum[] = { 296dc91428aSMark Brown SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1, 297dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 298dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 299dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 300dc91428aSMark Brown SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1, 301dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 302dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 303dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 304dc91428aSMark Brown SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1, 305dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 306dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 307dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 3085be9c5b4SCharles Keepax SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1, 309dc91428aSMark Brown ARIZONA_DSP1_RATE_SHIFT, 0xf, 310dc91428aSMark Brown ARIZONA_RATE_ENUM_SIZE, 311dc91428aSMark Brown arizona_rate_text, arizona_rate_val), 312dc91428aSMark Brown }; 313dc91428aSMark Brown 314b6ed61cfSMark Brown const struct snd_kcontrol_new wm_adsp2_fw_controls[] = { 3151023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 3161023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 317b6ed61cfSMark Brown SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]), 3181023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 3191023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 320b6ed61cfSMark Brown SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]), 3211023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 3221023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 323b6ed61cfSMark Brown SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]), 3241023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 3251023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 326b6ed61cfSMark Brown SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]), 3271023dbd9SMark Brown }; 328b6ed61cfSMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls); 329b6ed61cfSMark Brown #endif 3302159ad93SMark Brown 3312159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 3322159ad93SMark Brown int type) 3332159ad93SMark Brown { 3342159ad93SMark Brown int i; 3352159ad93SMark Brown 3362159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 3372159ad93SMark Brown if (dsp->mem[i].type == type) 3382159ad93SMark Brown return &dsp->mem[i]; 3392159ad93SMark Brown 3402159ad93SMark Brown return NULL; 3412159ad93SMark Brown } 3422159ad93SMark Brown 3433809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 34445b9ee72SMark Brown unsigned int offset) 34545b9ee72SMark Brown { 3463809f001SCharles Keepax if (WARN_ON(!mem)) 3476c452bdaSTakashi Iwai return offset; 3483809f001SCharles Keepax switch (mem->type) { 34945b9ee72SMark Brown case WMFW_ADSP1_PM: 3503809f001SCharles Keepax return mem->base + (offset * 3); 35145b9ee72SMark Brown case WMFW_ADSP1_DM: 3523809f001SCharles Keepax return mem->base + (offset * 2); 35345b9ee72SMark Brown case WMFW_ADSP2_XM: 3543809f001SCharles Keepax return mem->base + (offset * 2); 35545b9ee72SMark Brown case WMFW_ADSP2_YM: 3563809f001SCharles Keepax return mem->base + (offset * 2); 35745b9ee72SMark Brown case WMFW_ADSP1_ZM: 3583809f001SCharles Keepax return mem->base + (offset * 2); 35945b9ee72SMark Brown default: 3606c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 36145b9ee72SMark Brown return offset; 36245b9ee72SMark Brown } 36345b9ee72SMark Brown } 36445b9ee72SMark Brown 3656ab2b7b4SDimitris Papastamos static int wm_coeff_info(struct snd_kcontrol *kcontrol, 3666ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 3676ab2b7b4SDimitris Papastamos { 3686ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 3696ab2b7b4SDimitris Papastamos 3706ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 3716ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 3726ab2b7b4SDimitris Papastamos return 0; 3736ab2b7b4SDimitris Papastamos } 3746ab2b7b4SDimitris Papastamos 3756ab2b7b4SDimitris Papastamos static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, 3766ab2b7b4SDimitris Papastamos const void *buf, size_t len) 3776ab2b7b4SDimitris Papastamos { 3786ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 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 3933809f001SCharles Keepax reg = ctl->alg_region.base; 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 4276ab2b7b4SDimitris Papastamos return wm_coeff_write_control(kcontrol, p, ctl->len); 4286ab2b7b4SDimitris Papastamos } 4296ab2b7b4SDimitris Papastamos 4306ab2b7b4SDimitris Papastamos static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, 4316ab2b7b4SDimitris Papastamos void *buf, size_t len) 4326ab2b7b4SDimitris Papastamos { 4336ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 4343809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 4356ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 4363809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 4376ab2b7b4SDimitris Papastamos void *scratch; 4386ab2b7b4SDimitris Papastamos int ret; 4396ab2b7b4SDimitris Papastamos unsigned int reg; 4406ab2b7b4SDimitris Papastamos 4413809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 4426ab2b7b4SDimitris Papastamos if (!mem) { 4433809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 4443809f001SCharles Keepax alg_region->type); 4456ab2b7b4SDimitris Papastamos return -EINVAL; 4466ab2b7b4SDimitris Papastamos } 4476ab2b7b4SDimitris Papastamos 4483809f001SCharles Keepax reg = ctl->alg_region.base; 4496ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 4506ab2b7b4SDimitris Papastamos 4516ab2b7b4SDimitris Papastamos scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); 4526ab2b7b4SDimitris Papastamos if (!scratch) 4536ab2b7b4SDimitris Papastamos return -ENOMEM; 4546ab2b7b4SDimitris Papastamos 4553809f001SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len); 4566ab2b7b4SDimitris Papastamos if (ret) { 4573809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 45843bc3bf6SDimitris Papastamos ctl->len, reg, ret); 4596ab2b7b4SDimitris Papastamos kfree(scratch); 4606ab2b7b4SDimitris Papastamos return ret; 4616ab2b7b4SDimitris Papastamos } 4623809f001SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg); 4636ab2b7b4SDimitris Papastamos 4646ab2b7b4SDimitris Papastamos memcpy(buf, scratch, ctl->len); 4656ab2b7b4SDimitris Papastamos kfree(scratch); 4666ab2b7b4SDimitris Papastamos 4676ab2b7b4SDimitris Papastamos return 0; 4686ab2b7b4SDimitris Papastamos } 4696ab2b7b4SDimitris Papastamos 4706ab2b7b4SDimitris Papastamos static int wm_coeff_get(struct snd_kcontrol *kcontrol, 4716ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 4726ab2b7b4SDimitris Papastamos { 4736ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; 4746ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 4756ab2b7b4SDimitris Papastamos 4766ab2b7b4SDimitris Papastamos memcpy(p, ctl->cache, ctl->len); 4776ab2b7b4SDimitris Papastamos return 0; 4786ab2b7b4SDimitris Papastamos } 4796ab2b7b4SDimitris Papastamos 4806ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 4813809f001SCharles Keepax struct wm_adsp *dsp; 4826ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 4836ab2b7b4SDimitris Papastamos struct work_struct work; 4846ab2b7b4SDimitris Papastamos }; 4856ab2b7b4SDimitris Papastamos 4863809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 4876ab2b7b4SDimitris Papastamos { 4886ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 4896ab2b7b4SDimitris Papastamos int ret; 4906ab2b7b4SDimitris Papastamos 49192bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 4926ab2b7b4SDimitris Papastamos return -EINVAL; 4936ab2b7b4SDimitris Papastamos 4946ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 4956ab2b7b4SDimitris Papastamos if (!kcontrol) 4966ab2b7b4SDimitris Papastamos return -ENOMEM; 4976ab2b7b4SDimitris Papastamos kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 4986ab2b7b4SDimitris Papastamos 4996ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 5006ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 5016ab2b7b4SDimitris Papastamos kcontrol->get = wm_coeff_get; 5026ab2b7b4SDimitris Papastamos kcontrol->put = wm_coeff_put; 5036ab2b7b4SDimitris Papastamos kcontrol->private_value = (unsigned long)ctl; 5046ab2b7b4SDimitris Papastamos 5053809f001SCharles Keepax ret = snd_soc_add_card_controls(dsp->card, 50681ad93ecSDimitris Papastamos kcontrol, 1); 5076ab2b7b4SDimitris Papastamos if (ret < 0) 5086ab2b7b4SDimitris Papastamos goto err_kcontrol; 5096ab2b7b4SDimitris Papastamos 5106ab2b7b4SDimitris Papastamos kfree(kcontrol); 5116ab2b7b4SDimitris Papastamos 5123809f001SCharles Keepax ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, 51381ad93ecSDimitris Papastamos ctl->name); 51481ad93ecSDimitris Papastamos 5153809f001SCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 5163809f001SCharles Keepax 5176ab2b7b4SDimitris Papastamos return 0; 5186ab2b7b4SDimitris Papastamos 5196ab2b7b4SDimitris Papastamos err_kcontrol: 5206ab2b7b4SDimitris Papastamos kfree(kcontrol); 5216ab2b7b4SDimitris Papastamos return ret; 5226ab2b7b4SDimitris Papastamos } 5236ab2b7b4SDimitris Papastamos 5242159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 5252159ad93SMark Brown { 526cf17c83cSMark Brown LIST_HEAD(buf_list); 5272159ad93SMark Brown const struct firmware *firmware; 5282159ad93SMark Brown struct regmap *regmap = dsp->regmap; 5292159ad93SMark Brown unsigned int pos = 0; 5302159ad93SMark Brown const struct wmfw_header *header; 5312159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 5322159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 5332159ad93SMark Brown const struct wmfw_footer *footer; 5342159ad93SMark Brown const struct wmfw_region *region; 5352159ad93SMark Brown const struct wm_adsp_region *mem; 5362159ad93SMark Brown const char *region_name; 5372159ad93SMark Brown char *file, *text; 538cf17c83cSMark Brown struct wm_adsp_buf *buf; 5392159ad93SMark Brown unsigned int reg; 5402159ad93SMark Brown int regions = 0; 5412159ad93SMark Brown int ret, offset, type, sizes; 5422159ad93SMark Brown 5432159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 5442159ad93SMark Brown if (file == NULL) 5452159ad93SMark Brown return -ENOMEM; 5462159ad93SMark Brown 5471023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 5481023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 5492159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 5502159ad93SMark Brown 5512159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 5522159ad93SMark Brown if (ret != 0) { 5532159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 5542159ad93SMark Brown goto out; 5552159ad93SMark Brown } 5562159ad93SMark Brown ret = -EINVAL; 5572159ad93SMark Brown 5582159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 5592159ad93SMark Brown if (pos >= firmware->size) { 5602159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 5612159ad93SMark Brown file, firmware->size); 5622159ad93SMark Brown goto out_fw; 5632159ad93SMark Brown } 5642159ad93SMark Brown 5652159ad93SMark Brown header = (void*)&firmware->data[0]; 5662159ad93SMark Brown 5672159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 5682159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 5692159ad93SMark Brown goto out_fw; 5702159ad93SMark Brown } 5712159ad93SMark Brown 5722159ad93SMark Brown if (header->ver != 0) { 5732159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 5742159ad93SMark Brown file, header->ver); 5752159ad93SMark Brown goto out_fw; 5762159ad93SMark Brown } 5773626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 5782159ad93SMark Brown 5792159ad93SMark Brown if (header->core != dsp->type) { 5802159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 5812159ad93SMark Brown file, header->core, dsp->type); 5822159ad93SMark Brown goto out_fw; 5832159ad93SMark Brown } 5842159ad93SMark Brown 5852159ad93SMark Brown switch (dsp->type) { 5862159ad93SMark Brown case WMFW_ADSP1: 5872159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 5882159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 5892159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 5902159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 5912159ad93SMark Brown 5922159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 5932159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 5942159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 5952159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 5962159ad93SMark Brown break; 5972159ad93SMark Brown 5982159ad93SMark Brown case WMFW_ADSP2: 5992159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 6002159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 6012159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 6022159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 6032159ad93SMark Brown 6042159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 6052159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 6062159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 6072159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 6082159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 6092159ad93SMark Brown break; 6102159ad93SMark Brown 6112159ad93SMark Brown default: 6126c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 6132159ad93SMark Brown goto out_fw; 6142159ad93SMark Brown } 6152159ad93SMark Brown 6162159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 6172159ad93SMark Brown sizes + sizeof(*footer)) { 6182159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 6192159ad93SMark Brown file, le32_to_cpu(header->len)); 6202159ad93SMark Brown goto out_fw; 6212159ad93SMark Brown } 6222159ad93SMark Brown 6232159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 6242159ad93SMark Brown le64_to_cpu(footer->timestamp)); 6252159ad93SMark Brown 6262159ad93SMark Brown while (pos < firmware->size && 6272159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 6282159ad93SMark Brown region = (void *)&(firmware->data[pos]); 6292159ad93SMark Brown region_name = "Unknown"; 6302159ad93SMark Brown reg = 0; 6312159ad93SMark Brown text = NULL; 6322159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 6332159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 6342159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 6352159ad93SMark Brown 6362159ad93SMark Brown switch (type) { 6372159ad93SMark Brown case WMFW_NAME_TEXT: 6382159ad93SMark Brown region_name = "Firmware name"; 6392159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 6402159ad93SMark Brown GFP_KERNEL); 6412159ad93SMark Brown break; 6422159ad93SMark Brown case WMFW_INFO_TEXT: 6432159ad93SMark Brown region_name = "Information"; 6442159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 6452159ad93SMark Brown GFP_KERNEL); 6462159ad93SMark Brown break; 6472159ad93SMark Brown case WMFW_ABSOLUTE: 6482159ad93SMark Brown region_name = "Absolute"; 6492159ad93SMark Brown reg = offset; 6502159ad93SMark Brown break; 6512159ad93SMark Brown case WMFW_ADSP1_PM: 6522159ad93SMark Brown region_name = "PM"; 65345b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6542159ad93SMark Brown break; 6552159ad93SMark Brown case WMFW_ADSP1_DM: 6562159ad93SMark Brown region_name = "DM"; 65745b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6582159ad93SMark Brown break; 6592159ad93SMark Brown case WMFW_ADSP2_XM: 6602159ad93SMark Brown region_name = "XM"; 66145b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6622159ad93SMark Brown break; 6632159ad93SMark Brown case WMFW_ADSP2_YM: 6642159ad93SMark Brown region_name = "YM"; 66545b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6662159ad93SMark Brown break; 6672159ad93SMark Brown case WMFW_ADSP1_ZM: 6682159ad93SMark Brown region_name = "ZM"; 66945b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 6702159ad93SMark Brown break; 6712159ad93SMark Brown default: 6722159ad93SMark Brown adsp_warn(dsp, 6732159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 6742159ad93SMark Brown file, regions, type, pos, pos); 6752159ad93SMark Brown break; 6762159ad93SMark Brown } 6772159ad93SMark Brown 6782159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 6792159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 6802159ad93SMark Brown region_name); 6812159ad93SMark Brown 6822159ad93SMark Brown if (text) { 6832159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 6842159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 6852159ad93SMark Brown kfree(text); 6862159ad93SMark Brown } 6872159ad93SMark Brown 6882159ad93SMark Brown if (reg) { 689cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 690cdcd7f72SCharles Keepax le32_to_cpu(region->len), 691cf17c83cSMark Brown &buf_list); 692a76fefabSMark Brown if (!buf) { 693a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 6947328823dSDimitris Papastamos ret = -ENOMEM; 6957328823dSDimitris Papastamos goto out_fw; 696a76fefabSMark Brown } 697a76fefabSMark Brown 698cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 699cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 7002159ad93SMark Brown if (ret != 0) { 7012159ad93SMark Brown adsp_err(dsp, 702cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 7032159ad93SMark Brown file, regions, 704cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 7052159ad93SMark Brown region_name, ret); 7062159ad93SMark Brown goto out_fw; 7072159ad93SMark Brown } 7082159ad93SMark Brown } 7092159ad93SMark Brown 7102159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 7112159ad93SMark Brown regions++; 7122159ad93SMark Brown } 7132159ad93SMark Brown 714cf17c83cSMark Brown ret = regmap_async_complete(regmap); 715cf17c83cSMark Brown if (ret != 0) { 716cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 717cf17c83cSMark Brown goto out_fw; 718cf17c83cSMark Brown } 719cf17c83cSMark Brown 7202159ad93SMark Brown if (pos > firmware->size) 7212159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 7222159ad93SMark Brown file, regions, pos - firmware->size); 7232159ad93SMark Brown 7242159ad93SMark Brown out_fw: 725cf17c83cSMark Brown regmap_async_complete(regmap); 726cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 7272159ad93SMark Brown release_firmware(firmware); 7282159ad93SMark Brown out: 7292159ad93SMark Brown kfree(file); 7302159ad93SMark Brown 7312159ad93SMark Brown return ret; 7322159ad93SMark Brown } 7332159ad93SMark Brown 7343809f001SCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 7356ab2b7b4SDimitris Papastamos { 7366ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 7376ab2b7b4SDimitris Papastamos int ret; 7386ab2b7b4SDimitris Papastamos 7393809f001SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 7400c2e3f34SDimitris Papastamos if (!ctl->enabled || ctl->set) 7416ab2b7b4SDimitris Papastamos continue; 7426ab2b7b4SDimitris Papastamos ret = wm_coeff_read_control(ctl->kcontrol, 7436ab2b7b4SDimitris Papastamos ctl->cache, 7446ab2b7b4SDimitris Papastamos ctl->len); 7456ab2b7b4SDimitris Papastamos if (ret < 0) 7466ab2b7b4SDimitris Papastamos return ret; 7476ab2b7b4SDimitris Papastamos } 7486ab2b7b4SDimitris Papastamos 7496ab2b7b4SDimitris Papastamos return 0; 7506ab2b7b4SDimitris Papastamos } 7516ab2b7b4SDimitris Papastamos 7523809f001SCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 7536ab2b7b4SDimitris Papastamos { 7546ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 7556ab2b7b4SDimitris Papastamos int ret; 7566ab2b7b4SDimitris Papastamos 7573809f001SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 7586ab2b7b4SDimitris Papastamos if (!ctl->enabled) 7596ab2b7b4SDimitris Papastamos continue; 7600c2e3f34SDimitris Papastamos if (ctl->set) { 7616ab2b7b4SDimitris Papastamos ret = wm_coeff_write_control(ctl->kcontrol, 7626ab2b7b4SDimitris Papastamos ctl->cache, 7636ab2b7b4SDimitris Papastamos ctl->len); 7646ab2b7b4SDimitris Papastamos if (ret < 0) 7656ab2b7b4SDimitris Papastamos return ret; 7666ab2b7b4SDimitris Papastamos } 7676ab2b7b4SDimitris Papastamos } 7686ab2b7b4SDimitris Papastamos 7696ab2b7b4SDimitris Papastamos return 0; 7706ab2b7b4SDimitris Papastamos } 7716ab2b7b4SDimitris Papastamos 7726ab2b7b4SDimitris Papastamos static void wm_adsp_ctl_work(struct work_struct *work) 7736ab2b7b4SDimitris Papastamos { 7746ab2b7b4SDimitris Papastamos struct wmfw_ctl_work *ctl_work = container_of(work, 7756ab2b7b4SDimitris Papastamos struct wmfw_ctl_work, 7766ab2b7b4SDimitris Papastamos work); 7776ab2b7b4SDimitris Papastamos 7783809f001SCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 7796ab2b7b4SDimitris Papastamos kfree(ctl_work); 7806ab2b7b4SDimitris Papastamos } 7816ab2b7b4SDimitris Papastamos 78292bb4c32SDimitris Papastamos static int wm_adsp_create_control(struct wm_adsp *dsp, 7833809f001SCharles Keepax const struct wm_adsp_alg_region *alg_region) 7846ab2b7b4SDimitris Papastamos { 7856ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 7866ab2b7b4SDimitris Papastamos struct wmfw_ctl_work *ctl_work; 7876ab2b7b4SDimitris Papastamos char *name; 7886ab2b7b4SDimitris Papastamos char *region_name; 7896ab2b7b4SDimitris Papastamos int ret; 7906ab2b7b4SDimitris Papastamos 7916ab2b7b4SDimitris Papastamos name = kmalloc(PAGE_SIZE, GFP_KERNEL); 7926ab2b7b4SDimitris Papastamos if (!name) 7936ab2b7b4SDimitris Papastamos return -ENOMEM; 7946ab2b7b4SDimitris Papastamos 7953809f001SCharles Keepax switch (alg_region->type) { 7966ab2b7b4SDimitris Papastamos case WMFW_ADSP1_PM: 7976ab2b7b4SDimitris Papastamos region_name = "PM"; 7986ab2b7b4SDimitris Papastamos break; 7996ab2b7b4SDimitris Papastamos case WMFW_ADSP1_DM: 8006ab2b7b4SDimitris Papastamos region_name = "DM"; 8016ab2b7b4SDimitris Papastamos break; 8026ab2b7b4SDimitris Papastamos case WMFW_ADSP2_XM: 8036ab2b7b4SDimitris Papastamos region_name = "XM"; 8046ab2b7b4SDimitris Papastamos break; 8056ab2b7b4SDimitris Papastamos case WMFW_ADSP2_YM: 8066ab2b7b4SDimitris Papastamos region_name = "YM"; 8076ab2b7b4SDimitris Papastamos break; 8086ab2b7b4SDimitris Papastamos case WMFW_ADSP1_ZM: 8096ab2b7b4SDimitris Papastamos region_name = "ZM"; 8106ab2b7b4SDimitris Papastamos break; 8116ab2b7b4SDimitris Papastamos default: 8129dbce044SDan Carpenter ret = -EINVAL; 8139dbce044SDan Carpenter goto err_name; 8146ab2b7b4SDimitris Papastamos } 8156ab2b7b4SDimitris Papastamos 8166ab2b7b4SDimitris Papastamos snprintf(name, PAGE_SIZE, "DSP%d %s %x", 8173809f001SCharles Keepax dsp->num, region_name, alg_region->alg); 8186ab2b7b4SDimitris Papastamos 81981ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, 8206ab2b7b4SDimitris Papastamos list) { 8216ab2b7b4SDimitris Papastamos if (!strcmp(ctl->name, name)) { 8226ab2b7b4SDimitris Papastamos if (!ctl->enabled) 8236ab2b7b4SDimitris Papastamos ctl->enabled = 1; 8249dbce044SDan Carpenter goto found; 8256ab2b7b4SDimitris Papastamos } 8266ab2b7b4SDimitris Papastamos } 8276ab2b7b4SDimitris Papastamos 8286ab2b7b4SDimitris Papastamos ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 8296ab2b7b4SDimitris Papastamos if (!ctl) { 8306ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8316ab2b7b4SDimitris Papastamos goto err_name; 8326ab2b7b4SDimitris Papastamos } 8333809f001SCharles Keepax ctl->alg_region = *alg_region; 8346ab2b7b4SDimitris Papastamos ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 8356ab2b7b4SDimitris Papastamos if (!ctl->name) { 8366ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8376ab2b7b4SDimitris Papastamos goto err_ctl; 8386ab2b7b4SDimitris Papastamos } 8396ab2b7b4SDimitris Papastamos ctl->enabled = 1; 8400c2e3f34SDimitris Papastamos ctl->set = 0; 8416ab2b7b4SDimitris Papastamos ctl->ops.xget = wm_coeff_get; 8426ab2b7b4SDimitris Papastamos ctl->ops.xput = wm_coeff_put; 8433809f001SCharles Keepax ctl->dsp = dsp; 8446ab2b7b4SDimitris Papastamos 8453809f001SCharles Keepax ctl->len = alg_region->len; 8466ab2b7b4SDimitris Papastamos ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 8476ab2b7b4SDimitris Papastamos if (!ctl->cache) { 8486ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8496ab2b7b4SDimitris Papastamos goto err_ctl_name; 8506ab2b7b4SDimitris Papastamos } 8516ab2b7b4SDimitris Papastamos 8526ab2b7b4SDimitris Papastamos ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 8536ab2b7b4SDimitris Papastamos if (!ctl_work) { 8546ab2b7b4SDimitris Papastamos ret = -ENOMEM; 8556ab2b7b4SDimitris Papastamos goto err_ctl_cache; 8566ab2b7b4SDimitris Papastamos } 8576ab2b7b4SDimitris Papastamos 8583809f001SCharles Keepax ctl_work->dsp = dsp; 8596ab2b7b4SDimitris Papastamos ctl_work->ctl = ctl; 8606ab2b7b4SDimitris Papastamos INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 8616ab2b7b4SDimitris Papastamos schedule_work(&ctl_work->work); 8626ab2b7b4SDimitris Papastamos 8639dbce044SDan Carpenter found: 8646ab2b7b4SDimitris Papastamos kfree(name); 8656ab2b7b4SDimitris Papastamos 8666ab2b7b4SDimitris Papastamos return 0; 8676ab2b7b4SDimitris Papastamos 8686ab2b7b4SDimitris Papastamos err_ctl_cache: 8696ab2b7b4SDimitris Papastamos kfree(ctl->cache); 8706ab2b7b4SDimitris Papastamos err_ctl_name: 8716ab2b7b4SDimitris Papastamos kfree(ctl->name); 8726ab2b7b4SDimitris Papastamos err_ctl: 8736ab2b7b4SDimitris Papastamos kfree(ctl); 8746ab2b7b4SDimitris Papastamos err_name: 8756ab2b7b4SDimitris Papastamos kfree(name); 8766ab2b7b4SDimitris Papastamos return ret; 8776ab2b7b4SDimitris Papastamos } 8786ab2b7b4SDimitris Papastamos 8793809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 880b618a185SCharles Keepax unsigned int pos, unsigned int len) 881db40517cSMark Brown { 882b618a185SCharles Keepax void *alg; 883b618a185SCharles Keepax int ret; 884db40517cSMark Brown __be32 val; 885db40517cSMark Brown 8863809f001SCharles Keepax if (n_algs == 0) { 887b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 888b618a185SCharles Keepax return ERR_PTR(-EINVAL); 889db40517cSMark Brown } 890db40517cSMark Brown 8913809f001SCharles Keepax if (n_algs > 1024) { 8923809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 893b618a185SCharles Keepax return ERR_PTR(-EINVAL); 894b618a185SCharles Keepax } 895b618a185SCharles Keepax 896b618a185SCharles Keepax /* Read the terminator first to validate the length */ 897b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); 898b618a185SCharles Keepax if (ret != 0) { 899b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 900b618a185SCharles Keepax ret); 901b618a185SCharles Keepax return ERR_PTR(ret); 902b618a185SCharles Keepax } 903b618a185SCharles Keepax 904b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 905b618a185SCharles Keepax adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 906b618a185SCharles Keepax pos + len, be32_to_cpu(val)); 907b618a185SCharles Keepax 908b618a185SCharles Keepax alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); 909b618a185SCharles Keepax if (!alg) 910b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 911b618a185SCharles Keepax 912b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); 913b618a185SCharles Keepax if (ret != 0) { 914b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", 915b618a185SCharles Keepax ret); 916b618a185SCharles Keepax kfree(alg); 917b618a185SCharles Keepax return ERR_PTR(ret); 918b618a185SCharles Keepax } 919b618a185SCharles Keepax 920b618a185SCharles Keepax return alg; 921b618a185SCharles Keepax } 922b618a185SCharles Keepax 923b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 924b618a185SCharles Keepax { 925b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 926b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 9273809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 928b618a185SCharles Keepax const struct wm_adsp_region *mem; 929b618a185SCharles Keepax unsigned int pos, len; 9303809f001SCharles Keepax size_t n_algs; 931b618a185SCharles Keepax int i, ret; 932b618a185SCharles Keepax 933b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 9346c452bdaSTakashi Iwai if (WARN_ON(!mem)) 935db40517cSMark Brown return -EINVAL; 936db40517cSMark Brown 937b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 938db40517cSMark Brown sizeof(adsp1_id)); 939db40517cSMark Brown if (ret != 0) { 940db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 941db40517cSMark Brown ret); 942db40517cSMark Brown return ret; 943db40517cSMark Brown } 944db40517cSMark Brown 9453809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 946f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 947db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 948f395a218SMark Brown dsp->fw_id, 949db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 950db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 951db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 9523809f001SCharles Keepax n_algs); 953db40517cSMark Brown 9543809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 9553809f001SCharles Keepax if (!alg_region) 956ac50009fSMark Brown return -ENOMEM; 9573809f001SCharles Keepax alg_region->type = WMFW_ADSP1_ZM; 9583809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp1_id.fw.id); 9593809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp1_id.zm); 9603809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 961ac50009fSMark Brown 9623809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 9633809f001SCharles Keepax if (!alg_region) 964ac50009fSMark Brown return -ENOMEM; 9653809f001SCharles Keepax alg_region->type = WMFW_ADSP1_DM; 9663809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp1_id.fw.id); 9673809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp1_id.dm); 9683809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 969ac50009fSMark Brown 970db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 9713809f001SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / 2; 972db40517cSMark Brown 9733809f001SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 974b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 975b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 976db40517cSMark Brown 9773809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 978471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 979db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 980db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 981db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 982471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 983471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 984471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 985471f4885SMark Brown 9863809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 9873809f001SCharles Keepax if (!alg_region) { 988d6d52179SJS Park ret = -ENOMEM; 989d6d52179SJS Park goto out; 990d6d52179SJS Park } 9913809f001SCharles Keepax alg_region->type = WMFW_ADSP1_DM; 9923809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 9933809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp1_alg[i].dm); 9943809f001SCharles Keepax alg_region->len = 0; 9953809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 9963809f001SCharles Keepax if (i + 1 < n_algs) { 9973809f001SCharles Keepax alg_region->len = be32_to_cpu(adsp1_alg[i + 1].dm); 9983809f001SCharles Keepax alg_region->len -= be32_to_cpu(adsp1_alg[i].dm); 9993809f001SCharles Keepax alg_region->len *= 4; 10003809f001SCharles Keepax wm_adsp_create_control(dsp, alg_region); 10016ab2b7b4SDimitris Papastamos } else { 10026ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 10036ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 10046ab2b7b4SDimitris Papastamos } 1005471f4885SMark Brown 10063809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 10073809f001SCharles Keepax if (!alg_region) { 1008d6d52179SJS Park ret = -ENOMEM; 1009d6d52179SJS Park goto out; 1010d6d52179SJS Park } 10113809f001SCharles Keepax alg_region->type = WMFW_ADSP1_ZM; 10123809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 10133809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp1_alg[i].zm); 10143809f001SCharles Keepax alg_region->len = 0; 10153809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 10163809f001SCharles Keepax if (i + 1 < n_algs) { 10173809f001SCharles Keepax alg_region->len = be32_to_cpu(adsp1_alg[i + 1].zm); 10183809f001SCharles Keepax alg_region->len -= be32_to_cpu(adsp1_alg[i].zm); 10193809f001SCharles Keepax alg_region->len *= 4; 10203809f001SCharles Keepax wm_adsp_create_control(dsp, alg_region); 10216ab2b7b4SDimitris Papastamos } else { 10226ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 10236ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 10246ab2b7b4SDimitris Papastamos } 1025b618a185SCharles Keepax } 1026db40517cSMark Brown 1027b618a185SCharles Keepax out: 1028b618a185SCharles Keepax kfree(adsp1_alg); 1029b618a185SCharles Keepax return ret; 1030b618a185SCharles Keepax } 1031b618a185SCharles Keepax 1032b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 1033b618a185SCharles Keepax { 1034b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 1035b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 10363809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1037b618a185SCharles Keepax const struct wm_adsp_region *mem; 1038b618a185SCharles Keepax unsigned int pos, len; 10393809f001SCharles Keepax size_t n_algs; 1040b618a185SCharles Keepax int i, ret; 1041b618a185SCharles Keepax 1042b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 1043b618a185SCharles Keepax if (WARN_ON(!mem)) 1044b618a185SCharles Keepax return -EINVAL; 1045b618a185SCharles Keepax 1046b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1047b618a185SCharles Keepax sizeof(adsp2_id)); 1048b618a185SCharles Keepax if (ret != 0) { 1049b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 1050b618a185SCharles Keepax ret); 1051b618a185SCharles Keepax return ret; 1052b618a185SCharles Keepax } 1053b618a185SCharles Keepax 10543809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 1055b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 1056b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1057b618a185SCharles Keepax dsp->fw_id, 1058b618a185SCharles Keepax (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, 1059b618a185SCharles Keepax (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, 1060b618a185SCharles Keepax be32_to_cpu(adsp2_id.fw.ver) & 0xff, 10613809f001SCharles Keepax n_algs); 1062b618a185SCharles Keepax 10633809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 10643809f001SCharles Keepax if (!alg_region) 1065b618a185SCharles Keepax return -ENOMEM; 10663809f001SCharles Keepax alg_region->type = WMFW_ADSP2_XM; 10673809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp2_id.fw.id); 10683809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp2_id.xm); 10693809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1070b618a185SCharles Keepax 10713809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 10723809f001SCharles Keepax if (!alg_region) 1073b618a185SCharles Keepax return -ENOMEM; 10743809f001SCharles Keepax alg_region->type = WMFW_ADSP2_YM; 10753809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp2_id.fw.id); 10763809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp2_id.ym); 10773809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1078b618a185SCharles Keepax 10793809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 10803809f001SCharles Keepax if (!alg_region) 1081b618a185SCharles Keepax return -ENOMEM; 10823809f001SCharles Keepax alg_region->type = WMFW_ADSP2_ZM; 10833809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp2_id.fw.id); 10843809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp2_id.zm); 10853809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1086b618a185SCharles Keepax 1087b618a185SCharles Keepax pos = sizeof(adsp2_id) / 2; 10883809f001SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / 2; 1089b618a185SCharles Keepax 10903809f001SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1091b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 1092b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 1093b618a185SCharles Keepax 10943809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1095471f4885SMark Brown adsp_info(dsp, 1096471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1097db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 1098db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1099db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1100471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1101471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 1102471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 1103471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 1104471f4885SMark Brown 11053809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 11063809f001SCharles Keepax if (!alg_region) { 1107d6d52179SJS Park ret = -ENOMEM; 1108d6d52179SJS Park goto out; 1109d6d52179SJS Park } 11103809f001SCharles Keepax alg_region->type = WMFW_ADSP2_XM; 11113809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 11123809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp2_alg[i].xm); 11133809f001SCharles Keepax alg_region->len = 0; 11143809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 11153809f001SCharles Keepax if (i + 1 < n_algs) { 11163809f001SCharles Keepax alg_region->len = be32_to_cpu(adsp2_alg[i + 1].xm); 11173809f001SCharles Keepax alg_region->len -= be32_to_cpu(adsp2_alg[i].xm); 11183809f001SCharles Keepax alg_region->len *= 4; 11193809f001SCharles Keepax wm_adsp_create_control(dsp, alg_region); 11206ab2b7b4SDimitris Papastamos } else { 11216ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 11226ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 11236ab2b7b4SDimitris Papastamos } 1124471f4885SMark Brown 11253809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 11263809f001SCharles Keepax if (!alg_region) { 1127d6d52179SJS Park ret = -ENOMEM; 1128d6d52179SJS Park goto out; 1129d6d52179SJS Park } 11303809f001SCharles Keepax alg_region->type = WMFW_ADSP2_YM; 11313809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 11323809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp2_alg[i].ym); 11333809f001SCharles Keepax alg_region->len = 0; 11343809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 11353809f001SCharles Keepax if (i + 1 < n_algs) { 11363809f001SCharles Keepax alg_region->len = be32_to_cpu(adsp2_alg[i + 1].ym); 11373809f001SCharles Keepax alg_region->len -= be32_to_cpu(adsp2_alg[i].ym); 11383809f001SCharles Keepax alg_region->len *= 4; 11393809f001SCharles Keepax wm_adsp_create_control(dsp, alg_region); 11406ab2b7b4SDimitris Papastamos } else { 11416ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 11426ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 11436ab2b7b4SDimitris Papastamos } 1144471f4885SMark Brown 11453809f001SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 11463809f001SCharles Keepax if (!alg_region) { 1147d6d52179SJS Park ret = -ENOMEM; 1148d6d52179SJS Park goto out; 1149d6d52179SJS Park } 11503809f001SCharles Keepax alg_region->type = WMFW_ADSP2_ZM; 11513809f001SCharles Keepax alg_region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 11523809f001SCharles Keepax alg_region->base = be32_to_cpu(adsp2_alg[i].zm); 11533809f001SCharles Keepax alg_region->len = 0; 11543809f001SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 11553809f001SCharles Keepax if (i + 1 < n_algs) { 11563809f001SCharles Keepax alg_region->len = be32_to_cpu(adsp2_alg[i + 1].zm); 11573809f001SCharles Keepax alg_region->len -= be32_to_cpu(adsp2_alg[i].zm); 11583809f001SCharles Keepax alg_region->len *= 4; 11593809f001SCharles Keepax wm_adsp_create_control(dsp, alg_region); 11606ab2b7b4SDimitris Papastamos } else { 11616ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 11626ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 11636ab2b7b4SDimitris Papastamos } 1164db40517cSMark Brown } 1165db40517cSMark Brown 1166db40517cSMark Brown out: 1167b618a185SCharles Keepax kfree(adsp2_alg); 1168db40517cSMark Brown return ret; 1169db40517cSMark Brown } 1170db40517cSMark Brown 11712159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 11722159ad93SMark Brown { 1173cf17c83cSMark Brown LIST_HEAD(buf_list); 11742159ad93SMark Brown struct regmap *regmap = dsp->regmap; 11752159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 11762159ad93SMark Brown struct wmfw_coeff_item *blk; 11772159ad93SMark Brown const struct firmware *firmware; 1178471f4885SMark Brown const struct wm_adsp_region *mem; 1179471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 11802159ad93SMark Brown const char *region_name; 11812159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 11822159ad93SMark Brown char *file; 1183cf17c83cSMark Brown struct wm_adsp_buf *buf; 11842159ad93SMark Brown 11852159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 11862159ad93SMark Brown if (file == NULL) 11872159ad93SMark Brown return -ENOMEM; 11882159ad93SMark Brown 11891023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 11901023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 11912159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 11922159ad93SMark Brown 11932159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 11942159ad93SMark Brown if (ret != 0) { 11952159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 11962159ad93SMark Brown ret = 0; 11972159ad93SMark Brown goto out; 11982159ad93SMark Brown } 11992159ad93SMark Brown ret = -EINVAL; 12002159ad93SMark Brown 12012159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 12022159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 12032159ad93SMark Brown file, firmware->size); 12042159ad93SMark Brown goto out_fw; 12052159ad93SMark Brown } 12062159ad93SMark Brown 12072159ad93SMark Brown hdr = (void*)&firmware->data[0]; 12082159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 12092159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 1210a4cdbec7SCharles Keepax goto out_fw; 12112159ad93SMark Brown } 12122159ad93SMark Brown 1213c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 1214c712326dSMark Brown case 1: 1215c712326dSMark Brown break; 1216c712326dSMark Brown default: 1217c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 1218c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 1219c712326dSMark Brown ret = -EINVAL; 1220c712326dSMark Brown goto out_fw; 1221c712326dSMark Brown } 1222c712326dSMark Brown 12232159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 12242159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 12252159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 12262159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 12272159ad93SMark Brown 12282159ad93SMark Brown pos = le32_to_cpu(hdr->len); 12292159ad93SMark Brown 12302159ad93SMark Brown blocks = 0; 12312159ad93SMark Brown while (pos < firmware->size && 12322159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 12332159ad93SMark Brown blk = (void*)(&firmware->data[pos]); 12342159ad93SMark Brown 1235c712326dSMark Brown type = le16_to_cpu(blk->type); 1236c712326dSMark Brown offset = le16_to_cpu(blk->offset); 12372159ad93SMark Brown 12382159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 12392159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 12402159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 12412159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 12422159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 12432159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 12442159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 12452159ad93SMark Brown 12462159ad93SMark Brown reg = 0; 12472159ad93SMark Brown region_name = "Unknown"; 12482159ad93SMark Brown switch (type) { 1249c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 1250c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 12512159ad93SMark Brown break; 1252c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 1253f395a218SMark Brown /* 1254f395a218SMark Brown * Old files may use this for global 1255f395a218SMark Brown * coefficients. 1256f395a218SMark Brown */ 1257f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 1258f395a218SMark Brown offset == 0) { 1259f395a218SMark Brown region_name = "global coefficients"; 1260f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 1261f395a218SMark Brown if (!mem) { 1262f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 1263f395a218SMark Brown break; 1264f395a218SMark Brown } 1265f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 1266f395a218SMark Brown 1267f395a218SMark Brown } else { 12682159ad93SMark Brown region_name = "register"; 12692159ad93SMark Brown reg = offset; 1270f395a218SMark Brown } 12712159ad93SMark Brown break; 1272471f4885SMark Brown 1273471f4885SMark Brown case WMFW_ADSP1_DM: 1274471f4885SMark Brown case WMFW_ADSP1_ZM: 1275471f4885SMark Brown case WMFW_ADSP2_XM: 1276471f4885SMark Brown case WMFW_ADSP2_YM: 1277471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 1278471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 1279471f4885SMark Brown type, le32_to_cpu(blk->id)); 1280471f4885SMark Brown 1281471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 1282471f4885SMark Brown if (!mem) { 1283471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 1284471f4885SMark Brown break; 1285471f4885SMark Brown } 1286471f4885SMark Brown 1287471f4885SMark Brown reg = 0; 1288471f4885SMark Brown list_for_each_entry(alg_region, 1289471f4885SMark Brown &dsp->alg_regions, list) { 1290471f4885SMark Brown if (le32_to_cpu(blk->id) == alg_region->alg && 1291471f4885SMark Brown type == alg_region->type) { 1292338c5188SMark Brown reg = alg_region->base; 1293471f4885SMark Brown reg = wm_adsp_region_to_reg(mem, 1294471f4885SMark Brown reg); 1295338c5188SMark Brown reg += offset; 1296d733dc08SCharles Keepax break; 1297471f4885SMark Brown } 1298471f4885SMark Brown } 1299471f4885SMark Brown 1300471f4885SMark Brown if (reg == 0) 1301471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 1302471f4885SMark Brown type, le32_to_cpu(blk->id)); 1303471f4885SMark Brown break; 1304471f4885SMark Brown 13052159ad93SMark Brown default: 130625c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 130725c62f7eSMark Brown file, blocks, type, pos); 13082159ad93SMark Brown break; 13092159ad93SMark Brown } 13102159ad93SMark Brown 13112159ad93SMark Brown if (reg) { 1312cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 1313cf17c83cSMark Brown le32_to_cpu(blk->len), 1314cf17c83cSMark Brown &buf_list); 1315a76fefabSMark Brown if (!buf) { 1316a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 1317f4b82812SWei Yongjun ret = -ENOMEM; 1318f4b82812SWei Yongjun goto out_fw; 1319a76fefabSMark Brown } 1320a76fefabSMark Brown 132120da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 132220da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 132320da6d5aSMark Brown reg); 1324cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 13252159ad93SMark Brown le32_to_cpu(blk->len)); 13262159ad93SMark Brown if (ret != 0) { 13272159ad93SMark Brown adsp_err(dsp, 132843bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 132943bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 13302159ad93SMark Brown } 13312159ad93SMark Brown } 13322159ad93SMark Brown 1333be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 13342159ad93SMark Brown blocks++; 13352159ad93SMark Brown } 13362159ad93SMark Brown 1337cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1338cf17c83cSMark Brown if (ret != 0) 1339cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1340cf17c83cSMark Brown 13412159ad93SMark Brown if (pos > firmware->size) 13422159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 13432159ad93SMark Brown file, blocks, pos - firmware->size); 13442159ad93SMark Brown 13452159ad93SMark Brown out_fw: 13469da7a5a9SCharles Keepax regmap_async_complete(regmap); 13472159ad93SMark Brown release_firmware(firmware); 1348cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 13492159ad93SMark Brown out: 13502159ad93SMark Brown kfree(file); 1351f4b82812SWei Yongjun return ret; 13522159ad93SMark Brown } 13532159ad93SMark Brown 13543809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 13555e7a7a22SMark Brown { 13563809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 13575e7a7a22SMark Brown 13585e7a7a22SMark Brown return 0; 13595e7a7a22SMark Brown } 13605e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 13615e7a7a22SMark Brown 13622159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 13632159ad93SMark Brown struct snd_kcontrol *kcontrol, 13642159ad93SMark Brown int event) 13652159ad93SMark Brown { 136672718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 13672159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 13682159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 1369b0101b4fSDimitris Papastamos struct wm_adsp_alg_region *alg_region; 13706ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 13712159ad93SMark Brown int ret; 137294e205bfSChris Rattray int val; 13732159ad93SMark Brown 137400200107SLars-Peter Clausen dsp->card = codec->component.card; 137592bb4c32SDimitris Papastamos 13762159ad93SMark Brown switch (event) { 13772159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 13782159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 13792159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 13802159ad93SMark Brown 138194e205bfSChris Rattray /* 138294e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 138394e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 138494e205bfSChris Rattray */ 138594e205bfSChris Rattray if(dsp->sysclk_reg) { 138694e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 138794e205bfSChris Rattray if (ret != 0) { 138894e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 138994e205bfSChris Rattray ret); 139094e205bfSChris Rattray return ret; 139194e205bfSChris Rattray } 139294e205bfSChris Rattray 139394e205bfSChris Rattray val = (val & dsp->sysclk_mask) 139494e205bfSChris Rattray >> dsp->sysclk_shift; 139594e205bfSChris Rattray 139694e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 139794e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 139894e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 139994e205bfSChris Rattray if (ret != 0) { 140094e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 140194e205bfSChris Rattray ret); 140294e205bfSChris Rattray return ret; 140394e205bfSChris Rattray } 140494e205bfSChris Rattray } 140594e205bfSChris Rattray 14062159ad93SMark Brown ret = wm_adsp_load(dsp); 14072159ad93SMark Brown if (ret != 0) 14082159ad93SMark Brown goto err; 14092159ad93SMark Brown 1410b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 1411db40517cSMark Brown if (ret != 0) 1412db40517cSMark Brown goto err; 1413db40517cSMark Brown 14142159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 14152159ad93SMark Brown if (ret != 0) 14162159ad93SMark Brown goto err; 14172159ad93SMark Brown 14180c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 141981ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 14206ab2b7b4SDimitris Papastamos if (ret != 0) 14216ab2b7b4SDimitris Papastamos goto err; 14226ab2b7b4SDimitris Papastamos 14230c2e3f34SDimitris Papastamos /* Sync set controls */ 142481ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 14256ab2b7b4SDimitris Papastamos if (ret != 0) 14266ab2b7b4SDimitris Papastamos goto err; 14276ab2b7b4SDimitris Papastamos 14282159ad93SMark Brown /* Start the core running */ 14292159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14302159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 14312159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 14322159ad93SMark Brown break; 14332159ad93SMark Brown 14342159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 14352159ad93SMark Brown /* Halt the core */ 14362159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14372159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 14382159ad93SMark Brown 14392159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 14402159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 14412159ad93SMark Brown 14422159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14432159ad93SMark Brown ADSP1_SYS_ENA, 0); 14446ab2b7b4SDimitris Papastamos 144581ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 14466ab2b7b4SDimitris Papastamos ctl->enabled = 0; 1447b0101b4fSDimitris Papastamos 1448b0101b4fSDimitris Papastamos while (!list_empty(&dsp->alg_regions)) { 1449b0101b4fSDimitris Papastamos alg_region = list_first_entry(&dsp->alg_regions, 1450b0101b4fSDimitris Papastamos struct wm_adsp_alg_region, 1451b0101b4fSDimitris Papastamos list); 1452b0101b4fSDimitris Papastamos list_del(&alg_region->list); 1453b0101b4fSDimitris Papastamos kfree(alg_region); 1454b0101b4fSDimitris Papastamos } 14552159ad93SMark Brown break; 14562159ad93SMark Brown 14572159ad93SMark Brown default: 14582159ad93SMark Brown break; 14592159ad93SMark Brown } 14602159ad93SMark Brown 14612159ad93SMark Brown return 0; 14622159ad93SMark Brown 14632159ad93SMark Brown err: 14642159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 14652159ad93SMark Brown ADSP1_SYS_ENA, 0); 14662159ad93SMark Brown return ret; 14672159ad93SMark Brown } 14682159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 14692159ad93SMark Brown 14702159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 14712159ad93SMark Brown { 14722159ad93SMark Brown unsigned int val; 14732159ad93SMark Brown int ret, count; 14742159ad93SMark Brown 14751552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 14762159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 14772159ad93SMark Brown if (ret != 0) 14782159ad93SMark Brown return ret; 14792159ad93SMark Brown 14802159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 1481939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 14822159ad93SMark Brown ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, 14832159ad93SMark Brown &val); 14842159ad93SMark Brown if (ret != 0) 14852159ad93SMark Brown return ret; 1486939fd1e8SCharles Keepax 1487939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 1488939fd1e8SCharles Keepax break; 1489939fd1e8SCharles Keepax 1490939fd1e8SCharles Keepax msleep(1); 1491939fd1e8SCharles Keepax } 14922159ad93SMark Brown 14932159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 14942159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 14952159ad93SMark Brown return -EBUSY; 14962159ad93SMark Brown } 14972159ad93SMark Brown 14982159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 14992159ad93SMark Brown 15002159ad93SMark Brown return 0; 15012159ad93SMark Brown } 15022159ad93SMark Brown 150318b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 15042159ad93SMark Brown { 1505d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 1506d8a64d6aSCharles Keepax struct wm_adsp, 1507d8a64d6aSCharles Keepax boot_work); 15082159ad93SMark Brown int ret; 1509d8a64d6aSCharles Keepax unsigned int val; 15102159ad93SMark Brown 1511dd49e2c8SMark Brown /* 1512dd49e2c8SMark Brown * For simplicity set the DSP clock rate to be the 1513dd49e2c8SMark Brown * SYSCLK rate rather than making it configurable. 1514dd49e2c8SMark Brown */ 1515dd49e2c8SMark Brown ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); 1516dd49e2c8SMark Brown if (ret != 0) { 1517d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 1518d8a64d6aSCharles Keepax return; 1519dd49e2c8SMark Brown } 1520dd49e2c8SMark Brown val = (val & ARIZONA_SYSCLK_FREQ_MASK) 1521dd49e2c8SMark Brown >> ARIZONA_SYSCLK_FREQ_SHIFT; 1522dd49e2c8SMark Brown 15231552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, 1524dd49e2c8SMark Brown dsp->base + ADSP2_CLOCKING, 1525dd49e2c8SMark Brown ADSP2_CLK_SEL_MASK, val); 1526dd49e2c8SMark Brown if (ret != 0) { 1527d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 1528d8a64d6aSCharles Keepax return; 1529dd49e2c8SMark Brown } 1530dd49e2c8SMark Brown 1531973838a0SMark Brown if (dsp->dvfs) { 1532973838a0SMark Brown ret = regmap_read(dsp->regmap, 1533973838a0SMark Brown dsp->base + ADSP2_CLOCKING, &val); 1534973838a0SMark Brown if (ret != 0) { 153562c35b3bSCharles Keepax adsp_err(dsp, "Failed to read clocking: %d\n", ret); 1536d8a64d6aSCharles Keepax return; 1537973838a0SMark Brown } 1538973838a0SMark Brown 153925c6fdb0SMark Brown if ((val & ADSP2_CLK_SEL_MASK) >= 3) { 1540973838a0SMark Brown ret = regulator_enable(dsp->dvfs); 1541973838a0SMark Brown if (ret != 0) { 154262c35b3bSCharles Keepax adsp_err(dsp, 1543973838a0SMark Brown "Failed to enable supply: %d\n", 1544973838a0SMark Brown ret); 1545d8a64d6aSCharles Keepax return; 1546973838a0SMark Brown } 1547973838a0SMark Brown 1548973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 1549973838a0SMark Brown 1800000, 1550973838a0SMark Brown 1800000); 1551973838a0SMark Brown if (ret != 0) { 155262c35b3bSCharles Keepax adsp_err(dsp, 1553973838a0SMark Brown "Failed to raise supply: %d\n", 1554973838a0SMark Brown ret); 1555d8a64d6aSCharles Keepax return; 1556973838a0SMark Brown } 1557973838a0SMark Brown } 1558973838a0SMark Brown } 1559973838a0SMark Brown 15602159ad93SMark Brown ret = wm_adsp2_ena(dsp); 15612159ad93SMark Brown if (ret != 0) 1562d8a64d6aSCharles Keepax return; 15632159ad93SMark Brown 15642159ad93SMark Brown ret = wm_adsp_load(dsp); 15652159ad93SMark Brown if (ret != 0) 15662159ad93SMark Brown goto err; 15672159ad93SMark Brown 1568b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 1569db40517cSMark Brown if (ret != 0) 1570db40517cSMark Brown goto err; 1571db40517cSMark Brown 15722159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 15732159ad93SMark Brown if (ret != 0) 15742159ad93SMark Brown goto err; 15752159ad93SMark Brown 15760c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 157781ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 15786ab2b7b4SDimitris Papastamos if (ret != 0) 15796ab2b7b4SDimitris Papastamos goto err; 15806ab2b7b4SDimitris Papastamos 15810c2e3f34SDimitris Papastamos /* Sync set controls */ 158281ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 15836ab2b7b4SDimitris Papastamos if (ret != 0) 15846ab2b7b4SDimitris Papastamos goto err; 15856ab2b7b4SDimitris Papastamos 15861023dbd9SMark Brown dsp->running = true; 1587d8a64d6aSCharles Keepax 1588d8a64d6aSCharles Keepax return; 1589d8a64d6aSCharles Keepax 1590d8a64d6aSCharles Keepax err: 1591d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1592d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 1593d8a64d6aSCharles Keepax } 1594d8a64d6aSCharles Keepax 159512db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 159612db5eddSCharles Keepax struct snd_kcontrol *kcontrol, int event) 159712db5eddSCharles Keepax { 159872718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 159912db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 160012db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 160112db5eddSCharles Keepax 160200200107SLars-Peter Clausen dsp->card = codec->component.card; 160312db5eddSCharles Keepax 160412db5eddSCharles Keepax switch (event) { 160512db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 160612db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 160712db5eddSCharles Keepax break; 160812db5eddSCharles Keepax default: 160912db5eddSCharles Keepax break; 1610cab27258SCharles Keepax } 161112db5eddSCharles Keepax 161212db5eddSCharles Keepax return 0; 161312db5eddSCharles Keepax } 161412db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 161512db5eddSCharles Keepax 1616d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 1617d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 1618d8a64d6aSCharles Keepax { 161972718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 1620d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 1621d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 1622d8a64d6aSCharles Keepax struct wm_adsp_alg_region *alg_region; 1623d8a64d6aSCharles Keepax struct wm_coeff_ctl *ctl; 1624d8a64d6aSCharles Keepax int ret; 1625d8a64d6aSCharles Keepax 1626d8a64d6aSCharles Keepax switch (event) { 1627d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 1628d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 1629d8a64d6aSCharles Keepax 1630d8a64d6aSCharles Keepax if (!dsp->running) 1631d8a64d6aSCharles Keepax return -EIO; 1632d8a64d6aSCharles Keepax 1633d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 1634d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 163500e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 163600e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 1637d8a64d6aSCharles Keepax if (ret != 0) 1638d8a64d6aSCharles Keepax goto err; 16392159ad93SMark Brown break; 16402159ad93SMark Brown 16412159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 16421023dbd9SMark Brown dsp->running = false; 16431023dbd9SMark Brown 16442159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1645a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | 1646a7f9be7eSMark Brown ADSP2_START, 0); 1647973838a0SMark Brown 16482d30b575SMark Brown /* Make sure DMAs are quiesced */ 16492d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 16502d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 16512d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 16522d30b575SMark Brown 1653973838a0SMark Brown if (dsp->dvfs) { 1654973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 1200000, 1655973838a0SMark Brown 1800000); 1656973838a0SMark Brown if (ret != 0) 165762c35b3bSCharles Keepax adsp_warn(dsp, 1658973838a0SMark Brown "Failed to lower supply: %d\n", 1659973838a0SMark Brown ret); 1660973838a0SMark Brown 1661973838a0SMark Brown ret = regulator_disable(dsp->dvfs); 1662973838a0SMark Brown if (ret != 0) 166362c35b3bSCharles Keepax adsp_err(dsp, 1664973838a0SMark Brown "Failed to enable supply: %d\n", 1665973838a0SMark Brown ret); 1666973838a0SMark Brown } 1667471f4885SMark Brown 166881ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 16696ab2b7b4SDimitris Papastamos ctl->enabled = 0; 16706ab2b7b4SDimitris Papastamos 1671471f4885SMark Brown while (!list_empty(&dsp->alg_regions)) { 1672471f4885SMark Brown alg_region = list_first_entry(&dsp->alg_regions, 1673471f4885SMark Brown struct wm_adsp_alg_region, 1674471f4885SMark Brown list); 1675471f4885SMark Brown list_del(&alg_region->list); 1676471f4885SMark Brown kfree(alg_region); 1677471f4885SMark Brown } 1678ddbc5efeSCharles Keepax 1679ddbc5efeSCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 16802159ad93SMark Brown break; 16812159ad93SMark Brown 16822159ad93SMark Brown default: 16832159ad93SMark Brown break; 16842159ad93SMark Brown } 16852159ad93SMark Brown 16862159ad93SMark Brown return 0; 16872159ad93SMark Brown err: 16882159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1689a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 16902159ad93SMark Brown return ret; 16912159ad93SMark Brown } 16922159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 1693973838a0SMark Brown 16943809f001SCharles Keepax int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs) 1695973838a0SMark Brown { 1696973838a0SMark Brown int ret; 1697973838a0SMark Brown 169810a2b662SMark Brown /* 169910a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 170010a2b662SMark Brown * power saving. 170110a2b662SMark Brown */ 17023809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 170310a2b662SMark Brown ADSP2_MEM_ENA, 0); 170410a2b662SMark Brown if (ret != 0) { 17053809f001SCharles Keepax adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); 170610a2b662SMark Brown return ret; 170710a2b662SMark Brown } 170810a2b662SMark Brown 17093809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 17103809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 17113809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 17126ab2b7b4SDimitris Papastamos 1713973838a0SMark Brown if (dvfs) { 17143809f001SCharles Keepax dsp->dvfs = devm_regulator_get(dsp->dev, "DCVDD"); 17153809f001SCharles Keepax if (IS_ERR(dsp->dvfs)) { 17163809f001SCharles Keepax ret = PTR_ERR(dsp->dvfs); 17173809f001SCharles Keepax adsp_err(dsp, "Failed to get DCVDD: %d\n", ret); 171881ad93ecSDimitris Papastamos return ret; 1719973838a0SMark Brown } 1720973838a0SMark Brown 17213809f001SCharles Keepax ret = regulator_enable(dsp->dvfs); 1722973838a0SMark Brown if (ret != 0) { 17233809f001SCharles Keepax adsp_err(dsp, "Failed to enable DCVDD: %d\n", ret); 172481ad93ecSDimitris Papastamos return ret; 1725973838a0SMark Brown } 1726973838a0SMark Brown 17273809f001SCharles Keepax ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000); 1728973838a0SMark Brown if (ret != 0) { 17293809f001SCharles Keepax adsp_err(dsp, "Failed to initialise DVFS: %d\n", ret); 173081ad93ecSDimitris Papastamos return ret; 1731973838a0SMark Brown } 1732973838a0SMark Brown 17333809f001SCharles Keepax ret = regulator_disable(dsp->dvfs); 1734973838a0SMark Brown if (ret != 0) { 17353809f001SCharles Keepax adsp_err(dsp, "Failed to disable DCVDD: %d\n", ret); 173681ad93ecSDimitris Papastamos return ret; 1737973838a0SMark Brown } 1738973838a0SMark Brown } 1739973838a0SMark Brown 1740973838a0SMark Brown return 0; 1741973838a0SMark Brown } 1742973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 17430a37c6efSPraveen Diwakar 17440a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 1745