12159ad93SMark Brown /* 22159ad93SMark Brown * wm_adsp.c -- Wolfson ADSP support 32159ad93SMark Brown * 42159ad93SMark Brown * Copyright 2012 Wolfson Microelectronics plc 52159ad93SMark Brown * 62159ad93SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 72159ad93SMark Brown * 82159ad93SMark Brown * This program is free software; you can redistribute it and/or modify 92159ad93SMark Brown * it under the terms of the GNU General Public License version 2 as 102159ad93SMark Brown * published by the Free Software Foundation. 112159ad93SMark Brown */ 122159ad93SMark Brown 132159ad93SMark Brown #include <linux/module.h> 142159ad93SMark Brown #include <linux/moduleparam.h> 152159ad93SMark Brown #include <linux/init.h> 162159ad93SMark Brown #include <linux/delay.h> 172159ad93SMark Brown #include <linux/firmware.h> 182159ad93SMark Brown #include <linux/pm.h> 192159ad93SMark Brown #include <linux/pm_runtime.h> 202159ad93SMark Brown #include <linux/regmap.h> 21973838a0SMark Brown #include <linux/regulator/consumer.h> 222159ad93SMark Brown #include <linux/slab.h> 232159ad93SMark Brown #include <sound/core.h> 242159ad93SMark Brown #include <sound/pcm.h> 252159ad93SMark Brown #include <sound/pcm_params.h> 262159ad93SMark Brown #include <sound/soc.h> 272159ad93SMark Brown #include <sound/jack.h> 282159ad93SMark Brown #include <sound/initval.h> 292159ad93SMark Brown #include <sound/tlv.h> 302159ad93SMark Brown 312159ad93SMark Brown #include <linux/mfd/arizona/registers.h> 322159ad93SMark Brown 332159ad93SMark Brown #include "wm_adsp.h" 342159ad93SMark Brown 352159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 362159ad93SMark Brown dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 372159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 382159ad93SMark Brown dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 392159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 402159ad93SMark Brown dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 412159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 422159ad93SMark Brown dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 432159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 442159ad93SMark Brown dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 452159ad93SMark Brown 462159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 472159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 482159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 492159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 502159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 512159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 522159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 532159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 542159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 552159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 562159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 572159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 582159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 592159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 602159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 612159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 622159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 632159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 642159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 652159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 662159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 672159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 682159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 692159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 702159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 712159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 722159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 732159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 742159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 752159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 762159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 772159ad93SMark Brown 782159ad93SMark Brown /* 792159ad93SMark Brown * ADSP1 Control 19 802159ad93SMark Brown */ 812159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 822159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 832159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 842159ad93SMark Brown 852159ad93SMark Brown 862159ad93SMark Brown /* 872159ad93SMark Brown * ADSP1 Control 30 882159ad93SMark Brown */ 892159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 902159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 912159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 932159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 942159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 952159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 962159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 972159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 982159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 992159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1002159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1012159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1022159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1032159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1042159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1052159ad93SMark Brown 10694e205bfSChris Rattray /* 10794e205bfSChris Rattray * ADSP1 Control 31 10894e205bfSChris Rattray */ 10994e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11094e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11194e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11294e205bfSChris Rattray 1132159ad93SMark Brown #define ADSP2_CONTROL 0 114973838a0SMark Brown #define ADSP2_CLOCKING 1 1152159ad93SMark Brown #define ADSP2_STATUS1 4 1162159ad93SMark Brown 1172159ad93SMark Brown /* 1182159ad93SMark Brown * ADSP2 Control 1192159ad93SMark Brown */ 1202159ad93SMark Brown 1212159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1222159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1232159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1242159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1252159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1262159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1272159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1282159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1292159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1302159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1312159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1322159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1332159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1342159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1352159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1362159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1372159ad93SMark Brown 1382159ad93SMark Brown /* 139973838a0SMark Brown * ADSP2 clocking 140973838a0SMark Brown */ 141973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 142973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 143973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 144973838a0SMark Brown 145973838a0SMark Brown /* 1462159ad93SMark Brown * ADSP2 Status 1 1472159ad93SMark Brown */ 1482159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1492159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1502159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1512159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1522159ad93SMark Brown 15336e8fe99SMark Brown #define WM_ADSP_NUM_FW 4 1541023dbd9SMark Brown 1551023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 15636e8fe99SMark Brown "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC" 1571023dbd9SMark Brown }; 1581023dbd9SMark Brown 1591023dbd9SMark Brown static struct { 1601023dbd9SMark Brown const char *file; 1611023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 1621023dbd9SMark Brown { .file = "mbc-vss" }, 1631023dbd9SMark Brown { .file = "tx" }, 16436e8fe99SMark Brown { .file = "tx-spk" }, 1651023dbd9SMark Brown { .file = "rx-anc" }, 1661023dbd9SMark Brown }; 1671023dbd9SMark Brown 1681023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 1691023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 1701023dbd9SMark Brown { 1711023dbd9SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 1721023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 1731023dbd9SMark Brown struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); 1741023dbd9SMark Brown 1751023dbd9SMark Brown ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; 1761023dbd9SMark Brown 1771023dbd9SMark Brown return 0; 1781023dbd9SMark Brown } 1791023dbd9SMark Brown 1801023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 1811023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 1821023dbd9SMark Brown { 1831023dbd9SMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 1841023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 1851023dbd9SMark Brown struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); 1861023dbd9SMark Brown 1871023dbd9SMark Brown if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) 1881023dbd9SMark Brown return 0; 1891023dbd9SMark Brown 1901023dbd9SMark Brown if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) 1911023dbd9SMark Brown return -EINVAL; 1921023dbd9SMark Brown 1931023dbd9SMark Brown if (adsp[e->shift_l].running) 1941023dbd9SMark Brown return -EBUSY; 1951023dbd9SMark Brown 1961023dbd9SMark Brown adsp->fw = ucontrol->value.integer.value[0]; 1971023dbd9SMark Brown 1981023dbd9SMark Brown return 0; 1991023dbd9SMark Brown } 2001023dbd9SMark Brown 2011023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 2021023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2031023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2041023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2051023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 2061023dbd9SMark Brown }; 2071023dbd9SMark Brown 2081023dbd9SMark Brown const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 2091023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 2101023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2111023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 2121023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2131023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 2141023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2151023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 2161023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 2171023dbd9SMark Brown }; 2181023dbd9SMark Brown EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 2192159ad93SMark Brown 2202159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 2212159ad93SMark Brown int type) 2222159ad93SMark Brown { 2232159ad93SMark Brown int i; 2242159ad93SMark Brown 2252159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 2262159ad93SMark Brown if (dsp->mem[i].type == type) 2272159ad93SMark Brown return &dsp->mem[i]; 2282159ad93SMark Brown 2292159ad93SMark Brown return NULL; 2302159ad93SMark Brown } 2312159ad93SMark Brown 23245b9ee72SMark Brown static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, 23345b9ee72SMark Brown unsigned int offset) 23445b9ee72SMark Brown { 23545b9ee72SMark Brown switch (region->type) { 23645b9ee72SMark Brown case WMFW_ADSP1_PM: 23745b9ee72SMark Brown return region->base + (offset * 3); 23845b9ee72SMark Brown case WMFW_ADSP1_DM: 23945b9ee72SMark Brown return region->base + (offset * 2); 24045b9ee72SMark Brown case WMFW_ADSP2_XM: 24145b9ee72SMark Brown return region->base + (offset * 2); 24245b9ee72SMark Brown case WMFW_ADSP2_YM: 24345b9ee72SMark Brown return region->base + (offset * 2); 24445b9ee72SMark Brown case WMFW_ADSP1_ZM: 24545b9ee72SMark Brown return region->base + (offset * 2); 24645b9ee72SMark Brown default: 24745b9ee72SMark Brown WARN_ON(NULL != "Unknown memory region type"); 24845b9ee72SMark Brown return offset; 24945b9ee72SMark Brown } 25045b9ee72SMark Brown } 25145b9ee72SMark Brown 2522159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 2532159ad93SMark Brown { 2542159ad93SMark Brown const struct firmware *firmware; 2552159ad93SMark Brown struct regmap *regmap = dsp->regmap; 2562159ad93SMark Brown unsigned int pos = 0; 2572159ad93SMark Brown const struct wmfw_header *header; 2582159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 2592159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 2602159ad93SMark Brown const struct wmfw_footer *footer; 2612159ad93SMark Brown const struct wmfw_region *region; 2622159ad93SMark Brown const struct wm_adsp_region *mem; 2632159ad93SMark Brown const char *region_name; 2642159ad93SMark Brown char *file, *text; 265a76fefabSMark Brown void *buf; 2662159ad93SMark Brown unsigned int reg; 2672159ad93SMark Brown int regions = 0; 2682159ad93SMark Brown int ret, offset, type, sizes; 2692159ad93SMark Brown 2702159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 2712159ad93SMark Brown if (file == NULL) 2722159ad93SMark Brown return -ENOMEM; 2732159ad93SMark Brown 2741023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 2751023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 2762159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 2772159ad93SMark Brown 2782159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 2792159ad93SMark Brown if (ret != 0) { 2802159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 2812159ad93SMark Brown goto out; 2822159ad93SMark Brown } 2832159ad93SMark Brown ret = -EINVAL; 2842159ad93SMark Brown 2852159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 2862159ad93SMark Brown if (pos >= firmware->size) { 2872159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 2882159ad93SMark Brown file, firmware->size); 2892159ad93SMark Brown goto out_fw; 2902159ad93SMark Brown } 2912159ad93SMark Brown 2922159ad93SMark Brown header = (void*)&firmware->data[0]; 2932159ad93SMark Brown 2942159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 2952159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2962159ad93SMark Brown goto out_fw; 2972159ad93SMark Brown } 2982159ad93SMark Brown 2992159ad93SMark Brown if (header->ver != 0) { 3002159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 3012159ad93SMark Brown file, header->ver); 3022159ad93SMark Brown goto out_fw; 3032159ad93SMark Brown } 3042159ad93SMark Brown 3052159ad93SMark Brown if (header->core != dsp->type) { 3062159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 3072159ad93SMark Brown file, header->core, dsp->type); 3082159ad93SMark Brown goto out_fw; 3092159ad93SMark Brown } 3102159ad93SMark Brown 3112159ad93SMark Brown switch (dsp->type) { 3122159ad93SMark Brown case WMFW_ADSP1: 3132159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 3142159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 3152159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 3162159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 3172159ad93SMark Brown 3182159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 3192159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 3202159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 3212159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 3222159ad93SMark Brown break; 3232159ad93SMark Brown 3242159ad93SMark Brown case WMFW_ADSP2: 3252159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 3262159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 3272159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 3282159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 3292159ad93SMark Brown 3302159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 3312159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 3322159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 3332159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 3342159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 3352159ad93SMark Brown break; 3362159ad93SMark Brown 3372159ad93SMark Brown default: 3382159ad93SMark Brown BUG_ON(NULL == "Unknown DSP type"); 3392159ad93SMark Brown goto out_fw; 3402159ad93SMark Brown } 3412159ad93SMark Brown 3422159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 3432159ad93SMark Brown sizes + sizeof(*footer)) { 3442159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 3452159ad93SMark Brown file, le32_to_cpu(header->len)); 3462159ad93SMark Brown goto out_fw; 3472159ad93SMark Brown } 3482159ad93SMark Brown 3492159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 3502159ad93SMark Brown le64_to_cpu(footer->timestamp)); 3512159ad93SMark Brown 3522159ad93SMark Brown while (pos < firmware->size && 3532159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 3542159ad93SMark Brown region = (void *)&(firmware->data[pos]); 3552159ad93SMark Brown region_name = "Unknown"; 3562159ad93SMark Brown reg = 0; 3572159ad93SMark Brown text = NULL; 3582159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 3592159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 3602159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 3612159ad93SMark Brown 3622159ad93SMark Brown switch (type) { 3632159ad93SMark Brown case WMFW_NAME_TEXT: 3642159ad93SMark Brown region_name = "Firmware name"; 3652159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 3662159ad93SMark Brown GFP_KERNEL); 3672159ad93SMark Brown break; 3682159ad93SMark Brown case WMFW_INFO_TEXT: 3692159ad93SMark Brown region_name = "Information"; 3702159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 3712159ad93SMark Brown GFP_KERNEL); 3722159ad93SMark Brown break; 3732159ad93SMark Brown case WMFW_ABSOLUTE: 3742159ad93SMark Brown region_name = "Absolute"; 3752159ad93SMark Brown reg = offset; 3762159ad93SMark Brown break; 3772159ad93SMark Brown case WMFW_ADSP1_PM: 3782159ad93SMark Brown BUG_ON(!mem); 3792159ad93SMark Brown region_name = "PM"; 38045b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 3812159ad93SMark Brown break; 3822159ad93SMark Brown case WMFW_ADSP1_DM: 3832159ad93SMark Brown BUG_ON(!mem); 3842159ad93SMark Brown region_name = "DM"; 38545b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 3862159ad93SMark Brown break; 3872159ad93SMark Brown case WMFW_ADSP2_XM: 3882159ad93SMark Brown BUG_ON(!mem); 3892159ad93SMark Brown region_name = "XM"; 39045b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 3912159ad93SMark Brown break; 3922159ad93SMark Brown case WMFW_ADSP2_YM: 3932159ad93SMark Brown BUG_ON(!mem); 3942159ad93SMark Brown region_name = "YM"; 39545b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 3962159ad93SMark Brown break; 3972159ad93SMark Brown case WMFW_ADSP1_ZM: 3982159ad93SMark Brown BUG_ON(!mem); 3992159ad93SMark Brown region_name = "ZM"; 40045b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 4012159ad93SMark Brown break; 4022159ad93SMark Brown default: 4032159ad93SMark Brown adsp_warn(dsp, 4042159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 4052159ad93SMark Brown file, regions, type, pos, pos); 4062159ad93SMark Brown break; 4072159ad93SMark Brown } 4082159ad93SMark Brown 4092159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 4102159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 4112159ad93SMark Brown region_name); 4122159ad93SMark Brown 4132159ad93SMark Brown if (text) { 4142159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 4152159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 4162159ad93SMark Brown kfree(text); 4172159ad93SMark Brown } 4182159ad93SMark Brown 4192159ad93SMark Brown if (reg) { 420a76fefabSMark Brown buf = kmemdup(region->data, le32_to_cpu(region->len), 4217881fd0fSMark Brown GFP_KERNEL | GFP_DMA); 422a76fefabSMark Brown if (!buf) { 423a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 424a76fefabSMark Brown return -ENOMEM; 425a76fefabSMark Brown } 426a76fefabSMark Brown 427a76fefabSMark Brown ret = regmap_raw_write(regmap, reg, buf, 4282159ad93SMark Brown le32_to_cpu(region->len)); 429a76fefabSMark Brown 430a76fefabSMark Brown kfree(buf); 431a76fefabSMark Brown 4322159ad93SMark Brown if (ret != 0) { 4332159ad93SMark Brown adsp_err(dsp, 4342159ad93SMark Brown "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 4352159ad93SMark Brown file, regions, 4362159ad93SMark Brown le32_to_cpu(region->len), offset, 4372159ad93SMark Brown region_name, ret); 4382159ad93SMark Brown goto out_fw; 4392159ad93SMark Brown } 4402159ad93SMark Brown } 4412159ad93SMark Brown 4422159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 4432159ad93SMark Brown regions++; 4442159ad93SMark Brown } 4452159ad93SMark Brown 4462159ad93SMark Brown if (pos > firmware->size) 4472159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 4482159ad93SMark Brown file, regions, pos - firmware->size); 4492159ad93SMark Brown 4502159ad93SMark Brown out_fw: 4512159ad93SMark Brown release_firmware(firmware); 4522159ad93SMark Brown out: 4532159ad93SMark Brown kfree(file); 4542159ad93SMark Brown 4552159ad93SMark Brown return ret; 4562159ad93SMark Brown } 4572159ad93SMark Brown 458db40517cSMark Brown static int wm_adsp_setup_algs(struct wm_adsp *dsp) 459db40517cSMark Brown { 460db40517cSMark Brown struct regmap *regmap = dsp->regmap; 461db40517cSMark Brown struct wmfw_adsp1_id_hdr adsp1_id; 462db40517cSMark Brown struct wmfw_adsp2_id_hdr adsp2_id; 463db40517cSMark Brown struct wmfw_adsp1_alg_hdr *adsp1_alg; 464db40517cSMark Brown struct wmfw_adsp2_alg_hdr *adsp2_alg; 465d62f4bc6SMark Brown void *alg, *buf; 466471f4885SMark Brown struct wm_adsp_alg_region *region; 467db40517cSMark Brown const struct wm_adsp_region *mem; 468db40517cSMark Brown unsigned int pos, term; 469d62f4bc6SMark Brown size_t algs, buf_size; 470db40517cSMark Brown __be32 val; 471db40517cSMark Brown int i, ret; 472db40517cSMark Brown 473db40517cSMark Brown switch (dsp->type) { 474db40517cSMark Brown case WMFW_ADSP1: 475db40517cSMark Brown mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 476db40517cSMark Brown break; 477db40517cSMark Brown case WMFW_ADSP2: 478db40517cSMark Brown mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 479db40517cSMark Brown break; 480db40517cSMark Brown default: 481db40517cSMark Brown mem = NULL; 482db40517cSMark Brown break; 483db40517cSMark Brown } 484db40517cSMark Brown 485db40517cSMark Brown if (mem == NULL) { 486db40517cSMark Brown BUG_ON(mem != NULL); 487db40517cSMark Brown return -EINVAL; 488db40517cSMark Brown } 489db40517cSMark Brown 490db40517cSMark Brown switch (dsp->type) { 491db40517cSMark Brown case WMFW_ADSP1: 492db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base, &adsp1_id, 493db40517cSMark Brown sizeof(adsp1_id)); 494db40517cSMark Brown if (ret != 0) { 495db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 496db40517cSMark Brown ret); 497db40517cSMark Brown return ret; 498db40517cSMark Brown } 499db40517cSMark Brown 500d62f4bc6SMark Brown buf = &adsp1_id; 501d62f4bc6SMark Brown buf_size = sizeof(adsp1_id); 502d62f4bc6SMark Brown 503db40517cSMark Brown algs = be32_to_cpu(adsp1_id.algs); 504db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 505db40517cSMark Brown be32_to_cpu(adsp1_id.fw.id), 506db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 507db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 508db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 509db40517cSMark Brown algs); 510db40517cSMark Brown 511db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 512db40517cSMark Brown term = pos + ((sizeof(*adsp1_alg) * algs) / 2); 513db40517cSMark Brown break; 514db40517cSMark Brown 515db40517cSMark Brown case WMFW_ADSP2: 516db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base, &adsp2_id, 517db40517cSMark Brown sizeof(adsp2_id)); 518db40517cSMark Brown if (ret != 0) { 519db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 520db40517cSMark Brown ret); 521db40517cSMark Brown return ret; 522db40517cSMark Brown } 523db40517cSMark Brown 524d62f4bc6SMark Brown buf = &adsp2_id; 525d62f4bc6SMark Brown buf_size = sizeof(adsp2_id); 526d62f4bc6SMark Brown 527db40517cSMark Brown algs = be32_to_cpu(adsp2_id.algs); 528db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 529db40517cSMark Brown be32_to_cpu(adsp2_id.fw.id), 530db40517cSMark Brown (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, 531db40517cSMark Brown (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, 532db40517cSMark Brown be32_to_cpu(adsp2_id.fw.ver) & 0xff, 533db40517cSMark Brown algs); 534db40517cSMark Brown 535db40517cSMark Brown pos = sizeof(adsp2_id) / 2; 536db40517cSMark Brown term = pos + ((sizeof(*adsp2_alg) * algs) / 2); 537db40517cSMark Brown break; 538db40517cSMark Brown 539db40517cSMark Brown default: 540db40517cSMark Brown BUG_ON(NULL == "Unknown DSP type"); 541db40517cSMark Brown return -EINVAL; 542db40517cSMark Brown } 543db40517cSMark Brown 544db40517cSMark Brown if (algs == 0) { 545db40517cSMark Brown adsp_err(dsp, "No algorithms\n"); 546db40517cSMark Brown return -EINVAL; 547db40517cSMark Brown } 548db40517cSMark Brown 549d62f4bc6SMark Brown if (algs > 1024) { 550d62f4bc6SMark Brown adsp_err(dsp, "Algorithm count %zx excessive\n", algs); 551d62f4bc6SMark Brown print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, 552d62f4bc6SMark Brown buf, buf_size); 553d62f4bc6SMark Brown return -EINVAL; 554d62f4bc6SMark Brown } 555d62f4bc6SMark Brown 556db40517cSMark Brown /* Read the terminator first to validate the length */ 557db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); 558db40517cSMark Brown if (ret != 0) { 559db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm list end: %d\n", 560db40517cSMark Brown ret); 561db40517cSMark Brown return ret; 562db40517cSMark Brown } 563db40517cSMark Brown 564db40517cSMark Brown if (be32_to_cpu(val) != 0xbedead) 565db40517cSMark Brown adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 566db40517cSMark Brown term, be32_to_cpu(val)); 567db40517cSMark Brown 568f2a93e2aSMark Brown alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); 569db40517cSMark Brown if (!alg) 570db40517cSMark Brown return -ENOMEM; 571db40517cSMark Brown 572db40517cSMark Brown ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); 573db40517cSMark Brown if (ret != 0) { 574db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm list: %d\n", 575db40517cSMark Brown ret); 576db40517cSMark Brown goto out; 577db40517cSMark Brown } 578db40517cSMark Brown 579db40517cSMark Brown adsp1_alg = alg; 580db40517cSMark Brown adsp2_alg = alg; 581db40517cSMark Brown 582db40517cSMark Brown for (i = 0; i < algs; i++) { 583db40517cSMark Brown switch (dsp->type) { 584db40517cSMark Brown case WMFW_ADSP1: 585471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 586db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 587db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 588db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 589471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 590471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 591471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 592471f4885SMark Brown 593471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 594471f4885SMark Brown if (!region) 595471f4885SMark Brown return -ENOMEM; 596471f4885SMark Brown region->type = WMFW_ADSP1_DM; 597471f4885SMark Brown region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 598471f4885SMark Brown region->base = be32_to_cpu(adsp1_alg[i].dm); 5997480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 600471f4885SMark Brown 601471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 602471f4885SMark Brown if (!region) 603471f4885SMark Brown return -ENOMEM; 604471f4885SMark Brown region->type = WMFW_ADSP1_ZM; 605471f4885SMark Brown region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 606471f4885SMark Brown region->base = be32_to_cpu(adsp1_alg[i].zm); 6077480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 608db40517cSMark Brown break; 609db40517cSMark Brown 610db40517cSMark Brown case WMFW_ADSP2: 611471f4885SMark Brown adsp_info(dsp, 612471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 613db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 614db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 615db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 616471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 617471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 618471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 619471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 620471f4885SMark Brown 621471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 622471f4885SMark Brown if (!region) 623471f4885SMark Brown return -ENOMEM; 624471f4885SMark Brown region->type = WMFW_ADSP2_XM; 625471f4885SMark Brown region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 626471f4885SMark Brown region->base = be32_to_cpu(adsp2_alg[i].xm); 6277480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 628471f4885SMark Brown 629471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 630471f4885SMark Brown if (!region) 631471f4885SMark Brown return -ENOMEM; 632471f4885SMark Brown region->type = WMFW_ADSP2_YM; 633471f4885SMark Brown region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 634471f4885SMark Brown region->base = be32_to_cpu(adsp2_alg[i].ym); 6357480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 636471f4885SMark Brown 637471f4885SMark Brown region = kzalloc(sizeof(*region), GFP_KERNEL); 638471f4885SMark Brown if (!region) 639471f4885SMark Brown return -ENOMEM; 640471f4885SMark Brown region->type = WMFW_ADSP2_ZM; 641471f4885SMark Brown region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 642471f4885SMark Brown region->base = be32_to_cpu(adsp2_alg[i].zm); 6437480800eSMark Brown list_add_tail(®ion->list, &dsp->alg_regions); 644db40517cSMark Brown break; 645db40517cSMark Brown } 646db40517cSMark Brown } 647db40517cSMark Brown 648db40517cSMark Brown out: 649db40517cSMark Brown kfree(alg); 650db40517cSMark Brown return ret; 651db40517cSMark Brown } 652db40517cSMark Brown 6532159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 6542159ad93SMark Brown { 6552159ad93SMark Brown struct regmap *regmap = dsp->regmap; 6562159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 6572159ad93SMark Brown struct wmfw_coeff_item *blk; 6582159ad93SMark Brown const struct firmware *firmware; 659471f4885SMark Brown const struct wm_adsp_region *mem; 660471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 6612159ad93SMark Brown const char *region_name; 6622159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 6632159ad93SMark Brown char *file; 664a76fefabSMark Brown void *buf; 6652159ad93SMark Brown 6662159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 6672159ad93SMark Brown if (file == NULL) 6682159ad93SMark Brown return -ENOMEM; 6692159ad93SMark Brown 6701023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 6711023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 6722159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 6732159ad93SMark Brown 6742159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 6752159ad93SMark Brown if (ret != 0) { 6762159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 6772159ad93SMark Brown ret = 0; 6782159ad93SMark Brown goto out; 6792159ad93SMark Brown } 6802159ad93SMark Brown ret = -EINVAL; 6812159ad93SMark Brown 6822159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 6832159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 6842159ad93SMark Brown file, firmware->size); 6852159ad93SMark Brown goto out_fw; 6862159ad93SMark Brown } 6872159ad93SMark Brown 6882159ad93SMark Brown hdr = (void*)&firmware->data[0]; 6892159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 6902159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 6912159ad93SMark Brown return -EINVAL; 6922159ad93SMark Brown } 6932159ad93SMark Brown 694c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 695c712326dSMark Brown case 1: 696c712326dSMark Brown break; 697c712326dSMark Brown default: 698c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 699c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 700c712326dSMark Brown ret = -EINVAL; 701c712326dSMark Brown goto out_fw; 702c712326dSMark Brown } 703c712326dSMark Brown 7042159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 7052159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 7062159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 7072159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 7082159ad93SMark Brown 7092159ad93SMark Brown pos = le32_to_cpu(hdr->len); 7102159ad93SMark Brown 7112159ad93SMark Brown blocks = 0; 7122159ad93SMark Brown while (pos < firmware->size && 7132159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 7142159ad93SMark Brown blk = (void*)(&firmware->data[pos]); 7152159ad93SMark Brown 716c712326dSMark Brown type = le16_to_cpu(blk->type); 717c712326dSMark Brown offset = le16_to_cpu(blk->offset); 7182159ad93SMark Brown 7192159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 7202159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 7212159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 7222159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 7232159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 7242159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 7252159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 7262159ad93SMark Brown 7272159ad93SMark Brown reg = 0; 7282159ad93SMark Brown region_name = "Unknown"; 7292159ad93SMark Brown switch (type) { 730c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 731c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 7322159ad93SMark Brown break; 733c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 7342159ad93SMark Brown region_name = "register"; 7352159ad93SMark Brown reg = offset; 7362159ad93SMark Brown break; 737471f4885SMark Brown 738471f4885SMark Brown case WMFW_ADSP1_DM: 739471f4885SMark Brown case WMFW_ADSP1_ZM: 740471f4885SMark Brown case WMFW_ADSP2_XM: 741471f4885SMark Brown case WMFW_ADSP2_YM: 742471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 743471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 744471f4885SMark Brown type, le32_to_cpu(blk->id)); 745471f4885SMark Brown 746471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 747471f4885SMark Brown if (!mem) { 748471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 749471f4885SMark Brown break; 750471f4885SMark Brown } 751471f4885SMark Brown 752471f4885SMark Brown reg = 0; 753471f4885SMark Brown list_for_each_entry(alg_region, 754471f4885SMark Brown &dsp->alg_regions, list) { 755471f4885SMark Brown if (le32_to_cpu(blk->id) == alg_region->alg && 756471f4885SMark Brown type == alg_region->type) { 757338c5188SMark Brown reg = alg_region->base; 758471f4885SMark Brown reg = wm_adsp_region_to_reg(mem, 759471f4885SMark Brown reg); 760338c5188SMark Brown reg += offset; 761471f4885SMark Brown } 762471f4885SMark Brown } 763471f4885SMark Brown 764471f4885SMark Brown if (reg == 0) 765471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 766471f4885SMark Brown type, le32_to_cpu(blk->id)); 767471f4885SMark Brown break; 768471f4885SMark Brown 7692159ad93SMark Brown default: 77025c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 77125c62f7eSMark Brown file, blocks, type, pos); 7722159ad93SMark Brown break; 7732159ad93SMark Brown } 7742159ad93SMark Brown 7752159ad93SMark Brown if (reg) { 776a76fefabSMark Brown buf = kmemdup(blk->data, le32_to_cpu(blk->len), 7777881fd0fSMark Brown GFP_KERNEL | GFP_DMA); 778a76fefabSMark Brown if (!buf) { 779a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 780a76fefabSMark Brown return -ENOMEM; 781a76fefabSMark Brown } 782a76fefabSMark Brown 78320da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 78420da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 78520da6d5aSMark Brown reg); 7862159ad93SMark Brown ret = regmap_raw_write(regmap, reg, blk->data, 7872159ad93SMark Brown le32_to_cpu(blk->len)); 7882159ad93SMark Brown if (ret != 0) { 7892159ad93SMark Brown adsp_err(dsp, 7902159ad93SMark Brown "%s.%d: Failed to write to %x in %s\n", 7912159ad93SMark Brown file, blocks, reg, region_name); 7922159ad93SMark Brown } 793a76fefabSMark Brown 794a76fefabSMark Brown kfree(buf); 7952159ad93SMark Brown } 7962159ad93SMark Brown 7972159ad93SMark Brown pos += le32_to_cpu(blk->len) + sizeof(*blk); 7982159ad93SMark Brown blocks++; 7992159ad93SMark Brown } 8002159ad93SMark Brown 8012159ad93SMark Brown if (pos > firmware->size) 8022159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 8032159ad93SMark Brown file, blocks, pos - firmware->size); 8042159ad93SMark Brown 8052159ad93SMark Brown out_fw: 8062159ad93SMark Brown release_firmware(firmware); 8072159ad93SMark Brown out: 8082159ad93SMark Brown kfree(file); 8092159ad93SMark Brown return 0; 8102159ad93SMark Brown } 8112159ad93SMark Brown 8125e7a7a22SMark Brown int wm_adsp1_init(struct wm_adsp *adsp) 8135e7a7a22SMark Brown { 8145e7a7a22SMark Brown INIT_LIST_HEAD(&adsp->alg_regions); 8155e7a7a22SMark Brown 8165e7a7a22SMark Brown return 0; 8175e7a7a22SMark Brown } 8185e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 8195e7a7a22SMark Brown 8202159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 8212159ad93SMark Brown struct snd_kcontrol *kcontrol, 8222159ad93SMark Brown int event) 8232159ad93SMark Brown { 8242159ad93SMark Brown struct snd_soc_codec *codec = w->codec; 8252159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 8262159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 8272159ad93SMark Brown int ret; 82894e205bfSChris Rattray int val; 8292159ad93SMark Brown 8302159ad93SMark Brown switch (event) { 8312159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 8322159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 8332159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 8342159ad93SMark Brown 83594e205bfSChris Rattray /* 83694e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 83794e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 83894e205bfSChris Rattray */ 83994e205bfSChris Rattray if(dsp->sysclk_reg) { 84094e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 84194e205bfSChris Rattray if (ret != 0) { 84294e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 84394e205bfSChris Rattray ret); 84494e205bfSChris Rattray return ret; 84594e205bfSChris Rattray } 84694e205bfSChris Rattray 84794e205bfSChris Rattray val = (val & dsp->sysclk_mask) 84894e205bfSChris Rattray >> dsp->sysclk_shift; 84994e205bfSChris Rattray 85094e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 85194e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 85294e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 85394e205bfSChris Rattray if (ret != 0) { 85494e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 85594e205bfSChris Rattray ret); 85694e205bfSChris Rattray return ret; 85794e205bfSChris Rattray } 85894e205bfSChris Rattray } 85994e205bfSChris Rattray 8602159ad93SMark Brown ret = wm_adsp_load(dsp); 8612159ad93SMark Brown if (ret != 0) 8622159ad93SMark Brown goto err; 8632159ad93SMark Brown 864db40517cSMark Brown ret = wm_adsp_setup_algs(dsp); 865db40517cSMark Brown if (ret != 0) 866db40517cSMark Brown goto err; 867db40517cSMark Brown 8682159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 8692159ad93SMark Brown if (ret != 0) 8702159ad93SMark Brown goto err; 8712159ad93SMark Brown 8722159ad93SMark Brown /* Start the core running */ 8732159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 8742159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 8752159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 8762159ad93SMark Brown break; 8772159ad93SMark Brown 8782159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 8792159ad93SMark Brown /* Halt the core */ 8802159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 8812159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 8822159ad93SMark Brown 8832159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 8842159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 8852159ad93SMark Brown 8862159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 8872159ad93SMark Brown ADSP1_SYS_ENA, 0); 8882159ad93SMark Brown break; 8892159ad93SMark Brown 8902159ad93SMark Brown default: 8912159ad93SMark Brown break; 8922159ad93SMark Brown } 8932159ad93SMark Brown 8942159ad93SMark Brown return 0; 8952159ad93SMark Brown 8962159ad93SMark Brown err: 8972159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 8982159ad93SMark Brown ADSP1_SYS_ENA, 0); 8992159ad93SMark Brown return ret; 9002159ad93SMark Brown } 9012159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 9022159ad93SMark Brown 9032159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 9042159ad93SMark Brown { 9052159ad93SMark Brown unsigned int val; 9062159ad93SMark Brown int ret, count; 9072159ad93SMark Brown 9082159ad93SMark Brown ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 9092159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 9102159ad93SMark Brown if (ret != 0) 9112159ad93SMark Brown return ret; 9122159ad93SMark Brown 9132159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 9142159ad93SMark Brown count = 0; 9152159ad93SMark Brown do { 9162159ad93SMark Brown ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, 9172159ad93SMark Brown &val); 9182159ad93SMark Brown if (ret != 0) 9192159ad93SMark Brown return ret; 9202159ad93SMark Brown } while (!(val & ADSP2_RAM_RDY) && ++count < 10); 9212159ad93SMark Brown 9222159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 9232159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 9242159ad93SMark Brown return -EBUSY; 9252159ad93SMark Brown } 9262159ad93SMark Brown 9272159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 9282159ad93SMark Brown adsp_info(dsp, "RAM ready after %d polls\n", count); 9292159ad93SMark Brown 9302159ad93SMark Brown return 0; 9312159ad93SMark Brown } 9322159ad93SMark Brown 9332159ad93SMark Brown int wm_adsp2_event(struct snd_soc_dapm_widget *w, 9342159ad93SMark Brown struct snd_kcontrol *kcontrol, int event) 9352159ad93SMark Brown { 9362159ad93SMark Brown struct snd_soc_codec *codec = w->codec; 9372159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 9382159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 939471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 940973838a0SMark Brown unsigned int val; 9412159ad93SMark Brown int ret; 9422159ad93SMark Brown 9432159ad93SMark Brown switch (event) { 9442159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 945dd49e2c8SMark Brown /* 946dd49e2c8SMark Brown * For simplicity set the DSP clock rate to be the 947dd49e2c8SMark Brown * SYSCLK rate rather than making it configurable. 948dd49e2c8SMark Brown */ 949dd49e2c8SMark Brown ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); 950dd49e2c8SMark Brown if (ret != 0) { 951dd49e2c8SMark Brown adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 952dd49e2c8SMark Brown ret); 953dd49e2c8SMark Brown return ret; 954dd49e2c8SMark Brown } 955dd49e2c8SMark Brown val = (val & ARIZONA_SYSCLK_FREQ_MASK) 956dd49e2c8SMark Brown >> ARIZONA_SYSCLK_FREQ_SHIFT; 957dd49e2c8SMark Brown 958dd49e2c8SMark Brown ret = regmap_update_bits(dsp->regmap, 959dd49e2c8SMark Brown dsp->base + ADSP2_CLOCKING, 960dd49e2c8SMark Brown ADSP2_CLK_SEL_MASK, val); 961dd49e2c8SMark Brown if (ret != 0) { 962dd49e2c8SMark Brown adsp_err(dsp, "Failed to set clock rate: %d\n", 963dd49e2c8SMark Brown ret); 964dd49e2c8SMark Brown return ret; 965dd49e2c8SMark Brown } 966dd49e2c8SMark Brown 967973838a0SMark Brown if (dsp->dvfs) { 968973838a0SMark Brown ret = regmap_read(dsp->regmap, 969973838a0SMark Brown dsp->base + ADSP2_CLOCKING, &val); 970973838a0SMark Brown if (ret != 0) { 971973838a0SMark Brown dev_err(dsp->dev, 972973838a0SMark Brown "Failed to read clocking: %d\n", ret); 973973838a0SMark Brown return ret; 974973838a0SMark Brown } 975973838a0SMark Brown 97625c6fdb0SMark Brown if ((val & ADSP2_CLK_SEL_MASK) >= 3) { 977973838a0SMark Brown ret = regulator_enable(dsp->dvfs); 978973838a0SMark Brown if (ret != 0) { 979973838a0SMark Brown dev_err(dsp->dev, 980973838a0SMark Brown "Failed to enable supply: %d\n", 981973838a0SMark Brown ret); 982973838a0SMark Brown return ret; 983973838a0SMark Brown } 984973838a0SMark Brown 985973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 986973838a0SMark Brown 1800000, 987973838a0SMark Brown 1800000); 988973838a0SMark Brown if (ret != 0) { 989973838a0SMark Brown dev_err(dsp->dev, 990973838a0SMark Brown "Failed to raise supply: %d\n", 991973838a0SMark Brown ret); 992973838a0SMark Brown return ret; 993973838a0SMark Brown } 994973838a0SMark Brown } 995973838a0SMark Brown } 996973838a0SMark Brown 9972159ad93SMark Brown ret = wm_adsp2_ena(dsp); 9982159ad93SMark Brown if (ret != 0) 9992159ad93SMark Brown return ret; 10002159ad93SMark Brown 10012159ad93SMark Brown ret = wm_adsp_load(dsp); 10022159ad93SMark Brown if (ret != 0) 10032159ad93SMark Brown goto err; 10042159ad93SMark Brown 1005db40517cSMark Brown ret = wm_adsp_setup_algs(dsp); 1006db40517cSMark Brown if (ret != 0) 1007db40517cSMark Brown goto err; 1008db40517cSMark Brown 10092159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 10102159ad93SMark Brown if (ret != 0) 10112159ad93SMark Brown goto err; 10122159ad93SMark Brown 10132159ad93SMark Brown ret = regmap_update_bits(dsp->regmap, 10142159ad93SMark Brown dsp->base + ADSP2_CONTROL, 1015a7f9be7eSMark Brown ADSP2_CORE_ENA | ADSP2_START, 1016a7f9be7eSMark Brown ADSP2_CORE_ENA | ADSP2_START); 10172159ad93SMark Brown if (ret != 0) 10182159ad93SMark Brown goto err; 10191023dbd9SMark Brown 10201023dbd9SMark Brown dsp->running = true; 10212159ad93SMark Brown break; 10222159ad93SMark Brown 10232159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 10241023dbd9SMark Brown dsp->running = false; 10251023dbd9SMark Brown 10262159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1027a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | 1028a7f9be7eSMark Brown ADSP2_START, 0); 1029973838a0SMark Brown 1030973838a0SMark Brown if (dsp->dvfs) { 1031973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 1200000, 1032973838a0SMark Brown 1800000); 1033973838a0SMark Brown if (ret != 0) 1034973838a0SMark Brown dev_warn(dsp->dev, 1035973838a0SMark Brown "Failed to lower supply: %d\n", 1036973838a0SMark Brown ret); 1037973838a0SMark Brown 1038973838a0SMark Brown ret = regulator_disable(dsp->dvfs); 1039973838a0SMark Brown if (ret != 0) 1040973838a0SMark Brown dev_err(dsp->dev, 1041973838a0SMark Brown "Failed to enable supply: %d\n", 1042973838a0SMark Brown ret); 1043973838a0SMark Brown } 1044471f4885SMark Brown 1045471f4885SMark Brown while (!list_empty(&dsp->alg_regions)) { 1046471f4885SMark Brown alg_region = list_first_entry(&dsp->alg_regions, 1047471f4885SMark Brown struct wm_adsp_alg_region, 1048471f4885SMark Brown list); 1049471f4885SMark Brown list_del(&alg_region->list); 1050471f4885SMark Brown kfree(alg_region); 1051471f4885SMark Brown } 10522159ad93SMark Brown break; 10532159ad93SMark Brown 10542159ad93SMark Brown default: 10552159ad93SMark Brown break; 10562159ad93SMark Brown } 10572159ad93SMark Brown 10582159ad93SMark Brown return 0; 10592159ad93SMark Brown err: 10602159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 1061a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 10622159ad93SMark Brown return ret; 10632159ad93SMark Brown } 10642159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 1065973838a0SMark Brown 1066973838a0SMark Brown int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) 1067973838a0SMark Brown { 1068973838a0SMark Brown int ret; 1069973838a0SMark Brown 107010a2b662SMark Brown /* 107110a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 107210a2b662SMark Brown * power saving. 107310a2b662SMark Brown */ 107410a2b662SMark Brown ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL, 107510a2b662SMark Brown ADSP2_MEM_ENA, 0); 107610a2b662SMark Brown if (ret != 0) { 107710a2b662SMark Brown adsp_err(adsp, "Failed to clear memory retention: %d\n", ret); 107810a2b662SMark Brown return ret; 107910a2b662SMark Brown } 108010a2b662SMark Brown 1081471f4885SMark Brown INIT_LIST_HEAD(&adsp->alg_regions); 1082471f4885SMark Brown 1083973838a0SMark Brown if (dvfs) { 1084973838a0SMark Brown adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); 1085973838a0SMark Brown if (IS_ERR(adsp->dvfs)) { 1086973838a0SMark Brown ret = PTR_ERR(adsp->dvfs); 1087973838a0SMark Brown dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); 1088973838a0SMark Brown return ret; 1089973838a0SMark Brown } 1090973838a0SMark Brown 1091973838a0SMark Brown ret = regulator_enable(adsp->dvfs); 1092973838a0SMark Brown if (ret != 0) { 1093973838a0SMark Brown dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", 1094973838a0SMark Brown ret); 1095973838a0SMark Brown return ret; 1096973838a0SMark Brown } 1097973838a0SMark Brown 1098973838a0SMark Brown ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); 1099973838a0SMark Brown if (ret != 0) { 1100973838a0SMark Brown dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", 1101973838a0SMark Brown ret); 1102973838a0SMark Brown return ret; 1103973838a0SMark Brown } 1104973838a0SMark Brown 1105973838a0SMark Brown ret = regulator_disable(adsp->dvfs); 1106973838a0SMark Brown if (ret != 0) { 1107973838a0SMark Brown dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", 1108973838a0SMark Brown ret); 1109973838a0SMark Brown return ret; 1110973838a0SMark Brown } 1111973838a0SMark Brown } 1112973838a0SMark Brown 1113973838a0SMark Brown return 0; 1114973838a0SMark Brown } 1115973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 1116