1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22159ad93SMark Brown /* 32159ad93SMark Brown * wm_adsp.c -- Wolfson ADSP support 42159ad93SMark Brown * 52159ad93SMark Brown * Copyright 2012 Wolfson Microelectronics plc 62159ad93SMark Brown * 72159ad93SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 82159ad93SMark Brown */ 92159ad93SMark Brown 10605391d0SRichard Fitzgerald #include <linux/ctype.h> 112159ad93SMark Brown #include <linux/module.h> 122159ad93SMark Brown #include <linux/moduleparam.h> 132159ad93SMark Brown #include <linux/init.h> 142159ad93SMark Brown #include <linux/delay.h> 152159ad93SMark Brown #include <linux/firmware.h> 16cf17c83cSMark Brown #include <linux/list.h> 172159ad93SMark Brown #include <linux/pm.h> 182159ad93SMark Brown #include <linux/pm_runtime.h> 192159ad93SMark Brown #include <linux/regmap.h> 20973838a0SMark Brown #include <linux/regulator/consumer.h> 212159ad93SMark Brown #include <linux/slab.h> 22cdcd7f72SCharles Keepax #include <linux/vmalloc.h> 236ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 24f9f55e31SRichard Fitzgerald #include <linux/debugfs.h> 252159ad93SMark Brown #include <sound/core.h> 262159ad93SMark Brown #include <sound/pcm.h> 272159ad93SMark Brown #include <sound/pcm_params.h> 282159ad93SMark Brown #include <sound/soc.h> 292159ad93SMark Brown #include <sound/jack.h> 302159ad93SMark Brown #include <sound/initval.h> 312159ad93SMark Brown #include <sound/tlv.h> 322159ad93SMark Brown 332159ad93SMark Brown #include "wm_adsp.h" 342159ad93SMark Brown 352159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 36605391d0SRichard Fitzgerald dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 372159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 38605391d0SRichard Fitzgerald dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 392159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 40605391d0SRichard Fitzgerald dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 412159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 42605391d0SRichard Fitzgerald dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 432159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 44605391d0SRichard Fitzgerald dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 452159ad93SMark Brown 466ab1d0ccSSimon Trimmer #define cs_dsp_err(_dsp, fmt, ...) \ 476ab1d0ccSSimon Trimmer dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 486ab1d0ccSSimon Trimmer #define cs_dsp_warn(_dsp, fmt, ...) \ 496ab1d0ccSSimon Trimmer dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 506ab1d0ccSSimon Trimmer #define cs_dsp_info(_dsp, fmt, ...) \ 516ab1d0ccSSimon Trimmer dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 526ab1d0ccSSimon Trimmer #define cs_dsp_dbg(_dsp, fmt, ...) \ 536ab1d0ccSSimon Trimmer dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 546ab1d0ccSSimon Trimmer 550d3fba3eSCharles Keepax #define compr_err(_obj, fmt, ...) \ 560d3fba3eSCharles Keepax adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 570d3fba3eSCharles Keepax ##__VA_ARGS__) 580d3fba3eSCharles Keepax #define compr_dbg(_obj, fmt, ...) \ 590d3fba3eSCharles Keepax adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 600d3fba3eSCharles Keepax ##__VA_ARGS__) 610d3fba3eSCharles Keepax 622159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 632159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 642159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 652159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 662159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 672159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 682159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 692159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 702159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 712159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 722159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 732159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 742159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 752159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 762159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 772159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 782159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 792159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 802159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 812159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 822159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 832159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 842159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 852159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 862159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 872159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 882159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 892159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 902159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 912159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 922159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 932159ad93SMark Brown 942159ad93SMark Brown /* 952159ad93SMark Brown * ADSP1 Control 19 962159ad93SMark Brown */ 972159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 982159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 992159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 1002159ad93SMark Brown 1012159ad93SMark Brown 1022159ad93SMark Brown /* 1032159ad93SMark Brown * ADSP1 Control 30 1042159ad93SMark Brown */ 1052159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 1062159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 1072159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 1082159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 1092159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1102159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1112159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1122159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1132159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1142159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1152159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1162159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1172159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1182159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1192159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1202159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1212159ad93SMark Brown 12294e205bfSChris Rattray /* 12394e205bfSChris Rattray * ADSP1 Control 31 12494e205bfSChris Rattray */ 12594e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 12694e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 12794e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 12894e205bfSChris Rattray 1292d30b575SMark Brown #define ADSP2_CONTROL 0x0 1302d30b575SMark Brown #define ADSP2_CLOCKING 0x1 131e1ea1879SRichard Fitzgerald #define ADSP2V2_CLOCKING 0x2 1322d30b575SMark Brown #define ADSP2_STATUS1 0x4 1332d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1342d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 135e1ea1879SRichard Fitzgerald #define ADSP2V2_WDMA_CONFIG_2 0x32 1362d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1372159ad93SMark Brown 13810337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 13910337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 14010337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 14110337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 14210337b07SRichard Fitzgerald 143e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH0_1 0x40 144e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH2_3 0x42 145e1ea1879SRichard Fitzgerald 1462159ad93SMark Brown /* 1472159ad93SMark Brown * ADSP2 Control 1482159ad93SMark Brown */ 1492159ad93SMark Brown 1502159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1512159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1522159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1532159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1542159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1552159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1562159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1572159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1582159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1592159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1602159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1612159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1622159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1632159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1642159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1652159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1662159ad93SMark Brown 1672159ad93SMark Brown /* 168973838a0SMark Brown * ADSP2 clocking 169973838a0SMark Brown */ 170973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 171973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 172973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 173973838a0SMark Brown 174973838a0SMark Brown /* 175e1ea1879SRichard Fitzgerald * ADSP2V2 clocking 176e1ea1879SRichard Fitzgerald */ 177e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 178e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 179e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 180e1ea1879SRichard Fitzgerald 181e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 182e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 183e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 184e1ea1879SRichard Fitzgerald 185e1ea1879SRichard Fitzgerald /* 1862159ad93SMark Brown * ADSP2 Status 1 1872159ad93SMark Brown */ 1882159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1892159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1902159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1912159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1922159ad93SMark Brown 19351a2c944SMayuresh Kulkarni /* 19451a2c944SMayuresh Kulkarni * ADSP2 Lock support 19551a2c944SMayuresh Kulkarni */ 19651a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_0 0x5555 19751a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_1 0xAAAA 19851a2c944SMayuresh Kulkarni 19951a2c944SMayuresh Kulkarni #define ADSP2_WATCHDOG 0x0A 20051a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR 0x52 20151a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_STATUS 0x64 20251a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 20351a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 20451a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 20551a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 20651a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 20751a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_CTRL 0x7A 20851a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 20951a2c944SMayuresh Kulkarni 21051a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 211a4d328efSCharles Keepax #define ADSP2_ADDR_ERR_MASK 0x4000 21251a2c944SMayuresh Kulkarni #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 21351a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 21451a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_EINT 0x0001 21551a2c944SMayuresh Kulkarni 21651a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 21751a2c944SMayuresh Kulkarni #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 21851a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 21951a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 22051a2c944SMayuresh Kulkarni #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 22151a2c944SMayuresh Kulkarni 22251a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_SHIFT 16 22351a2c944SMayuresh Kulkarni 2249ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 2259ee78757SCharles Keepax 2265beb8eeaSSimon Trimmer #define CS_DSP_ACKED_CTL_TIMEOUT_MS 100 2275beb8eeaSSimon Trimmer #define CS_DSP_ACKED_CTL_N_QUICKPOLLS 10 2285beb8eeaSSimon Trimmer #define CS_DSP_ACKED_CTL_MIN_VALUE 0 2295beb8eeaSSimon Trimmer #define CS_DSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 230f4f0c4c6SRichard Fitzgerald 231f4f0c4c6SRichard Fitzgerald /* 232f4f0c4c6SRichard Fitzgerald * Event control messages 233f4f0c4c6SRichard Fitzgerald */ 2345beb8eeaSSimon Trimmer #define CS_DSP_FW_EVENT_SHUTDOWN 0x000001 235f4f0c4c6SRichard Fitzgerald 236170b1e12SWen Shi /* 2372ae58138SRichard Fitzgerald * HALO system info 2382ae58138SRichard Fitzgerald */ 2392ae58138SRichard Fitzgerald #define HALO_AHBM_WINDOW_DEBUG_0 0x02040 2402ae58138SRichard Fitzgerald #define HALO_AHBM_WINDOW_DEBUG_1 0x02044 2412ae58138SRichard Fitzgerald 2422ae58138SRichard Fitzgerald /* 243170b1e12SWen Shi * HALO core 244170b1e12SWen Shi */ 245170b1e12SWen Shi #define HALO_SCRATCH1 0x005c0 246170b1e12SWen Shi #define HALO_SCRATCH2 0x005c8 247170b1e12SWen Shi #define HALO_SCRATCH3 0x005d0 248170b1e12SWen Shi #define HALO_SCRATCH4 0x005d8 249170b1e12SWen Shi #define HALO_CCM_CORE_CONTROL 0x41000 250170b1e12SWen Shi #define HALO_CORE_SOFT_RESET 0x00010 2518bc144f9SStuart Henderson #define HALO_WDT_CONTROL 0x47000 252170b1e12SWen Shi 253170b1e12SWen Shi /* 254170b1e12SWen Shi * HALO MPU banks 255170b1e12SWen Shi */ 256170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_0 0x43000 257170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_0 0x43004 258170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_0 0x43008 259170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_0 0x4300C 260170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_0 0x43014 261170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_1 0x43018 262170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_1 0x4301C 263170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_1 0x43020 264170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_1 0x43024 265170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_1 0x4302C 266170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_2 0x43030 267170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_2 0x43034 268170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_2 0x43038 269170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_2 0x4303C 270170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_2 0x43044 271170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_3 0x43048 272170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_3 0x4304C 273170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_3 0x43050 274170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_3 0x43054 275170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_3 0x4305C 2762ae58138SRichard Fitzgerald #define HALO_MPU_XM_VIO_ADDR 0x43100 2772ae58138SRichard Fitzgerald #define HALO_MPU_XM_VIO_STATUS 0x43104 2782ae58138SRichard Fitzgerald #define HALO_MPU_YM_VIO_ADDR 0x43108 2792ae58138SRichard Fitzgerald #define HALO_MPU_YM_VIO_STATUS 0x4310C 2802ae58138SRichard Fitzgerald #define HALO_MPU_PM_VIO_ADDR 0x43110 2812ae58138SRichard Fitzgerald #define HALO_MPU_PM_VIO_STATUS 0x43114 282170b1e12SWen Shi #define HALO_MPU_LOCK_CONFIG 0x43140 283170b1e12SWen Shi 284170b1e12SWen Shi /* 2852ae58138SRichard Fitzgerald * HALO_AHBM_WINDOW_DEBUG_1 2862ae58138SRichard Fitzgerald */ 2872ae58138SRichard Fitzgerald #define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 2882ae58138SRichard Fitzgerald #define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 2892ae58138SRichard Fitzgerald #define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff 2902ae58138SRichard Fitzgerald 2912ae58138SRichard Fitzgerald /* 292170b1e12SWen Shi * HALO_CCM_CORE_CONTROL 293170b1e12SWen Shi */ 294e5883322SCharles Keepax #define HALO_CORE_RESET 0x00000200 295170b1e12SWen Shi #define HALO_CORE_EN 0x00000001 296170b1e12SWen Shi 297170b1e12SWen Shi /* 298170b1e12SWen Shi * HALO_CORE_SOFT_RESET 299170b1e12SWen Shi */ 300170b1e12SWen Shi #define HALO_CORE_SOFT_RESET_MASK 0x00000001 301170b1e12SWen Shi 3022ae58138SRichard Fitzgerald /* 3038bc144f9SStuart Henderson * HALO_WDT_CONTROL 3048bc144f9SStuart Henderson */ 3058bc144f9SStuart Henderson #define HALO_WDT_EN_MASK 0x00000001 3068bc144f9SStuart Henderson 3078bc144f9SStuart Henderson /* 3082ae58138SRichard Fitzgerald * HALO_MPU_?M_VIO_STATUS 3092ae58138SRichard Fitzgerald */ 3102ae58138SRichard Fitzgerald #define HALO_MPU_VIO_STS_MASK 0x007e0000 3112ae58138SRichard Fitzgerald #define HALO_MPU_VIO_STS_SHIFT 17 3122ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 3132ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff 3142ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 3152ae58138SRichard Fitzgerald 3165beb8eeaSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp1_ops; 3175beb8eeaSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp2_ops[]; 3185beb8eeaSSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ops; 3194e08d50dSCharles Keepax 3205beb8eeaSSimon Trimmer struct cs_dsp_buf { 321cf17c83cSMark Brown struct list_head list; 322cf17c83cSMark Brown void *buf; 323cf17c83cSMark Brown }; 324cf17c83cSMark Brown 3255beb8eeaSSimon Trimmer static struct cs_dsp_buf *cs_dsp_buf_alloc(const void *src, size_t len, 326cf17c83cSMark Brown struct list_head *list) 327cf17c83cSMark Brown { 3285beb8eeaSSimon Trimmer struct cs_dsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 329cf17c83cSMark Brown 330cf17c83cSMark Brown if (buf == NULL) 331cf17c83cSMark Brown return NULL; 332cf17c83cSMark Brown 333cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 334cf17c83cSMark Brown if (!buf->buf) { 3354d41c74dSRichard Fitzgerald kfree(buf); 336cf17c83cSMark Brown return NULL; 337cf17c83cSMark Brown } 338cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 339cf17c83cSMark Brown 340cf17c83cSMark Brown if (list) 341cf17c83cSMark Brown list_add_tail(&buf->list, list); 342cf17c83cSMark Brown 343cf17c83cSMark Brown return buf; 344cf17c83cSMark Brown } 345cf17c83cSMark Brown 3465beb8eeaSSimon Trimmer static void cs_dsp_buf_free(struct list_head *list) 347cf17c83cSMark Brown { 348cf17c83cSMark Brown while (!list_empty(list)) { 3495beb8eeaSSimon Trimmer struct cs_dsp_buf *buf = list_first_entry(list, 3505beb8eeaSSimon Trimmer struct cs_dsp_buf, 351cf17c83cSMark Brown list); 352cf17c83cSMark Brown list_del(&buf->list); 353cdcd7f72SCharles Keepax vfree(buf->buf); 354cf17c83cSMark Brown kfree(buf); 355cf17c83cSMark Brown } 356cf17c83cSMark Brown } 357cf17c83cSMark Brown 358dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 35904d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 36004d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 36104d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 36204d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 36304d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 36404d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 36504d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 36604d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 36704d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 368d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_CALI 10 369d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_DIAG 11 370d6fea46eSVlad Karpovich #define WM_ADSP_FW_MISC 12 37104d1300fSCharles Keepax 372d6fea46eSVlad Karpovich #define WM_ADSP_NUM_FW 13 373dd84f925SMark Brown 3741023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 375dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 37604d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 377dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 378dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 37904d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 380dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 38104d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 38204d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 38304d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 38404d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 385d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_CALI] = "Calibration", 386d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_DIAG] = "Diagnostic", 38704d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 3881023dbd9SMark Brown }; 3891023dbd9SMark Brown 3902cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 3912cd19bdbSCharles Keepax __be32 sys_enable; 3922cd19bdbSCharles Keepax __be32 fw_id; 3932cd19bdbSCharles Keepax __be32 fw_rev; 3942cd19bdbSCharles Keepax __be32 boot_status; 3952cd19bdbSCharles Keepax __be32 watchdog; 3962cd19bdbSCharles Keepax __be32 dma_buffer_size; 3972cd19bdbSCharles Keepax __be32 rdma[6]; 3982cd19bdbSCharles Keepax __be32 wdma[8]; 3992cd19bdbSCharles Keepax __be32 build_job_name[3]; 4002cd19bdbSCharles Keepax __be32 build_job_number; 4012cd19bdbSCharles Keepax }; 4022cd19bdbSCharles Keepax 403170b1e12SWen Shi struct wm_halo_system_config_xm_hdr { 404170b1e12SWen Shi __be32 halo_heartbeat; 405170b1e12SWen Shi __be32 build_job_name[3]; 406170b1e12SWen Shi __be32 build_job_number; 407170b1e12SWen Shi }; 408170b1e12SWen Shi 4092cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 4102cd19bdbSCharles Keepax __be32 magic; 4112cd19bdbSCharles Keepax __be32 smoothing; 4122cd19bdbSCharles Keepax __be32 threshold; 4132cd19bdbSCharles Keepax __be32 host_buf_ptr; 4142cd19bdbSCharles Keepax __be32 start_seq; 4152cd19bdbSCharles Keepax __be32 high_water_mark; 4162cd19bdbSCharles Keepax __be32 low_water_mark; 4172cd19bdbSCharles Keepax __be64 smoothed_power; 4182cd19bdbSCharles Keepax }; 4192cd19bdbSCharles Keepax 4204f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 { 4214f2d4eabSStuart Henderson __be32 host_buf_ptr; /* Host buffer pointer */ 4224f2d4eabSStuart Henderson __be32 versions; /* Version numbers */ 4234f2d4eabSStuart Henderson __be32 name[4]; /* The buffer name */ 4244f2d4eabSStuart Henderson }; 4254f2d4eabSStuart Henderson 4262cd19bdbSCharles Keepax struct wm_adsp_buffer { 4272a2aefa4SRichard Fitzgerald __be32 buf1_base; /* Base addr of first buffer area */ 4282a2aefa4SRichard Fitzgerald __be32 buf1_size; /* Size of buf1 area in DSP words */ 4292a2aefa4SRichard Fitzgerald __be32 buf2_base; /* Base addr of 2nd buffer area */ 4302a2aefa4SRichard Fitzgerald __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 4312a2aefa4SRichard Fitzgerald __be32 buf3_base; /* Base addr of buf3 area */ 4322a2aefa4SRichard Fitzgerald __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 4332cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 4342cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 4352cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 4362cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 4372cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 4382cd19bdbSCharles Keepax __be32 error; /* error if any */ 4392cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 4402cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 4412cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 4422cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 4432cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 4442cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 4452cd19bdbSCharles Keepax }; 4462cd19bdbSCharles Keepax 447721be3beSCharles Keepax struct wm_adsp_compr; 448721be3beSCharles Keepax 4492cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 4504f2d4eabSStuart Henderson struct list_head list; 4512cd19bdbSCharles Keepax struct wm_adsp *dsp; 452721be3beSCharles Keepax struct wm_adsp_compr *compr; 4532cd19bdbSCharles Keepax 4542cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 4552cd19bdbSCharles Keepax u32 host_buf_ptr; 456565ace46SCharles Keepax 457565ace46SCharles Keepax u32 error; 458565ace46SCharles Keepax u32 irq_count; 459565ace46SCharles Keepax int read_index; 460565ace46SCharles Keepax int avail; 461fb13f19dSAndrew Ford int host_buf_mem_type; 4624f2d4eabSStuart Henderson 4634f2d4eabSStuart Henderson char *name; 4642cd19bdbSCharles Keepax }; 4652cd19bdbSCharles Keepax 466406abc95SCharles Keepax struct wm_adsp_compr { 4674f2d4eabSStuart Henderson struct list_head list; 468406abc95SCharles Keepax struct wm_adsp *dsp; 46995fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 470406abc95SCharles Keepax 471406abc95SCharles Keepax struct snd_compr_stream *stream; 472406abc95SCharles Keepax struct snd_compressed_buffer size; 473565ace46SCharles Keepax 47483a40ce9SCharles Keepax u32 *raw_buf; 475565ace46SCharles Keepax unsigned int copied_total; 476da2b3358SCharles Keepax 477da2b3358SCharles Keepax unsigned int sample_rate; 4784f2d4eabSStuart Henderson 4794f2d4eabSStuart Henderson const char *name; 480406abc95SCharles Keepax }; 481406abc95SCharles Keepax 4825beb8eeaSSimon Trimmer #define CS_DSP_DATA_WORD_SIZE 3 483406abc95SCharles Keepax 484406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 485406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 4865beb8eeaSSimon Trimmer #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * CS_DSP_DATA_WORD_SIZE) 4875beb8eeaSSimon Trimmer #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE) 488406abc95SCharles Keepax 4892cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 4902cd19bdbSCharles Keepax 4912cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 4922cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 4932cd19bdbSCharles Keepax 4942cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 4952cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 4962cd19bdbSCharles Keepax 4974f2d4eabSStuart Henderson #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 4984f2d4eabSStuart Henderson 4994f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 5004f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 5014f2d4eabSStuart Henderson 5022cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 5032cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 5042cd19bdbSCharles Keepax 5052cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 5062cd19bdbSCharles Keepax unsigned int offset; 5072cd19bdbSCharles Keepax unsigned int cumulative_size; 5082cd19bdbSCharles Keepax unsigned int mem_type; 5092cd19bdbSCharles Keepax unsigned int base_addr; 5102cd19bdbSCharles Keepax }; 5112cd19bdbSCharles Keepax 5122cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 5132cd19bdbSCharles Keepax unsigned int mem_type; 5142cd19bdbSCharles Keepax unsigned int base_offset; 5152cd19bdbSCharles Keepax unsigned int size_offset; 5162cd19bdbSCharles Keepax }; 5172cd19bdbSCharles Keepax 5183a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 5192cd19bdbSCharles Keepax { 5202cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5212a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf1_base), 5222a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_size), 5232cd19bdbSCharles Keepax }, 5242cd19bdbSCharles Keepax { 5252cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5262a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf2_base), 5272a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 5282cd19bdbSCharles Keepax }, 5292cd19bdbSCharles Keepax { 5302cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 5312a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf3_base), 5322a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf_total_size), 5332cd19bdbSCharles Keepax }, 5342cd19bdbSCharles Keepax }; 5352cd19bdbSCharles Keepax 536406abc95SCharles Keepax struct wm_adsp_fw_caps { 537406abc95SCharles Keepax u32 id; 538406abc95SCharles Keepax struct snd_codec_desc desc; 5392cd19bdbSCharles Keepax int num_regions; 5403a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 541406abc95SCharles Keepax }; 542406abc95SCharles Keepax 543e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 544406abc95SCharles Keepax { 545406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 546406abc95SCharles Keepax .desc = { 5473bbc2705SRichard Fitzgerald .max_ch = 8, 548406abc95SCharles Keepax .sample_rates = { 16000 }, 549406abc95SCharles Keepax .num_sample_rates = 1, 550406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 551406abc95SCharles Keepax }, 552e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 553e6d00f34SCharles Keepax .region_defs = default_regions, 554406abc95SCharles Keepax }, 555406abc95SCharles Keepax }; 556406abc95SCharles Keepax 5577ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 5587ce4283cSCharles Keepax { 5597ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 5607ce4283cSCharles Keepax .desc = { 5617ce4283cSCharles Keepax .max_ch = 8, 5627ce4283cSCharles Keepax .sample_rates = { 5637ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 5647ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 5657ce4283cSCharles Keepax 96000, 176400, 192000 5667ce4283cSCharles Keepax }, 5677ce4283cSCharles Keepax .num_sample_rates = 15, 5687ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 5697ce4283cSCharles Keepax }, 5707ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 5717ce4283cSCharles Keepax .region_defs = default_regions, 572406abc95SCharles Keepax }, 573406abc95SCharles Keepax }; 574406abc95SCharles Keepax 575406abc95SCharles Keepax static const struct { 5761023dbd9SMark Brown const char *file; 577406abc95SCharles Keepax int compr_direction; 578406abc95SCharles Keepax int num_caps; 579406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 58020b7f7c5SCharles Keepax bool voice_trigger; 5811023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 582dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 58304d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 584dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 585dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 58604d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 587dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 588406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 589406abc95SCharles Keepax .file = "ctrl", 590406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 591e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 592e6d00f34SCharles Keepax .caps = ctrl_caps, 59320b7f7c5SCharles Keepax .voice_trigger = true, 594406abc95SCharles Keepax }, 59504d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 5967ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 5977ce4283cSCharles Keepax .file = "trace", 5987ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 5997ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 6007ce4283cSCharles Keepax .caps = trace_caps, 6017ce4283cSCharles Keepax }, 60204d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 603d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" }, 604d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" }, 60504d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 6061023dbd9SMark Brown }; 6071023dbd9SMark Brown 6086ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 6096ab2b7b4SDimitris Papastamos const char *name; 6100700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 6119ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 612df6c505cSSimon Trimmer struct work_struct work; 6136ab2b7b4SDimitris Papastamos }; 6146ab2b7b4SDimitris Papastamos 6155beb8eeaSSimon Trimmer static const char *cs_dsp_mem_region_name(unsigned int type) 6169ce5e6e6SRichard Fitzgerald { 6179ce5e6e6SRichard Fitzgerald switch (type) { 6189ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 6199ce5e6e6SRichard Fitzgerald return "PM"; 620170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 621170b1e12SWen Shi return "PM_PACKED"; 6229ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 6239ce5e6e6SRichard Fitzgerald return "DM"; 6249ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 6259ce5e6e6SRichard Fitzgerald return "XM"; 626170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 627170b1e12SWen Shi return "XM_PACKED"; 6289ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 6299ce5e6e6SRichard Fitzgerald return "YM"; 630170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 631170b1e12SWen Shi return "YM_PACKED"; 6329ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 6339ce5e6e6SRichard Fitzgerald return "ZM"; 6349ce5e6e6SRichard Fitzgerald default: 6359ce5e6e6SRichard Fitzgerald return NULL; 6369ce5e6e6SRichard Fitzgerald } 6379ce5e6e6SRichard Fitzgerald } 6389ce5e6e6SRichard Fitzgerald 639f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 6405beb8eeaSSimon Trimmer static void cs_dsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 641f9f55e31SRichard Fitzgerald { 642f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 643f9f55e31SRichard Fitzgerald 644f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 645f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 646f9f55e31SRichard Fitzgerald } 647f9f55e31SRichard Fitzgerald 6485beb8eeaSSimon Trimmer static void cs_dsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 649f9f55e31SRichard Fitzgerald { 650f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 651f9f55e31SRichard Fitzgerald 652f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 653f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 654f9f55e31SRichard Fitzgerald } 655f9f55e31SRichard Fitzgerald 6565beb8eeaSSimon Trimmer static void cs_dsp_debugfs_clear(struct wm_adsp *dsp) 657f9f55e31SRichard Fitzgerald { 658f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 659f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 660f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 661f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 662f9f55e31SRichard Fitzgerald } 663f9f55e31SRichard Fitzgerald 6645beb8eeaSSimon Trimmer static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file, 665f9f55e31SRichard Fitzgerald char __user *user_buf, 666f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 667f9f55e31SRichard Fitzgerald { 668f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 669f9f55e31SRichard Fitzgerald ssize_t ret; 670f9f55e31SRichard Fitzgerald 671078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 672f9f55e31SRichard Fitzgerald 67328823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 674f9f55e31SRichard Fitzgerald ret = 0; 675f9f55e31SRichard Fitzgerald else 676f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 677f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 678f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 679f9f55e31SRichard Fitzgerald 680078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 681f9f55e31SRichard Fitzgerald return ret; 682f9f55e31SRichard Fitzgerald } 683f9f55e31SRichard Fitzgerald 6845beb8eeaSSimon Trimmer static ssize_t cs_dsp_debugfs_bin_read(struct file *file, 685f9f55e31SRichard Fitzgerald char __user *user_buf, 686f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 687f9f55e31SRichard Fitzgerald { 688f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 689f9f55e31SRichard Fitzgerald ssize_t ret; 690f9f55e31SRichard Fitzgerald 691078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 692f9f55e31SRichard Fitzgerald 69328823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 694f9f55e31SRichard Fitzgerald ret = 0; 695f9f55e31SRichard Fitzgerald else 696f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 697f9f55e31SRichard Fitzgerald dsp->bin_file_name, 698f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 699f9f55e31SRichard Fitzgerald 700078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 701f9f55e31SRichard Fitzgerald return ret; 702f9f55e31SRichard Fitzgerald } 703f9f55e31SRichard Fitzgerald 704f9f55e31SRichard Fitzgerald static const struct { 705f9f55e31SRichard Fitzgerald const char *name; 706f9f55e31SRichard Fitzgerald const struct file_operations fops; 7075beb8eeaSSimon Trimmer } cs_dsp_debugfs_fops[] = { 708f9f55e31SRichard Fitzgerald { 709f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 710f9f55e31SRichard Fitzgerald .fops = { 711f9f55e31SRichard Fitzgerald .open = simple_open, 7125beb8eeaSSimon Trimmer .read = cs_dsp_debugfs_wmfw_read, 713f9f55e31SRichard Fitzgerald }, 714f9f55e31SRichard Fitzgerald }, 715f9f55e31SRichard Fitzgerald { 716f9f55e31SRichard Fitzgerald .name = "bin_file_name", 717f9f55e31SRichard Fitzgerald .fops = { 718f9f55e31SRichard Fitzgerald .open = simple_open, 7195beb8eeaSSimon Trimmer .read = cs_dsp_debugfs_bin_read, 720f9f55e31SRichard Fitzgerald }, 721f9f55e31SRichard Fitzgerald }, 722f9f55e31SRichard Fitzgerald }; 723f9f55e31SRichard Fitzgerald 7245beb8eeaSSimon Trimmer static void cs_dsp_init_debugfs(struct wm_adsp *dsp, 7250fe1daa6SKuninori Morimoto struct snd_soc_component *component) 726f9f55e31SRichard Fitzgerald { 727f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 728f9f55e31SRichard Fitzgerald int i; 729f9f55e31SRichard Fitzgerald 730605391d0SRichard Fitzgerald root = debugfs_create_dir(dsp->name, component->debugfs_root); 731f9f55e31SRichard Fitzgerald 7327f807f28SGreg Kroah-Hartman debugfs_create_bool("booted", 0444, root, &dsp->booted); 7337f807f28SGreg Kroah-Hartman debugfs_create_bool("running", 0444, root, &dsp->running); 7347f807f28SGreg Kroah-Hartman debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); 7357f807f28SGreg Kroah-Hartman debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); 736f9f55e31SRichard Fitzgerald 7375beb8eeaSSimon Trimmer for (i = 0; i < ARRAY_SIZE(cs_dsp_debugfs_fops); ++i) 7385beb8eeaSSimon Trimmer debugfs_create_file(cs_dsp_debugfs_fops[i].name, 0444, root, 7395beb8eeaSSimon Trimmer dsp, &cs_dsp_debugfs_fops[i].fops); 740f9f55e31SRichard Fitzgerald 741f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 742f9f55e31SRichard Fitzgerald } 743f9f55e31SRichard Fitzgerald 7445beb8eeaSSimon Trimmer static void cs_dsp_cleanup_debugfs(struct wm_adsp *dsp) 745f9f55e31SRichard Fitzgerald { 7465beb8eeaSSimon Trimmer cs_dsp_debugfs_clear(dsp); 747e6d0b92aSCharles Keepax debugfs_remove_recursive(dsp->debugfs_root); 748e6d0b92aSCharles Keepax dsp->debugfs_root = NULL; 749f9f55e31SRichard Fitzgerald } 750f9f55e31SRichard Fitzgerald #else 7515beb8eeaSSimon Trimmer static inline void cs_dsp_init_debugfs(struct wm_adsp *dsp, 7520fe1daa6SKuninori Morimoto struct snd_soc_component *component) 753f9f55e31SRichard Fitzgerald { 754f9f55e31SRichard Fitzgerald } 755f9f55e31SRichard Fitzgerald 7565beb8eeaSSimon Trimmer static inline void cs_dsp_cleanup_debugfs(struct wm_adsp *dsp) 757f9f55e31SRichard Fitzgerald { 758f9f55e31SRichard Fitzgerald } 759f9f55e31SRichard Fitzgerald 7605beb8eeaSSimon Trimmer static inline void cs_dsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 761f9f55e31SRichard Fitzgerald const char *s) 762f9f55e31SRichard Fitzgerald { 763f9f55e31SRichard Fitzgerald } 764f9f55e31SRichard Fitzgerald 7655beb8eeaSSimon Trimmer static inline void cs_dsp_debugfs_save_binname(struct wm_adsp *dsp, 766f9f55e31SRichard Fitzgerald const char *s) 767f9f55e31SRichard Fitzgerald { 768f9f55e31SRichard Fitzgerald } 769f9f55e31SRichard Fitzgerald 7705beb8eeaSSimon Trimmer static inline void cs_dsp_debugfs_clear(struct wm_adsp *dsp) 771f9f55e31SRichard Fitzgerald { 772f9f55e31SRichard Fitzgerald } 773f9f55e31SRichard Fitzgerald #endif 774f9f55e31SRichard Fitzgerald 7750a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 7761023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7771023dbd9SMark Brown { 7780fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7791023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7800fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 7811023dbd9SMark Brown 78215c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 7831023dbd9SMark Brown 7841023dbd9SMark Brown return 0; 7851023dbd9SMark Brown } 7860a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get); 7871023dbd9SMark Brown 7880a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 7891023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7901023dbd9SMark Brown { 7910fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7921023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7930fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 794d27c5e15SCharles Keepax int ret = 0; 7951023dbd9SMark Brown 79615c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 7971023dbd9SMark Brown return 0; 7981023dbd9SMark Brown 79915c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 8001023dbd9SMark Brown return -EINVAL; 8011023dbd9SMark Brown 802d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 8031023dbd9SMark Brown 8044f2d4eabSStuart Henderson if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) 805d27c5e15SCharles Keepax ret = -EBUSY; 806d27c5e15SCharles Keepax else 80715c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 8081023dbd9SMark Brown 809d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 810d27c5e15SCharles Keepax 811d27c5e15SCharles Keepax return ret; 8121023dbd9SMark Brown } 8130a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put); 8141023dbd9SMark Brown 8150a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = { 8161023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8171023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8181023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8191023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 820e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 821e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 822e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8231023dbd9SMark Brown }; 8240a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 8252159ad93SMark Brown 8265beb8eeaSSimon Trimmer static const struct cs_dsp_region *cs_dsp_find_region(struct wm_adsp *dsp, 8272159ad93SMark Brown int type) 8282159ad93SMark Brown { 8292159ad93SMark Brown int i; 8302159ad93SMark Brown 8312159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 8322159ad93SMark Brown if (dsp->mem[i].type == type) 8332159ad93SMark Brown return &dsp->mem[i]; 8342159ad93SMark Brown 8352159ad93SMark Brown return NULL; 8362159ad93SMark Brown } 8372159ad93SMark Brown 8385beb8eeaSSimon Trimmer static unsigned int cs_dsp_region_to_reg(struct cs_dsp_region const *mem, 83945b9ee72SMark Brown unsigned int offset) 84045b9ee72SMark Brown { 8413809f001SCharles Keepax switch (mem->type) { 84245b9ee72SMark Brown case WMFW_ADSP1_PM: 8433809f001SCharles Keepax return mem->base + (offset * 3); 84445b9ee72SMark Brown case WMFW_ADSP1_DM: 84545b9ee72SMark Brown case WMFW_ADSP2_XM: 84645b9ee72SMark Brown case WMFW_ADSP2_YM: 84745b9ee72SMark Brown case WMFW_ADSP1_ZM: 8483809f001SCharles Keepax return mem->base + (offset * 2); 84945b9ee72SMark Brown default: 8506c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 85145b9ee72SMark Brown return offset; 85245b9ee72SMark Brown } 85345b9ee72SMark Brown } 85445b9ee72SMark Brown 8555beb8eeaSSimon Trimmer static unsigned int cs_dsp_halo_region_to_reg(struct cs_dsp_region const *mem, 856170b1e12SWen Shi unsigned int offset) 857170b1e12SWen Shi { 858170b1e12SWen Shi switch (mem->type) { 859170b1e12SWen Shi case WMFW_ADSP2_XM: 860170b1e12SWen Shi case WMFW_ADSP2_YM: 861170b1e12SWen Shi return mem->base + (offset * 4); 862170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 863170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 864170b1e12SWen Shi return (mem->base + (offset * 3)) & ~0x3; 865170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 866170b1e12SWen Shi return mem->base + (offset * 5); 867170b1e12SWen Shi default: 868170b1e12SWen Shi WARN(1, "Unknown memory region type"); 869170b1e12SWen Shi return offset; 870170b1e12SWen Shi } 871170b1e12SWen Shi } 872170b1e12SWen Shi 8735beb8eeaSSimon Trimmer static void cs_dsp_read_fw_status(struct wm_adsp *dsp, 8744049ce86SCharles Keepax int noffs, unsigned int *offs) 87510337b07SRichard Fitzgerald { 87620e00db2SRichard Fitzgerald unsigned int i; 87710337b07SRichard Fitzgerald int ret; 87810337b07SRichard Fitzgerald 8794049ce86SCharles Keepax for (i = 0; i < noffs; ++i) { 8804049ce86SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 88110337b07SRichard Fitzgerald if (ret) { 8826ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 88310337b07SRichard Fitzgerald return; 88410337b07SRichard Fitzgerald } 88520e00db2SRichard Fitzgerald } 8864049ce86SCharles Keepax } 8874049ce86SCharles Keepax 8885beb8eeaSSimon Trimmer static void cs_dsp_adsp2_show_fw_status(struct wm_adsp *dsp) 8894049ce86SCharles Keepax { 8904049ce86SCharles Keepax unsigned int offs[] = { 8914049ce86SCharles Keepax ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 8924049ce86SCharles Keepax }; 8934049ce86SCharles Keepax 8945beb8eeaSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 89510337b07SRichard Fitzgerald 8966ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 8974049ce86SCharles Keepax offs[0], offs[1], offs[2], offs[3]); 89810337b07SRichard Fitzgerald } 89910337b07SRichard Fitzgerald 9005beb8eeaSSimon Trimmer static void cs_dsp_adsp2v2_show_fw_status(struct wm_adsp *dsp) 901e1ea1879SRichard Fitzgerald { 9024049ce86SCharles Keepax unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 903e1ea1879SRichard Fitzgerald 9045beb8eeaSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 905e1ea1879SRichard Fitzgerald 9066ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9074049ce86SCharles Keepax offs[0] & 0xFFFF, offs[0] >> 16, 9084049ce86SCharles Keepax offs[1] & 0xFFFF, offs[1] >> 16); 909e1ea1879SRichard Fitzgerald } 910e1ea1879SRichard Fitzgerald 9115beb8eeaSSimon Trimmer static void cs_dsp_halo_show_fw_status(struct wm_adsp *dsp) 912170b1e12SWen Shi { 913170b1e12SWen Shi unsigned int offs[] = { 914170b1e12SWen Shi HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, 915170b1e12SWen Shi }; 916170b1e12SWen Shi 9175beb8eeaSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 918170b1e12SWen Shi 9196ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 920170b1e12SWen Shi offs[0], offs[1], offs[2], offs[3]); 921170b1e12SWen Shi } 922170b1e12SWen Shi 9239ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 9249ee78757SCharles Keepax { 9259ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 9269ee78757SCharles Keepax } 9279ee78757SCharles Keepax 9280700bc2fSSimon Trimmer static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg) 929b396ebcaSRichard Fitzgerald { 9305beb8eeaSSimon Trimmer const struct cs_dsp_alg_region *alg_region = &ctl->alg_region; 931b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 9325beb8eeaSSimon Trimmer const struct cs_dsp_region *mem; 933b396ebcaSRichard Fitzgerald 9345beb8eeaSSimon Trimmer mem = cs_dsp_find_region(dsp, alg_region->type); 935b396ebcaSRichard Fitzgerald if (!mem) { 9366ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", 937b396ebcaSRichard Fitzgerald alg_region->type); 938b396ebcaSRichard Fitzgerald return -EINVAL; 939b396ebcaSRichard Fitzgerald } 940b396ebcaSRichard Fitzgerald 941170b1e12SWen Shi *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); 942b396ebcaSRichard Fitzgerald 943b396ebcaSRichard Fitzgerald return 0; 944b396ebcaSRichard Fitzgerald } 945b396ebcaSRichard Fitzgerald 9467585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 9476ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 9486ab2b7b4SDimitris Papastamos { 9499ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9509ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9519ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9520700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 9536ab2b7b4SDimitris Papastamos 9540700bc2fSSimon Trimmer switch (cs_ctl->type) { 955a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 956a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 9575beb8eeaSSimon Trimmer uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE; 9585beb8eeaSSimon Trimmer uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE; 959a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 960a23ebba8SRichard Fitzgerald uinfo->count = 1; 961a23ebba8SRichard Fitzgerald break; 962a23ebba8SRichard Fitzgerald default: 9636ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 9640700bc2fSSimon Trimmer uinfo->count = cs_ctl->len; 965a23ebba8SRichard Fitzgerald break; 966a23ebba8SRichard Fitzgerald } 967a23ebba8SRichard Fitzgerald 9686ab2b7b4SDimitris Papastamos return 0; 9696ab2b7b4SDimitris Papastamos } 9706ab2b7b4SDimitris Papastamos 9710700bc2fSSimon Trimmer static int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, 972f4f0c4c6SRichard Fitzgerald unsigned int event_id) 973f4f0c4c6SRichard Fitzgerald { 974f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 975a0b653e8SRichard Fitzgerald __be32 val = cpu_to_be32(event_id); 976f4f0c4c6SRichard Fitzgerald unsigned int reg; 977f4f0c4c6SRichard Fitzgerald int i, ret; 978f4f0c4c6SRichard Fitzgerald 979edb1d6d7SSimon Trimmer if (!dsp->running) 980edb1d6d7SSimon Trimmer return -EPERM; 981edb1d6d7SSimon Trimmer 9825beb8eeaSSimon Trimmer ret = cs_dsp_coeff_base_reg(ctl, ®); 983f4f0c4c6SRichard Fitzgerald if (ret) 984f4f0c4c6SRichard Fitzgerald return ret; 985f4f0c4c6SRichard Fitzgerald 9866ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 987f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 9885beb8eeaSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), ctl->offset); 989f4f0c4c6SRichard Fitzgerald 990f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 991f4f0c4c6SRichard Fitzgerald if (ret) { 9926ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 993f4f0c4c6SRichard Fitzgerald return ret; 994f4f0c4c6SRichard Fitzgerald } 995f4f0c4c6SRichard Fitzgerald 996f4f0c4c6SRichard Fitzgerald /* 997f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 998f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 999f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 1000f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 1001f4f0c4c6SRichard Fitzgerald */ 10025beb8eeaSSimon Trimmer for (i = 0; i < CS_DSP_ACKED_CTL_TIMEOUT_MS;) { 1003f4f0c4c6SRichard Fitzgerald switch (i) { 10045beb8eeaSSimon Trimmer case 0 ... CS_DSP_ACKED_CTL_N_QUICKPOLLS - 1: 1005f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 1006f4f0c4c6SRichard Fitzgerald i++; 1007f4f0c4c6SRichard Fitzgerald break; 1008f4f0c4c6SRichard Fitzgerald default: 1009f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 1010f4f0c4c6SRichard Fitzgerald i += 10; 1011f4f0c4c6SRichard Fitzgerald break; 1012f4f0c4c6SRichard Fitzgerald } 1013f4f0c4c6SRichard Fitzgerald 1014f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1015f4f0c4c6SRichard Fitzgerald if (ret) { 10166ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 1017f4f0c4c6SRichard Fitzgerald return ret; 1018f4f0c4c6SRichard Fitzgerald } 1019f4f0c4c6SRichard Fitzgerald 1020f4f0c4c6SRichard Fitzgerald if (val == 0) { 10216ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 1022f4f0c4c6SRichard Fitzgerald return 0; 1023f4f0c4c6SRichard Fitzgerald } 1024f4f0c4c6SRichard Fitzgerald } 1025f4f0c4c6SRichard Fitzgerald 10266ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 1027f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 10285beb8eeaSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), 1029f4f0c4c6SRichard Fitzgerald ctl->offset); 1030f4f0c4c6SRichard Fitzgerald 1031f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 1032f4f0c4c6SRichard Fitzgerald } 1033f4f0c4c6SRichard Fitzgerald 10340700bc2fSSimon Trimmer static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, 10356ab2b7b4SDimitris Papastamos const void *buf, size_t len) 10366ab2b7b4SDimitris Papastamos { 10373809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 10386ab2b7b4SDimitris Papastamos void *scratch; 10396ab2b7b4SDimitris Papastamos int ret; 10406ab2b7b4SDimitris Papastamos unsigned int reg; 10416ab2b7b4SDimitris Papastamos 10425beb8eeaSSimon Trimmer ret = cs_dsp_coeff_base_reg(ctl, ®); 1043b396ebcaSRichard Fitzgerald if (ret) 1044b396ebcaSRichard Fitzgerald return ret; 10456ab2b7b4SDimitris Papastamos 10464f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 10476ab2b7b4SDimitris Papastamos if (!scratch) 10486ab2b7b4SDimitris Papastamos return -ENOMEM; 10496ab2b7b4SDimitris Papastamos 10503809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 10514f8ea6d7SCharles Keepax len); 10526ab2b7b4SDimitris Papastamos if (ret) { 10536ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 10544f8ea6d7SCharles Keepax len, reg, ret); 10556ab2b7b4SDimitris Papastamos kfree(scratch); 10566ab2b7b4SDimitris Papastamos return ret; 10576ab2b7b4SDimitris Papastamos } 10586ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 10596ab2b7b4SDimitris Papastamos 10606ab2b7b4SDimitris Papastamos kfree(scratch); 10616ab2b7b4SDimitris Papastamos 10626ab2b7b4SDimitris Papastamos return 0; 10636ab2b7b4SDimitris Papastamos } 10646ab2b7b4SDimitris Papastamos 10650700bc2fSSimon Trimmer static int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, 106673ecf1a6SCharles Keepax const void *buf, size_t len) 106773ecf1a6SCharles Keepax { 106873ecf1a6SCharles Keepax int ret = 0; 106973ecf1a6SCharles Keepax 107073ecf1a6SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 107173ecf1a6SCharles Keepax ret = -EPERM; 107273ecf1a6SCharles Keepax else if (buf != ctl->cache) 107373ecf1a6SCharles Keepax memcpy(ctl->cache, buf, len); 107473ecf1a6SCharles Keepax 107573ecf1a6SCharles Keepax ctl->set = 1; 107673ecf1a6SCharles Keepax if (ctl->enabled && ctl->dsp->running) 10775beb8eeaSSimon Trimmer ret = cs_dsp_coeff_write_ctrl_raw(ctl, buf, len); 107873ecf1a6SCharles Keepax 107973ecf1a6SCharles Keepax return ret; 108073ecf1a6SCharles Keepax } 108173ecf1a6SCharles Keepax 10827585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 10836ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 10846ab2b7b4SDimitris Papastamos { 10859ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10869ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10879ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10880700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 10896ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1090168d10e7SCharles Keepax int ret = 0; 1091168d10e7SCharles Keepax 10920700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 10930700bc2fSSimon Trimmer ret = cs_dsp_coeff_write_ctrl(cs_ctl, p, cs_ctl->len); 10940700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 1095168d10e7SCharles Keepax 1096168d10e7SCharles Keepax return ret; 10976ab2b7b4SDimitris Papastamos } 10986ab2b7b4SDimitris Papastamos 10999ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 11009ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 11019ee78757SCharles Keepax { 11029ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11039ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11049ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11050700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 11069ee78757SCharles Keepax int ret = 0; 11079ee78757SCharles Keepax 11080700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 11099ee78757SCharles Keepax 11100700bc2fSSimon Trimmer if (copy_from_user(cs_ctl->cache, bytes, size)) 11119ee78757SCharles Keepax ret = -EFAULT; 111273ecf1a6SCharles Keepax else 11130700bc2fSSimon Trimmer ret = cs_dsp_coeff_write_ctrl(cs_ctl, cs_ctl->cache, size); 11149ee78757SCharles Keepax 11150700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 11169ee78757SCharles Keepax 11179ee78757SCharles Keepax return ret; 11189ee78757SCharles Keepax } 11199ee78757SCharles Keepax 1120a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 1121a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1122a23ebba8SRichard Fitzgerald { 1123a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 1124a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 1125a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11260700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 1127a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 1128a23ebba8SRichard Fitzgerald int ret; 1129a23ebba8SRichard Fitzgerald 1130a23ebba8SRichard Fitzgerald if (val == 0) 1131a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 1132a23ebba8SRichard Fitzgerald 11330700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 1134a23ebba8SRichard Fitzgerald 1135edb1d6d7SSimon Trimmer if (cs_ctl->enabled) 11360700bc2fSSimon Trimmer ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); 1137a23ebba8SRichard Fitzgerald else 1138a23ebba8SRichard Fitzgerald ret = -EPERM; 1139a23ebba8SRichard Fitzgerald 11400700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 1141a23ebba8SRichard Fitzgerald 1142a23ebba8SRichard Fitzgerald return ret; 1143a23ebba8SRichard Fitzgerald } 1144a23ebba8SRichard Fitzgerald 11450700bc2fSSimon Trimmer static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, 11466ab2b7b4SDimitris Papastamos void *buf, size_t len) 11476ab2b7b4SDimitris Papastamos { 11483809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 11496ab2b7b4SDimitris Papastamos void *scratch; 11506ab2b7b4SDimitris Papastamos int ret; 11516ab2b7b4SDimitris Papastamos unsigned int reg; 11526ab2b7b4SDimitris Papastamos 11535beb8eeaSSimon Trimmer ret = cs_dsp_coeff_base_reg(ctl, ®); 1154b396ebcaSRichard Fitzgerald if (ret) 1155b396ebcaSRichard Fitzgerald return ret; 11566ab2b7b4SDimitris Papastamos 11574f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 11586ab2b7b4SDimitris Papastamos if (!scratch) 11596ab2b7b4SDimitris Papastamos return -ENOMEM; 11606ab2b7b4SDimitris Papastamos 11614f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 11626ab2b7b4SDimitris Papastamos if (ret) { 11636ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 11645602a643SCharles Keepax len, reg, ret); 11656ab2b7b4SDimitris Papastamos kfree(scratch); 11666ab2b7b4SDimitris Papastamos return ret; 11676ab2b7b4SDimitris Papastamos } 11686ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 11696ab2b7b4SDimitris Papastamos 11704f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 11716ab2b7b4SDimitris Papastamos kfree(scratch); 11726ab2b7b4SDimitris Papastamos 11736ab2b7b4SDimitris Papastamos return 0; 11746ab2b7b4SDimitris Papastamos } 11756ab2b7b4SDimitris Papastamos 11760700bc2fSSimon Trimmer static int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) 117773ecf1a6SCharles Keepax { 117873ecf1a6SCharles Keepax int ret = 0; 117973ecf1a6SCharles Keepax 118073ecf1a6SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 118173ecf1a6SCharles Keepax if (ctl->enabled && ctl->dsp->running) 11825beb8eeaSSimon Trimmer return cs_dsp_coeff_read_ctrl_raw(ctl, buf, len); 118373ecf1a6SCharles Keepax else 118473ecf1a6SCharles Keepax return -EPERM; 118573ecf1a6SCharles Keepax } else { 118673ecf1a6SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 11875beb8eeaSSimon Trimmer ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 118873ecf1a6SCharles Keepax 118973ecf1a6SCharles Keepax if (buf != ctl->cache) 119073ecf1a6SCharles Keepax memcpy(buf, ctl->cache, len); 119173ecf1a6SCharles Keepax } 119273ecf1a6SCharles Keepax 119373ecf1a6SCharles Keepax return ret; 119473ecf1a6SCharles Keepax } 119573ecf1a6SCharles Keepax 11967585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 11976ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 11986ab2b7b4SDimitris Papastamos { 11999ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 12009ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 12019ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12020700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 12036ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 120473ecf1a6SCharles Keepax int ret; 1205168d10e7SCharles Keepax 12060700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 12070700bc2fSSimon Trimmer ret = cs_dsp_coeff_read_ctrl(cs_ctl, p, cs_ctl->len); 12080700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 120926c22a19SCharles Keepax 1210168d10e7SCharles Keepax return ret; 12116ab2b7b4SDimitris Papastamos } 12126ab2b7b4SDimitris Papastamos 12139ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 12149ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 12159ee78757SCharles Keepax { 12169ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 12179ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 12189ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12190700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 12209ee78757SCharles Keepax int ret = 0; 12219ee78757SCharles Keepax 12220700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 12239ee78757SCharles Keepax 12240700bc2fSSimon Trimmer ret = cs_dsp_coeff_read_ctrl(cs_ctl, cs_ctl->cache, size); 12259ee78757SCharles Keepax 12260700bc2fSSimon Trimmer if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) 12279ee78757SCharles Keepax ret = -EFAULT; 12289ee78757SCharles Keepax 12290700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 12309ee78757SCharles Keepax 12319ee78757SCharles Keepax return ret; 12329ee78757SCharles Keepax } 12339ee78757SCharles Keepax 1234a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1235a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1236a23ebba8SRichard Fitzgerald { 1237a23ebba8SRichard Fitzgerald /* 1238a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1239a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1240a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1241a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1242a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1243a23ebba8SRichard Fitzgerald */ 1244a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1245a23ebba8SRichard Fitzgerald 1246a23ebba8SRichard Fitzgerald return 0; 1247a23ebba8SRichard Fitzgerald } 1248a23ebba8SRichard Fitzgerald 12499ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 12509ee78757SCharles Keepax { 12519ee78757SCharles Keepax unsigned int out, rd, wr, vol; 12529ee78757SCharles Keepax 12539ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 12549ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 12559ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 12569ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12579ee78757SCharles Keepax 12589ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 12599ee78757SCharles Keepax } else { 12609ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 12619ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 12629ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12639ee78757SCharles Keepax 12649ee78757SCharles Keepax out = 0; 12659ee78757SCharles Keepax } 12669ee78757SCharles Keepax 12679ee78757SCharles Keepax if (in) { 12689ee78757SCharles Keepax out |= rd; 12699ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 12709ee78757SCharles Keepax out |= wr; 12719ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 12729ee78757SCharles Keepax out |= vol; 12739ee78757SCharles Keepax } else { 12749ee78757SCharles Keepax out |= rd | wr | vol; 12759ee78757SCharles Keepax } 12769ee78757SCharles Keepax 12779ee78757SCharles Keepax return out; 12789ee78757SCharles Keepax } 12799ee78757SCharles Keepax 12803809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 12816ab2b7b4SDimitris Papastamos { 12820700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 12836ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 12846ab2b7b4SDimitris Papastamos int ret; 12856ab2b7b4SDimitris Papastamos 12866ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 12876ab2b7b4SDimitris Papastamos if (!kcontrol) 12886ab2b7b4SDimitris Papastamos return -ENOMEM; 12896ab2b7b4SDimitris Papastamos 12906ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 12916ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 12929ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 12939ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 12949ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 12950700bc2fSSimon Trimmer kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len); 1296a23ebba8SRichard Fitzgerald 12970700bc2fSSimon Trimmer switch (cs_ctl->type) { 1298a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1299a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1300a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1301a23ebba8SRichard Fitzgerald break; 1302a23ebba8SRichard Fitzgerald default: 1303d7789f5bSRichard Fitzgerald if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 13040700bc2fSSimon Trimmer ctl->bytes_ext.max = cs_ctl->len; 13059ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 13069ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1307d7789f5bSRichard Fitzgerald } else { 1308d7789f5bSRichard Fitzgerald kcontrol->get = wm_coeff_get; 1309d7789f5bSRichard Fitzgerald kcontrol->put = wm_coeff_put; 1310d7789f5bSRichard Fitzgerald } 1311a23ebba8SRichard Fitzgerald break; 1312a23ebba8SRichard Fitzgerald } 131326c22a19SCharles Keepax 13140fe1daa6SKuninori Morimoto ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); 13156ab2b7b4SDimitris Papastamos if (ret < 0) 13166ab2b7b4SDimitris Papastamos goto err_kcontrol; 13176ab2b7b4SDimitris Papastamos 13186ab2b7b4SDimitris Papastamos kfree(kcontrol); 13196ab2b7b4SDimitris Papastamos 13206ab2b7b4SDimitris Papastamos return 0; 13216ab2b7b4SDimitris Papastamos 13226ab2b7b4SDimitris Papastamos err_kcontrol: 13236ab2b7b4SDimitris Papastamos kfree(kcontrol); 13246ab2b7b4SDimitris Papastamos return ret; 13256ab2b7b4SDimitris Papastamos } 13266ab2b7b4SDimitris Papastamos 13275beb8eeaSSimon Trimmer static int cs_dsp_coeff_init_control_caches(struct wm_adsp *dsp) 1328b21acc1cSCharles Keepax { 13290700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1330b21acc1cSCharles Keepax int ret; 1331b21acc1cSCharles Keepax 1332b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1333b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1334b21acc1cSCharles Keepax continue; 133526c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 133626c22a19SCharles Keepax continue; 133726c22a19SCharles Keepax 133804ff40a9SRichard Fitzgerald /* 133904ff40a9SRichard Fitzgerald * For readable controls populate the cache from the DSP memory. 134004ff40a9SRichard Fitzgerald * For non-readable controls the cache was zero-filled when 134104ff40a9SRichard Fitzgerald * created so we don't need to do anything. 134204ff40a9SRichard Fitzgerald */ 134304ff40a9SRichard Fitzgerald if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 13445beb8eeaSSimon Trimmer ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 1345b21acc1cSCharles Keepax if (ret < 0) 1346b21acc1cSCharles Keepax return ret; 1347b21acc1cSCharles Keepax } 134804ff40a9SRichard Fitzgerald } 1349b21acc1cSCharles Keepax 1350b21acc1cSCharles Keepax return 0; 1351b21acc1cSCharles Keepax } 1352b21acc1cSCharles Keepax 13535beb8eeaSSimon Trimmer static int cs_dsp_coeff_sync_controls(struct wm_adsp *dsp) 1354b21acc1cSCharles Keepax { 13550700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1356b21acc1cSCharles Keepax int ret; 1357b21acc1cSCharles Keepax 1358b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1359b21acc1cSCharles Keepax if (!ctl->enabled) 1360b21acc1cSCharles Keepax continue; 136126c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 13625beb8eeaSSimon Trimmer ret = cs_dsp_coeff_write_ctrl_raw(ctl, ctl->cache, 136373ecf1a6SCharles Keepax ctl->len); 1364b21acc1cSCharles Keepax if (ret < 0) 1365b21acc1cSCharles Keepax return ret; 1366b21acc1cSCharles Keepax } 1367b21acc1cSCharles Keepax } 1368b21acc1cSCharles Keepax 1369b21acc1cSCharles Keepax return 0; 1370b21acc1cSCharles Keepax } 1371b21acc1cSCharles Keepax 13725beb8eeaSSimon Trimmer static void cs_dsp_signal_event_controls(struct wm_adsp *dsp, 1373f4f0c4c6SRichard Fitzgerald unsigned int event) 1374f4f0c4c6SRichard Fitzgerald { 13750700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1376f4f0c4c6SRichard Fitzgerald int ret; 1377f4f0c4c6SRichard Fitzgerald 1378f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1379f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1380f4f0c4c6SRichard Fitzgerald continue; 1381f4f0c4c6SRichard Fitzgerald 138287aa6374SCharles Keepax if (!ctl->enabled) 138387aa6374SCharles Keepax continue; 138487aa6374SCharles Keepax 13855beb8eeaSSimon Trimmer ret = cs_dsp_coeff_write_acked_control(ctl, event); 1386f4f0c4c6SRichard Fitzgerald if (ret) 13876ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, 1388f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1389f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1390f4f0c4c6SRichard Fitzgerald } 1391f4f0c4c6SRichard Fitzgerald } 1392f4f0c4c6SRichard Fitzgerald 1393b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1394b21acc1cSCharles Keepax { 1395df6c505cSSimon Trimmer struct wm_coeff_ctl *ctl = container_of(work, 1396df6c505cSSimon Trimmer struct wm_coeff_ctl, 1397b21acc1cSCharles Keepax work); 13980700bc2fSSimon Trimmer wmfw_add_ctl(ctl->cs_ctl->dsp, ctl); 1399b21acc1cSCharles Keepax } 1400b21acc1cSCharles Keepax 14010700bc2fSSimon Trimmer static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl) 140266225e98SRichard Fitzgerald { 14030700bc2fSSimon Trimmer kfree(ctl->cache); 14040700bc2fSSimon Trimmer kfree(ctl->subname); 14050700bc2fSSimon Trimmer kfree(ctl); 14060700bc2fSSimon Trimmer } 14070700bc2fSSimon Trimmer 14080700bc2fSSimon Trimmer static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) 14090700bc2fSSimon Trimmer { 14100700bc2fSSimon Trimmer struct wm_adsp *dsp = cs_ctl->dsp; 14110700bc2fSSimon Trimmer struct wm_coeff_ctl *ctl; 14120700bc2fSSimon Trimmer char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 14130700bc2fSSimon Trimmer const char *region_name; 14140700bc2fSSimon Trimmer int ret; 14150700bc2fSSimon Trimmer 14160700bc2fSSimon Trimmer if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) 14170700bc2fSSimon Trimmer return 0; 14180700bc2fSSimon Trimmer 14190700bc2fSSimon Trimmer region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type); 14200700bc2fSSimon Trimmer if (!region_name) { 14210700bc2fSSimon Trimmer adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type); 14220700bc2fSSimon Trimmer return -EINVAL; 14230700bc2fSSimon Trimmer } 14240700bc2fSSimon Trimmer 14250700bc2fSSimon Trimmer switch (dsp->fw_ver) { 14260700bc2fSSimon Trimmer case 0: 14270700bc2fSSimon Trimmer case 1: 14280700bc2fSSimon Trimmer snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", 14290700bc2fSSimon Trimmer dsp->name, region_name, cs_ctl->alg_region.alg); 14300700bc2fSSimon Trimmer break; 14310700bc2fSSimon Trimmer case 2: 14320700bc2fSSimon Trimmer ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 14330700bc2fSSimon Trimmer "%s%c %.12s %x", dsp->name, *region_name, 14340700bc2fSSimon Trimmer wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); 14350700bc2fSSimon Trimmer break; 14360700bc2fSSimon Trimmer default: 14370700bc2fSSimon Trimmer ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 14380700bc2fSSimon Trimmer "%s %.12s %x", dsp->name, 14390700bc2fSSimon Trimmer wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); 14400700bc2fSSimon Trimmer break; 14410700bc2fSSimon Trimmer } 14420700bc2fSSimon Trimmer 14430700bc2fSSimon Trimmer if (cs_ctl->subname) { 14440700bc2fSSimon Trimmer int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 14450700bc2fSSimon Trimmer int skip = 0; 14460700bc2fSSimon Trimmer 14470700bc2fSSimon Trimmer if (dsp->component->name_prefix) 14480700bc2fSSimon Trimmer avail -= strlen(dsp->component->name_prefix) + 1; 14490700bc2fSSimon Trimmer 14500700bc2fSSimon Trimmer /* Truncate the subname from the start if it is too long */ 14510700bc2fSSimon Trimmer if (cs_ctl->subname_len > avail) 14520700bc2fSSimon Trimmer skip = cs_ctl->subname_len - avail; 14530700bc2fSSimon Trimmer 14540700bc2fSSimon Trimmer snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, 14550700bc2fSSimon Trimmer " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip); 14560700bc2fSSimon Trimmer } 14570700bc2fSSimon Trimmer 14580700bc2fSSimon Trimmer ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 14590700bc2fSSimon Trimmer if (!ctl) 14600700bc2fSSimon Trimmer return -ENOMEM; 14610700bc2fSSimon Trimmer ctl->cs_ctl = cs_ctl; 14620700bc2fSSimon Trimmer 14630700bc2fSSimon Trimmer ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 14640700bc2fSSimon Trimmer if (!ctl->name) { 14650700bc2fSSimon Trimmer ret = -ENOMEM; 14660700bc2fSSimon Trimmer goto err_ctl; 14670700bc2fSSimon Trimmer } 14680700bc2fSSimon Trimmer 14690700bc2fSSimon Trimmer cs_ctl->priv = ctl; 14700700bc2fSSimon Trimmer 14710700bc2fSSimon Trimmer INIT_WORK(&ctl->work, wm_adsp_ctl_work); 14720700bc2fSSimon Trimmer schedule_work(&ctl->work); 14730700bc2fSSimon Trimmer 14740700bc2fSSimon Trimmer return 0; 14750700bc2fSSimon Trimmer 14760700bc2fSSimon Trimmer err_ctl: 14770700bc2fSSimon Trimmer kfree(ctl); 14780700bc2fSSimon Trimmer 14790700bc2fSSimon Trimmer return ret; 14800700bc2fSSimon Trimmer } 14810700bc2fSSimon Trimmer 14820700bc2fSSimon Trimmer static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) 14830700bc2fSSimon Trimmer { 14840700bc2fSSimon Trimmer struct wm_coeff_ctl *ctl = cs_ctl->priv; 14850700bc2fSSimon Trimmer 1486df6c505cSSimon Trimmer cancel_work_sync(&ctl->work); 1487df6c505cSSimon Trimmer 148866225e98SRichard Fitzgerald kfree(ctl->name); 148966225e98SRichard Fitzgerald kfree(ctl); 149066225e98SRichard Fitzgerald } 149166225e98SRichard Fitzgerald 14925beb8eeaSSimon Trimmer static int cs_dsp_create_control(struct wm_adsp *dsp, 14935beb8eeaSSimon Trimmer const struct cs_dsp_alg_region *alg_region, 14942323736dSCharles Keepax unsigned int offset, unsigned int len, 149526c22a19SCharles Keepax const char *subname, unsigned int subname_len, 1496d07a6d45SSimon Trimmer unsigned int flags, unsigned int type) 1497b21acc1cSCharles Keepax { 14980700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1499b21acc1cSCharles Keepax int ret; 1500b21acc1cSCharles Keepax 150164779607SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1502*2169f2f1SSimon Trimmer if (ctl->fw_name == dsp->fw_name && 150364779607SCharles Keepax ctl->alg_region.alg == alg_region->alg && 150464779607SCharles Keepax ctl->alg_region.type == alg_region->type) { 150564779607SCharles Keepax if ((!subname && !ctl->subname) || 150664779607SCharles Keepax (subname && !strncmp(ctl->subname, subname, ctl->subname_len))) { 150764779607SCharles Keepax if (!ctl->enabled) 150864779607SCharles Keepax ctl->enabled = 1; 150964779607SCharles Keepax return 0; 151064779607SCharles Keepax } 151164779607SCharles Keepax } 151264779607SCharles Keepax } 151364779607SCharles Keepax 1514b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1515b21acc1cSCharles Keepax if (!ctl) 1516b21acc1cSCharles Keepax return -ENOMEM; 1517*2169f2f1SSimon Trimmer 1518*2169f2f1SSimon Trimmer ctl->fw_name = dsp->fw_name; 1519b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 15200700bc2fSSimon Trimmer if (subname && dsp->fw_ver >= 2) { 1521eb65ccdbSLi Xu ctl->subname_len = subname_len; 1522eb65ccdbSLi Xu ctl->subname = kmemdup(subname, 1523eb65ccdbSLi Xu strlen(subname) + 1, GFP_KERNEL); 1524eb65ccdbSLi Xu if (!ctl->subname) { 1525eb65ccdbSLi Xu ret = -ENOMEM; 15260700bc2fSSimon Trimmer goto err_ctl; 1527eb65ccdbSLi Xu } 1528eb65ccdbSLi Xu } 1529b21acc1cSCharles Keepax ctl->enabled = 1; 1530b21acc1cSCharles Keepax ctl->set = 0; 1531b21acc1cSCharles Keepax ctl->dsp = dsp; 1532b21acc1cSCharles Keepax 153326c22a19SCharles Keepax ctl->flags = flags; 15348eb084d0SStuart Henderson ctl->type = type; 15352323736dSCharles Keepax ctl->offset = offset; 1536b21acc1cSCharles Keepax ctl->len = len; 1537b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1538b21acc1cSCharles Keepax if (!ctl->cache) { 1539b21acc1cSCharles Keepax ret = -ENOMEM; 1540eb65ccdbSLi Xu goto err_ctl_subname; 1541b21acc1cSCharles Keepax } 1542b21acc1cSCharles Keepax 15432323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 15442323736dSCharles Keepax 15450700bc2fSSimon Trimmer ret = wm_adsp_control_add(ctl); 15460700bc2fSSimon Trimmer if (ret) 15470700bc2fSSimon Trimmer goto err_list_del; 1548b21acc1cSCharles Keepax 1549b21acc1cSCharles Keepax return 0; 1550b21acc1cSCharles Keepax 15510700bc2fSSimon Trimmer err_list_del: 15520700bc2fSSimon Trimmer list_del(&ctl->list); 15530700bc2fSSimon Trimmer kfree(ctl->cache); 1554eb65ccdbSLi Xu err_ctl_subname: 1555eb65ccdbSLi Xu kfree(ctl->subname); 1556b21acc1cSCharles Keepax err_ctl: 1557b21acc1cSCharles Keepax kfree(ctl); 1558b21acc1cSCharles Keepax 1559b21acc1cSCharles Keepax return ret; 1560b21acc1cSCharles Keepax } 1561b21acc1cSCharles Keepax 15625beb8eeaSSimon Trimmer struct cs_dsp_coeff_parsed_alg { 15632323736dSCharles Keepax int id; 15642323736dSCharles Keepax const u8 *name; 15652323736dSCharles Keepax int name_len; 15662323736dSCharles Keepax int ncoeff; 15672323736dSCharles Keepax }; 15682323736dSCharles Keepax 15695beb8eeaSSimon Trimmer struct cs_dsp_coeff_parsed_coeff { 15702323736dSCharles Keepax int offset; 15712323736dSCharles Keepax int mem_type; 15722323736dSCharles Keepax const u8 *name; 15732323736dSCharles Keepax int name_len; 1574d07a6d45SSimon Trimmer unsigned int ctl_type; 15752323736dSCharles Keepax int flags; 15762323736dSCharles Keepax int len; 15772323736dSCharles Keepax }; 15782323736dSCharles Keepax 15795beb8eeaSSimon Trimmer static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1580cb5b57a9SCharles Keepax { 1581cb5b57a9SCharles Keepax int length; 1582cb5b57a9SCharles Keepax 1583cb5b57a9SCharles Keepax switch (bytes) { 1584cb5b57a9SCharles Keepax case 1: 1585cb5b57a9SCharles Keepax length = **pos; 1586cb5b57a9SCharles Keepax break; 1587cb5b57a9SCharles Keepax case 2: 15888299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1589cb5b57a9SCharles Keepax break; 1590cb5b57a9SCharles Keepax default: 1591cb5b57a9SCharles Keepax return 0; 1592cb5b57a9SCharles Keepax } 1593cb5b57a9SCharles Keepax 1594cb5b57a9SCharles Keepax if (str) 1595cb5b57a9SCharles Keepax *str = *pos + bytes; 1596cb5b57a9SCharles Keepax 1597cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1598cb5b57a9SCharles Keepax 1599cb5b57a9SCharles Keepax return length; 1600cb5b57a9SCharles Keepax } 1601cb5b57a9SCharles Keepax 16025beb8eeaSSimon Trimmer static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos) 1603cb5b57a9SCharles Keepax { 1604cb5b57a9SCharles Keepax int val = 0; 1605cb5b57a9SCharles Keepax 1606cb5b57a9SCharles Keepax switch (bytes) { 1607cb5b57a9SCharles Keepax case 2: 16088299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1609cb5b57a9SCharles Keepax break; 1610cb5b57a9SCharles Keepax case 4: 16118299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1612cb5b57a9SCharles Keepax break; 1613cb5b57a9SCharles Keepax default: 1614cb5b57a9SCharles Keepax break; 1615cb5b57a9SCharles Keepax } 1616cb5b57a9SCharles Keepax 1617cb5b57a9SCharles Keepax *pos += bytes; 1618cb5b57a9SCharles Keepax 1619cb5b57a9SCharles Keepax return val; 1620cb5b57a9SCharles Keepax } 1621cb5b57a9SCharles Keepax 16225beb8eeaSSimon Trimmer static inline void cs_dsp_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 16235beb8eeaSSimon Trimmer struct cs_dsp_coeff_parsed_alg *blk) 16242323736dSCharles Keepax { 16252323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 16262323736dSCharles Keepax 1627cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1628cb5b57a9SCharles Keepax case 0: 1629cb5b57a9SCharles Keepax case 1: 16302323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 16312323736dSCharles Keepax *data = raw->data; 16322323736dSCharles Keepax 16332323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 16342323736dSCharles Keepax blk->name = raw->name; 16352323736dSCharles Keepax blk->name_len = strlen(raw->name); 16362323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1637cb5b57a9SCharles Keepax break; 1638cb5b57a9SCharles Keepax default: 16395beb8eeaSSimon Trimmer blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), data); 16405beb8eeaSSimon Trimmer blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), data, 1641cb5b57a9SCharles Keepax &blk->name); 16425beb8eeaSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u16), data, NULL); 16435beb8eeaSSimon Trimmer blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), data); 1644cb5b57a9SCharles Keepax break; 1645cb5b57a9SCharles Keepax } 16462323736dSCharles Keepax 16476ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 16486ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 16496ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 16502323736dSCharles Keepax } 16512323736dSCharles Keepax 16525beb8eeaSSimon Trimmer static inline void cs_dsp_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 16535beb8eeaSSimon Trimmer struct cs_dsp_coeff_parsed_coeff *blk) 16542323736dSCharles Keepax { 16552323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1656cb5b57a9SCharles Keepax const u8 *tmp; 1657cb5b57a9SCharles Keepax int length; 16582323736dSCharles Keepax 1659cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1660cb5b57a9SCharles Keepax case 0: 1661cb5b57a9SCharles Keepax case 1: 16622323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 16632323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 16642323736dSCharles Keepax 16652323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 16662323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 16672323736dSCharles Keepax blk->name = raw->name; 16682323736dSCharles Keepax blk->name_len = strlen(raw->name); 1669d07a6d45SSimon Trimmer blk->ctl_type = le16_to_cpu(raw->ctl_type); 16702323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 16712323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1672cb5b57a9SCharles Keepax break; 1673cb5b57a9SCharles Keepax default: 1674cb5b57a9SCharles Keepax tmp = *data; 16755beb8eeaSSimon Trimmer blk->offset = cs_dsp_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 16765beb8eeaSSimon Trimmer blk->mem_type = cs_dsp_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 16775beb8eeaSSimon Trimmer length = cs_dsp_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 16785beb8eeaSSimon Trimmer blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, 1679cb5b57a9SCharles Keepax &blk->name); 16805beb8eeaSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u8), &tmp, NULL); 16815beb8eeaSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u16), &tmp, NULL); 16825beb8eeaSSimon Trimmer blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 16835beb8eeaSSimon Trimmer blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp); 16845beb8eeaSSimon Trimmer blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp); 1685cb5b57a9SCharles Keepax 1686cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1687cb5b57a9SCharles Keepax break; 1688cb5b57a9SCharles Keepax } 16892323736dSCharles Keepax 16906ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 16916ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 16926ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 16936ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 16946ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 16956ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 16962323736dSCharles Keepax } 16972323736dSCharles Keepax 16985beb8eeaSSimon Trimmer static int cs_dsp_check_coeff_flags(struct wm_adsp *dsp, 16995beb8eeaSSimon Trimmer const struct cs_dsp_coeff_parsed_coeff *coeff_blk, 1700f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1701f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1702f4f0c4c6SRichard Fitzgerald { 1703f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1704f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 17056ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1706f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1707f4f0c4c6SRichard Fitzgerald return -EINVAL; 1708f4f0c4c6SRichard Fitzgerald } 1709f4f0c4c6SRichard Fitzgerald 1710f4f0c4c6SRichard Fitzgerald return 0; 1711f4f0c4c6SRichard Fitzgerald } 1712f4f0c4c6SRichard Fitzgerald 17135beb8eeaSSimon Trimmer static int cs_dsp_parse_coeff(struct wm_adsp *dsp, 17142323736dSCharles Keepax const struct wmfw_region *region) 17152323736dSCharles Keepax { 17165beb8eeaSSimon Trimmer struct cs_dsp_alg_region alg_region = {}; 17175beb8eeaSSimon Trimmer struct cs_dsp_coeff_parsed_alg alg_blk; 17185beb8eeaSSimon Trimmer struct cs_dsp_coeff_parsed_coeff coeff_blk; 17192323736dSCharles Keepax const u8 *data = region->data; 17202323736dSCharles Keepax int i, ret; 17212323736dSCharles Keepax 17225beb8eeaSSimon Trimmer cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk); 17232323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 17245beb8eeaSSimon Trimmer cs_dsp_coeff_parse_coeff(dsp, &data, &coeff_blk); 17252323736dSCharles Keepax 17262323736dSCharles Keepax switch (coeff_blk.ctl_type) { 1727d07a6d45SSimon Trimmer case WMFW_CTL_TYPE_BYTES: 17282323736dSCharles Keepax break; 1729a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1730a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1731a23ebba8SRichard Fitzgerald continue; /* ignore */ 1732a23ebba8SRichard Fitzgerald 17335beb8eeaSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1734a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1735a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1736a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1737a23ebba8SRichard Fitzgerald 0); 1738a23ebba8SRichard Fitzgerald if (ret) 1739a23ebba8SRichard Fitzgerald return -EINVAL; 1740a23ebba8SRichard Fitzgerald break; 1741f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 17425beb8eeaSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1743f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1744f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1745f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1746f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1747f4f0c4c6SRichard Fitzgerald 0); 1748f4f0c4c6SRichard Fitzgerald if (ret) 1749f4f0c4c6SRichard Fitzgerald return -EINVAL; 1750f4f0c4c6SRichard Fitzgerald break; 1751d52ed4b0SRichard Fitzgerald case WMFW_CTL_TYPE_HOST_BUFFER: 17525beb8eeaSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1753d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1754d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1755d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1756d52ed4b0SRichard Fitzgerald 0); 1757d52ed4b0SRichard Fitzgerald if (ret) 1758d52ed4b0SRichard Fitzgerald return -EINVAL; 1759d52ed4b0SRichard Fitzgerald break; 17602323736dSCharles Keepax default: 17616ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Unknown control type: %d\n", 17622323736dSCharles Keepax coeff_blk.ctl_type); 17632323736dSCharles Keepax return -EINVAL; 17642323736dSCharles Keepax } 17652323736dSCharles Keepax 17662323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 17672323736dSCharles Keepax alg_region.alg = alg_blk.id; 17682323736dSCharles Keepax 17695beb8eeaSSimon Trimmer ret = cs_dsp_create_control(dsp, &alg_region, 17702323736dSCharles Keepax coeff_blk.offset, 17712323736dSCharles Keepax coeff_blk.len, 17722323736dSCharles Keepax coeff_blk.name, 177326c22a19SCharles Keepax coeff_blk.name_len, 17748eb084d0SStuart Henderson coeff_blk.flags, 17758eb084d0SStuart Henderson coeff_blk.ctl_type); 17762323736dSCharles Keepax if (ret < 0) 17776ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to create control: %.*s, %d\n", 17782323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 17792323736dSCharles Keepax } 17802323736dSCharles Keepax 17812323736dSCharles Keepax return 0; 17822323736dSCharles Keepax } 17832323736dSCharles Keepax 17845beb8eeaSSimon Trimmer static unsigned int cs_dsp_adsp1_parse_sizes(struct wm_adsp *dsp, 17854e08d50dSCharles Keepax const char * const file, 17864e08d50dSCharles Keepax unsigned int pos, 17874e08d50dSCharles Keepax const struct firmware *firmware) 17884e08d50dSCharles Keepax { 17894e08d50dSCharles Keepax const struct wmfw_adsp1_sizes *adsp1_sizes; 17904e08d50dSCharles Keepax 17914e08d50dSCharles Keepax adsp1_sizes = (void *)&firmware->data[pos]; 17924e08d50dSCharles Keepax 17936ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 17944e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 17954e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->zm)); 17964e08d50dSCharles Keepax 17974e08d50dSCharles Keepax return pos + sizeof(*adsp1_sizes); 17984e08d50dSCharles Keepax } 17994e08d50dSCharles Keepax 18005beb8eeaSSimon Trimmer static unsigned int cs_dsp_adsp2_parse_sizes(struct wm_adsp *dsp, 18014e08d50dSCharles Keepax const char * const file, 18024e08d50dSCharles Keepax unsigned int pos, 18034e08d50dSCharles Keepax const struct firmware *firmware) 18044e08d50dSCharles Keepax { 18054e08d50dSCharles Keepax const struct wmfw_adsp2_sizes *adsp2_sizes; 18064e08d50dSCharles Keepax 18074e08d50dSCharles Keepax adsp2_sizes = (void *)&firmware->data[pos]; 18084e08d50dSCharles Keepax 18096ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 18104e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 18114e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 18124e08d50dSCharles Keepax 18134e08d50dSCharles Keepax return pos + sizeof(*adsp2_sizes); 18144e08d50dSCharles Keepax } 18154e08d50dSCharles Keepax 18165beb8eeaSSimon Trimmer static bool cs_dsp_validate_version(struct wm_adsp *dsp, unsigned int version) 18174e08d50dSCharles Keepax { 18184e08d50dSCharles Keepax switch (version) { 18194e08d50dSCharles Keepax case 0: 18206ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Deprecated file format %d\n", version); 18214e08d50dSCharles Keepax return true; 18224e08d50dSCharles Keepax case 1: 18234e08d50dSCharles Keepax case 2: 18244e08d50dSCharles Keepax return true; 18254e08d50dSCharles Keepax default: 18264e08d50dSCharles Keepax return false; 18274e08d50dSCharles Keepax } 18284e08d50dSCharles Keepax } 18294e08d50dSCharles Keepax 18305beb8eeaSSimon Trimmer static bool cs_dsp_halo_validate_version(struct wm_adsp *dsp, unsigned int version) 1831170b1e12SWen Shi { 1832170b1e12SWen Shi switch (version) { 1833170b1e12SWen Shi case 3: 1834170b1e12SWen Shi return true; 1835170b1e12SWen Shi default: 1836170b1e12SWen Shi return false; 1837170b1e12SWen Shi } 1838170b1e12SWen Shi } 1839170b1e12SWen Shi 1840*2169f2f1SSimon Trimmer static int cs_dsp_load(struct wm_adsp *dsp, const char *fw_file_name) 18412159ad93SMark Brown { 1842cf17c83cSMark Brown LIST_HEAD(buf_list); 18432159ad93SMark Brown const struct firmware *firmware; 18442159ad93SMark Brown struct regmap *regmap = dsp->regmap; 18452159ad93SMark Brown unsigned int pos = 0; 18462159ad93SMark Brown const struct wmfw_header *header; 18472159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 18482159ad93SMark Brown const struct wmfw_footer *footer; 18492159ad93SMark Brown const struct wmfw_region *region; 18505beb8eeaSSimon Trimmer const struct cs_dsp_region *mem; 18512159ad93SMark Brown const char *region_name; 18521cab2a84SRichard Fitzgerald char *file, *text = NULL; 18535beb8eeaSSimon Trimmer struct cs_dsp_buf *buf; 18542159ad93SMark Brown unsigned int reg; 18552159ad93SMark Brown int regions = 0; 18564e08d50dSCharles Keepax int ret, offset, type; 18572159ad93SMark Brown 18582159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 18592159ad93SMark Brown if (file == NULL) 18602159ad93SMark Brown return -ENOMEM; 18612159ad93SMark Brown 1862605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, 1863*2169f2f1SSimon Trimmer fw_file_name); 18642159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 18652159ad93SMark Brown 18662159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 18672159ad93SMark Brown if (ret != 0) { 18686ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to request '%s'\n", file); 18692159ad93SMark Brown goto out; 18702159ad93SMark Brown } 18712159ad93SMark Brown ret = -EINVAL; 18722159ad93SMark Brown 18732159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 18742159ad93SMark Brown if (pos >= firmware->size) { 18756ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: file too short, %zu bytes\n", 18762159ad93SMark Brown file, firmware->size); 18772159ad93SMark Brown goto out_fw; 18782159ad93SMark Brown } 18792159ad93SMark Brown 18802159ad93SMark Brown header = (void *)&firmware->data[0]; 18812159ad93SMark Brown 18822159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 18836ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: invalid magic\n", file); 18842159ad93SMark Brown goto out_fw; 18852159ad93SMark Brown } 18862159ad93SMark Brown 18874e08d50dSCharles Keepax if (!dsp->ops->validate_version(dsp, header->ver)) { 18886ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: unknown file format %d\n", 18892159ad93SMark Brown file, header->ver); 18902159ad93SMark Brown goto out_fw; 18912159ad93SMark Brown } 18922323736dSCharles Keepax 18936ab1d0ccSSimon Trimmer cs_dsp_info(dsp, "Firmware version: %d\n", header->ver); 18942323736dSCharles Keepax dsp->fw_ver = header->ver; 18952159ad93SMark Brown 18962159ad93SMark Brown if (header->core != dsp->type) { 18976ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: invalid core %d != %d\n", 18982159ad93SMark Brown file, header->core, dsp->type); 18992159ad93SMark Brown goto out_fw; 19002159ad93SMark Brown } 19012159ad93SMark Brown 19024e08d50dSCharles Keepax pos = sizeof(*header); 19034e08d50dSCharles Keepax pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 19042159ad93SMark Brown 19054e08d50dSCharles Keepax footer = (void *)&firmware->data[pos]; 19064e08d50dSCharles Keepax pos += sizeof(*footer); 19072159ad93SMark Brown 19084e08d50dSCharles Keepax if (le32_to_cpu(header->len) != pos) { 19096ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: unexpected header length %d\n", 19102159ad93SMark Brown file, le32_to_cpu(header->len)); 19112159ad93SMark Brown goto out_fw; 19122159ad93SMark Brown } 19132159ad93SMark Brown 19146ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, 19152159ad93SMark Brown le64_to_cpu(footer->timestamp)); 19162159ad93SMark Brown 19172159ad93SMark Brown while (pos < firmware->size && 191850dd2ea8SBen Hutchings sizeof(*region) < firmware->size - pos) { 19192159ad93SMark Brown region = (void *)&(firmware->data[pos]); 19202159ad93SMark Brown region_name = "Unknown"; 19212159ad93SMark Brown reg = 0; 19222159ad93SMark Brown text = NULL; 19232159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 19242159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 19252159ad93SMark Brown 19262159ad93SMark Brown switch (type) { 19272159ad93SMark Brown case WMFW_NAME_TEXT: 19282159ad93SMark Brown region_name = "Firmware name"; 19292159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 19302159ad93SMark Brown GFP_KERNEL); 19312159ad93SMark Brown break; 19322323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 19332323736dSCharles Keepax region_name = "Algorithm"; 19345beb8eeaSSimon Trimmer ret = cs_dsp_parse_coeff(dsp, region); 19352323736dSCharles Keepax if (ret != 0) 19362323736dSCharles Keepax goto out_fw; 19372323736dSCharles Keepax break; 19382159ad93SMark Brown case WMFW_INFO_TEXT: 19392159ad93SMark Brown region_name = "Information"; 19402159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 19412159ad93SMark Brown GFP_KERNEL); 19422159ad93SMark Brown break; 19432159ad93SMark Brown case WMFW_ABSOLUTE: 19442159ad93SMark Brown region_name = "Absolute"; 19452159ad93SMark Brown reg = offset; 19462159ad93SMark Brown break; 19472159ad93SMark Brown case WMFW_ADSP1_PM: 19482159ad93SMark Brown case WMFW_ADSP1_DM: 19492159ad93SMark Brown case WMFW_ADSP2_XM: 19502159ad93SMark Brown case WMFW_ADSP2_YM: 19512159ad93SMark Brown case WMFW_ADSP1_ZM: 1952170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 1953170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 1954170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 19555beb8eeaSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 1956170b1e12SWen Shi if (!mem) { 19576ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "No region of type: %x\n", type); 19583fba05a2SLuo Meng ret = -EINVAL; 1959170b1e12SWen Shi goto out_fw; 1960170b1e12SWen Shi } 1961170b1e12SWen Shi 19625beb8eeaSSimon Trimmer region_name = cs_dsp_mem_region_name(type); 1963170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, offset); 19642159ad93SMark Brown break; 19652159ad93SMark Brown default: 19666ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, 19672159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 19682159ad93SMark Brown file, regions, type, pos, pos); 19692159ad93SMark Brown break; 19702159ad93SMark Brown } 19712159ad93SMark Brown 19726ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 19732159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 19742159ad93SMark Brown region_name); 19752159ad93SMark Brown 197650dd2ea8SBen Hutchings if (le32_to_cpu(region->len) > 197750dd2ea8SBen Hutchings firmware->size - pos - sizeof(*region)) { 19786ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 19791cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 19801cab2a84SRichard Fitzgerald file, regions, region_name, 19811cab2a84SRichard Fitzgerald le32_to_cpu(region->len), firmware->size); 19821cab2a84SRichard Fitzgerald ret = -EINVAL; 19831cab2a84SRichard Fitzgerald goto out_fw; 19841cab2a84SRichard Fitzgerald } 19851cab2a84SRichard Fitzgerald 19862159ad93SMark Brown if (text) { 19872159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 19886ab1d0ccSSimon Trimmer cs_dsp_info(dsp, "%s: %s\n", file, text); 19892159ad93SMark Brown kfree(text); 19901cab2a84SRichard Fitzgerald text = NULL; 19912159ad93SMark Brown } 19922159ad93SMark Brown 19932159ad93SMark Brown if (reg) { 19945beb8eeaSSimon Trimmer buf = cs_dsp_buf_alloc(region->data, 1995cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1996cf17c83cSMark Brown &buf_list); 1997a76fefabSMark Brown if (!buf) { 19986ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 19997328823dSDimitris Papastamos ret = -ENOMEM; 20007328823dSDimitris Papastamos goto out_fw; 2001a76fefabSMark Brown } 2002a76fefabSMark Brown 2003cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 2004cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 20052159ad93SMark Brown if (ret != 0) { 20066ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 2007cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 20082159ad93SMark Brown file, regions, 2009cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 20102159ad93SMark Brown region_name, ret); 20112159ad93SMark Brown goto out_fw; 20122159ad93SMark Brown } 20132159ad93SMark Brown } 20142159ad93SMark Brown 20152159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 20162159ad93SMark Brown regions++; 20172159ad93SMark Brown } 20182159ad93SMark Brown 2019cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2020cf17c83cSMark Brown if (ret != 0) { 20216ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 2022cf17c83cSMark Brown goto out_fw; 2023cf17c83cSMark Brown } 2024cf17c83cSMark Brown 20252159ad93SMark Brown if (pos > firmware->size) 20266ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 20272159ad93SMark Brown file, regions, pos - firmware->size); 20282159ad93SMark Brown 20295beb8eeaSSimon Trimmer cs_dsp_debugfs_save_wmfwname(dsp, file); 2030f9f55e31SRichard Fitzgerald 20312159ad93SMark Brown out_fw: 2032cf17c83cSMark Brown regmap_async_complete(regmap); 20335beb8eeaSSimon Trimmer cs_dsp_buf_free(&buf_list); 20342159ad93SMark Brown release_firmware(firmware); 20351cab2a84SRichard Fitzgerald kfree(text); 20362159ad93SMark Brown out: 20372159ad93SMark Brown kfree(file); 20382159ad93SMark Brown 20392159ad93SMark Brown return ret; 20402159ad93SMark Brown } 20412159ad93SMark Brown 2042eb65ccdbSLi Xu /* 20430700bc2fSSimon Trimmer * Find cs_dsp_coeff_ctl with input name as its subname 2044eb65ccdbSLi Xu * If not found, return NULL 2045eb65ccdbSLi Xu */ 20460700bc2fSSimon Trimmer static struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct wm_adsp *dsp, 2047eb65ccdbSLi Xu const char *name, int type, 2048eb65ccdbSLi Xu unsigned int alg) 2049eb65ccdbSLi Xu { 20500700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *pos, *rslt = NULL; 2051eb65ccdbSLi Xu 2052eb65ccdbSLi Xu list_for_each_entry(pos, &dsp->ctl_list, list) { 2053eb65ccdbSLi Xu if (!pos->subname) 2054eb65ccdbSLi Xu continue; 2055eb65ccdbSLi Xu if (strncmp(pos->subname, name, pos->subname_len) == 0 && 2056*2169f2f1SSimon Trimmer pos->fw_name == dsp->fw_name && 2057eb65ccdbSLi Xu pos->alg_region.alg == alg && 2058eb65ccdbSLi Xu pos->alg_region.type == type) { 2059eb65ccdbSLi Xu rslt = pos; 2060eb65ccdbSLi Xu break; 2061eb65ccdbSLi Xu } 2062eb65ccdbSLi Xu } 2063eb65ccdbSLi Xu 2064eb65ccdbSLi Xu return rslt; 2065eb65ccdbSLi Xu } 2066eb65ccdbSLi Xu 2067eb65ccdbSLi Xu int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, 2068eb65ccdbSLi Xu unsigned int alg, void *buf, size_t len) 2069eb65ccdbSLi Xu { 20700700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 2071eb65ccdbSLi Xu struct wm_coeff_ctl *ctl; 2072eb65ccdbSLi Xu struct snd_kcontrol *kcontrol; 207320441614SAdam Brickman char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 2074eb65ccdbSLi Xu int ret; 2075eb65ccdbSLi Xu 20760700bc2fSSimon Trimmer cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg); 20770700bc2fSSimon Trimmer if (!cs_ctl) 2078eb65ccdbSLi Xu return -EINVAL; 2079eb65ccdbSLi Xu 20800700bc2fSSimon Trimmer ctl = cs_ctl->priv; 20810700bc2fSSimon Trimmer 20820700bc2fSSimon Trimmer if (len > cs_ctl->len) 2083eb65ccdbSLi Xu return -EINVAL; 2084eb65ccdbSLi Xu 20850700bc2fSSimon Trimmer ret = cs_dsp_coeff_write_ctrl(cs_ctl, buf, len); 208620441614SAdam Brickman if (ret) 208720441614SAdam Brickman return ret; 2088eb65ccdbSLi Xu 20890700bc2fSSimon Trimmer if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) 209020441614SAdam Brickman return 0; 209120441614SAdam Brickman 209220441614SAdam Brickman if (dsp->component->name_prefix) 209320441614SAdam Brickman snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", 209420441614SAdam Brickman dsp->component->name_prefix, ctl->name); 209520441614SAdam Brickman else 209620441614SAdam Brickman snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", 209720441614SAdam Brickman ctl->name); 209820441614SAdam Brickman 209920441614SAdam Brickman kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); 210020441614SAdam Brickman if (!kcontrol) { 210120441614SAdam Brickman adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); 210220441614SAdam Brickman return -EINVAL; 210320441614SAdam Brickman } 210420441614SAdam Brickman 2105eb65ccdbSLi Xu snd_ctl_notify(dsp->component->card->snd_card, 2106eb65ccdbSLi Xu SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); 2107eb65ccdbSLi Xu 2108492df5b0SPierre-Louis Bossart return 0; 2109eb65ccdbSLi Xu } 2110eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); 2111eb65ccdbSLi Xu 2112eb65ccdbSLi Xu int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, 2113eb65ccdbSLi Xu unsigned int alg, void *buf, size_t len) 2114eb65ccdbSLi Xu { 21150700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 2116eb65ccdbSLi Xu 21170700bc2fSSimon Trimmer cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg); 21180700bc2fSSimon Trimmer if (!cs_ctl) 2119eb65ccdbSLi Xu return -EINVAL; 2120eb65ccdbSLi Xu 21210700bc2fSSimon Trimmer if (len > cs_ctl->len) 2122eb65ccdbSLi Xu return -EINVAL; 2123eb65ccdbSLi Xu 21240700bc2fSSimon Trimmer return cs_dsp_coeff_read_ctrl(cs_ctl, buf, len); 2125eb65ccdbSLi Xu } 2126eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); 2127eb65ccdbSLi Xu 21285beb8eeaSSimon Trimmer static void cs_dsp_ctl_fixup_base(struct wm_adsp *dsp, 21295beb8eeaSSimon Trimmer const struct cs_dsp_alg_region *alg_region) 21302323736dSCharles Keepax { 21310700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 21322323736dSCharles Keepax 21332323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 2134*2169f2f1SSimon Trimmer if (ctl->fw_name == dsp->fw_name && 21352323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 21362323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 21372323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 21382323736dSCharles Keepax } 21392323736dSCharles Keepax } 21402323736dSCharles Keepax } 21412323736dSCharles Keepax 21425beb8eeaSSimon Trimmer static void *cs_dsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 21435beb8eeaSSimon Trimmer const struct cs_dsp_region *mem, 2144b618a185SCharles Keepax unsigned int pos, unsigned int len) 2145db40517cSMark Brown { 2146b618a185SCharles Keepax void *alg; 21477f7cca08SCharles Keepax unsigned int reg; 2148b618a185SCharles Keepax int ret; 2149db40517cSMark Brown __be32 val; 2150db40517cSMark Brown 21513809f001SCharles Keepax if (n_algs == 0) { 21526ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "No algorithms\n"); 2153b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2154db40517cSMark Brown } 2155db40517cSMark Brown 21563809f001SCharles Keepax if (n_algs > 1024) { 21576ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 2158b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2159b618a185SCharles Keepax } 2160b618a185SCharles Keepax 2161b618a185SCharles Keepax /* Read the terminator first to validate the length */ 2162170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos + len); 21637f7cca08SCharles Keepax 21647f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 2165b618a185SCharles Keepax if (ret != 0) { 21666ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list end: %d\n", 2167b618a185SCharles Keepax ret); 2168b618a185SCharles Keepax return ERR_PTR(ret); 2169b618a185SCharles Keepax } 2170b618a185SCharles Keepax 2171b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 21726ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 21737f7cca08SCharles Keepax reg, be32_to_cpu(val)); 21747f7cca08SCharles Keepax 21757f7cca08SCharles Keepax /* Convert length from DSP words to bytes */ 21767f7cca08SCharles Keepax len *= sizeof(u32); 2177b618a185SCharles Keepax 2178517ee74eSCharles Keepax alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 2179b618a185SCharles Keepax if (!alg) 2180b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 2181b618a185SCharles Keepax 2182170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos); 21837f7cca08SCharles Keepax 21847f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, alg, len); 2185b618a185SCharles Keepax if (ret != 0) { 21866ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 2187b618a185SCharles Keepax kfree(alg); 2188b618a185SCharles Keepax return ERR_PTR(ret); 2189b618a185SCharles Keepax } 2190b618a185SCharles Keepax 2191b618a185SCharles Keepax return alg; 2192b618a185SCharles Keepax } 2193b618a185SCharles Keepax 21945beb8eeaSSimon Trimmer static struct cs_dsp_alg_region * 21955beb8eeaSSimon Trimmer cs_dsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 219614197095SCharles Keepax { 21975beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 219814197095SCharles Keepax 219914197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 220014197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 220114197095SCharles Keepax return alg_region; 220214197095SCharles Keepax } 220314197095SCharles Keepax 220414197095SCharles Keepax return NULL; 220514197095SCharles Keepax } 220614197095SCharles Keepax 22075beb8eeaSSimon Trimmer static struct cs_dsp_alg_region *cs_dsp_create_region(struct wm_adsp *dsp, 2208d9d20e17SCharles Keepax int type, __be32 id, 2209d9d20e17SCharles Keepax __be32 base) 2210d9d20e17SCharles Keepax { 22115beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 2212d9d20e17SCharles Keepax 2213d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 2214d9d20e17SCharles Keepax if (!alg_region) 2215d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 2216d9d20e17SCharles Keepax 2217d9d20e17SCharles Keepax alg_region->type = type; 2218d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 2219d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 2220d9d20e17SCharles Keepax 2221d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 2222d9d20e17SCharles Keepax 22232323736dSCharles Keepax if (dsp->fw_ver > 0) 22245beb8eeaSSimon Trimmer cs_dsp_ctl_fixup_base(dsp, alg_region); 22252323736dSCharles Keepax 2226d9d20e17SCharles Keepax return alg_region; 2227d9d20e17SCharles Keepax } 2228d9d20e17SCharles Keepax 22295beb8eeaSSimon Trimmer static void cs_dsp_free_alg_regions(struct wm_adsp *dsp) 223056574d54SRichard Fitzgerald { 22315beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 223256574d54SRichard Fitzgerald 223356574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 223456574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 22355beb8eeaSSimon Trimmer struct cs_dsp_alg_region, 223656574d54SRichard Fitzgerald list); 223756574d54SRichard Fitzgerald list_del(&alg_region->list); 223856574d54SRichard Fitzgerald kfree(alg_region); 223956574d54SRichard Fitzgerald } 224056574d54SRichard Fitzgerald } 224156574d54SRichard Fitzgerald 22425beb8eeaSSimon Trimmer static void cs_dsp_parse_wmfw_id_header(struct wm_adsp *dsp, 2243a5dcb24dSCharles Keepax struct wmfw_id_hdr *fw, int nalgs) 2244a5dcb24dSCharles Keepax { 2245a5dcb24dSCharles Keepax dsp->fw_id = be32_to_cpu(fw->id); 2246a5dcb24dSCharles Keepax dsp->fw_id_version = be32_to_cpu(fw->ver); 2247a5dcb24dSCharles Keepax 22486ab1d0ccSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 2249a5dcb24dSCharles Keepax dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 2250a5dcb24dSCharles Keepax (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2251a5dcb24dSCharles Keepax nalgs); 2252a5dcb24dSCharles Keepax } 2253a5dcb24dSCharles Keepax 22545beb8eeaSSimon Trimmer static void cs_dsp_parse_wmfw_v3_id_header(struct wm_adsp *dsp, 2255170b1e12SWen Shi struct wmfw_v3_id_hdr *fw, int nalgs) 2256170b1e12SWen Shi { 2257170b1e12SWen Shi dsp->fw_id = be32_to_cpu(fw->id); 2258170b1e12SWen Shi dsp->fw_id_version = be32_to_cpu(fw->ver); 2259170b1e12SWen Shi dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 2260170b1e12SWen Shi 22616ab1d0ccSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 2262170b1e12SWen Shi dsp->fw_id, dsp->fw_vendor_id, 2263170b1e12SWen Shi (dsp->fw_id_version & 0xff0000) >> 16, 2264170b1e12SWen Shi (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2265170b1e12SWen Shi nalgs); 2266170b1e12SWen Shi } 2267170b1e12SWen Shi 22685beb8eeaSSimon Trimmer static int cs_dsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions, 2269130dbe04SSimon Trimmer const int *type, __be32 *base) 2270170b1e12SWen Shi { 22715beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 2272170b1e12SWen Shi int i; 2273170b1e12SWen Shi 2274170b1e12SWen Shi for (i = 0; i < nregions; i++) { 22755beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, type[i], id, base[i]); 2276170b1e12SWen Shi if (IS_ERR(alg_region)) 2277170b1e12SWen Shi return PTR_ERR(alg_region); 2278170b1e12SWen Shi } 2279170b1e12SWen Shi 2280170b1e12SWen Shi return 0; 2281170b1e12SWen Shi } 2282170b1e12SWen Shi 22835beb8eeaSSimon Trimmer static int cs_dsp_adsp1_setup_algs(struct wm_adsp *dsp) 2284b618a185SCharles Keepax { 2285b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 2286b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 22875beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 22885beb8eeaSSimon Trimmer const struct cs_dsp_region *mem; 2289b618a185SCharles Keepax unsigned int pos, len; 22903809f001SCharles Keepax size_t n_algs; 2291b618a185SCharles Keepax int i, ret; 2292b618a185SCharles Keepax 22935beb8eeaSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP1_DM); 22946c452bdaSTakashi Iwai if (WARN_ON(!mem)) 2295db40517cSMark Brown return -EINVAL; 2296db40517cSMark Brown 2297b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 2298db40517cSMark Brown sizeof(adsp1_id)); 2299db40517cSMark Brown if (ret != 0) { 23006ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 2301db40517cSMark Brown ret); 2302db40517cSMark Brown return ret; 2303db40517cSMark Brown } 2304db40517cSMark Brown 23053809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 2306a5dcb24dSCharles Keepax 23075beb8eeaSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp1_id.fw, n_algs); 2308db40517cSMark Brown 23095beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 2310d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 2311d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2312d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2313ac50009fSMark Brown 23145beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 2315d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 2316d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2317d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2318ac50009fSMark Brown 23197f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 23207f7cca08SCharles Keepax pos = sizeof(adsp1_id) / sizeof(u32); 23217f7cca08SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 2322db40517cSMark Brown 23235beb8eeaSSimon Trimmer adsp1_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 2324b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 2325b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 2326db40517cSMark Brown 23273809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 23286ab1d0ccSSimon Trimmer cs_dsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 2329db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 2330db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 2331db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 2332471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 2333471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 2334471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 2335471f4885SMark Brown 23365beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 2337d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2338d9d20e17SCharles Keepax adsp1_alg[i].dm); 2339d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2340d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2341d6d52179SJS Park goto out; 2342d6d52179SJS Park } 23432323736dSCharles Keepax if (dsp->fw_ver == 0) { 23443809f001SCharles Keepax if (i + 1 < n_algs) { 23456958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 23466958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 23476958eb2aSCharles Keepax len *= 4; 23485beb8eeaSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 23498eb084d0SStuart Henderson len, NULL, 0, 0, 2350d07a6d45SSimon Trimmer WMFW_CTL_TYPE_BYTES); 23516ab2b7b4SDimitris Papastamos } else { 23526ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region DM with ID %x\n", 23536ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 23546ab2b7b4SDimitris Papastamos } 23552323736dSCharles Keepax } 2356471f4885SMark Brown 23575beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 2358d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2359d9d20e17SCharles Keepax adsp1_alg[i].zm); 2360d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2361d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2362d6d52179SJS Park goto out; 2363d6d52179SJS Park } 23642323736dSCharles Keepax if (dsp->fw_ver == 0) { 23653809f001SCharles Keepax if (i + 1 < n_algs) { 23666958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 23676958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 23686958eb2aSCharles Keepax len *= 4; 23695beb8eeaSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 23708eb084d0SStuart Henderson len, NULL, 0, 0, 2371d07a6d45SSimon Trimmer WMFW_CTL_TYPE_BYTES); 23726ab2b7b4SDimitris Papastamos } else { 23736ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 23746ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 23756ab2b7b4SDimitris Papastamos } 2376b618a185SCharles Keepax } 23772323736dSCharles Keepax } 2378db40517cSMark Brown 2379b618a185SCharles Keepax out: 2380b618a185SCharles Keepax kfree(adsp1_alg); 2381b618a185SCharles Keepax return ret; 2382b618a185SCharles Keepax } 2383b618a185SCharles Keepax 23845beb8eeaSSimon Trimmer static int cs_dsp_adsp2_setup_algs(struct wm_adsp *dsp) 2385b618a185SCharles Keepax { 2386b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 2387b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 23885beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 23895beb8eeaSSimon Trimmer const struct cs_dsp_region *mem; 2390b618a185SCharles Keepax unsigned int pos, len; 23913809f001SCharles Keepax size_t n_algs; 2392b618a185SCharles Keepax int i, ret; 2393b618a185SCharles Keepax 23945beb8eeaSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 2395b618a185SCharles Keepax if (WARN_ON(!mem)) 2396b618a185SCharles Keepax return -EINVAL; 2397b618a185SCharles Keepax 2398b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 2399b618a185SCharles Keepax sizeof(adsp2_id)); 2400b618a185SCharles Keepax if (ret != 0) { 24016ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 2402b618a185SCharles Keepax ret); 2403b618a185SCharles Keepax return ret; 2404b618a185SCharles Keepax } 2405b618a185SCharles Keepax 24063809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 2407a5dcb24dSCharles Keepax 24085beb8eeaSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp2_id.fw, n_algs); 2409b618a185SCharles Keepax 24105beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 2411d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2412d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2413d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2414b618a185SCharles Keepax 24155beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 2416d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2417d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2418d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2419b618a185SCharles Keepax 24205beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 2421d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2422d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2423d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2424b618a185SCharles Keepax 24257f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 24267f7cca08SCharles Keepax pos = sizeof(adsp2_id) / sizeof(u32); 24277f7cca08SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 2428b618a185SCharles Keepax 24295beb8eeaSSimon Trimmer adsp2_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 2430b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2431b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2432b618a185SCharles Keepax 24333809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 24346ab1d0ccSSimon Trimmer cs_dsp_info(dsp, 2435471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2436db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2437db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2438db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2439471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2440471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2441471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2442471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2443471f4885SMark Brown 24445beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 2445d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2446d9d20e17SCharles Keepax adsp2_alg[i].xm); 2447d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2448d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2449d6d52179SJS Park goto out; 2450d6d52179SJS Park } 24512323736dSCharles Keepax if (dsp->fw_ver == 0) { 24523809f001SCharles Keepax if (i + 1 < n_algs) { 24536958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 24546958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 24556958eb2aSCharles Keepax len *= 4; 24565beb8eeaSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 24578eb084d0SStuart Henderson len, NULL, 0, 0, 2458d07a6d45SSimon Trimmer WMFW_CTL_TYPE_BYTES); 24596ab2b7b4SDimitris Papastamos } else { 24606ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region XM with ID %x\n", 24616ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 24626ab2b7b4SDimitris Papastamos } 24632323736dSCharles Keepax } 2464471f4885SMark Brown 24655beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 2466d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2467d9d20e17SCharles Keepax adsp2_alg[i].ym); 2468d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2469d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2470d6d52179SJS Park goto out; 2471d6d52179SJS Park } 24722323736dSCharles Keepax if (dsp->fw_ver == 0) { 24733809f001SCharles Keepax if (i + 1 < n_algs) { 24746958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 24756958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 24766958eb2aSCharles Keepax len *= 4; 24775beb8eeaSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 24788eb084d0SStuart Henderson len, NULL, 0, 0, 2479d07a6d45SSimon Trimmer WMFW_CTL_TYPE_BYTES); 24806ab2b7b4SDimitris Papastamos } else { 24816ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region YM with ID %x\n", 24826ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 24836ab2b7b4SDimitris Papastamos } 24842323736dSCharles Keepax } 2485471f4885SMark Brown 24865beb8eeaSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 2487d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2488d9d20e17SCharles Keepax adsp2_alg[i].zm); 2489d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2490d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2491d6d52179SJS Park goto out; 2492d6d52179SJS Park } 24932323736dSCharles Keepax if (dsp->fw_ver == 0) { 24943809f001SCharles Keepax if (i + 1 < n_algs) { 24956958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 24966958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 24976958eb2aSCharles Keepax len *= 4; 24985beb8eeaSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 24998eb084d0SStuart Henderson len, NULL, 0, 0, 2500d07a6d45SSimon Trimmer WMFW_CTL_TYPE_BYTES); 25016ab2b7b4SDimitris Papastamos } else { 25026ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 25036ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 25046ab2b7b4SDimitris Papastamos } 2505db40517cSMark Brown } 25062323736dSCharles Keepax } 2507db40517cSMark Brown 2508db40517cSMark Brown out: 2509b618a185SCharles Keepax kfree(adsp2_alg); 2510db40517cSMark Brown return ret; 2511db40517cSMark Brown } 2512db40517cSMark Brown 25135beb8eeaSSimon Trimmer static int cs_dsp_halo_create_regions(struct wm_adsp *dsp, __be32 id, 2514170b1e12SWen Shi __be32 xm_base, __be32 ym_base) 2515170b1e12SWen Shi { 2516130dbe04SSimon Trimmer static const int types[] = { 2517170b1e12SWen Shi WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 2518170b1e12SWen Shi WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 2519170b1e12SWen Shi }; 2520170b1e12SWen Shi __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 2521170b1e12SWen Shi 25225beb8eeaSSimon Trimmer return cs_dsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); 2523170b1e12SWen Shi } 2524170b1e12SWen Shi 25255beb8eeaSSimon Trimmer static int cs_dsp_halo_setup_algs(struct wm_adsp *dsp) 2526170b1e12SWen Shi { 2527170b1e12SWen Shi struct wmfw_halo_id_hdr halo_id; 2528170b1e12SWen Shi struct wmfw_halo_alg_hdr *halo_alg; 25295beb8eeaSSimon Trimmer const struct cs_dsp_region *mem; 2530170b1e12SWen Shi unsigned int pos, len; 2531170b1e12SWen Shi size_t n_algs; 2532170b1e12SWen Shi int i, ret; 2533170b1e12SWen Shi 25345beb8eeaSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 2535170b1e12SWen Shi if (WARN_ON(!mem)) 2536170b1e12SWen Shi return -EINVAL; 2537170b1e12SWen Shi 2538170b1e12SWen Shi ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 2539170b1e12SWen Shi sizeof(halo_id)); 2540170b1e12SWen Shi if (ret != 0) { 25416ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 2542170b1e12SWen Shi ret); 2543170b1e12SWen Shi return ret; 2544170b1e12SWen Shi } 2545170b1e12SWen Shi 2546170b1e12SWen Shi n_algs = be32_to_cpu(halo_id.n_algs); 2547170b1e12SWen Shi 25485beb8eeaSSimon Trimmer cs_dsp_parse_wmfw_v3_id_header(dsp, &halo_id.fw, n_algs); 2549170b1e12SWen Shi 25505beb8eeaSSimon Trimmer ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, 2551b75a9799SCharles Keepax halo_id.xm_base, halo_id.ym_base); 2552170b1e12SWen Shi if (ret) 2553170b1e12SWen Shi return ret; 2554170b1e12SWen Shi 2555170b1e12SWen Shi /* Calculate offset and length in DSP words */ 2556170b1e12SWen Shi pos = sizeof(halo_id) / sizeof(u32); 2557170b1e12SWen Shi len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 2558170b1e12SWen Shi 25595beb8eeaSSimon Trimmer halo_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 2560170b1e12SWen Shi if (IS_ERR(halo_alg)) 2561170b1e12SWen Shi return PTR_ERR(halo_alg); 2562170b1e12SWen Shi 2563170b1e12SWen Shi for (i = 0; i < n_algs; i++) { 25646ab1d0ccSSimon Trimmer cs_dsp_info(dsp, 2565170b1e12SWen Shi "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 2566170b1e12SWen Shi i, be32_to_cpu(halo_alg[i].alg.id), 2567170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 2568170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 2569170b1e12SWen Shi be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 2570170b1e12SWen Shi be32_to_cpu(halo_alg[i].xm_base), 2571170b1e12SWen Shi be32_to_cpu(halo_alg[i].ym_base)); 2572170b1e12SWen Shi 25735beb8eeaSSimon Trimmer ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id, 2574170b1e12SWen Shi halo_alg[i].xm_base, 2575170b1e12SWen Shi halo_alg[i].ym_base); 2576170b1e12SWen Shi if (ret) 2577170b1e12SWen Shi goto out; 2578170b1e12SWen Shi } 2579170b1e12SWen Shi 2580170b1e12SWen Shi out: 2581170b1e12SWen Shi kfree(halo_alg); 2582170b1e12SWen Shi return ret; 2583170b1e12SWen Shi } 2584170b1e12SWen Shi 2585*2169f2f1SSimon Trimmer static int cs_dsp_load_coeff(struct wm_adsp *dsp, const char *fw_file_name) 25862159ad93SMark Brown { 2587cf17c83cSMark Brown LIST_HEAD(buf_list); 25882159ad93SMark Brown struct regmap *regmap = dsp->regmap; 25892159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 25902159ad93SMark Brown struct wmfw_coeff_item *blk; 25912159ad93SMark Brown const struct firmware *firmware; 25925beb8eeaSSimon Trimmer const struct cs_dsp_region *mem; 25935beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 25942159ad93SMark Brown const char *region_name; 25952159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 25962159ad93SMark Brown char *file; 25975beb8eeaSSimon Trimmer struct cs_dsp_buf *buf; 25982159ad93SMark Brown 25992159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 26002159ad93SMark Brown if (file == NULL) 26012159ad93SMark Brown return -ENOMEM; 26022159ad93SMark Brown 2603605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, 2604*2169f2f1SSimon Trimmer fw_file_name); 26052159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 26062159ad93SMark Brown 26072159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 26082159ad93SMark Brown if (ret != 0) { 26096ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Failed to request '%s'\n", file); 26102159ad93SMark Brown ret = 0; 26112159ad93SMark Brown goto out; 26122159ad93SMark Brown } 26132159ad93SMark Brown ret = -EINVAL; 26142159ad93SMark Brown 26152159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 26166ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: file too short, %zu bytes\n", 26172159ad93SMark Brown file, firmware->size); 26182159ad93SMark Brown goto out_fw; 26192159ad93SMark Brown } 26202159ad93SMark Brown 26212159ad93SMark Brown hdr = (void *)&firmware->data[0]; 26222159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 26236ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: invalid magic\n", file); 2624a4cdbec7SCharles Keepax goto out_fw; 26252159ad93SMark Brown } 26262159ad93SMark Brown 2627c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2628c712326dSMark Brown case 1: 2629c712326dSMark Brown break; 2630c712326dSMark Brown default: 26316ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2632c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2633c712326dSMark Brown ret = -EINVAL; 2634c712326dSMark Brown goto out_fw; 2635c712326dSMark Brown } 2636c712326dSMark Brown 26376ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 26382159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 26392159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 26402159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 26412159ad93SMark Brown 26422159ad93SMark Brown pos = le32_to_cpu(hdr->len); 26432159ad93SMark Brown 26442159ad93SMark Brown blocks = 0; 26452159ad93SMark Brown while (pos < firmware->size && 264650dd2ea8SBen Hutchings sizeof(*blk) < firmware->size - pos) { 26472159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 26482159ad93SMark Brown 2649c712326dSMark Brown type = le16_to_cpu(blk->type); 2650c712326dSMark Brown offset = le16_to_cpu(blk->offset); 26512159ad93SMark Brown 26526ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 26532159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 26542159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 26552159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 26562159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 26576ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 26582159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 26592159ad93SMark Brown 26602159ad93SMark Brown reg = 0; 26612159ad93SMark Brown region_name = "Unknown"; 26622159ad93SMark Brown switch (type) { 2663c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2664c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 2665779bedffSJames Schulman case (WMFW_METADATA << 8): 26662159ad93SMark Brown break; 2667c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2668f395a218SMark Brown /* 2669f395a218SMark Brown * Old files may use this for global 2670f395a218SMark Brown * coefficients. 2671f395a218SMark Brown */ 2672f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2673f395a218SMark Brown offset == 0) { 2674f395a218SMark Brown region_name = "global coefficients"; 26755beb8eeaSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2676f395a218SMark Brown if (!mem) { 26776ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "No ZM\n"); 2678f395a218SMark Brown break; 2679f395a218SMark Brown } 2680170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, 0); 2681f395a218SMark Brown 2682f395a218SMark Brown } else { 26832159ad93SMark Brown region_name = "register"; 26842159ad93SMark Brown reg = offset; 2685f395a218SMark Brown } 26862159ad93SMark Brown break; 2687471f4885SMark Brown 2688471f4885SMark Brown case WMFW_ADSP1_DM: 2689471f4885SMark Brown case WMFW_ADSP1_ZM: 2690471f4885SMark Brown case WMFW_ADSP2_XM: 2691471f4885SMark Brown case WMFW_ADSP2_YM: 2692170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 2693170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 2694170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 26956ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2696471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2697471f4885SMark Brown type, le32_to_cpu(blk->id)); 2698471f4885SMark Brown 26995beb8eeaSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2700471f4885SMark Brown if (!mem) { 27016ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", type); 2702471f4885SMark Brown break; 2703471f4885SMark Brown } 2704471f4885SMark Brown 27055beb8eeaSSimon Trimmer alg_region = cs_dsp_find_alg_region(dsp, type, 270614197095SCharles Keepax le32_to_cpu(blk->id)); 270714197095SCharles Keepax if (alg_region) { 2708338c5188SMark Brown reg = alg_region->base; 2709170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, reg); 2710338c5188SMark Brown reg += offset; 271114197095SCharles Keepax } else { 27126ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "No %x for algorithm %x\n", 2713471f4885SMark Brown type, le32_to_cpu(blk->id)); 271414197095SCharles Keepax } 2715471f4885SMark Brown break; 2716471f4885SMark Brown 27172159ad93SMark Brown default: 27186ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 271925c62f7eSMark Brown file, blocks, type, pos); 27202159ad93SMark Brown break; 27212159ad93SMark Brown } 27222159ad93SMark Brown 27232159ad93SMark Brown if (reg) { 272450dd2ea8SBen Hutchings if (le32_to_cpu(blk->len) > 272550dd2ea8SBen Hutchings firmware->size - pos - sizeof(*blk)) { 27266ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 27271cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 27281cab2a84SRichard Fitzgerald file, blocks, region_name, 27291cab2a84SRichard Fitzgerald le32_to_cpu(blk->len), 27301cab2a84SRichard Fitzgerald firmware->size); 27311cab2a84SRichard Fitzgerald ret = -EINVAL; 27321cab2a84SRichard Fitzgerald goto out_fw; 27331cab2a84SRichard Fitzgerald } 27341cab2a84SRichard Fitzgerald 27355beb8eeaSSimon Trimmer buf = cs_dsp_buf_alloc(blk->data, 2736cf17c83cSMark Brown le32_to_cpu(blk->len), 2737cf17c83cSMark Brown &buf_list); 2738a76fefabSMark Brown if (!buf) { 27396ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 2740f4b82812SWei Yongjun ret = -ENOMEM; 2741f4b82812SWei Yongjun goto out_fw; 2742a76fefabSMark Brown } 2743a76fefabSMark Brown 27446ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 274520da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 274620da6d5aSMark Brown reg); 2747cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 27482159ad93SMark Brown le32_to_cpu(blk->len)); 27492159ad93SMark Brown if (ret != 0) { 27506ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 275143bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 275243bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 27532159ad93SMark Brown } 27542159ad93SMark Brown } 27552159ad93SMark Brown 2756be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 27572159ad93SMark Brown blocks++; 27582159ad93SMark Brown } 27592159ad93SMark Brown 2760cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2761cf17c83cSMark Brown if (ret != 0) 27626ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 2763cf17c83cSMark Brown 27642159ad93SMark Brown if (pos > firmware->size) 27656ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 27662159ad93SMark Brown file, blocks, pos - firmware->size); 27672159ad93SMark Brown 27685beb8eeaSSimon Trimmer cs_dsp_debugfs_save_binname(dsp, file); 2769f9f55e31SRichard Fitzgerald 27702159ad93SMark Brown out_fw: 27719da7a5a9SCharles Keepax regmap_async_complete(regmap); 27722159ad93SMark Brown release_firmware(firmware); 27735beb8eeaSSimon Trimmer cs_dsp_buf_free(&buf_list); 27742159ad93SMark Brown out: 27752159ad93SMark Brown kfree(file); 2776f4b82812SWei Yongjun return ret; 27772159ad93SMark Brown } 27782159ad93SMark Brown 27795beb8eeaSSimon Trimmer static int cs_dsp_create_name(struct wm_adsp *dsp) 2780605391d0SRichard Fitzgerald { 2781605391d0SRichard Fitzgerald char *p; 2782605391d0SRichard Fitzgerald 2783605391d0SRichard Fitzgerald if (!dsp->name) { 2784605391d0SRichard Fitzgerald dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2785605391d0SRichard Fitzgerald dsp->num); 2786605391d0SRichard Fitzgerald if (!dsp->name) 2787605391d0SRichard Fitzgerald return -ENOMEM; 2788605391d0SRichard Fitzgerald } 2789605391d0SRichard Fitzgerald 2790605391d0SRichard Fitzgerald if (!dsp->fwf_name) { 2791605391d0SRichard Fitzgerald p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); 2792605391d0SRichard Fitzgerald if (!p) 2793605391d0SRichard Fitzgerald return -ENOMEM; 2794605391d0SRichard Fitzgerald 2795605391d0SRichard Fitzgerald dsp->fwf_name = p; 2796605391d0SRichard Fitzgerald for (; *p != 0; ++p) 2797605391d0SRichard Fitzgerald *p = tolower(*p); 2798605391d0SRichard Fitzgerald } 2799605391d0SRichard Fitzgerald 2800605391d0SRichard Fitzgerald return 0; 2801605391d0SRichard Fitzgerald } 2802605391d0SRichard Fitzgerald 280325ca837bSSimon Trimmer static int cs_dsp_common_init(struct wm_adsp *dsp) 28045e7a7a22SMark Brown { 2805605391d0SRichard Fitzgerald int ret; 2806605391d0SRichard Fitzgerald 28075beb8eeaSSimon Trimmer ret = cs_dsp_create_name(dsp); 2808605391d0SRichard Fitzgerald if (ret) 2809605391d0SRichard Fitzgerald return ret; 2810605391d0SRichard Fitzgerald 28113809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 2812dcad34f8SRichard Fitzgerald INIT_LIST_HEAD(&dsp->ctl_list); 28135e7a7a22SMark Brown 2814078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2815078e7183SCharles Keepax 28165e7a7a22SMark Brown return 0; 28175e7a7a22SMark Brown } 2818dcad34f8SRichard Fitzgerald 281925ca837bSSimon Trimmer static void wm_adsp_common_init(struct wm_adsp *dsp) 282025ca837bSSimon Trimmer { 282125ca837bSSimon Trimmer INIT_LIST_HEAD(&dsp->compr_list); 282225ca837bSSimon Trimmer INIT_LIST_HEAD(&dsp->buffer_list); 282325ca837bSSimon Trimmer } 282425ca837bSSimon Trimmer 282525ca837bSSimon Trimmer static int cs_dsp_adsp1_init(struct wm_adsp *dsp) 2826dcad34f8SRichard Fitzgerald { 28275beb8eeaSSimon Trimmer dsp->ops = &cs_dsp_adsp1_ops; 28284e08d50dSCharles Keepax 282925ca837bSSimon Trimmer return cs_dsp_common_init(dsp); 283025ca837bSSimon Trimmer } 283125ca837bSSimon Trimmer 283225ca837bSSimon Trimmer int wm_adsp1_init(struct wm_adsp *dsp) 283325ca837bSSimon Trimmer { 283425ca837bSSimon Trimmer wm_adsp_common_init(dsp); 283525ca837bSSimon Trimmer 283625ca837bSSimon Trimmer return cs_dsp_adsp1_init(dsp); 2837dcad34f8SRichard Fitzgerald } 28385e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 28395e7a7a22SMark Brown 2840*2169f2f1SSimon Trimmer static int cs_dsp_adsp1_power_up(struct wm_adsp *dsp, const char *fw_file_name, const char *fw_name) 28412159ad93SMark Brown { 28427585a5b0SCharles Keepax unsigned int val; 2843186152dfSSimon Trimmer int ret; 284492bb4c32SDimitris Papastamos 2845078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2846078e7183SCharles Keepax 2847*2169f2f1SSimon Trimmer dsp->fw_name = fw_name; 2848*2169f2f1SSimon Trimmer 28492159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28502159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 28512159ad93SMark Brown 285294e205bfSChris Rattray /* 285394e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 285494e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 285594e205bfSChris Rattray */ 285694e205bfSChris Rattray if (dsp->sysclk_reg) { 285794e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 285894e205bfSChris Rattray if (ret != 0) { 28596ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 2860078e7183SCharles Keepax goto err_mutex; 286194e205bfSChris Rattray } 286294e205bfSChris Rattray 28637d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 286494e205bfSChris Rattray 286594e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 286694e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 286794e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 286894e205bfSChris Rattray if (ret != 0) { 28696ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2870078e7183SCharles Keepax goto err_mutex; 287194e205bfSChris Rattray } 287294e205bfSChris Rattray } 287394e205bfSChris Rattray 2874*2169f2f1SSimon Trimmer ret = cs_dsp_load(dsp, fw_file_name); 28752159ad93SMark Brown if (ret != 0) 2876078e7183SCharles Keepax goto err_ena; 28772159ad93SMark Brown 28785beb8eeaSSimon Trimmer ret = cs_dsp_adsp1_setup_algs(dsp); 2879db40517cSMark Brown if (ret != 0) 2880078e7183SCharles Keepax goto err_ena; 2881db40517cSMark Brown 2882*2169f2f1SSimon Trimmer ret = cs_dsp_load_coeff(dsp, fw_file_name); 28832159ad93SMark Brown if (ret != 0) 2884078e7183SCharles Keepax goto err_ena; 28852159ad93SMark Brown 28860c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 28875beb8eeaSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 28886ab2b7b4SDimitris Papastamos if (ret != 0) 2889078e7183SCharles Keepax goto err_ena; 28906ab2b7b4SDimitris Papastamos 28910c2e3f34SDimitris Papastamos /* Sync set controls */ 28925beb8eeaSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 28936ab2b7b4SDimitris Papastamos if (ret != 0) 2894078e7183SCharles Keepax goto err_ena; 28956ab2b7b4SDimitris Papastamos 289628823ebaSCharles Keepax dsp->booted = true; 289728823ebaSCharles Keepax 28982159ad93SMark Brown /* Start the core running */ 28992159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 29002159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 29012159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 290228823ebaSCharles Keepax 290328823ebaSCharles Keepax dsp->running = true; 29042159ad93SMark Brown 2905186152dfSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2906186152dfSSimon Trimmer 2907186152dfSSimon Trimmer return 0; 2908186152dfSSimon Trimmer 2909186152dfSSimon Trimmer err_ena: 2910186152dfSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2911186152dfSSimon Trimmer ADSP1_SYS_ENA, 0); 2912186152dfSSimon Trimmer err_mutex: 2913186152dfSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2914186152dfSSimon Trimmer return ret; 2915186152dfSSimon Trimmer } 2916186152dfSSimon Trimmer 2917186152dfSSimon Trimmer static void cs_dsp_adsp1_power_down(struct wm_adsp *dsp) 2918186152dfSSimon Trimmer { 29190700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2920186152dfSSimon Trimmer 2921186152dfSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2922186152dfSSimon Trimmer 292328823ebaSCharles Keepax dsp->running = false; 292428823ebaSCharles Keepax dsp->booted = false; 292528823ebaSCharles Keepax 29262159ad93SMark Brown /* Halt the core */ 29272159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 29282159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 29292159ad93SMark Brown 29302159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 29312159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 29322159ad93SMark Brown 29332159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 29342159ad93SMark Brown ADSP1_SYS_ENA, 0); 29356ab2b7b4SDimitris Papastamos 293681ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 29376ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2938b0101b4fSDimitris Papastamos 29395beb8eeaSSimon Trimmer cs_dsp_free_alg_regions(dsp); 29402159ad93SMark Brown 2941186152dfSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2942186152dfSSimon Trimmer } 2943186152dfSSimon Trimmer 2944186152dfSSimon Trimmer int wm_adsp1_event(struct snd_soc_dapm_widget *w, 2945186152dfSSimon Trimmer struct snd_kcontrol *kcontrol, 2946186152dfSSimon Trimmer int event) 2947186152dfSSimon Trimmer { 2948186152dfSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 2949186152dfSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 2950186152dfSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 2951186152dfSSimon Trimmer int ret = 0; 2952186152dfSSimon Trimmer 2953186152dfSSimon Trimmer dsp->component = component; 2954186152dfSSimon Trimmer 2955186152dfSSimon Trimmer switch (event) { 2956186152dfSSimon Trimmer case SND_SOC_DAPM_POST_PMU: 2957*2169f2f1SSimon Trimmer ret = cs_dsp_adsp1_power_up(dsp, 2958*2169f2f1SSimon Trimmer wm_adsp_fw[dsp->fw].file, 2959*2169f2f1SSimon Trimmer wm_adsp_fw_text[dsp->fw]); 2960186152dfSSimon Trimmer break; 2961186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMD: 2962186152dfSSimon Trimmer cs_dsp_adsp1_power_down(dsp); 2963186152dfSSimon Trimmer break; 29642159ad93SMark Brown default: 29652159ad93SMark Brown break; 29662159ad93SMark Brown } 29672159ad93SMark Brown 29682159ad93SMark Brown return ret; 29692159ad93SMark Brown } 29702159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 29712159ad93SMark Brown 29725beb8eeaSSimon Trimmer static int cs_dsp_adsp2v2_enable_core(struct wm_adsp *dsp) 29732159ad93SMark Brown { 29742159ad93SMark Brown unsigned int val; 29752159ad93SMark Brown int ret, count; 29762159ad93SMark Brown 29772159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2978939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 29797d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 29802159ad93SMark Brown if (ret != 0) 29812159ad93SMark Brown return ret; 2982939fd1e8SCharles Keepax 2983939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2984939fd1e8SCharles Keepax break; 2985939fd1e8SCharles Keepax 29861fa96f3fSCharles Keepax usleep_range(250, 500); 2987939fd1e8SCharles Keepax } 29882159ad93SMark Brown 29892159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 29906ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to start DSP RAM\n"); 29912159ad93SMark Brown return -EBUSY; 29922159ad93SMark Brown } 29932159ad93SMark Brown 29946ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "RAM ready after %d polls\n", count); 29952159ad93SMark Brown 29962159ad93SMark Brown return 0; 29972159ad93SMark Brown } 29982159ad93SMark Brown 29995beb8eeaSSimon Trimmer static int cs_dsp_adsp2_enable_core(struct wm_adsp *dsp) 30004e08d50dSCharles Keepax { 30014e08d50dSCharles Keepax int ret; 30024e08d50dSCharles Keepax 30034e08d50dSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 30044e08d50dSCharles Keepax ADSP2_SYS_ENA, ADSP2_SYS_ENA); 30054e08d50dSCharles Keepax if (ret != 0) 30064e08d50dSCharles Keepax return ret; 30074e08d50dSCharles Keepax 30085beb8eeaSSimon Trimmer return cs_dsp_adsp2v2_enable_core(dsp); 30094e08d50dSCharles Keepax } 30104e08d50dSCharles Keepax 30115beb8eeaSSimon Trimmer static int cs_dsp_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 30122b0ee49fSCharles Keepax { 30132b0ee49fSCharles Keepax struct regmap *regmap = dsp->regmap; 30142b0ee49fSCharles Keepax unsigned int code0, code1, lock_reg; 30152b0ee49fSCharles Keepax 30165beb8eeaSSimon Trimmer if (!(lock_regions & CS_ADSP2_REGION_ALL)) 30172b0ee49fSCharles Keepax return 0; 30182b0ee49fSCharles Keepax 30195beb8eeaSSimon Trimmer lock_regions &= CS_ADSP2_REGION_ALL; 30202b0ee49fSCharles Keepax lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 30212b0ee49fSCharles Keepax 30222b0ee49fSCharles Keepax while (lock_regions) { 30232b0ee49fSCharles Keepax code0 = code1 = 0; 30242b0ee49fSCharles Keepax if (lock_regions & BIT(0)) { 30252b0ee49fSCharles Keepax code0 = ADSP2_LOCK_CODE_0; 30262b0ee49fSCharles Keepax code1 = ADSP2_LOCK_CODE_1; 30272b0ee49fSCharles Keepax } 30282b0ee49fSCharles Keepax if (lock_regions & BIT(1)) { 30292b0ee49fSCharles Keepax code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 30302b0ee49fSCharles Keepax code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 30312b0ee49fSCharles Keepax } 30322b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code0); 30332b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code1); 30342b0ee49fSCharles Keepax lock_regions >>= 2; 30352b0ee49fSCharles Keepax lock_reg += 2; 30362b0ee49fSCharles Keepax } 30372b0ee49fSCharles Keepax 30382b0ee49fSCharles Keepax return 0; 30392b0ee49fSCharles Keepax } 30402b0ee49fSCharles Keepax 30415beb8eeaSSimon Trimmer static int cs_dsp_adsp2_enable_memory(struct wm_adsp *dsp) 30424e08d50dSCharles Keepax { 30434e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 30444e08d50dSCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 30454e08d50dSCharles Keepax } 30464e08d50dSCharles Keepax 30475beb8eeaSSimon Trimmer static void cs_dsp_adsp2_disable_memory(struct wm_adsp *dsp) 30484e08d50dSCharles Keepax { 30494e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 30504e08d50dSCharles Keepax ADSP2_MEM_ENA, 0); 30514e08d50dSCharles Keepax } 30524e08d50dSCharles Keepax 30535beb8eeaSSimon Trimmer static void cs_dsp_adsp2_disable_core(struct wm_adsp *dsp) 30544e08d50dSCharles Keepax { 30554e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 30564e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 30574e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 30584e08d50dSCharles Keepax 30594e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 30604e08d50dSCharles Keepax ADSP2_SYS_ENA, 0); 30614e08d50dSCharles Keepax } 30624e08d50dSCharles Keepax 30635beb8eeaSSimon Trimmer static void cs_dsp_adsp2v2_disable_core(struct wm_adsp *dsp) 30644e08d50dSCharles Keepax { 30654e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 30664e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 30674e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 30684e08d50dSCharles Keepax } 30694e08d50dSCharles Keepax 30705beb8eeaSSimon Trimmer static int cs_dsp_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions) 3071170b1e12SWen Shi { 3072170b1e12SWen Shi struct reg_sequence config[] = { 3073170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 3074170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 3075170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 3076170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 3077170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 3078170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 3079170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 3080170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 3081170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 3082170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 3083170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 3084170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 3085170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 3086170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 3087170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 3088170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 3089170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 3090170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 3091170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 3092170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 3093170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 3094170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 3095170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 3096170b1e12SWen Shi }; 3097170b1e12SWen Shi 3098170b1e12SWen Shi return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 3099170b1e12SWen Shi } 3100170b1e12SWen Shi 310125ca837bSSimon Trimmer static int cs_dsp_set_dspclk(struct wm_adsp *dsp, unsigned int freq) 3102d82d767fSCharles Keepax { 3103d82d767fSCharles Keepax int ret; 3104d82d767fSCharles Keepax 3105b9070df4SRichard Fitzgerald ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 3106d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 3107d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 3108b9070df4SRichard Fitzgerald if (ret) 31096ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 3110b9070df4SRichard Fitzgerald 3111b9070df4SRichard Fitzgerald return ret; 3112e1ea1879SRichard Fitzgerald } 311325ca837bSSimon Trimmer 311425ca837bSSimon Trimmer int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) 311525ca837bSSimon Trimmer { 311625ca837bSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 311725ca837bSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 311825ca837bSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 311925ca837bSSimon Trimmer 312025ca837bSSimon Trimmer return cs_dsp_set_dspclk(dsp, freq); 312125ca837bSSimon Trimmer } 3122b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); 3123d82d767fSCharles Keepax 3124af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 3125af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3126af813a6fSCharles Keepax { 31270fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3128b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3129b1470d4cSAjit Pandey struct soc_mixer_control *mc = 3130b1470d4cSAjit Pandey (struct soc_mixer_control *)kcontrol->private_value; 3131b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3132af813a6fSCharles Keepax 3133af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 3134af813a6fSCharles Keepax 3135af813a6fSCharles Keepax return 0; 3136af813a6fSCharles Keepax } 3137af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 3138af813a6fSCharles Keepax 3139af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 3140af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3141af813a6fSCharles Keepax { 31420fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3143b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 31440fe1daa6SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 3145af813a6fSCharles Keepax struct soc_mixer_control *mc = 3146af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 3147b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3148af813a6fSCharles Keepax char preload[32]; 3149af813a6fSCharles Keepax 3150605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 3151af813a6fSCharles Keepax 3152af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 3153af813a6fSCharles Keepax 3154af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 315595a594d0SCharles Keepax snd_soc_component_force_enable_pin(component, preload); 3156af813a6fSCharles Keepax else 315795a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3158af813a6fSCharles Keepax 3159af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 3160af813a6fSCharles Keepax 3161868e49a4SStuart Henderson flush_work(&dsp->boot_work); 3162868e49a4SStuart Henderson 3163af813a6fSCharles Keepax return 0; 3164af813a6fSCharles Keepax } 3165af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 3166af813a6fSCharles Keepax 31675beb8eeaSSimon Trimmer static void cs_dsp_stop_watchdog(struct wm_adsp *dsp) 316851a2c944SMayuresh Kulkarni { 316951a2c944SMayuresh Kulkarni regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 317051a2c944SMayuresh Kulkarni ADSP2_WDT_ENA_MASK, 0); 317151a2c944SMayuresh Kulkarni } 317251a2c944SMayuresh Kulkarni 31735beb8eeaSSimon Trimmer static void cs_dsp_halo_stop_watchdog(struct wm_adsp *dsp) 31748bc144f9SStuart Henderson { 31758bc144f9SStuart Henderson regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 31768bc144f9SStuart Henderson HALO_WDT_EN_MASK, 0); 31778bc144f9SStuart Henderson } 31788bc144f9SStuart Henderson 3179*2169f2f1SSimon Trimmer static void cs_dsp_power_up(struct wm_adsp *dsp, const char *fw_file_name, 3180*2169f2f1SSimon Trimmer const char *fw_name) 318112db5eddSCharles Keepax { 3182186152dfSSimon Trimmer int ret; 3183186152dfSSimon Trimmer 3184186152dfSSimon Trimmer mutex_lock(&dsp->pwr_lock); 3185186152dfSSimon Trimmer 3186*2169f2f1SSimon Trimmer dsp->fw_name = fw_name; 3187*2169f2f1SSimon Trimmer 3188186152dfSSimon Trimmer if (dsp->ops->enable_memory) { 3189186152dfSSimon Trimmer ret = dsp->ops->enable_memory(dsp); 3190186152dfSSimon Trimmer if (ret != 0) 3191186152dfSSimon Trimmer goto err_mutex; 3192186152dfSSimon Trimmer } 3193186152dfSSimon Trimmer 3194186152dfSSimon Trimmer if (dsp->ops->enable_core) { 3195186152dfSSimon Trimmer ret = dsp->ops->enable_core(dsp); 3196186152dfSSimon Trimmer if (ret != 0) 3197186152dfSSimon Trimmer goto err_mem; 3198186152dfSSimon Trimmer } 3199186152dfSSimon Trimmer 3200*2169f2f1SSimon Trimmer ret = cs_dsp_load(dsp, fw_file_name); 3201186152dfSSimon Trimmer if (ret != 0) 3202186152dfSSimon Trimmer goto err_ena; 3203186152dfSSimon Trimmer 3204186152dfSSimon Trimmer ret = dsp->ops->setup_algs(dsp); 3205186152dfSSimon Trimmer if (ret != 0) 3206186152dfSSimon Trimmer goto err_ena; 3207186152dfSSimon Trimmer 3208*2169f2f1SSimon Trimmer ret = cs_dsp_load_coeff(dsp, fw_file_name); 3209186152dfSSimon Trimmer if (ret != 0) 3210186152dfSSimon Trimmer goto err_ena; 3211186152dfSSimon Trimmer 3212186152dfSSimon Trimmer /* Initialize caches for enabled and unset controls */ 3213186152dfSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 3214186152dfSSimon Trimmer if (ret != 0) 3215186152dfSSimon Trimmer goto err_ena; 3216186152dfSSimon Trimmer 3217186152dfSSimon Trimmer if (dsp->ops->disable_core) 3218186152dfSSimon Trimmer dsp->ops->disable_core(dsp); 3219186152dfSSimon Trimmer 3220186152dfSSimon Trimmer dsp->booted = true; 3221186152dfSSimon Trimmer 3222186152dfSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3223186152dfSSimon Trimmer 3224186152dfSSimon Trimmer return; 3225186152dfSSimon Trimmer 3226186152dfSSimon Trimmer err_ena: 3227186152dfSSimon Trimmer if (dsp->ops->disable_core) 3228186152dfSSimon Trimmer dsp->ops->disable_core(dsp); 3229186152dfSSimon Trimmer err_mem: 3230186152dfSSimon Trimmer if (dsp->ops->disable_memory) 3231186152dfSSimon Trimmer dsp->ops->disable_memory(dsp); 3232186152dfSSimon Trimmer err_mutex: 3233186152dfSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3234186152dfSSimon Trimmer } 3235186152dfSSimon Trimmer 3236186152dfSSimon Trimmer static void cs_dsp_power_down(struct wm_adsp *dsp) 3237186152dfSSimon Trimmer { 32380700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 323912db5eddSCharles Keepax 3240bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3241bb24ee41SCharles Keepax 32425beb8eeaSSimon Trimmer cs_dsp_debugfs_clear(dsp); 324357a60cc3SCharles Keepax 324457a60cc3SCharles Keepax dsp->fw_id = 0; 324557a60cc3SCharles Keepax dsp->fw_id_version = 0; 324657a60cc3SCharles Keepax 324757a60cc3SCharles Keepax dsp->booted = false; 324857a60cc3SCharles Keepax 32494e08d50dSCharles Keepax if (dsp->ops->disable_memory) 32504e08d50dSCharles Keepax dsp->ops->disable_memory(dsp); 325157a60cc3SCharles Keepax 325257a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 325357a60cc3SCharles Keepax ctl->enabled = 0; 325457a60cc3SCharles Keepax 32555beb8eeaSSimon Trimmer cs_dsp_free_alg_regions(dsp); 325657a60cc3SCharles Keepax 3257bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3258bb24ee41SCharles Keepax 32596ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Shutdown complete\n"); 3260186152dfSSimon Trimmer } 3261186152dfSSimon Trimmer 3262186152dfSSimon Trimmer static void wm_adsp_boot_work(struct work_struct *work) 3263186152dfSSimon Trimmer { 3264186152dfSSimon Trimmer struct wm_adsp *dsp = container_of(work, 3265186152dfSSimon Trimmer struct wm_adsp, 3266186152dfSSimon Trimmer boot_work); 3267186152dfSSimon Trimmer 3268*2169f2f1SSimon Trimmer cs_dsp_power_up(dsp, 3269*2169f2f1SSimon Trimmer wm_adsp_fw[dsp->fw].file, 3270*2169f2f1SSimon Trimmer wm_adsp_fw_text[dsp->fw]); 3271186152dfSSimon Trimmer } 3272186152dfSSimon Trimmer 3273186152dfSSimon Trimmer int wm_adsp_early_event(struct snd_soc_dapm_widget *w, 3274186152dfSSimon Trimmer struct snd_kcontrol *kcontrol, int event) 3275186152dfSSimon Trimmer { 3276186152dfSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 3277186152dfSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3278186152dfSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 3279186152dfSSimon Trimmer 3280186152dfSSimon Trimmer switch (event) { 3281186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMU: 3282186152dfSSimon Trimmer queue_work(system_unbound_wq, &dsp->boot_work); 3283186152dfSSimon Trimmer break; 3284186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMD: 3285186152dfSSimon Trimmer cs_dsp_power_down(dsp); 328657a60cc3SCharles Keepax break; 328712db5eddSCharles Keepax default: 328812db5eddSCharles Keepax break; 3289cab27258SCharles Keepax } 329012db5eddSCharles Keepax 329112db5eddSCharles Keepax return 0; 329212db5eddSCharles Keepax } 32934e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event); 329412db5eddSCharles Keepax 32955beb8eeaSSimon Trimmer static int cs_dsp_adsp2_start_core(struct wm_adsp *dsp) 32964e08d50dSCharles Keepax { 32974e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 32984e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 32994e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 33004e08d50dSCharles Keepax } 33014e08d50dSCharles Keepax 33025beb8eeaSSimon Trimmer static void cs_dsp_adsp2_stop_core(struct wm_adsp *dsp) 33034e08d50dSCharles Keepax { 33044e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 33054e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 33064e08d50dSCharles Keepax } 33074e08d50dSCharles Keepax 3308186152dfSSimon Trimmer static int wm_adsp_event_post_run(struct wm_adsp *dsp) 3309d8a64d6aSCharles Keepax { 3310186152dfSSimon Trimmer if (wm_adsp_fw[dsp->fw].num_caps != 0) 3311186152dfSSimon Trimmer return wm_adsp_buffer_init(dsp); 3312d8a64d6aSCharles Keepax 3313186152dfSSimon Trimmer return 0; 3314186152dfSSimon Trimmer } 3315186152dfSSimon Trimmer 3316186152dfSSimon Trimmer static void wm_adsp_event_post_stop(struct wm_adsp *dsp) 3317186152dfSSimon Trimmer { 3318186152dfSSimon Trimmer if (wm_adsp_fw[dsp->fw].num_caps != 0) 3319186152dfSSimon Trimmer wm_adsp_buffer_free(dsp); 3320186152dfSSimon Trimmer 3321186152dfSSimon Trimmer dsp->fatal_error = false; 3322186152dfSSimon Trimmer } 3323186152dfSSimon Trimmer 3324186152dfSSimon Trimmer static int cs_dsp_run(struct wm_adsp *dsp) 3325186152dfSSimon Trimmer { 3326186152dfSSimon Trimmer int ret; 3327d8a64d6aSCharles Keepax 3328bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3329bb24ee41SCharles Keepax 3330bb24ee41SCharles Keepax if (!dsp->booted) { 3331bb24ee41SCharles Keepax ret = -EIO; 3332bb24ee41SCharles Keepax goto err; 3333bb24ee41SCharles Keepax } 3334d8a64d6aSCharles Keepax 33354e08d50dSCharles Keepax if (dsp->ops->enable_core) { 33364e08d50dSCharles Keepax ret = dsp->ops->enable_core(dsp); 333790d19ba5SCharles Keepax if (ret != 0) 333890d19ba5SCharles Keepax goto err; 33394e08d50dSCharles Keepax } 334090d19ba5SCharles Keepax 3341cef45771SCharles Keepax /* Sync set controls */ 33425beb8eeaSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 3343cef45771SCharles Keepax if (ret != 0) 3344cef45771SCharles Keepax goto err; 3345cef45771SCharles Keepax 33464e08d50dSCharles Keepax if (dsp->ops->lock_memory) { 33474e08d50dSCharles Keepax ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 33484e08d50dSCharles Keepax if (ret != 0) { 33496ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "Error configuring MPU: %d\n", ret); 33504e08d50dSCharles Keepax goto err; 33514e08d50dSCharles Keepax } 33524e08d50dSCharles Keepax } 335351a2c944SMayuresh Kulkarni 33544e08d50dSCharles Keepax if (dsp->ops->start_core) { 33554e08d50dSCharles Keepax ret = dsp->ops->start_core(dsp); 3356d8a64d6aSCharles Keepax if (ret != 0) 3357d8a64d6aSCharles Keepax goto err; 33584e08d50dSCharles Keepax } 33592cd19bdbSCharles Keepax 336004ae0859SCharles Keepax dsp->running = true; 336104ae0859SCharles Keepax 3362186152dfSSimon Trimmer ret = wm_adsp_event_post_run(dsp); 3363bb24ee41SCharles Keepax if (ret < 0) 336448c2c993SCharles Keepax goto err; 33652cd19bdbSCharles Keepax 3366612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 33672159ad93SMark Brown 3368186152dfSSimon Trimmer return 0; 3369186152dfSSimon Trimmer 3370186152dfSSimon Trimmer err: 3371186152dfSSimon Trimmer if (dsp->ops->stop_core) 3372186152dfSSimon Trimmer dsp->ops->stop_core(dsp); 3373186152dfSSimon Trimmer if (dsp->ops->disable_core) 3374186152dfSSimon Trimmer dsp->ops->disable_core(dsp); 3375186152dfSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3376186152dfSSimon Trimmer 3377186152dfSSimon Trimmer return ret; 3378186152dfSSimon Trimmer } 3379186152dfSSimon Trimmer 3380186152dfSSimon Trimmer static void cs_dsp_stop(struct wm_adsp *dsp) 3381186152dfSSimon Trimmer { 3382f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 33835beb8eeaSSimon Trimmer cs_dsp_signal_event_controls(dsp, CS_DSP_FW_EVENT_SHUTDOWN); 3384f4f0c4c6SRichard Fitzgerald 33854e08d50dSCharles Keepax if (dsp->ops->stop_watchdog) 33864e08d50dSCharles Keepax dsp->ops->stop_watchdog(dsp); 338751a2c944SMayuresh Kulkarni 338810337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 33894e08d50dSCharles Keepax if (dsp->ops->show_fw_status) 33904e08d50dSCharles Keepax dsp->ops->show_fw_status(dsp); 339110337b07SRichard Fitzgerald 3392078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 3393078e7183SCharles Keepax 33941023dbd9SMark Brown dsp->running = false; 33951023dbd9SMark Brown 33964e08d50dSCharles Keepax if (dsp->ops->stop_core) 33974e08d50dSCharles Keepax dsp->ops->stop_core(dsp); 33984e08d50dSCharles Keepax if (dsp->ops->disable_core) 33994e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 34002d30b575SMark Brown 3401186152dfSSimon Trimmer wm_adsp_event_post_stop(dsp); 3402a2bcbc1bSCharles Keepax 3403078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3404078e7183SCharles Keepax 34056ab1d0ccSSimon Trimmer cs_dsp_dbg(dsp, "Execution stopped\n"); 3406186152dfSSimon Trimmer } 34072159ad93SMark Brown 3408186152dfSSimon Trimmer int wm_adsp_event(struct snd_soc_dapm_widget *w, 3409186152dfSSimon Trimmer struct snd_kcontrol *kcontrol, int event) 3410186152dfSSimon Trimmer { 3411186152dfSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 3412186152dfSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3413186152dfSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 3414186152dfSSimon Trimmer int ret = 0; 3415186152dfSSimon Trimmer 3416186152dfSSimon Trimmer switch (event) { 3417186152dfSSimon Trimmer case SND_SOC_DAPM_POST_PMU: 3418186152dfSSimon Trimmer flush_work(&dsp->boot_work); 3419186152dfSSimon Trimmer ret = cs_dsp_run(dsp); 3420186152dfSSimon Trimmer break; 3421186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMD: 3422186152dfSSimon Trimmer cs_dsp_stop(dsp); 3423186152dfSSimon Trimmer break; 34242159ad93SMark Brown default: 34252159ad93SMark Brown break; 34262159ad93SMark Brown } 34272159ad93SMark Brown 34282159ad93SMark Brown return ret; 34292159ad93SMark Brown } 34304e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event); 3431973838a0SMark Brown 34325beb8eeaSSimon Trimmer static int cs_dsp_halo_start_core(struct wm_adsp *dsp) 3433170b1e12SWen Shi { 3434170b1e12SWen Shi return regmap_update_bits(dsp->regmap, 3435170b1e12SWen Shi dsp->base + HALO_CCM_CORE_CONTROL, 3436e5883322SCharles Keepax HALO_CORE_RESET | HALO_CORE_EN, 3437e5883322SCharles Keepax HALO_CORE_RESET | HALO_CORE_EN); 3438170b1e12SWen Shi } 3439170b1e12SWen Shi 34405beb8eeaSSimon Trimmer static void cs_dsp_halo_stop_core(struct wm_adsp *dsp) 3441170b1e12SWen Shi { 3442170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 3443170b1e12SWen Shi HALO_CORE_EN, 0); 3444170b1e12SWen Shi 3445809589a9SCharles Keepax /* reset halo core with CORE_SOFT_RESET */ 3446170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 3447170b1e12SWen Shi HALO_CORE_SOFT_RESET_MASK, 1); 3448170b1e12SWen Shi } 3449170b1e12SWen Shi 34500fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 3451f5e2ce92SRichard Fitzgerald { 3452af813a6fSCharles Keepax char preload[32]; 3453af813a6fSCharles Keepax 3454605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 345595a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3456685f51a5SRichard Fitzgerald 34575beb8eeaSSimon Trimmer cs_dsp_init_debugfs(dsp, component); 3458f9f55e31SRichard Fitzgerald 34590fe1daa6SKuninori Morimoto dsp->component = component; 3460af813a6fSCharles Keepax 34610a047f07SRichard Fitzgerald return 0; 3462f5e2ce92SRichard Fitzgerald } 34630fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 3464f5e2ce92SRichard Fitzgerald 34650fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 3466f5e2ce92SRichard Fitzgerald { 34675beb8eeaSSimon Trimmer cs_dsp_cleanup_debugfs(dsp); 3468f9f55e31SRichard Fitzgerald 3469f5e2ce92SRichard Fitzgerald return 0; 3470f5e2ce92SRichard Fitzgerald } 34710fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 3472f5e2ce92SRichard Fitzgerald 347325ca837bSSimon Trimmer static int cs_dsp_adsp2_init(struct wm_adsp *dsp) 3474973838a0SMark Brown { 3475973838a0SMark Brown int ret; 3476973838a0SMark Brown 3477e1ea1879SRichard Fitzgerald switch (dsp->rev) { 3478e1ea1879SRichard Fitzgerald case 0: 347910a2b662SMark Brown /* 348010a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 348110a2b662SMark Brown * power saving. 348210a2b662SMark Brown */ 34833809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 348410a2b662SMark Brown ADSP2_MEM_ENA, 0); 3485e1ea1879SRichard Fitzgerald if (ret) { 34866ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 3487e1ea1879SRichard Fitzgerald "Failed to clear memory retention: %d\n", ret); 348810a2b662SMark Brown return ret; 348910a2b662SMark Brown } 34904e08d50dSCharles Keepax 34915beb8eeaSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[0]; 34924e08d50dSCharles Keepax break; 34934e08d50dSCharles Keepax case 1: 34945beb8eeaSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[1]; 3495e1ea1879SRichard Fitzgerald break; 3496e1ea1879SRichard Fitzgerald default: 34975beb8eeaSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[2]; 3498e1ea1879SRichard Fitzgerald break; 3499e1ea1879SRichard Fitzgerald } 350010a2b662SMark Brown 350125ca837bSSimon Trimmer return cs_dsp_common_init(dsp); 350225ca837bSSimon Trimmer } 350325ca837bSSimon Trimmer 350425ca837bSSimon Trimmer int wm_adsp2_init(struct wm_adsp *dsp) 350525ca837bSSimon Trimmer { 35064e08d50dSCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 35076ab2b7b4SDimitris Papastamos 35086092be2dSCharles Keepax dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr); 35096092be2dSCharles Keepax 351025ca837bSSimon Trimmer wm_adsp_common_init(dsp); 351125ca837bSSimon Trimmer 351225ca837bSSimon Trimmer return cs_dsp_adsp2_init(dsp); 3513973838a0SMark Brown } 3514973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 35150a37c6efSPraveen Diwakar 351625ca837bSSimon Trimmer static int cs_dsp_halo_init(struct wm_adsp *dsp) 3517170b1e12SWen Shi { 35185beb8eeaSSimon Trimmer dsp->ops = &cs_dsp_halo_ops; 3519170b1e12SWen Shi 352025ca837bSSimon Trimmer return cs_dsp_common_init(dsp); 352125ca837bSSimon Trimmer } 352225ca837bSSimon Trimmer 352325ca837bSSimon Trimmer int wm_halo_init(struct wm_adsp *dsp) 352425ca837bSSimon Trimmer { 3525170b1e12SWen Shi INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 3526170b1e12SWen Shi 35276092be2dSCharles Keepax dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr); 35286092be2dSCharles Keepax 352925ca837bSSimon Trimmer wm_adsp_common_init(dsp); 353025ca837bSSimon Trimmer 353125ca837bSSimon Trimmer return cs_dsp_halo_init(dsp); 3532170b1e12SWen Shi } 3533170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init); 3534170b1e12SWen Shi 353525ca837bSSimon Trimmer static void cs_dsp_remove(struct wm_adsp *dsp) 353666225e98SRichard Fitzgerald { 35370700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 353866225e98SRichard Fitzgerald 353966225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 35400700bc2fSSimon Trimmer ctl = list_first_entry(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list); 35410700bc2fSSimon Trimmer 35420700bc2fSSimon Trimmer wm_adsp_control_remove(ctl); 35430700bc2fSSimon Trimmer 354466225e98SRichard Fitzgerald list_del(&ctl->list); 35455beb8eeaSSimon Trimmer cs_dsp_free_ctl_blk(ctl); 354666225e98SRichard Fitzgerald } 354766225e98SRichard Fitzgerald } 354825ca837bSSimon Trimmer 354925ca837bSSimon Trimmer void wm_adsp2_remove(struct wm_adsp *dsp) 355025ca837bSSimon Trimmer { 355125ca837bSSimon Trimmer cs_dsp_remove(dsp); 355225ca837bSSimon Trimmer } 355366225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 355466225e98SRichard Fitzgerald 3555edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 3556edd71350SCharles Keepax { 3557edd71350SCharles Keepax return compr->buf != NULL; 3558edd71350SCharles Keepax } 3559edd71350SCharles Keepax 3560edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 3561edd71350SCharles Keepax { 35624f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf = NULL, *tmp; 35634f2d4eabSStuart Henderson 3564a2bcbc1bSCharles Keepax if (compr->dsp->fatal_error) 3565a2bcbc1bSCharles Keepax return -EINVAL; 3566a2bcbc1bSCharles Keepax 35674f2d4eabSStuart Henderson list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { 35684f2d4eabSStuart Henderson if (!tmp->name || !strcmp(compr->name, tmp->name)) { 35694f2d4eabSStuart Henderson buf = tmp; 35704f2d4eabSStuart Henderson break; 35714f2d4eabSStuart Henderson } 35724f2d4eabSStuart Henderson } 35734f2d4eabSStuart Henderson 35744f2d4eabSStuart Henderson if (!buf) 3575edd71350SCharles Keepax return -EINVAL; 3576edd71350SCharles Keepax 35774f2d4eabSStuart Henderson compr->buf = buf; 3578789b930aSCharles Keepax buf->compr = compr; 3579edd71350SCharles Keepax 3580edd71350SCharles Keepax return 0; 3581edd71350SCharles Keepax } 3582edd71350SCharles Keepax 3583721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 3584721be3beSCharles Keepax { 3585721be3beSCharles Keepax if (!compr) 3586721be3beSCharles Keepax return; 3587721be3beSCharles Keepax 3588721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 3589721be3beSCharles Keepax if (compr->stream) 3590721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 3591721be3beSCharles Keepax 3592721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 3593721be3beSCharles Keepax compr->buf->compr = NULL; 3594721be3beSCharles Keepax compr->buf = NULL; 3595721be3beSCharles Keepax } 3596721be3beSCharles Keepax } 3597721be3beSCharles Keepax 3598406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 3599406abc95SCharles Keepax { 36004f2d4eabSStuart Henderson struct wm_adsp_compr *compr, *tmp; 36014f2d4eabSStuart Henderson struct snd_soc_pcm_runtime *rtd = stream->private_data; 3602406abc95SCharles Keepax int ret = 0; 3603406abc95SCharles Keepax 3604406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3605406abc95SCharles Keepax 3606406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 36070d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support compressed API\n", 3608b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 3609406abc95SCharles Keepax ret = -ENXIO; 3610406abc95SCharles Keepax goto out; 3611406abc95SCharles Keepax } 3612406abc95SCharles Keepax 3613406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 36140d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support stream direction\n", 3615b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 3616406abc95SCharles Keepax ret = -EINVAL; 3617406abc95SCharles Keepax goto out; 3618406abc95SCharles Keepax } 3619406abc95SCharles Keepax 36204f2d4eabSStuart Henderson list_for_each_entry(tmp, &dsp->compr_list, list) { 3621b5cb8558SKuninori Morimoto if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) { 36220d3fba3eSCharles Keepax adsp_err(dsp, "%s: Only a single stream supported per dai\n", 3623b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 362495fe9597SCharles Keepax ret = -EBUSY; 362595fe9597SCharles Keepax goto out; 362695fe9597SCharles Keepax } 36274f2d4eabSStuart Henderson } 362895fe9597SCharles Keepax 3629406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 3630406abc95SCharles Keepax if (!compr) { 3631406abc95SCharles Keepax ret = -ENOMEM; 3632406abc95SCharles Keepax goto out; 3633406abc95SCharles Keepax } 3634406abc95SCharles Keepax 3635406abc95SCharles Keepax compr->dsp = dsp; 3636406abc95SCharles Keepax compr->stream = stream; 3637b5cb8558SKuninori Morimoto compr->name = asoc_rtd_to_codec(rtd, 0)->name; 3638406abc95SCharles Keepax 36394f2d4eabSStuart Henderson list_add_tail(&compr->list, &dsp->compr_list); 3640406abc95SCharles Keepax 3641406abc95SCharles Keepax stream->runtime->private_data = compr; 3642406abc95SCharles Keepax 3643406abc95SCharles Keepax out: 3644406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3645406abc95SCharles Keepax 3646406abc95SCharles Keepax return ret; 3647406abc95SCharles Keepax } 3648406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 3649406abc95SCharles Keepax 36503a5ccf25SKuninori Morimoto int wm_adsp_compr_free(struct snd_soc_component *component, 36513a5ccf25SKuninori Morimoto struct snd_compr_stream *stream) 3652406abc95SCharles Keepax { 3653406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3654406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3655406abc95SCharles Keepax 3656406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3657406abc95SCharles Keepax 3658721be3beSCharles Keepax wm_adsp_compr_detach(compr); 36594f2d4eabSStuart Henderson list_del(&compr->list); 3660406abc95SCharles Keepax 366183a40ce9SCharles Keepax kfree(compr->raw_buf); 3662406abc95SCharles Keepax kfree(compr); 3663406abc95SCharles Keepax 3664406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3665406abc95SCharles Keepax 3666406abc95SCharles Keepax return 0; 3667406abc95SCharles Keepax } 3668406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 3669406abc95SCharles Keepax 3670406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 3671406abc95SCharles Keepax struct snd_compr_params *params) 3672406abc95SCharles Keepax { 3673406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3674406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3675406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3676406abc95SCharles Keepax const struct snd_codec_desc *desc; 3677406abc95SCharles Keepax int i, j; 3678406abc95SCharles Keepax 3679406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 3680406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 3681406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 3682406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 36835beb8eeaSSimon Trimmer params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) { 36840d3fba3eSCharles Keepax compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", 3685406abc95SCharles Keepax params->buffer.fragment_size, 3686406abc95SCharles Keepax params->buffer.fragments); 3687406abc95SCharles Keepax 3688406abc95SCharles Keepax return -EINVAL; 3689406abc95SCharles Keepax } 3690406abc95SCharles Keepax 3691406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 3692406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 3693406abc95SCharles Keepax desc = &caps->desc; 3694406abc95SCharles Keepax 3695406abc95SCharles Keepax if (caps->id != params->codec.id) 3696406abc95SCharles Keepax continue; 3697406abc95SCharles Keepax 3698406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 3699406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 3700406abc95SCharles Keepax continue; 3701406abc95SCharles Keepax } else { 3702406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 3703406abc95SCharles Keepax continue; 3704406abc95SCharles Keepax } 3705406abc95SCharles Keepax 3706406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 3707406abc95SCharles Keepax continue; 3708406abc95SCharles Keepax 3709406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 3710406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 3711406abc95SCharles Keepax return 0; 3712406abc95SCharles Keepax } 3713406abc95SCharles Keepax 37140d3fba3eSCharles Keepax compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 3715406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 3716406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 3717406abc95SCharles Keepax return -EINVAL; 3718406abc95SCharles Keepax } 3719406abc95SCharles Keepax 3720565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 3721565ace46SCharles Keepax { 37225beb8eeaSSimon Trimmer return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE; 3723565ace46SCharles Keepax } 3724565ace46SCharles Keepax 37253a5ccf25SKuninori Morimoto int wm_adsp_compr_set_params(struct snd_soc_component *component, 37263a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 3727406abc95SCharles Keepax struct snd_compr_params *params) 3728406abc95SCharles Keepax { 3729406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 373083a40ce9SCharles Keepax unsigned int size; 3731406abc95SCharles Keepax int ret; 3732406abc95SCharles Keepax 3733406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 3734406abc95SCharles Keepax if (ret) 3735406abc95SCharles Keepax return ret; 3736406abc95SCharles Keepax 3737406abc95SCharles Keepax compr->size = params->buffer; 3738406abc95SCharles Keepax 37390d3fba3eSCharles Keepax compr_dbg(compr, "fragment_size=%d fragments=%d\n", 3740406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 3741406abc95SCharles Keepax 374283a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 374383a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 374483a40ce9SCharles Keepax if (!compr->raw_buf) 374583a40ce9SCharles Keepax return -ENOMEM; 374683a40ce9SCharles Keepax 3747da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 3748da2b3358SCharles Keepax 3749406abc95SCharles Keepax return 0; 3750406abc95SCharles Keepax } 3751406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 3752406abc95SCharles Keepax 37533a5ccf25SKuninori Morimoto int wm_adsp_compr_get_caps(struct snd_soc_component *component, 37543a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 3755406abc95SCharles Keepax struct snd_compr_caps *caps) 3756406abc95SCharles Keepax { 3757406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3758406abc95SCharles Keepax int fw = compr->dsp->fw; 3759406abc95SCharles Keepax int i; 3760406abc95SCharles Keepax 3761406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 3762406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 3763406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 3764406abc95SCharles Keepax 3765406abc95SCharles Keepax caps->num_codecs = i; 3766406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 3767406abc95SCharles Keepax 3768406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 3769406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 3770406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 3771406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 3772406abc95SCharles Keepax } 3773406abc95SCharles Keepax 3774406abc95SCharles Keepax return 0; 3775406abc95SCharles Keepax } 3776406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 3777406abc95SCharles Keepax 37785beb8eeaSSimon Trimmer static int cs_dsp_read_raw_data_block(struct wm_adsp *dsp, int mem_type, 37792cd19bdbSCharles Keepax unsigned int mem_addr, 37807726e498SRichard Fitzgerald unsigned int num_words, __be32 *data) 37812cd19bdbSCharles Keepax { 37825beb8eeaSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 37837726e498SRichard Fitzgerald unsigned int reg; 37842cd19bdbSCharles Keepax int ret; 37852cd19bdbSCharles Keepax 37862cd19bdbSCharles Keepax if (!mem) 37872cd19bdbSCharles Keepax return -EINVAL; 37882cd19bdbSCharles Keepax 3789170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 37902cd19bdbSCharles Keepax 37912cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 37922cd19bdbSCharles Keepax sizeof(*data) * num_words); 37932cd19bdbSCharles Keepax if (ret < 0) 37942cd19bdbSCharles Keepax return ret; 37952cd19bdbSCharles Keepax 37962cd19bdbSCharles Keepax return 0; 37972cd19bdbSCharles Keepax } 37982cd19bdbSCharles Keepax 37995beb8eeaSSimon Trimmer static inline int cs_dsp_read_data_word(struct wm_adsp *dsp, int mem_type, 38002cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 38012cd19bdbSCharles Keepax { 38027726e498SRichard Fitzgerald __be32 raw; 38037726e498SRichard Fitzgerald int ret; 38047726e498SRichard Fitzgerald 38055beb8eeaSSimon Trimmer ret = cs_dsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw); 3806fe9989fbSRichard Fitzgerald if (ret < 0) 38077726e498SRichard Fitzgerald return ret; 38087726e498SRichard Fitzgerald 38097726e498SRichard Fitzgerald *data = be32_to_cpu(raw) & 0x00ffffffu; 38107726e498SRichard Fitzgerald 38117726e498SRichard Fitzgerald return 0; 38122cd19bdbSCharles Keepax } 38132cd19bdbSCharles Keepax 38145beb8eeaSSimon Trimmer static int cs_dsp_write_data_word(struct wm_adsp *dsp, int mem_type, 38152cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 38162cd19bdbSCharles Keepax { 38175beb8eeaSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 3818a0b653e8SRichard Fitzgerald __be32 val = cpu_to_be32(data & 0x00ffffffu); 38192cd19bdbSCharles Keepax unsigned int reg; 38202cd19bdbSCharles Keepax 38212cd19bdbSCharles Keepax if (!mem) 38222cd19bdbSCharles Keepax return -EINVAL; 38232cd19bdbSCharles Keepax 3824170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 38252cd19bdbSCharles Keepax 3826a0b653e8SRichard Fitzgerald return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 38272cd19bdbSCharles Keepax } 38282cd19bdbSCharles Keepax 38292cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 38302cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 38312cd19bdbSCharles Keepax { 38325beb8eeaSSimon Trimmer return cs_dsp_read_data_word(buf->dsp, buf->host_buf_mem_type, 38332cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 38342cd19bdbSCharles Keepax } 38352cd19bdbSCharles Keepax 38362cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 38372cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 38382cd19bdbSCharles Keepax { 38395beb8eeaSSimon Trimmer return cs_dsp_write_data_word(buf->dsp, buf->host_buf_mem_type, 38402cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 38412cd19bdbSCharles Keepax } 38422cd19bdbSCharles Keepax 38435beb8eeaSSimon Trimmer static void cs_dsp_remove_padding(u32 *buf, int nwords) 3844cc7d6ce9SCharles Keepax { 38457726e498SRichard Fitzgerald const __be32 *pack_in = (__be32 *)buf; 3846cc7d6ce9SCharles Keepax u8 *pack_out = (u8 *)buf; 38477726e498SRichard Fitzgerald int i; 3848cc7d6ce9SCharles Keepax 38497726e498SRichard Fitzgerald /* 38507726e498SRichard Fitzgerald * DSP words from the register map have pad bytes and the data bytes 38517726e498SRichard Fitzgerald * are in swapped order. This swaps back to the original little-endian 38527726e498SRichard Fitzgerald * order and strips the pad bytes. 38537726e498SRichard Fitzgerald */ 3854cc7d6ce9SCharles Keepax for (i = 0; i < nwords; i++) { 38557726e498SRichard Fitzgerald u32 word = be32_to_cpu(*pack_in++); 38567726e498SRichard Fitzgerald *pack_out++ = (u8)word; 38577726e498SRichard Fitzgerald *pack_out++ = (u8)(word >> 8); 38587726e498SRichard Fitzgerald *pack_out++ = (u8)(word >> 16); 3859cc7d6ce9SCharles Keepax } 3860cc7d6ce9SCharles Keepax } 3861cc7d6ce9SCharles Keepax 38621e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 38631e38f069SCharles Keepax { 38641e38f069SCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 38651e38f069SCharles Keepax struct wm_adsp_buffer_region *region; 38661e38f069SCharles Keepax u32 offset = 0; 38671e38f069SCharles Keepax int i, ret; 38681e38f069SCharles Keepax 3869a792af69SCharles Keepax buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), 3870a792af69SCharles Keepax GFP_KERNEL); 3871a792af69SCharles Keepax if (!buf->regions) 3872a792af69SCharles Keepax return -ENOMEM; 3873a792af69SCharles Keepax 38741e38f069SCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 38751e38f069SCharles Keepax region = &buf->regions[i]; 38761e38f069SCharles Keepax 38771e38f069SCharles Keepax region->offset = offset; 38781e38f069SCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 38791e38f069SCharles Keepax 38801e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 38811e38f069SCharles Keepax ®ion->base_addr); 38821e38f069SCharles Keepax if (ret < 0) 38831e38f069SCharles Keepax return ret; 38841e38f069SCharles Keepax 38851e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 38861e38f069SCharles Keepax &offset); 38871e38f069SCharles Keepax if (ret < 0) 38881e38f069SCharles Keepax return ret; 38891e38f069SCharles Keepax 38901e38f069SCharles Keepax region->cumulative_size = offset; 38911e38f069SCharles Keepax 38920d3fba3eSCharles Keepax compr_dbg(buf, 38931e38f069SCharles Keepax "region=%d type=%d base=%08x off=%08x size=%08x\n", 38941e38f069SCharles Keepax i, region->mem_type, region->base_addr, 38951e38f069SCharles Keepax region->offset, region->cumulative_size); 38961e38f069SCharles Keepax } 38971e38f069SCharles Keepax 38981e38f069SCharles Keepax return 0; 38991e38f069SCharles Keepax } 39001e38f069SCharles Keepax 39011e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 39021e38f069SCharles Keepax { 39031e38f069SCharles Keepax buf->irq_count = 0xFFFFFFFF; 39041e38f069SCharles Keepax buf->read_index = -1; 39051e38f069SCharles Keepax buf->avail = 0; 39061e38f069SCharles Keepax } 39071e38f069SCharles Keepax 3908a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) 3909a792af69SCharles Keepax { 3910a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3911a792af69SCharles Keepax 3912a792af69SCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3913a792af69SCharles Keepax if (!buf) 3914a792af69SCharles Keepax return NULL; 3915a792af69SCharles Keepax 3916a792af69SCharles Keepax buf->dsp = dsp; 3917a792af69SCharles Keepax 3918a792af69SCharles Keepax wm_adsp_buffer_clear(buf); 3919a792af69SCharles Keepax 39204f2d4eabSStuart Henderson list_add_tail(&buf->list, &dsp->buffer_list); 3921a792af69SCharles Keepax 3922a792af69SCharles Keepax return buf; 3923a792af69SCharles Keepax } 3924a792af69SCharles Keepax 3925a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) 39262cd19bdbSCharles Keepax { 39275beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 3928a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 39292cd19bdbSCharles Keepax u32 xmalg, addr, magic; 39302cd19bdbSCharles Keepax int i, ret; 39312cd19bdbSCharles Keepax 39325beb8eeaSSimon Trimmer alg_region = cs_dsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 39339daf4fd0SLi Xu if (!alg_region) { 39349daf4fd0SLi Xu adsp_err(dsp, "No algorithm region found\n"); 39359daf4fd0SLi Xu return -EINVAL; 39369daf4fd0SLi Xu } 39379daf4fd0SLi Xu 3938a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(dsp); 3939a792af69SCharles Keepax if (!buf) 3940a792af69SCharles Keepax return -ENOMEM; 3941a792af69SCharles Keepax 39426092be2dSCharles Keepax xmalg = dsp->sys_config_size / sizeof(__be32); 39432cd19bdbSCharles Keepax 39442cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 39455beb8eeaSSimon Trimmer ret = cs_dsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 39462cd19bdbSCharles Keepax if (ret < 0) 39472cd19bdbSCharles Keepax return ret; 39482cd19bdbSCharles Keepax 39492cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 3950a792af69SCharles Keepax return -ENODEV; 39512cd19bdbSCharles Keepax 39522cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 39532cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 39545beb8eeaSSimon Trimmer ret = cs_dsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 39552cd19bdbSCharles Keepax &buf->host_buf_ptr); 39562cd19bdbSCharles Keepax if (ret < 0) 39572cd19bdbSCharles Keepax return ret; 39582cd19bdbSCharles Keepax 39592cd19bdbSCharles Keepax if (buf->host_buf_ptr) 39602cd19bdbSCharles Keepax break; 39612cd19bdbSCharles Keepax 39622cd19bdbSCharles Keepax usleep_range(1000, 2000); 39632cd19bdbSCharles Keepax } 39642cd19bdbSCharles Keepax 39652cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 39662cd19bdbSCharles Keepax return -EIO; 39672cd19bdbSCharles Keepax 3968fb13f19dSAndrew Ford buf->host_buf_mem_type = WMFW_ADSP2_XM; 3969fb13f19dSAndrew Ford 3970a792af69SCharles Keepax ret = wm_adsp_buffer_populate(buf); 3971a792af69SCharles Keepax if (ret < 0) 3972a792af69SCharles Keepax return ret; 3973a792af69SCharles Keepax 39740d3fba3eSCharles Keepax compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); 39752cd19bdbSCharles Keepax 39762cd19bdbSCharles Keepax return 0; 39772cd19bdbSCharles Keepax } 39782cd19bdbSCharles Keepax 39790700bc2fSSimon Trimmer static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) 3980d52ed4b0SRichard Fitzgerald { 39814f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 coeff_v1; 3982a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 39830700bc2fSSimon Trimmer struct wm_adsp *dsp = cs_ctl->dsp; 398404ae0859SCharles Keepax unsigned int version; 3985a792af69SCharles Keepax int ret, i; 3986d52ed4b0SRichard Fitzgerald 3987d52ed4b0SRichard Fitzgerald for (i = 0; i < 5; ++i) { 39880700bc2fSSimon Trimmer ret = cs_dsp_coeff_read_ctrl(cs_ctl, &coeff_v1, sizeof(coeff_v1)); 3989d52ed4b0SRichard Fitzgerald if (ret < 0) 3990d52ed4b0SRichard Fitzgerald return ret; 3991d52ed4b0SRichard Fitzgerald 399204ae0859SCharles Keepax if (coeff_v1.host_buf_ptr) 3993d52ed4b0SRichard Fitzgerald break; 3994d52ed4b0SRichard Fitzgerald 3995d52ed4b0SRichard Fitzgerald usleep_range(1000, 2000); 3996d52ed4b0SRichard Fitzgerald } 3997d52ed4b0SRichard Fitzgerald 399804ae0859SCharles Keepax if (!coeff_v1.host_buf_ptr) { 39990700bc2fSSimon Trimmer adsp_err(dsp, "Failed to acquire host buffer\n"); 4000d52ed4b0SRichard Fitzgerald return -EIO; 4001d52ed4b0SRichard Fitzgerald } 4002d52ed4b0SRichard Fitzgerald 40030700bc2fSSimon Trimmer buf = wm_adsp_buffer_alloc(dsp); 40042cd19bdbSCharles Keepax if (!buf) 40052cd19bdbSCharles Keepax return -ENOMEM; 40062cd19bdbSCharles Keepax 40070700bc2fSSimon Trimmer buf->host_buf_mem_type = cs_ctl->alg_region.type; 400804ae0859SCharles Keepax buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr); 40092cd19bdbSCharles Keepax 40102cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 4011a792af69SCharles Keepax if (ret < 0) 4012a792af69SCharles Keepax return ret; 4013a792af69SCharles Keepax 40144f2d4eabSStuart Henderson /* 40154f2d4eabSStuart Henderson * v0 host_buffer coefficients didn't have versioning, so if the 40164f2d4eabSStuart Henderson * control is one word, assume version 0. 40174f2d4eabSStuart Henderson */ 40180700bc2fSSimon Trimmer if (cs_ctl->len == 4) { 40190d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); 4020a792af69SCharles Keepax return 0; 40212cd19bdbSCharles Keepax } 40222cd19bdbSCharles Keepax 4023a0b653e8SRichard Fitzgerald version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK; 4024a0b653e8SRichard Fitzgerald version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; 40254f2d4eabSStuart Henderson 4026a0b653e8SRichard Fitzgerald if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { 40270700bc2fSSimon Trimmer adsp_err(dsp, 40284f2d4eabSStuart Henderson "Host buffer coeff ver %u > supported version %u\n", 4029a0b653e8SRichard Fitzgerald version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); 40304f2d4eabSStuart Henderson return -EINVAL; 40314f2d4eabSStuart Henderson } 40324f2d4eabSStuart Henderson 40335beb8eeaSSimon Trimmer cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); 40344f2d4eabSStuart Henderson 40350700bc2fSSimon Trimmer buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part, 40364f2d4eabSStuart Henderson (char *)&coeff_v1.name); 40374f2d4eabSStuart Henderson 40380d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", 4039a0b653e8SRichard Fitzgerald buf->host_buf_ptr, version); 40404f2d4eabSStuart Henderson 4041a0b653e8SRichard Fitzgerald return version; 40424f2d4eabSStuart Henderson } 40434f2d4eabSStuart Henderson 4044a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 4045a792af69SCharles Keepax { 40460700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 4047a792af69SCharles Keepax int ret; 4048a792af69SCharles Keepax 40490700bc2fSSimon Trimmer list_for_each_entry(cs_ctl, &dsp->ctl_list, list) { 40500700bc2fSSimon Trimmer if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 4051a792af69SCharles Keepax continue; 4052a792af69SCharles Keepax 40530700bc2fSSimon Trimmer if (!cs_ctl->enabled) 4054a792af69SCharles Keepax continue; 4055a792af69SCharles Keepax 40560700bc2fSSimon Trimmer ret = wm_adsp_buffer_parse_coeff(cs_ctl); 4057a792af69SCharles Keepax if (ret < 0) { 4058a792af69SCharles Keepax adsp_err(dsp, "Failed to parse coeff: %d\n", ret); 4059a792af69SCharles Keepax goto error; 40604f2d4eabSStuart Henderson } else if (ret == 0) { 40614f2d4eabSStuart Henderson /* Only one buffer supported for version 0 */ 4062a792af69SCharles Keepax return 0; 4063a792af69SCharles Keepax } 40644f2d4eabSStuart Henderson } 4065a792af69SCharles Keepax 40664f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 4067a792af69SCharles Keepax /* Fall back to legacy support */ 4068a792af69SCharles Keepax ret = wm_adsp_buffer_parse_legacy(dsp); 4069a792af69SCharles Keepax if (ret) { 4070a792af69SCharles Keepax adsp_err(dsp, "Failed to parse legacy: %d\n", ret); 4071a792af69SCharles Keepax goto error; 4072a792af69SCharles Keepax } 4073a792af69SCharles Keepax } 40742cd19bdbSCharles Keepax 40752cd19bdbSCharles Keepax return 0; 40762cd19bdbSCharles Keepax 4077a792af69SCharles Keepax error: 4078a792af69SCharles Keepax wm_adsp_buffer_free(dsp); 40792cd19bdbSCharles Keepax return ret; 40802cd19bdbSCharles Keepax } 40812cd19bdbSCharles Keepax 40822cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 40832cd19bdbSCharles Keepax { 40844f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf, *tmp; 4085721be3beSCharles Keepax 40864f2d4eabSStuart Henderson list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { 40874f2d4eabSStuart Henderson wm_adsp_compr_detach(buf->compr); 40882cd19bdbSCharles Keepax 40894f2d4eabSStuart Henderson kfree(buf->name); 40904f2d4eabSStuart Henderson kfree(buf->regions); 40914f2d4eabSStuart Henderson list_del(&buf->list); 40924f2d4eabSStuart Henderson kfree(buf); 40932cd19bdbSCharles Keepax } 40942cd19bdbSCharles Keepax 40952cd19bdbSCharles Keepax return 0; 40962cd19bdbSCharles Keepax } 40972cd19bdbSCharles Keepax 4098f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 4099f938f348SStuart Henderson { 4100f938f348SStuart Henderson int ret; 4101f938f348SStuart Henderson 4102f938f348SStuart Henderson ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 4103f938f348SStuart Henderson if (ret < 0) { 410448ead31cSCharles Keepax compr_err(buf, "Failed to check buffer error: %d\n", ret); 4105f938f348SStuart Henderson return ret; 4106f938f348SStuart Henderson } 4107f938f348SStuart Henderson if (buf->error != 0) { 410848ead31cSCharles Keepax compr_err(buf, "Buffer error occurred: %d\n", buf->error); 4109f938f348SStuart Henderson return -EIO; 4110f938f348SStuart Henderson } 4111f938f348SStuart Henderson 4112f938f348SStuart Henderson return 0; 4113f938f348SStuart Henderson } 4114f938f348SStuart Henderson 41153a5ccf25SKuninori Morimoto int wm_adsp_compr_trigger(struct snd_soc_component *component, 41163a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, int cmd) 411795fe9597SCharles Keepax { 411895fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 411995fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 412095fe9597SCharles Keepax int ret = 0; 412195fe9597SCharles Keepax 41220d3fba3eSCharles Keepax compr_dbg(compr, "Trigger: %d\n", cmd); 412395fe9597SCharles Keepax 412495fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 412595fe9597SCharles Keepax 412695fe9597SCharles Keepax switch (cmd) { 412795fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 412861fc060cSCharles Keepax if (!wm_adsp_compr_attached(compr)) { 412995fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 413095fe9597SCharles Keepax if (ret < 0) { 41310d3fba3eSCharles Keepax compr_err(compr, "Failed to link buffer and stream: %d\n", 413295fe9597SCharles Keepax ret); 413395fe9597SCharles Keepax break; 413495fe9597SCharles Keepax } 413561fc060cSCharles Keepax } 413661fc060cSCharles Keepax 4137f938f348SStuart Henderson ret = wm_adsp_buffer_get_error(compr->buf); 4138f938f348SStuart Henderson if (ret < 0) 4139f938f348SStuart Henderson break; 4140f938f348SStuart Henderson 4141565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 4142565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 4143565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 4144565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 4145565ace46SCharles Keepax if (ret < 0) { 41460d3fba3eSCharles Keepax compr_err(compr, "Failed to set high water mark: %d\n", 4147565ace46SCharles Keepax ret); 4148565ace46SCharles Keepax break; 4149565ace46SCharles Keepax } 415095fe9597SCharles Keepax break; 415195fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 415243d147beSCharles Keepax if (wm_adsp_compr_attached(compr)) 4153639e5eb3SCharles Keepax wm_adsp_buffer_clear(compr->buf); 415495fe9597SCharles Keepax break; 415595fe9597SCharles Keepax default: 415695fe9597SCharles Keepax ret = -EINVAL; 415795fe9597SCharles Keepax break; 415895fe9597SCharles Keepax } 415995fe9597SCharles Keepax 416095fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 416195fe9597SCharles Keepax 416295fe9597SCharles Keepax return ret; 416395fe9597SCharles Keepax } 416495fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 416595fe9597SCharles Keepax 4166565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 4167565ace46SCharles Keepax { 4168565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 4169565ace46SCharles Keepax 4170565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 4171565ace46SCharles Keepax } 4172565ace46SCharles Keepax 4173565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 4174565ace46SCharles Keepax { 4175565ace46SCharles Keepax u32 next_read_index, next_write_index; 4176565ace46SCharles Keepax int write_index, read_index, avail; 4177565ace46SCharles Keepax int ret; 4178565ace46SCharles Keepax 4179565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 4180565ace46SCharles Keepax if (buf->read_index < 0) { 4181565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 4182565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 4183565ace46SCharles Keepax &next_read_index); 4184565ace46SCharles Keepax if (ret < 0) 4185565ace46SCharles Keepax return ret; 4186565ace46SCharles Keepax 4187565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 4188565ace46SCharles Keepax 4189565ace46SCharles Keepax if (read_index < 0) { 41900d3fba3eSCharles Keepax compr_dbg(buf, "Avail check on unstarted stream\n"); 4191565ace46SCharles Keepax return 0; 4192565ace46SCharles Keepax } 4193565ace46SCharles Keepax 4194565ace46SCharles Keepax buf->read_index = read_index; 4195565ace46SCharles Keepax } 4196565ace46SCharles Keepax 4197565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 4198565ace46SCharles Keepax &next_write_index); 4199565ace46SCharles Keepax if (ret < 0) 4200565ace46SCharles Keepax return ret; 4201565ace46SCharles Keepax 4202565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 4203565ace46SCharles Keepax 4204565ace46SCharles Keepax avail = write_index - buf->read_index; 4205565ace46SCharles Keepax if (avail < 0) 4206565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 4207565ace46SCharles Keepax 42080d3fba3eSCharles Keepax compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 42095beb8eeaSSimon Trimmer buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE); 4210565ace46SCharles Keepax 4211565ace46SCharles Keepax buf->avail = avail; 4212565ace46SCharles Keepax 4213565ace46SCharles Keepax return 0; 4214565ace46SCharles Keepax } 4215565ace46SCharles Keepax 4216565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 4217565ace46SCharles Keepax { 4218612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 4219612047f0SCharles Keepax struct wm_adsp_compr *compr; 4220565ace46SCharles Keepax int ret = 0; 4221565ace46SCharles Keepax 4222565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 4223565ace46SCharles Keepax 42244f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 4225565ace46SCharles Keepax ret = -ENODEV; 4226565ace46SCharles Keepax goto out; 4227565ace46SCharles Keepax } 42280d3fba3eSCharles Keepax 4229565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 4230565ace46SCharles Keepax 42314f2d4eabSStuart Henderson list_for_each_entry(buf, &dsp->buffer_list, list) { 42324f2d4eabSStuart Henderson compr = buf->compr; 42334f2d4eabSStuart Henderson 42349771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 42359771b18aSCharles Keepax if (ret < 0) 42365847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 4237565ace46SCharles Keepax 4238565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 4239565ace46SCharles Keepax &buf->irq_count); 4240565ace46SCharles Keepax if (ret < 0) { 42410d3fba3eSCharles Keepax compr_err(buf, "Failed to get irq_count: %d\n", ret); 4242565ace46SCharles Keepax goto out; 4243565ace46SCharles Keepax } 4244565ace46SCharles Keepax 4245565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4246565ace46SCharles Keepax if (ret < 0) { 42470d3fba3eSCharles Keepax compr_err(buf, "Error reading avail: %d\n", ret); 4248565ace46SCharles Keepax goto out; 4249565ace46SCharles Keepax } 4250565ace46SCharles Keepax 425120b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 425220b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 425320b7f7c5SCharles Keepax 42545847609eSCharles Keepax out_notify: 4255c7dae7c4SCharles Keepax if (compr && compr->stream) 425683a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 42574f2d4eabSStuart Henderson } 425883a40ce9SCharles Keepax 4259565ace46SCharles Keepax out: 4260565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4261565ace46SCharles Keepax 4262565ace46SCharles Keepax return ret; 4263565ace46SCharles Keepax } 4264565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 4265565ace46SCharles Keepax 4266565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 4267565ace46SCharles Keepax { 4268565ace46SCharles Keepax if (buf->irq_count & 0x01) 4269565ace46SCharles Keepax return 0; 4270565ace46SCharles Keepax 42710d3fba3eSCharles Keepax compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); 4272565ace46SCharles Keepax 4273565ace46SCharles Keepax buf->irq_count |= 0x01; 4274565ace46SCharles Keepax 4275565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 4276565ace46SCharles Keepax buf->irq_count); 4277565ace46SCharles Keepax } 4278565ace46SCharles Keepax 42793a5ccf25SKuninori Morimoto int wm_adsp_compr_pointer(struct snd_soc_component *component, 42803a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 4281565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 4282565ace46SCharles Keepax { 4283565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 4284565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 4285612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 4286565ace46SCharles Keepax int ret = 0; 4287565ace46SCharles Keepax 42880d3fba3eSCharles Keepax compr_dbg(compr, "Pointer request\n"); 4289565ace46SCharles Keepax 4290565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 4291565ace46SCharles Keepax 4292612047f0SCharles Keepax buf = compr->buf; 4293612047f0SCharles Keepax 4294aa612f2bSCharles Keepax if (dsp->fatal_error || !buf || buf->error) { 42958d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 4296565ace46SCharles Keepax ret = -EIO; 4297565ace46SCharles Keepax goto out; 4298565ace46SCharles Keepax } 4299565ace46SCharles Keepax 4300565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 4301565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4302565ace46SCharles Keepax if (ret < 0) { 43030d3fba3eSCharles Keepax compr_err(compr, "Error reading avail: %d\n", ret); 4304565ace46SCharles Keepax goto out; 4305565ace46SCharles Keepax } 4306565ace46SCharles Keepax 4307565ace46SCharles Keepax /* 4308565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 4309565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 4310565ace46SCharles Keepax */ 4311565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 43125847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 43138d280664SCharles Keepax if (ret < 0) { 4314789b930aSCharles Keepax if (buf->error) 43158d280664SCharles Keepax snd_compr_stop_error(stream, 43168d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 43175847609eSCharles Keepax goto out; 43188d280664SCharles Keepax } 43195847609eSCharles Keepax 4320565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 4321565ace46SCharles Keepax if (ret < 0) { 43220d3fba3eSCharles Keepax compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", 4323565ace46SCharles Keepax ret); 4324565ace46SCharles Keepax goto out; 4325565ace46SCharles Keepax } 4326565ace46SCharles Keepax } 4327565ace46SCharles Keepax } 4328565ace46SCharles Keepax 4329565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 43305beb8eeaSSimon Trimmer tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; 4331da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 4332565ace46SCharles Keepax 4333565ace46SCharles Keepax out: 4334565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4335565ace46SCharles Keepax 4336565ace46SCharles Keepax return ret; 4337565ace46SCharles Keepax } 4338565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 4339565ace46SCharles Keepax 434083a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 434183a40ce9SCharles Keepax { 434283a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 434383a40ce9SCharles Keepax unsigned int adsp_addr; 434483a40ce9SCharles Keepax int mem_type, nwords, max_read; 4345cc7d6ce9SCharles Keepax int i, ret; 434683a40ce9SCharles Keepax 434783a40ce9SCharles Keepax /* Calculate read parameters */ 434883a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 434983a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 435083a40ce9SCharles Keepax break; 435183a40ce9SCharles Keepax 435283a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 435383a40ce9SCharles Keepax return -EINVAL; 435483a40ce9SCharles Keepax 435583a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 435683a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 435783a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 435883a40ce9SCharles Keepax 435983a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 436083a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 436183a40ce9SCharles Keepax 436283a40ce9SCharles Keepax if (nwords > target) 436383a40ce9SCharles Keepax nwords = target; 436483a40ce9SCharles Keepax if (nwords > buf->avail) 436583a40ce9SCharles Keepax nwords = buf->avail; 436683a40ce9SCharles Keepax if (nwords > max_read) 436783a40ce9SCharles Keepax nwords = max_read; 436883a40ce9SCharles Keepax if (!nwords) 436983a40ce9SCharles Keepax return 0; 437083a40ce9SCharles Keepax 437183a40ce9SCharles Keepax /* Read data from DSP */ 43725beb8eeaSSimon Trimmer ret = cs_dsp_read_raw_data_block(buf->dsp, mem_type, adsp_addr, 4373a0b653e8SRichard Fitzgerald nwords, (__be32 *)compr->raw_buf); 437483a40ce9SCharles Keepax if (ret < 0) 437583a40ce9SCharles Keepax return ret; 437683a40ce9SCharles Keepax 43775beb8eeaSSimon Trimmer cs_dsp_remove_padding(compr->raw_buf, nwords); 437883a40ce9SCharles Keepax 437983a40ce9SCharles Keepax /* update read index to account for words read */ 438083a40ce9SCharles Keepax buf->read_index += nwords; 438183a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 438283a40ce9SCharles Keepax buf->read_index = 0; 438383a40ce9SCharles Keepax 438483a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 438583a40ce9SCharles Keepax buf->read_index); 438683a40ce9SCharles Keepax if (ret < 0) 438783a40ce9SCharles Keepax return ret; 438883a40ce9SCharles Keepax 438983a40ce9SCharles Keepax /* update avail to account for words read */ 439083a40ce9SCharles Keepax buf->avail -= nwords; 439183a40ce9SCharles Keepax 439283a40ce9SCharles Keepax return nwords; 439383a40ce9SCharles Keepax } 439483a40ce9SCharles Keepax 439583a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 439683a40ce9SCharles Keepax char __user *buf, size_t count) 439783a40ce9SCharles Keepax { 4398aa612f2bSCharles Keepax struct wm_adsp *dsp = compr->dsp; 439983a40ce9SCharles Keepax int ntotal = 0; 440083a40ce9SCharles Keepax int nwords, nbytes; 440183a40ce9SCharles Keepax 44020d3fba3eSCharles Keepax compr_dbg(compr, "Requested read of %zu bytes\n", count); 440383a40ce9SCharles Keepax 4404aa612f2bSCharles Keepax if (dsp->fatal_error || !compr->buf || compr->buf->error) { 44058d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 440683a40ce9SCharles Keepax return -EIO; 44078d280664SCharles Keepax } 440883a40ce9SCharles Keepax 44095beb8eeaSSimon Trimmer count /= CS_DSP_DATA_WORD_SIZE; 441083a40ce9SCharles Keepax 441183a40ce9SCharles Keepax do { 441283a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 441383a40ce9SCharles Keepax if (nwords < 0) { 44140d3fba3eSCharles Keepax compr_err(compr, "Failed to capture block: %d\n", 44150d3fba3eSCharles Keepax nwords); 441683a40ce9SCharles Keepax return nwords; 441783a40ce9SCharles Keepax } 441883a40ce9SCharles Keepax 44195beb8eeaSSimon Trimmer nbytes = nwords * CS_DSP_DATA_WORD_SIZE; 442083a40ce9SCharles Keepax 44210d3fba3eSCharles Keepax compr_dbg(compr, "Read %d bytes\n", nbytes); 442283a40ce9SCharles Keepax 442383a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 44240d3fba3eSCharles Keepax compr_err(compr, "Failed to copy data to user: %d, %d\n", 442583a40ce9SCharles Keepax ntotal, nbytes); 442683a40ce9SCharles Keepax return -EFAULT; 442783a40ce9SCharles Keepax } 442883a40ce9SCharles Keepax 442983a40ce9SCharles Keepax count -= nwords; 443083a40ce9SCharles Keepax ntotal += nbytes; 443183a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 443283a40ce9SCharles Keepax 443383a40ce9SCharles Keepax compr->copied_total += ntotal; 443483a40ce9SCharles Keepax 443583a40ce9SCharles Keepax return ntotal; 443683a40ce9SCharles Keepax } 443783a40ce9SCharles Keepax 44383a5ccf25SKuninori Morimoto int wm_adsp_compr_copy(struct snd_soc_component *component, 44393a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, char __user *buf, 444083a40ce9SCharles Keepax size_t count) 444183a40ce9SCharles Keepax { 444283a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 444383a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 444483a40ce9SCharles Keepax int ret; 444583a40ce9SCharles Keepax 444683a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 444783a40ce9SCharles Keepax 444883a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 444983a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 445083a40ce9SCharles Keepax else 445183a40ce9SCharles Keepax ret = -ENOTSUPP; 445283a40ce9SCharles Keepax 445383a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 445483a40ce9SCharles Keepax 445583a40ce9SCharles Keepax return ret; 445683a40ce9SCharles Keepax } 445783a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 445883a40ce9SCharles Keepax 4459a2bcbc1bSCharles Keepax static void wm_adsp_fatal_error(struct wm_adsp *dsp) 4460a2bcbc1bSCharles Keepax { 4461a2bcbc1bSCharles Keepax struct wm_adsp_compr *compr; 4462a2bcbc1bSCharles Keepax 4463a2bcbc1bSCharles Keepax dsp->fatal_error = true; 4464a2bcbc1bSCharles Keepax 4465a2bcbc1bSCharles Keepax list_for_each_entry(compr, &dsp->compr_list, list) { 4466aa612f2bSCharles Keepax if (compr->stream) 4467a2bcbc1bSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 4468a2bcbc1bSCharles Keepax } 4469a2bcbc1bSCharles Keepax } 4470a2bcbc1bSCharles Keepax 447125ca837bSSimon Trimmer static void cs_dsp_adsp2_bus_error(struct wm_adsp *dsp) 447251a2c944SMayuresh Kulkarni { 447351a2c944SMayuresh Kulkarni unsigned int val; 447451a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 447551a2c944SMayuresh Kulkarni int ret = 0; 447651a2c944SMayuresh Kulkarni 4477a2225a6dSCharles Keepax mutex_lock(&dsp->pwr_lock); 4478a2225a6dSCharles Keepax 447951a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 448051a2c944SMayuresh Kulkarni if (ret) { 44816ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 448251a2c944SMayuresh Kulkarni "Failed to read Region Lock Ctrl register: %d\n", ret); 4483a2225a6dSCharles Keepax goto error; 448451a2c944SMayuresh Kulkarni } 448551a2c944SMayuresh Kulkarni 448651a2c944SMayuresh Kulkarni if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 44876ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "watchdog timeout error\n"); 448881ed8845SCharles Keepax dsp->ops->stop_watchdog(dsp); 4489a2bcbc1bSCharles Keepax wm_adsp_fatal_error(dsp); 449051a2c944SMayuresh Kulkarni } 449151a2c944SMayuresh Kulkarni 4492a4d328efSCharles Keepax if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 4493a4d328efSCharles Keepax if (val & ADSP2_ADDR_ERR_MASK) 44946ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "bus error: address error\n"); 449551a2c944SMayuresh Kulkarni else 44966ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "bus error: region lock error\n"); 449751a2c944SMayuresh Kulkarni 449851a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 449951a2c944SMayuresh Kulkarni if (ret) { 45006ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 450151a2c944SMayuresh Kulkarni "Failed to read Bus Err Addr register: %d\n", 450251a2c944SMayuresh Kulkarni ret); 4503a2225a6dSCharles Keepax goto error; 450451a2c944SMayuresh Kulkarni } 450551a2c944SMayuresh Kulkarni 45066ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "bus error address = 0x%x\n", 450751a2c944SMayuresh Kulkarni val & ADSP2_BUS_ERR_ADDR_MASK); 450851a2c944SMayuresh Kulkarni 450951a2c944SMayuresh Kulkarni ret = regmap_read(regmap, 451051a2c944SMayuresh Kulkarni dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 451151a2c944SMayuresh Kulkarni &val); 451251a2c944SMayuresh Kulkarni if (ret) { 45136ab1d0ccSSimon Trimmer cs_dsp_err(dsp, 451451a2c944SMayuresh Kulkarni "Failed to read Pmem Xmem Err Addr register: %d\n", 451551a2c944SMayuresh Kulkarni ret); 4516a2225a6dSCharles Keepax goto error; 451751a2c944SMayuresh Kulkarni } 451851a2c944SMayuresh Kulkarni 45196ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "xmem error address = 0x%x\n", 452051a2c944SMayuresh Kulkarni val & ADSP2_XMEM_ERR_ADDR_MASK); 45216ab1d0ccSSimon Trimmer cs_dsp_err(dsp, "pmem error address = 0x%x\n", 452251a2c944SMayuresh Kulkarni (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 452351a2c944SMayuresh Kulkarni ADSP2_PMEM_ERR_ADDR_SHIFT); 452451a2c944SMayuresh Kulkarni } 452551a2c944SMayuresh Kulkarni 452651a2c944SMayuresh Kulkarni regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 452751a2c944SMayuresh Kulkarni ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 452851a2c944SMayuresh Kulkarni 4529a2225a6dSCharles Keepax error: 4530a2225a6dSCharles Keepax mutex_unlock(&dsp->pwr_lock); 453125ca837bSSimon Trimmer } 453225ca837bSSimon Trimmer 453325ca837bSSimon Trimmer irqreturn_t wm_adsp2_bus_error(int irq, void *data) 453425ca837bSSimon Trimmer { 453525ca837bSSimon Trimmer struct wm_adsp *dsp = (struct wm_adsp *)data; 453625ca837bSSimon Trimmer 453725ca837bSSimon Trimmer cs_dsp_adsp2_bus_error(dsp); 4538a2225a6dSCharles Keepax 453951a2c944SMayuresh Kulkarni return IRQ_HANDLED; 454051a2c944SMayuresh Kulkarni } 454151a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 454251a2c944SMayuresh Kulkarni 454325ca837bSSimon Trimmer static void cs_dsp_halo_bus_error(struct wm_adsp *dsp) 45442ae58138SRichard Fitzgerald { 45452ae58138SRichard Fitzgerald struct regmap *regmap = dsp->regmap; 45462ae58138SRichard Fitzgerald unsigned int fault[6]; 45472ae58138SRichard Fitzgerald struct reg_sequence clear[] = { 45482ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 45492ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 45502ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 45512ae58138SRichard Fitzgerald }; 45522ae58138SRichard Fitzgerald int ret; 45532ae58138SRichard Fitzgerald 45542ae58138SRichard Fitzgerald mutex_lock(&dsp->pwr_lock); 45552ae58138SRichard Fitzgerald 45562ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 45572ae58138SRichard Fitzgerald fault); 45582ae58138SRichard Fitzgerald if (ret) { 45596ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 45602ae58138SRichard Fitzgerald goto exit_unlock; 45612ae58138SRichard Fitzgerald } 45622ae58138SRichard Fitzgerald 45636ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 45642ae58138SRichard Fitzgerald *fault & HALO_AHBM_FLAGS_ERR_MASK, 45652ae58138SRichard Fitzgerald (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 45662ae58138SRichard Fitzgerald HALO_AHBM_CORE_ERR_ADDR_SHIFT); 45672ae58138SRichard Fitzgerald 45682ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 45692ae58138SRichard Fitzgerald fault); 45702ae58138SRichard Fitzgerald if (ret) { 45716ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 45722ae58138SRichard Fitzgerald goto exit_unlock; 45732ae58138SRichard Fitzgerald } 45742ae58138SRichard Fitzgerald 45756ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 45762ae58138SRichard Fitzgerald 45772ae58138SRichard Fitzgerald ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 45782ae58138SRichard Fitzgerald fault, ARRAY_SIZE(fault)); 45792ae58138SRichard Fitzgerald if (ret) { 45806ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 45812ae58138SRichard Fitzgerald goto exit_unlock; 45822ae58138SRichard Fitzgerald } 45832ae58138SRichard Fitzgerald 45846ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 45856ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 45866ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 45872ae58138SRichard Fitzgerald 45882ae58138SRichard Fitzgerald ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 45892ae58138SRichard Fitzgerald if (ret) 45906ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 45912ae58138SRichard Fitzgerald 45922ae58138SRichard Fitzgerald exit_unlock: 45932ae58138SRichard Fitzgerald mutex_unlock(&dsp->pwr_lock); 459425ca837bSSimon Trimmer } 459525ca837bSSimon Trimmer 459625ca837bSSimon Trimmer irqreturn_t wm_halo_bus_error(int irq, void *data) 459725ca837bSSimon Trimmer { 459825ca837bSSimon Trimmer struct wm_adsp *dsp = (struct wm_adsp *)data; 459925ca837bSSimon Trimmer 460025ca837bSSimon Trimmer cs_dsp_halo_bus_error(dsp); 46012ae58138SRichard Fitzgerald 46022ae58138SRichard Fitzgerald return IRQ_HANDLED; 46032ae58138SRichard Fitzgerald } 46042ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error); 46052ae58138SRichard Fitzgerald 460625ca837bSSimon Trimmer static void cs_dsp_halo_wdt_expire(struct wm_adsp *dsp) 46078bc144f9SStuart Henderson { 46088bc144f9SStuart Henderson mutex_lock(&dsp->pwr_lock); 46098bc144f9SStuart Henderson 46106ab1d0ccSSimon Trimmer cs_dsp_warn(dsp, "WDT Expiry Fault\n"); 46116ab1d0ccSSimon Trimmer 461281ed8845SCharles Keepax dsp->ops->stop_watchdog(dsp); 46138bc144f9SStuart Henderson wm_adsp_fatal_error(dsp); 46148bc144f9SStuart Henderson 46158bc144f9SStuart Henderson mutex_unlock(&dsp->pwr_lock); 461625ca837bSSimon Trimmer } 461725ca837bSSimon Trimmer 461825ca837bSSimon Trimmer irqreturn_t wm_halo_wdt_expire(int irq, void *data) 461925ca837bSSimon Trimmer { 462025ca837bSSimon Trimmer struct wm_adsp *dsp = data; 462125ca837bSSimon Trimmer 462225ca837bSSimon Trimmer cs_dsp_halo_wdt_expire(dsp); 46238bc144f9SStuart Henderson 46248bc144f9SStuart Henderson return IRQ_HANDLED; 46258bc144f9SStuart Henderson } 46268bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); 46278bc144f9SStuart Henderson 46285beb8eeaSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp1_ops = { 46295beb8eeaSSimon Trimmer .validate_version = cs_dsp_validate_version, 46305beb8eeaSSimon Trimmer .parse_sizes = cs_dsp_adsp1_parse_sizes, 46315beb8eeaSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 46324e08d50dSCharles Keepax }; 46334e08d50dSCharles Keepax 46345beb8eeaSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp2_ops[] = { 46354e08d50dSCharles Keepax { 46365beb8eeaSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 46375beb8eeaSSimon Trimmer .validate_version = cs_dsp_validate_version, 46385beb8eeaSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 46395beb8eeaSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 46404e08d50dSCharles Keepax 46415beb8eeaSSimon Trimmer .show_fw_status = cs_dsp_adsp2_show_fw_status, 46424e08d50dSCharles Keepax 46435beb8eeaSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 46445beb8eeaSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 46454e08d50dSCharles Keepax 46465beb8eeaSSimon Trimmer .enable_core = cs_dsp_adsp2_enable_core, 46475beb8eeaSSimon Trimmer .disable_core = cs_dsp_adsp2_disable_core, 46484e08d50dSCharles Keepax 46495beb8eeaSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 46505beb8eeaSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 46514e08d50dSCharles Keepax 46524e08d50dSCharles Keepax }, 46534e08d50dSCharles Keepax { 46545beb8eeaSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 46555beb8eeaSSimon Trimmer .validate_version = cs_dsp_validate_version, 46565beb8eeaSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 46575beb8eeaSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 46584e08d50dSCharles Keepax 46595beb8eeaSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 46604e08d50dSCharles Keepax 46615beb8eeaSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 46625beb8eeaSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 46635beb8eeaSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 46644e08d50dSCharles Keepax 46655beb8eeaSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 46665beb8eeaSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 46674e08d50dSCharles Keepax 46685beb8eeaSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 46695beb8eeaSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 46704e08d50dSCharles Keepax }, 46714e08d50dSCharles Keepax { 46725beb8eeaSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 46735beb8eeaSSimon Trimmer .validate_version = cs_dsp_validate_version, 46745beb8eeaSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 46755beb8eeaSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 46764e08d50dSCharles Keepax 46775beb8eeaSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 46785beb8eeaSSimon Trimmer .stop_watchdog = cs_dsp_stop_watchdog, 46794e08d50dSCharles Keepax 46805beb8eeaSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 46815beb8eeaSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 46825beb8eeaSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 46834e08d50dSCharles Keepax 46845beb8eeaSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 46855beb8eeaSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 46864e08d50dSCharles Keepax 46875beb8eeaSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 46885beb8eeaSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 46894e08d50dSCharles Keepax }, 46904e08d50dSCharles Keepax }; 46914e08d50dSCharles Keepax 46925beb8eeaSSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ops = { 46935beb8eeaSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 46945beb8eeaSSimon Trimmer .validate_version = cs_dsp_halo_validate_version, 46955beb8eeaSSimon Trimmer .setup_algs = cs_dsp_halo_setup_algs, 46965beb8eeaSSimon Trimmer .region_to_reg = cs_dsp_halo_region_to_reg, 4697170b1e12SWen Shi 46985beb8eeaSSimon Trimmer .show_fw_status = cs_dsp_halo_show_fw_status, 46995beb8eeaSSimon Trimmer .stop_watchdog = cs_dsp_halo_stop_watchdog, 4700170b1e12SWen Shi 47015beb8eeaSSimon Trimmer .lock_memory = cs_dsp_halo_configure_mpu, 4702170b1e12SWen Shi 47035beb8eeaSSimon Trimmer .start_core = cs_dsp_halo_start_core, 47045beb8eeaSSimon Trimmer .stop_core = cs_dsp_halo_stop_core, 4705170b1e12SWen Shi }; 4706170b1e12SWen Shi 47070a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 4708