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 1062159ad93SMark Brown #define ADSP2_CONTROL 0 107973838a0SMark Brown #define ADSP2_CLOCKING 1 1082159ad93SMark Brown #define ADSP2_STATUS1 4 1092159ad93SMark Brown 1102159ad93SMark Brown /* 1112159ad93SMark Brown * ADSP2 Control 1122159ad93SMark Brown */ 1132159ad93SMark Brown 1142159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1152159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1162159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1172159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1182159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1192159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1202159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1212159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1222159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1232159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1242159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1252159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1262159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1272159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1282159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1292159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1302159ad93SMark Brown 1312159ad93SMark Brown /* 132973838a0SMark Brown * ADSP2 clocking 133973838a0SMark Brown */ 134973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 135973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 136973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 137973838a0SMark Brown 138973838a0SMark Brown /* 1392159ad93SMark Brown * ADSP2 Status 1 1402159ad93SMark Brown */ 1412159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1422159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1432159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1442159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1452159ad93SMark Brown 1462159ad93SMark Brown 1472159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 1482159ad93SMark Brown int type) 1492159ad93SMark Brown { 1502159ad93SMark Brown int i; 1512159ad93SMark Brown 1522159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 1532159ad93SMark Brown if (dsp->mem[i].type == type) 1542159ad93SMark Brown return &dsp->mem[i]; 1552159ad93SMark Brown 1562159ad93SMark Brown return NULL; 1572159ad93SMark Brown } 1582159ad93SMark Brown 1592159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 1602159ad93SMark Brown { 1612159ad93SMark Brown const struct firmware *firmware; 1622159ad93SMark Brown struct regmap *regmap = dsp->regmap; 1632159ad93SMark Brown unsigned int pos = 0; 1642159ad93SMark Brown const struct wmfw_header *header; 1652159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 1662159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 1672159ad93SMark Brown const struct wmfw_footer *footer; 1682159ad93SMark Brown const struct wmfw_region *region; 1692159ad93SMark Brown const struct wm_adsp_region *mem; 1702159ad93SMark Brown const char *region_name; 1712159ad93SMark Brown char *file, *text; 1722159ad93SMark Brown unsigned int reg; 1732159ad93SMark Brown int regions = 0; 1742159ad93SMark Brown int ret, offset, type, sizes; 1752159ad93SMark Brown 1762159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 1772159ad93SMark Brown if (file == NULL) 1782159ad93SMark Brown return -ENOMEM; 1792159ad93SMark Brown 1802159ad93SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num); 1812159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 1822159ad93SMark Brown 1832159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 1842159ad93SMark Brown if (ret != 0) { 1852159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 1862159ad93SMark Brown goto out; 1872159ad93SMark Brown } 1882159ad93SMark Brown ret = -EINVAL; 1892159ad93SMark Brown 1902159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 1912159ad93SMark Brown if (pos >= firmware->size) { 1922159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 1932159ad93SMark Brown file, firmware->size); 1942159ad93SMark Brown goto out_fw; 1952159ad93SMark Brown } 1962159ad93SMark Brown 1972159ad93SMark Brown header = (void*)&firmware->data[0]; 1982159ad93SMark Brown 1992159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 2002159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2012159ad93SMark Brown goto out_fw; 2022159ad93SMark Brown } 2032159ad93SMark Brown 2042159ad93SMark Brown if (header->ver != 0) { 2052159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 2062159ad93SMark Brown file, header->ver); 2072159ad93SMark Brown goto out_fw; 2082159ad93SMark Brown } 2092159ad93SMark Brown 2102159ad93SMark Brown if (header->core != dsp->type) { 2112159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 2122159ad93SMark Brown file, header->core, dsp->type); 2132159ad93SMark Brown goto out_fw; 2142159ad93SMark Brown } 2152159ad93SMark Brown 2162159ad93SMark Brown switch (dsp->type) { 2172159ad93SMark Brown case WMFW_ADSP1: 2182159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 2192159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 2202159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 2212159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 2222159ad93SMark Brown 2232159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 2242159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 2252159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 2262159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 2272159ad93SMark Brown break; 2282159ad93SMark Brown 2292159ad93SMark Brown case WMFW_ADSP2: 2302159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 2312159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 2322159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 2332159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 2342159ad93SMark Brown 2352159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 2362159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 2372159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 2382159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 2392159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 2402159ad93SMark Brown break; 2412159ad93SMark Brown 2422159ad93SMark Brown default: 2432159ad93SMark Brown BUG_ON(NULL == "Unknown DSP type"); 2442159ad93SMark Brown goto out_fw; 2452159ad93SMark Brown } 2462159ad93SMark Brown 2472159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 2482159ad93SMark Brown sizes + sizeof(*footer)) { 2492159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 2502159ad93SMark Brown file, le32_to_cpu(header->len)); 2512159ad93SMark Brown goto out_fw; 2522159ad93SMark Brown } 2532159ad93SMark Brown 2542159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 2552159ad93SMark Brown le64_to_cpu(footer->timestamp)); 2562159ad93SMark Brown 2572159ad93SMark Brown while (pos < firmware->size && 2582159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 2592159ad93SMark Brown region = (void *)&(firmware->data[pos]); 2602159ad93SMark Brown region_name = "Unknown"; 2612159ad93SMark Brown reg = 0; 2622159ad93SMark Brown text = NULL; 2632159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 2642159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 2652159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 2662159ad93SMark Brown 2672159ad93SMark Brown switch (type) { 2682159ad93SMark Brown case WMFW_NAME_TEXT: 2692159ad93SMark Brown region_name = "Firmware name"; 2702159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 2712159ad93SMark Brown GFP_KERNEL); 2722159ad93SMark Brown break; 2732159ad93SMark Brown case WMFW_INFO_TEXT: 2742159ad93SMark Brown region_name = "Information"; 2752159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 2762159ad93SMark Brown GFP_KERNEL); 2772159ad93SMark Brown break; 2782159ad93SMark Brown case WMFW_ABSOLUTE: 2792159ad93SMark Brown region_name = "Absolute"; 2802159ad93SMark Brown reg = offset; 2812159ad93SMark Brown break; 2822159ad93SMark Brown case WMFW_ADSP1_PM: 2832159ad93SMark Brown BUG_ON(!mem); 2842159ad93SMark Brown region_name = "PM"; 2852159ad93SMark Brown reg = mem->base + (offset * 3); 2862159ad93SMark Brown break; 2872159ad93SMark Brown case WMFW_ADSP1_DM: 2882159ad93SMark Brown BUG_ON(!mem); 2892159ad93SMark Brown region_name = "DM"; 2902159ad93SMark Brown reg = mem->base + (offset * 2); 2912159ad93SMark Brown break; 2922159ad93SMark Brown case WMFW_ADSP2_XM: 2932159ad93SMark Brown BUG_ON(!mem); 2942159ad93SMark Brown region_name = "XM"; 2952159ad93SMark Brown reg = mem->base + (offset * 2); 2962159ad93SMark Brown break; 2972159ad93SMark Brown case WMFW_ADSP2_YM: 2982159ad93SMark Brown BUG_ON(!mem); 2992159ad93SMark Brown region_name = "YM"; 3002159ad93SMark Brown reg = mem->base + (offset * 2); 3012159ad93SMark Brown break; 3022159ad93SMark Brown case WMFW_ADSP1_ZM: 3032159ad93SMark Brown BUG_ON(!mem); 3042159ad93SMark Brown region_name = "ZM"; 3052159ad93SMark Brown reg = mem->base + (offset * 2); 3062159ad93SMark Brown break; 3072159ad93SMark Brown default: 3082159ad93SMark Brown adsp_warn(dsp, 3092159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 3102159ad93SMark Brown file, regions, type, pos, pos); 3112159ad93SMark Brown break; 3122159ad93SMark Brown } 3132159ad93SMark Brown 3142159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 3152159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 3162159ad93SMark Brown region_name); 3172159ad93SMark Brown 3182159ad93SMark Brown if (text) { 3192159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 3202159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 3212159ad93SMark Brown kfree(text); 3222159ad93SMark Brown } 3232159ad93SMark Brown 3242159ad93SMark Brown if (reg) { 3252159ad93SMark Brown ret = regmap_raw_write(regmap, reg, region->data, 3262159ad93SMark Brown le32_to_cpu(region->len)); 3272159ad93SMark Brown if (ret != 0) { 3282159ad93SMark Brown adsp_err(dsp, 3292159ad93SMark Brown "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 3302159ad93SMark Brown file, regions, 3312159ad93SMark Brown le32_to_cpu(region->len), offset, 3322159ad93SMark Brown region_name, ret); 3332159ad93SMark Brown goto out_fw; 3342159ad93SMark Brown } 3352159ad93SMark Brown } 3362159ad93SMark Brown 3372159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 3382159ad93SMark Brown regions++; 3392159ad93SMark Brown } 3402159ad93SMark Brown 3412159ad93SMark Brown if (pos > firmware->size) 3422159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 3432159ad93SMark Brown file, regions, pos - firmware->size); 3442159ad93SMark Brown 3452159ad93SMark Brown out_fw: 3462159ad93SMark Brown release_firmware(firmware); 3472159ad93SMark Brown out: 3482159ad93SMark Brown kfree(file); 3492159ad93SMark Brown 3502159ad93SMark Brown return ret; 3512159ad93SMark Brown } 3522159ad93SMark Brown 3532159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 3542159ad93SMark Brown { 3552159ad93SMark Brown struct regmap *regmap = dsp->regmap; 3562159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 3572159ad93SMark Brown struct wmfw_coeff_item *blk; 3582159ad93SMark Brown const struct firmware *firmware; 3592159ad93SMark Brown const char *region_name; 3602159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 3612159ad93SMark Brown char *file; 3622159ad93SMark Brown 3632159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 3642159ad93SMark Brown if (file == NULL) 3652159ad93SMark Brown return -ENOMEM; 3662159ad93SMark Brown 3672159ad93SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num); 3682159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 3692159ad93SMark Brown 3702159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 3712159ad93SMark Brown if (ret != 0) { 3722159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 3732159ad93SMark Brown ret = 0; 3742159ad93SMark Brown goto out; 3752159ad93SMark Brown } 3762159ad93SMark Brown ret = -EINVAL; 3772159ad93SMark Brown 3782159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 3792159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 3802159ad93SMark Brown file, firmware->size); 3812159ad93SMark Brown goto out_fw; 3822159ad93SMark Brown } 3832159ad93SMark Brown 3842159ad93SMark Brown hdr = (void*)&firmware->data[0]; 3852159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 3862159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 3872159ad93SMark Brown return -EINVAL; 3882159ad93SMark Brown } 3892159ad93SMark Brown 3902159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 3912159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 3922159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 3932159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 3942159ad93SMark Brown 3952159ad93SMark Brown pos = le32_to_cpu(hdr->len); 3962159ad93SMark Brown 3972159ad93SMark Brown blocks = 0; 3982159ad93SMark Brown while (pos < firmware->size && 3992159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 4002159ad93SMark Brown blk = (void*)(&firmware->data[pos]); 4012159ad93SMark Brown 4022159ad93SMark Brown type = be32_to_cpu(blk->type) & 0xff; 4032159ad93SMark Brown offset = le32_to_cpu(blk->offset) & 0xffffff; 4042159ad93SMark Brown 4052159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 4062159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 4072159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 4082159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 4092159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 4102159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 4112159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 4122159ad93SMark Brown 4132159ad93SMark Brown reg = 0; 4142159ad93SMark Brown region_name = "Unknown"; 4152159ad93SMark Brown switch (type) { 4162159ad93SMark Brown case WMFW_NAME_TEXT: 4172159ad93SMark Brown case WMFW_INFO_TEXT: 4182159ad93SMark Brown break; 4192159ad93SMark Brown case WMFW_ABSOLUTE: 4202159ad93SMark Brown region_name = "register"; 4212159ad93SMark Brown reg = offset; 4222159ad93SMark Brown break; 4232159ad93SMark Brown default: 4242159ad93SMark Brown adsp_err(dsp, "Unknown region type %x\n", type); 4252159ad93SMark Brown break; 4262159ad93SMark Brown } 4272159ad93SMark Brown 4282159ad93SMark Brown if (reg) { 4292159ad93SMark Brown ret = regmap_raw_write(regmap, reg, blk->data, 4302159ad93SMark Brown le32_to_cpu(blk->len)); 4312159ad93SMark Brown if (ret != 0) { 4322159ad93SMark Brown adsp_err(dsp, 4332159ad93SMark Brown "%s.%d: Failed to write to %x in %s\n", 4342159ad93SMark Brown file, blocks, reg, region_name); 4352159ad93SMark Brown } 4362159ad93SMark Brown } 4372159ad93SMark Brown 4382159ad93SMark Brown pos += le32_to_cpu(blk->len) + sizeof(*blk); 4392159ad93SMark Brown blocks++; 4402159ad93SMark Brown } 4412159ad93SMark Brown 4422159ad93SMark Brown if (pos > firmware->size) 4432159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 4442159ad93SMark Brown file, blocks, pos - firmware->size); 4452159ad93SMark Brown 4462159ad93SMark Brown out_fw: 4472159ad93SMark Brown release_firmware(firmware); 4482159ad93SMark Brown out: 4492159ad93SMark Brown kfree(file); 4502159ad93SMark Brown return 0; 4512159ad93SMark Brown } 4522159ad93SMark Brown 4532159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 4542159ad93SMark Brown struct snd_kcontrol *kcontrol, 4552159ad93SMark Brown int event) 4562159ad93SMark Brown { 4572159ad93SMark Brown struct snd_soc_codec *codec = w->codec; 4582159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 4592159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 4602159ad93SMark Brown int ret; 4612159ad93SMark Brown 4622159ad93SMark Brown switch (event) { 4632159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 4642159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 4652159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 4662159ad93SMark Brown 4672159ad93SMark Brown ret = wm_adsp_load(dsp); 4682159ad93SMark Brown if (ret != 0) 4692159ad93SMark Brown goto err; 4702159ad93SMark Brown 4712159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 4722159ad93SMark Brown if (ret != 0) 4732159ad93SMark Brown goto err; 4742159ad93SMark Brown 4752159ad93SMark Brown /* Start the core running */ 4762159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 4772159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 4782159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 4792159ad93SMark Brown break; 4802159ad93SMark Brown 4812159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 4822159ad93SMark Brown /* Halt the core */ 4832159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 4842159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 4852159ad93SMark Brown 4862159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 4872159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 4882159ad93SMark Brown 4892159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 4902159ad93SMark Brown ADSP1_SYS_ENA, 0); 4912159ad93SMark Brown break; 4922159ad93SMark Brown 4932159ad93SMark Brown default: 4942159ad93SMark Brown break; 4952159ad93SMark Brown } 4962159ad93SMark Brown 4972159ad93SMark Brown return 0; 4982159ad93SMark Brown 4992159ad93SMark Brown err: 5002159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 5012159ad93SMark Brown ADSP1_SYS_ENA, 0); 5022159ad93SMark Brown return ret; 5032159ad93SMark Brown } 5042159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 5052159ad93SMark Brown 5062159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 5072159ad93SMark Brown { 5082159ad93SMark Brown unsigned int val; 5092159ad93SMark Brown int ret, count; 5102159ad93SMark Brown 5112159ad93SMark Brown ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 5122159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 5132159ad93SMark Brown if (ret != 0) 5142159ad93SMark Brown return ret; 5152159ad93SMark Brown 5162159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 5172159ad93SMark Brown count = 0; 5182159ad93SMark Brown do { 5192159ad93SMark Brown ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, 5202159ad93SMark Brown &val); 5212159ad93SMark Brown if (ret != 0) 5222159ad93SMark Brown return ret; 5232159ad93SMark Brown } while (!(val & ADSP2_RAM_RDY) && ++count < 10); 5242159ad93SMark Brown 5252159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 5262159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 5272159ad93SMark Brown return -EBUSY; 5282159ad93SMark Brown } 5292159ad93SMark Brown 5302159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 5312159ad93SMark Brown adsp_info(dsp, "RAM ready after %d polls\n", count); 5322159ad93SMark Brown 5332159ad93SMark Brown return 0; 5342159ad93SMark Brown } 5352159ad93SMark Brown 5362159ad93SMark Brown int wm_adsp2_event(struct snd_soc_dapm_widget *w, 5372159ad93SMark Brown struct snd_kcontrol *kcontrol, int event) 5382159ad93SMark Brown { 5392159ad93SMark Brown struct snd_soc_codec *codec = w->codec; 5402159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 5412159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 542973838a0SMark Brown unsigned int val; 5432159ad93SMark Brown int ret; 5442159ad93SMark Brown 5452159ad93SMark Brown switch (event) { 5462159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 547973838a0SMark Brown if (dsp->dvfs) { 548973838a0SMark Brown ret = regmap_read(dsp->regmap, 549973838a0SMark Brown dsp->base + ADSP2_CLOCKING, &val); 550973838a0SMark Brown if (ret != 0) { 551973838a0SMark Brown dev_err(dsp->dev, 552973838a0SMark Brown "Failed to read clocking: %d\n", ret); 553973838a0SMark Brown return ret; 554973838a0SMark Brown } 555973838a0SMark Brown 556973838a0SMark Brown if (val & ADSP2_CLK_SEL_MASK >= 3) { 557973838a0SMark Brown ret = regulator_enable(dsp->dvfs); 558973838a0SMark Brown if (ret != 0) { 559973838a0SMark Brown dev_err(dsp->dev, 560973838a0SMark Brown "Failed to enable supply: %d\n", 561973838a0SMark Brown ret); 562973838a0SMark Brown return ret; 563973838a0SMark Brown } 564973838a0SMark Brown 565973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 566973838a0SMark Brown 1800000, 567973838a0SMark Brown 1800000); 568973838a0SMark Brown if (ret != 0) { 569973838a0SMark Brown dev_err(dsp->dev, 570973838a0SMark Brown "Failed to raise supply: %d\n", 571973838a0SMark Brown ret); 572973838a0SMark Brown return ret; 573973838a0SMark Brown } 574973838a0SMark Brown } 575973838a0SMark Brown } 576973838a0SMark Brown 5772159ad93SMark Brown ret = wm_adsp2_ena(dsp); 5782159ad93SMark Brown if (ret != 0) 5792159ad93SMark Brown return ret; 5802159ad93SMark Brown 5812159ad93SMark Brown ret = wm_adsp_load(dsp); 5822159ad93SMark Brown if (ret != 0) 5832159ad93SMark Brown goto err; 5842159ad93SMark Brown 5852159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 5862159ad93SMark Brown if (ret != 0) 5872159ad93SMark Brown goto err; 5882159ad93SMark Brown 5892159ad93SMark Brown ret = regmap_update_bits(dsp->regmap, 5902159ad93SMark Brown dsp->base + ADSP2_CONTROL, 5912159ad93SMark Brown ADSP2_SYS_ENA | ADSP2_START, 0); 5922159ad93SMark Brown if (ret != 0) 5932159ad93SMark Brown goto err; 5942159ad93SMark Brown break; 5952159ad93SMark Brown 5962159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 5972159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 5982159ad93SMark Brown ADSP2_SYS_ENA | ADSP2_START, 0); 599973838a0SMark Brown 600973838a0SMark Brown if (dsp->dvfs) { 601973838a0SMark Brown ret = regulator_set_voltage(dsp->dvfs, 1200000, 602973838a0SMark Brown 1800000); 603973838a0SMark Brown if (ret != 0) 604973838a0SMark Brown dev_warn(dsp->dev, 605973838a0SMark Brown "Failed to lower supply: %d\n", 606973838a0SMark Brown ret); 607973838a0SMark Brown 608973838a0SMark Brown ret = regulator_disable(dsp->dvfs); 609973838a0SMark Brown if (ret != 0) 610973838a0SMark Brown dev_err(dsp->dev, 611973838a0SMark Brown "Failed to enable supply: %d\n", 612973838a0SMark Brown ret); 613973838a0SMark Brown } 6142159ad93SMark Brown break; 6152159ad93SMark Brown 6162159ad93SMark Brown default: 6172159ad93SMark Brown break; 6182159ad93SMark Brown } 6192159ad93SMark Brown 6202159ad93SMark Brown return 0; 6212159ad93SMark Brown err: 6222159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 6232159ad93SMark Brown ADSP2_SYS_ENA | ADSP2_START, 0); 6242159ad93SMark Brown return ret; 6252159ad93SMark Brown } 6262159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 627973838a0SMark Brown 628973838a0SMark Brown int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) 629973838a0SMark Brown { 630973838a0SMark Brown int ret; 631973838a0SMark Brown 63210a2b662SMark Brown /* 63310a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 63410a2b662SMark Brown * power saving. 63510a2b662SMark Brown */ 63610a2b662SMark Brown ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL, 63710a2b662SMark Brown ADSP2_MEM_ENA, 0); 63810a2b662SMark Brown if (ret != 0) { 63910a2b662SMark Brown adsp_err(adsp, "Failed to clear memory retention: %d\n", ret); 64010a2b662SMark Brown return ret; 64110a2b662SMark Brown } 64210a2b662SMark Brown 643973838a0SMark Brown if (dvfs) { 644973838a0SMark Brown adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); 645973838a0SMark Brown if (IS_ERR(adsp->dvfs)) { 646973838a0SMark Brown ret = PTR_ERR(adsp->dvfs); 647973838a0SMark Brown dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); 648973838a0SMark Brown return ret; 649973838a0SMark Brown } 650973838a0SMark Brown 651973838a0SMark Brown ret = regulator_enable(adsp->dvfs); 652973838a0SMark Brown if (ret != 0) { 653973838a0SMark Brown dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", 654973838a0SMark Brown ret); 655973838a0SMark Brown return ret; 656973838a0SMark Brown } 657973838a0SMark Brown 658973838a0SMark Brown ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); 659973838a0SMark Brown if (ret != 0) { 660973838a0SMark Brown dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", 661973838a0SMark Brown ret); 662973838a0SMark Brown return ret; 663973838a0SMark Brown } 664973838a0SMark Brown 665973838a0SMark Brown ret = regulator_disable(adsp->dvfs); 666973838a0SMark Brown if (ret != 0) { 667973838a0SMark Brown dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", 668973838a0SMark Brown ret); 669973838a0SMark Brown return ret; 670973838a0SMark Brown } 671973838a0SMark Brown } 672973838a0SMark Brown 673973838a0SMark Brown return 0; 674973838a0SMark Brown } 675973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 676