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 460d3fba3eSCharles Keepax #define compr_err(_obj, fmt, ...) \ 470d3fba3eSCharles Keepax adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 480d3fba3eSCharles Keepax ##__VA_ARGS__) 490d3fba3eSCharles Keepax #define compr_dbg(_obj, fmt, ...) \ 500d3fba3eSCharles Keepax adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 510d3fba3eSCharles Keepax ##__VA_ARGS__) 520d3fba3eSCharles Keepax 532159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 542159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 552159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 562159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 572159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 582159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 592159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 602159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 612159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 622159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 632159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 642159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 652159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 662159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 672159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 682159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 692159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 702159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 712159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 722159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 732159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 742159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 752159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 762159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 772159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 782159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 792159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 802159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 812159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 822159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 832159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 842159ad93SMark Brown 852159ad93SMark Brown /* 862159ad93SMark Brown * ADSP1 Control 19 872159ad93SMark Brown */ 882159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 892159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 902159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 912159ad93SMark Brown 922159ad93SMark Brown 932159ad93SMark Brown /* 942159ad93SMark Brown * ADSP1 Control 30 952159ad93SMark Brown */ 962159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 972159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 982159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 992159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 1002159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1012159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1022159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1032159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1042159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1052159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1062159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1072159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1082159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1092159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1102159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1112159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1122159ad93SMark Brown 11394e205bfSChris Rattray /* 11494e205bfSChris Rattray * ADSP1 Control 31 11594e205bfSChris Rattray */ 11694e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11794e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11894e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11994e205bfSChris Rattray 1202d30b575SMark Brown #define ADSP2_CONTROL 0x0 1212d30b575SMark Brown #define ADSP2_CLOCKING 0x1 122e1ea1879SRichard Fitzgerald #define ADSP2V2_CLOCKING 0x2 1232d30b575SMark Brown #define ADSP2_STATUS1 0x4 1242d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1252d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 126e1ea1879SRichard Fitzgerald #define ADSP2V2_WDMA_CONFIG_2 0x32 1272d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1282159ad93SMark Brown 12910337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 13010337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 13110337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 13210337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 13310337b07SRichard Fitzgerald 134e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH0_1 0x40 135e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH2_3 0x42 136e1ea1879SRichard Fitzgerald 1372159ad93SMark Brown /* 1382159ad93SMark Brown * ADSP2 Control 1392159ad93SMark Brown */ 1402159ad93SMark Brown 1412159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1422159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1432159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1442159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1452159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1462159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1472159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1482159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1492159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1502159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1512159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1522159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1532159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1542159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1552159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1562159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1572159ad93SMark Brown 1582159ad93SMark Brown /* 159973838a0SMark Brown * ADSP2 clocking 160973838a0SMark Brown */ 161973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 162973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 163973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 164973838a0SMark Brown 165973838a0SMark Brown /* 166e1ea1879SRichard Fitzgerald * ADSP2V2 clocking 167e1ea1879SRichard Fitzgerald */ 168e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 169e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 170e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 171e1ea1879SRichard Fitzgerald 172e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 173e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 174e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 175e1ea1879SRichard Fitzgerald 176e1ea1879SRichard Fitzgerald /* 1772159ad93SMark Brown * ADSP2 Status 1 1782159ad93SMark Brown */ 1792159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1802159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1812159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1822159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1832159ad93SMark Brown 18451a2c944SMayuresh Kulkarni /* 18551a2c944SMayuresh Kulkarni * ADSP2 Lock support 18651a2c944SMayuresh Kulkarni */ 18751a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_0 0x5555 18851a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_1 0xAAAA 18951a2c944SMayuresh Kulkarni 19051a2c944SMayuresh Kulkarni #define ADSP2_WATCHDOG 0x0A 19151a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR 0x52 19251a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_STATUS 0x64 19351a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 19451a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 19551a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 19651a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 19751a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 19851a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_CTRL 0x7A 19951a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 20051a2c944SMayuresh Kulkarni 20151a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 202a4d328efSCharles Keepax #define ADSP2_ADDR_ERR_MASK 0x4000 20351a2c944SMayuresh Kulkarni #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 20451a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 20551a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_EINT 0x0001 20651a2c944SMayuresh Kulkarni 20751a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 20851a2c944SMayuresh Kulkarni #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 20951a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 21051a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 21151a2c944SMayuresh Kulkarni #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 21251a2c944SMayuresh Kulkarni 21351a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_SHIFT 16 21451a2c944SMayuresh Kulkarni 2159ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 2169ee78757SCharles Keepax 217f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 218f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 219a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MIN_VALUE 0 220a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 221f4f0c4c6SRichard Fitzgerald 222f4f0c4c6SRichard Fitzgerald /* 223f4f0c4c6SRichard Fitzgerald * Event control messages 224f4f0c4c6SRichard Fitzgerald */ 225f4f0c4c6SRichard Fitzgerald #define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 226f4f0c4c6SRichard Fitzgerald 227170b1e12SWen Shi /* 2282ae58138SRichard Fitzgerald * HALO system info 2292ae58138SRichard Fitzgerald */ 2302ae58138SRichard Fitzgerald #define HALO_AHBM_WINDOW_DEBUG_0 0x02040 2312ae58138SRichard Fitzgerald #define HALO_AHBM_WINDOW_DEBUG_1 0x02044 2322ae58138SRichard Fitzgerald 2332ae58138SRichard Fitzgerald /* 234170b1e12SWen Shi * HALO core 235170b1e12SWen Shi */ 236170b1e12SWen Shi #define HALO_SCRATCH1 0x005c0 237170b1e12SWen Shi #define HALO_SCRATCH2 0x005c8 238170b1e12SWen Shi #define HALO_SCRATCH3 0x005d0 239170b1e12SWen Shi #define HALO_SCRATCH4 0x005d8 240170b1e12SWen Shi #define HALO_CCM_CORE_CONTROL 0x41000 241170b1e12SWen Shi #define HALO_CORE_SOFT_RESET 0x00010 2428bc144f9SStuart Henderson #define HALO_WDT_CONTROL 0x47000 243170b1e12SWen Shi 244170b1e12SWen Shi /* 245170b1e12SWen Shi * HALO MPU banks 246170b1e12SWen Shi */ 247170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_0 0x43000 248170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_0 0x43004 249170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_0 0x43008 250170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_0 0x4300C 251170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_0 0x43014 252170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_1 0x43018 253170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_1 0x4301C 254170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_1 0x43020 255170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_1 0x43024 256170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_1 0x4302C 257170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_2 0x43030 258170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_2 0x43034 259170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_2 0x43038 260170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_2 0x4303C 261170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_2 0x43044 262170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_3 0x43048 263170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_3 0x4304C 264170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_3 0x43050 265170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_3 0x43054 266170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_3 0x4305C 2672ae58138SRichard Fitzgerald #define HALO_MPU_XM_VIO_ADDR 0x43100 2682ae58138SRichard Fitzgerald #define HALO_MPU_XM_VIO_STATUS 0x43104 2692ae58138SRichard Fitzgerald #define HALO_MPU_YM_VIO_ADDR 0x43108 2702ae58138SRichard Fitzgerald #define HALO_MPU_YM_VIO_STATUS 0x4310C 2712ae58138SRichard Fitzgerald #define HALO_MPU_PM_VIO_ADDR 0x43110 2722ae58138SRichard Fitzgerald #define HALO_MPU_PM_VIO_STATUS 0x43114 273170b1e12SWen Shi #define HALO_MPU_LOCK_CONFIG 0x43140 274170b1e12SWen Shi 275170b1e12SWen Shi /* 2762ae58138SRichard Fitzgerald * HALO_AHBM_WINDOW_DEBUG_1 2772ae58138SRichard Fitzgerald */ 2782ae58138SRichard Fitzgerald #define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 2792ae58138SRichard Fitzgerald #define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 2802ae58138SRichard Fitzgerald #define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff 2812ae58138SRichard Fitzgerald 2822ae58138SRichard Fitzgerald /* 283170b1e12SWen Shi * HALO_CCM_CORE_CONTROL 284170b1e12SWen Shi */ 285170b1e12SWen Shi #define HALO_CORE_EN 0x00000001 286170b1e12SWen Shi 287170b1e12SWen Shi /* 288170b1e12SWen Shi * HALO_CORE_SOFT_RESET 289170b1e12SWen Shi */ 290170b1e12SWen Shi #define HALO_CORE_SOFT_RESET_MASK 0x00000001 291170b1e12SWen Shi 2922ae58138SRichard Fitzgerald /* 2938bc144f9SStuart Henderson * HALO_WDT_CONTROL 2948bc144f9SStuart Henderson */ 2958bc144f9SStuart Henderson #define HALO_WDT_EN_MASK 0x00000001 2968bc144f9SStuart Henderson 2978bc144f9SStuart Henderson /* 2982ae58138SRichard Fitzgerald * HALO_MPU_?M_VIO_STATUS 2992ae58138SRichard Fitzgerald */ 3002ae58138SRichard Fitzgerald #define HALO_MPU_VIO_STS_MASK 0x007e0000 3012ae58138SRichard Fitzgerald #define HALO_MPU_VIO_STS_SHIFT 17 3022ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 3032ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff 3042ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 3052ae58138SRichard Fitzgerald 306cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp1_ops; 307cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp2_ops[]; 308cd537873SCharles Keepax static struct wm_adsp_ops wm_halo_ops; 3094e08d50dSCharles Keepax 310cf17c83cSMark Brown struct wm_adsp_buf { 311cf17c83cSMark Brown struct list_head list; 312cf17c83cSMark Brown void *buf; 313cf17c83cSMark Brown }; 314cf17c83cSMark Brown 315cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 316cf17c83cSMark Brown struct list_head *list) 317cf17c83cSMark Brown { 318cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 319cf17c83cSMark Brown 320cf17c83cSMark Brown if (buf == NULL) 321cf17c83cSMark Brown return NULL; 322cf17c83cSMark Brown 323cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 324cf17c83cSMark Brown if (!buf->buf) { 3254d41c74dSRichard Fitzgerald kfree(buf); 326cf17c83cSMark Brown return NULL; 327cf17c83cSMark Brown } 328cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 329cf17c83cSMark Brown 330cf17c83cSMark Brown if (list) 331cf17c83cSMark Brown list_add_tail(&buf->list, list); 332cf17c83cSMark Brown 333cf17c83cSMark Brown return buf; 334cf17c83cSMark Brown } 335cf17c83cSMark Brown 336cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 337cf17c83cSMark Brown { 338cf17c83cSMark Brown while (!list_empty(list)) { 339cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 340cf17c83cSMark Brown struct wm_adsp_buf, 341cf17c83cSMark Brown list); 342cf17c83cSMark Brown list_del(&buf->list); 343cdcd7f72SCharles Keepax vfree(buf->buf); 344cf17c83cSMark Brown kfree(buf); 345cf17c83cSMark Brown } 346cf17c83cSMark Brown } 347cf17c83cSMark Brown 348dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 34904d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 35004d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 35104d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 35204d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 35304d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 35404d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 35504d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 35604d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 35704d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 358d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_CALI 10 359d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_DIAG 11 360d6fea46eSVlad Karpovich #define WM_ADSP_FW_MISC 12 36104d1300fSCharles Keepax 362d6fea46eSVlad Karpovich #define WM_ADSP_NUM_FW 13 363dd84f925SMark Brown 3641023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 365dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 36604d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 367dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 368dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 36904d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 370dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 37104d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 37204d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 37304d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 37404d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 375d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_CALI] = "Calibration", 376d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_DIAG] = "Diagnostic", 37704d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 3781023dbd9SMark Brown }; 3791023dbd9SMark Brown 3802cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 3812cd19bdbSCharles Keepax __be32 sys_enable; 3822cd19bdbSCharles Keepax __be32 fw_id; 3832cd19bdbSCharles Keepax __be32 fw_rev; 3842cd19bdbSCharles Keepax __be32 boot_status; 3852cd19bdbSCharles Keepax __be32 watchdog; 3862cd19bdbSCharles Keepax __be32 dma_buffer_size; 3872cd19bdbSCharles Keepax __be32 rdma[6]; 3882cd19bdbSCharles Keepax __be32 wdma[8]; 3892cd19bdbSCharles Keepax __be32 build_job_name[3]; 3902cd19bdbSCharles Keepax __be32 build_job_number; 3912cd19bdbSCharles Keepax }; 3922cd19bdbSCharles Keepax 393170b1e12SWen Shi struct wm_halo_system_config_xm_hdr { 394170b1e12SWen Shi __be32 halo_heartbeat; 395170b1e12SWen Shi __be32 build_job_name[3]; 396170b1e12SWen Shi __be32 build_job_number; 397170b1e12SWen Shi }; 398170b1e12SWen Shi 3992cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 4002cd19bdbSCharles Keepax __be32 magic; 4012cd19bdbSCharles Keepax __be32 smoothing; 4022cd19bdbSCharles Keepax __be32 threshold; 4032cd19bdbSCharles Keepax __be32 host_buf_ptr; 4042cd19bdbSCharles Keepax __be32 start_seq; 4052cd19bdbSCharles Keepax __be32 high_water_mark; 4062cd19bdbSCharles Keepax __be32 low_water_mark; 4072cd19bdbSCharles Keepax __be64 smoothed_power; 4082cd19bdbSCharles Keepax }; 4092cd19bdbSCharles Keepax 4104f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 { 4114f2d4eabSStuart Henderson __be32 host_buf_ptr; /* Host buffer pointer */ 4124f2d4eabSStuart Henderson __be32 versions; /* Version numbers */ 4134f2d4eabSStuart Henderson __be32 name[4]; /* The buffer name */ 4144f2d4eabSStuart Henderson }; 4154f2d4eabSStuart Henderson 4162cd19bdbSCharles Keepax struct wm_adsp_buffer { 4172a2aefa4SRichard Fitzgerald __be32 buf1_base; /* Base addr of first buffer area */ 4182a2aefa4SRichard Fitzgerald __be32 buf1_size; /* Size of buf1 area in DSP words */ 4192a2aefa4SRichard Fitzgerald __be32 buf2_base; /* Base addr of 2nd buffer area */ 4202a2aefa4SRichard Fitzgerald __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 4212a2aefa4SRichard Fitzgerald __be32 buf3_base; /* Base addr of buf3 area */ 4222a2aefa4SRichard Fitzgerald __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 4232cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 4242cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 4252cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 4262cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 4272cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 4282cd19bdbSCharles Keepax __be32 error; /* error if any */ 4292cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 4302cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 4312cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 4322cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 4332cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 4342cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 4352cd19bdbSCharles Keepax }; 4362cd19bdbSCharles Keepax 437721be3beSCharles Keepax struct wm_adsp_compr; 438721be3beSCharles Keepax 4392cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 4404f2d4eabSStuart Henderson struct list_head list; 4412cd19bdbSCharles Keepax struct wm_adsp *dsp; 442721be3beSCharles Keepax struct wm_adsp_compr *compr; 4432cd19bdbSCharles Keepax 4442cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 4452cd19bdbSCharles Keepax u32 host_buf_ptr; 446565ace46SCharles Keepax 447565ace46SCharles Keepax u32 error; 448565ace46SCharles Keepax u32 irq_count; 449565ace46SCharles Keepax int read_index; 450565ace46SCharles Keepax int avail; 451fb13f19dSAndrew Ford int host_buf_mem_type; 4524f2d4eabSStuart Henderson 4534f2d4eabSStuart Henderson char *name; 4542cd19bdbSCharles Keepax }; 4552cd19bdbSCharles Keepax 456406abc95SCharles Keepax struct wm_adsp_compr { 4574f2d4eabSStuart Henderson struct list_head list; 458406abc95SCharles Keepax struct wm_adsp *dsp; 45995fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 460406abc95SCharles Keepax 461406abc95SCharles Keepax struct snd_compr_stream *stream; 462406abc95SCharles Keepax struct snd_compressed_buffer size; 463565ace46SCharles Keepax 46483a40ce9SCharles Keepax u32 *raw_buf; 465565ace46SCharles Keepax unsigned int copied_total; 466da2b3358SCharles Keepax 467da2b3358SCharles Keepax unsigned int sample_rate; 4684f2d4eabSStuart Henderson 4694f2d4eabSStuart Henderson const char *name; 470406abc95SCharles Keepax }; 471406abc95SCharles Keepax 472406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 473406abc95SCharles Keepax 474406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 475406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 476406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 477406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 478406abc95SCharles Keepax 4792cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 4802cd19bdbSCharles Keepax 4812cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 4822cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 4832cd19bdbSCharles Keepax 4842cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 4852cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 4862cd19bdbSCharles Keepax 4874f2d4eabSStuart Henderson #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 4884f2d4eabSStuart Henderson 4894f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 4904f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 4914f2d4eabSStuart Henderson 4922cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 4932cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 4942cd19bdbSCharles Keepax 4952cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 4962cd19bdbSCharles Keepax unsigned int offset; 4972cd19bdbSCharles Keepax unsigned int cumulative_size; 4982cd19bdbSCharles Keepax unsigned int mem_type; 4992cd19bdbSCharles Keepax unsigned int base_addr; 5002cd19bdbSCharles Keepax }; 5012cd19bdbSCharles Keepax 5022cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 5032cd19bdbSCharles Keepax unsigned int mem_type; 5042cd19bdbSCharles Keepax unsigned int base_offset; 5052cd19bdbSCharles Keepax unsigned int size_offset; 5062cd19bdbSCharles Keepax }; 5072cd19bdbSCharles Keepax 5083a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 5092cd19bdbSCharles Keepax { 5102cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5112a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf1_base), 5122a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_size), 5132cd19bdbSCharles Keepax }, 5142cd19bdbSCharles Keepax { 5152cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5162a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf2_base), 5172a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 5182cd19bdbSCharles Keepax }, 5192cd19bdbSCharles Keepax { 5202cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 5212a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf3_base), 5222a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf_total_size), 5232cd19bdbSCharles Keepax }, 5242cd19bdbSCharles Keepax }; 5252cd19bdbSCharles Keepax 526406abc95SCharles Keepax struct wm_adsp_fw_caps { 527406abc95SCharles Keepax u32 id; 528406abc95SCharles Keepax struct snd_codec_desc desc; 5292cd19bdbSCharles Keepax int num_regions; 5303a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 531406abc95SCharles Keepax }; 532406abc95SCharles Keepax 533e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 534406abc95SCharles Keepax { 535406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 536406abc95SCharles Keepax .desc = { 5373bbc2705SRichard Fitzgerald .max_ch = 8, 538406abc95SCharles Keepax .sample_rates = { 16000 }, 539406abc95SCharles Keepax .num_sample_rates = 1, 540406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 541406abc95SCharles Keepax }, 542e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 543e6d00f34SCharles Keepax .region_defs = default_regions, 544406abc95SCharles Keepax }, 545406abc95SCharles Keepax }; 546406abc95SCharles Keepax 5477ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 5487ce4283cSCharles Keepax { 5497ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 5507ce4283cSCharles Keepax .desc = { 5517ce4283cSCharles Keepax .max_ch = 8, 5527ce4283cSCharles Keepax .sample_rates = { 5537ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 5547ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 5557ce4283cSCharles Keepax 96000, 176400, 192000 5567ce4283cSCharles Keepax }, 5577ce4283cSCharles Keepax .num_sample_rates = 15, 5587ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 5597ce4283cSCharles Keepax }, 5607ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 5617ce4283cSCharles Keepax .region_defs = default_regions, 562406abc95SCharles Keepax }, 563406abc95SCharles Keepax }; 564406abc95SCharles Keepax 565406abc95SCharles Keepax static const struct { 5661023dbd9SMark Brown const char *file; 567406abc95SCharles Keepax int compr_direction; 568406abc95SCharles Keepax int num_caps; 569406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 57020b7f7c5SCharles Keepax bool voice_trigger; 5711023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 572dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 57304d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 574dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 575dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 57604d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 577dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 578406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 579406abc95SCharles Keepax .file = "ctrl", 580406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 581e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 582e6d00f34SCharles Keepax .caps = ctrl_caps, 58320b7f7c5SCharles Keepax .voice_trigger = true, 584406abc95SCharles Keepax }, 58504d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 5867ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 5877ce4283cSCharles Keepax .file = "trace", 5887ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 5897ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 5907ce4283cSCharles Keepax .caps = trace_caps, 5917ce4283cSCharles Keepax }, 59204d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 593d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" }, 594d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" }, 59504d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 5961023dbd9SMark Brown }; 5971023dbd9SMark Brown 5986ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 5996ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 6006ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 6016ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 6026ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 6036ab2b7b4SDimitris Papastamos }; 6046ab2b7b4SDimitris Papastamos 6056ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 6066ab2b7b4SDimitris Papastamos const char *name; 6072323736dSCharles Keepax const char *fw_name; 608eb65ccdbSLi Xu /* Subname is needed to match with firmware */ 609eb65ccdbSLi Xu const char *subname; 610eb65ccdbSLi Xu unsigned int subname_len; 6113809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 6126ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 6133809f001SCharles Keepax struct wm_adsp *dsp; 6146ab2b7b4SDimitris Papastamos unsigned int enabled:1; 6156ab2b7b4SDimitris Papastamos struct list_head list; 6166ab2b7b4SDimitris Papastamos void *cache; 6172323736dSCharles Keepax unsigned int offset; 6186ab2b7b4SDimitris Papastamos size_t len; 6190c2e3f34SDimitris Papastamos unsigned int set:1; 6209ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 62126c22a19SCharles Keepax unsigned int flags; 622*f6212e0aSRichard Fitzgerald snd_ctl_elem_type_t type; 6236ab2b7b4SDimitris Papastamos }; 6246ab2b7b4SDimitris Papastamos 6259ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 6269ce5e6e6SRichard Fitzgerald { 6279ce5e6e6SRichard Fitzgerald switch (type) { 6289ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 6299ce5e6e6SRichard Fitzgerald return "PM"; 630170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 631170b1e12SWen Shi return "PM_PACKED"; 6329ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 6339ce5e6e6SRichard Fitzgerald return "DM"; 6349ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 6359ce5e6e6SRichard Fitzgerald return "XM"; 636170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 637170b1e12SWen Shi return "XM_PACKED"; 6389ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 6399ce5e6e6SRichard Fitzgerald return "YM"; 640170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 641170b1e12SWen Shi return "YM_PACKED"; 6429ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 6439ce5e6e6SRichard Fitzgerald return "ZM"; 6449ce5e6e6SRichard Fitzgerald default: 6459ce5e6e6SRichard Fitzgerald return NULL; 6469ce5e6e6SRichard Fitzgerald } 6479ce5e6e6SRichard Fitzgerald } 6489ce5e6e6SRichard Fitzgerald 649f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 650f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 651f9f55e31SRichard Fitzgerald { 652f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 653f9f55e31SRichard Fitzgerald 654f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 655f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 656f9f55e31SRichard Fitzgerald } 657f9f55e31SRichard Fitzgerald 658f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 659f9f55e31SRichard Fitzgerald { 660f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 661f9f55e31SRichard Fitzgerald 662f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 663f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 664f9f55e31SRichard Fitzgerald } 665f9f55e31SRichard Fitzgerald 666f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 667f9f55e31SRichard Fitzgerald { 668f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 669f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 670f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 671f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 672f9f55e31SRichard Fitzgerald } 673f9f55e31SRichard Fitzgerald 674f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 675f9f55e31SRichard Fitzgerald char __user *user_buf, 676f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 677f9f55e31SRichard Fitzgerald { 678f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 679f9f55e31SRichard Fitzgerald ssize_t ret; 680f9f55e31SRichard Fitzgerald 681078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 682f9f55e31SRichard Fitzgerald 68328823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 684f9f55e31SRichard Fitzgerald ret = 0; 685f9f55e31SRichard Fitzgerald else 686f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 687f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 688f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 689f9f55e31SRichard Fitzgerald 690078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 691f9f55e31SRichard Fitzgerald return ret; 692f9f55e31SRichard Fitzgerald } 693f9f55e31SRichard Fitzgerald 694f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 695f9f55e31SRichard Fitzgerald char __user *user_buf, 696f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 697f9f55e31SRichard Fitzgerald { 698f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 699f9f55e31SRichard Fitzgerald ssize_t ret; 700f9f55e31SRichard Fitzgerald 701078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 702f9f55e31SRichard Fitzgerald 70328823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 704f9f55e31SRichard Fitzgerald ret = 0; 705f9f55e31SRichard Fitzgerald else 706f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 707f9f55e31SRichard Fitzgerald dsp->bin_file_name, 708f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 709f9f55e31SRichard Fitzgerald 710078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 711f9f55e31SRichard Fitzgerald return ret; 712f9f55e31SRichard Fitzgerald } 713f9f55e31SRichard Fitzgerald 714f9f55e31SRichard Fitzgerald static const struct { 715f9f55e31SRichard Fitzgerald const char *name; 716f9f55e31SRichard Fitzgerald const struct file_operations fops; 717f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 718f9f55e31SRichard Fitzgerald { 719f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 720f9f55e31SRichard Fitzgerald .fops = { 721f9f55e31SRichard Fitzgerald .open = simple_open, 722f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 723f9f55e31SRichard Fitzgerald }, 724f9f55e31SRichard Fitzgerald }, 725f9f55e31SRichard Fitzgerald { 726f9f55e31SRichard Fitzgerald .name = "bin_file_name", 727f9f55e31SRichard Fitzgerald .fops = { 728f9f55e31SRichard Fitzgerald .open = simple_open, 729f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 730f9f55e31SRichard Fitzgerald }, 731f9f55e31SRichard Fitzgerald }, 732f9f55e31SRichard Fitzgerald }; 733f9f55e31SRichard Fitzgerald 734f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7350fe1daa6SKuninori Morimoto struct snd_soc_component *component) 736f9f55e31SRichard Fitzgerald { 737f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 738f9f55e31SRichard Fitzgerald int i; 739f9f55e31SRichard Fitzgerald 740605391d0SRichard Fitzgerald root = debugfs_create_dir(dsp->name, component->debugfs_root); 741f9f55e31SRichard Fitzgerald 7427f807f28SGreg Kroah-Hartman debugfs_create_bool("booted", 0444, root, &dsp->booted); 7437f807f28SGreg Kroah-Hartman debugfs_create_bool("running", 0444, root, &dsp->running); 7447f807f28SGreg Kroah-Hartman debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); 7457f807f28SGreg Kroah-Hartman debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); 746f9f55e31SRichard Fitzgerald 7477f807f28SGreg Kroah-Hartman for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) 7487f807f28SGreg Kroah-Hartman debugfs_create_file(wm_adsp_debugfs_fops[i].name, 0444, root, 7497f807f28SGreg Kroah-Hartman dsp, &wm_adsp_debugfs_fops[i].fops); 750f9f55e31SRichard Fitzgerald 751f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 752f9f55e31SRichard Fitzgerald } 753f9f55e31SRichard Fitzgerald 754f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 755f9f55e31SRichard Fitzgerald { 756f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 757f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 758f9f55e31SRichard Fitzgerald } 759f9f55e31SRichard Fitzgerald #else 760f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7610fe1daa6SKuninori Morimoto struct snd_soc_component *component) 762f9f55e31SRichard Fitzgerald { 763f9f55e31SRichard Fitzgerald } 764f9f55e31SRichard Fitzgerald 765f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 766f9f55e31SRichard Fitzgerald { 767f9f55e31SRichard Fitzgerald } 768f9f55e31SRichard Fitzgerald 769f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 770f9f55e31SRichard Fitzgerald const char *s) 771f9f55e31SRichard Fitzgerald { 772f9f55e31SRichard Fitzgerald } 773f9f55e31SRichard Fitzgerald 774f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 775f9f55e31SRichard Fitzgerald const char *s) 776f9f55e31SRichard Fitzgerald { 777f9f55e31SRichard Fitzgerald } 778f9f55e31SRichard Fitzgerald 779f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 780f9f55e31SRichard Fitzgerald { 781f9f55e31SRichard Fitzgerald } 782f9f55e31SRichard Fitzgerald #endif 783f9f55e31SRichard Fitzgerald 7840a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 7851023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7861023dbd9SMark Brown { 7870fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7881023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7890fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 7901023dbd9SMark Brown 79115c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 7921023dbd9SMark Brown 7931023dbd9SMark Brown return 0; 7941023dbd9SMark Brown } 7950a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get); 7961023dbd9SMark Brown 7970a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 7981023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7991023dbd9SMark Brown { 8000fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 8011023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 8020fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 803d27c5e15SCharles Keepax int ret = 0; 8041023dbd9SMark Brown 80515c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 8061023dbd9SMark Brown return 0; 8071023dbd9SMark Brown 80815c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 8091023dbd9SMark Brown return -EINVAL; 8101023dbd9SMark Brown 811d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 8121023dbd9SMark Brown 8134f2d4eabSStuart Henderson if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) 814d27c5e15SCharles Keepax ret = -EBUSY; 815d27c5e15SCharles Keepax else 81615c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 8171023dbd9SMark Brown 818d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 819d27c5e15SCharles Keepax 820d27c5e15SCharles Keepax return ret; 8211023dbd9SMark Brown } 8220a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put); 8231023dbd9SMark Brown 8240a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = { 8251023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8261023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8271023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8281023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 829e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 830e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 831e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8321023dbd9SMark Brown }; 8330a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 8342159ad93SMark Brown 8352159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 8362159ad93SMark Brown int type) 8372159ad93SMark Brown { 8382159ad93SMark Brown int i; 8392159ad93SMark Brown 8402159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 8412159ad93SMark Brown if (dsp->mem[i].type == type) 8422159ad93SMark Brown return &dsp->mem[i]; 8432159ad93SMark Brown 8442159ad93SMark Brown return NULL; 8452159ad93SMark Brown } 8462159ad93SMark Brown 8473809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 84845b9ee72SMark Brown unsigned int offset) 84945b9ee72SMark Brown { 8503809f001SCharles Keepax switch (mem->type) { 85145b9ee72SMark Brown case WMFW_ADSP1_PM: 8523809f001SCharles Keepax return mem->base + (offset * 3); 85345b9ee72SMark Brown case WMFW_ADSP1_DM: 85445b9ee72SMark Brown case WMFW_ADSP2_XM: 85545b9ee72SMark Brown case WMFW_ADSP2_YM: 85645b9ee72SMark Brown case WMFW_ADSP1_ZM: 8573809f001SCharles Keepax return mem->base + (offset * 2); 85845b9ee72SMark Brown default: 8596c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 86045b9ee72SMark Brown return offset; 86145b9ee72SMark Brown } 86245b9ee72SMark Brown } 86345b9ee72SMark Brown 864170b1e12SWen Shi static unsigned int wm_halo_region_to_reg(struct wm_adsp_region const *mem, 865170b1e12SWen Shi unsigned int offset) 866170b1e12SWen Shi { 867170b1e12SWen Shi switch (mem->type) { 868170b1e12SWen Shi case WMFW_ADSP2_XM: 869170b1e12SWen Shi case WMFW_ADSP2_YM: 870170b1e12SWen Shi return mem->base + (offset * 4); 871170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 872170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 873170b1e12SWen Shi return (mem->base + (offset * 3)) & ~0x3; 874170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 875170b1e12SWen Shi return mem->base + (offset * 5); 876170b1e12SWen Shi default: 877170b1e12SWen Shi WARN(1, "Unknown memory region type"); 878170b1e12SWen Shi return offset; 879170b1e12SWen Shi } 880170b1e12SWen Shi } 881170b1e12SWen Shi 8824049ce86SCharles Keepax static void wm_adsp_read_fw_status(struct wm_adsp *dsp, 8834049ce86SCharles Keepax int noffs, unsigned int *offs) 88410337b07SRichard Fitzgerald { 88520e00db2SRichard Fitzgerald unsigned int i; 88610337b07SRichard Fitzgerald int ret; 88710337b07SRichard Fitzgerald 8884049ce86SCharles Keepax for (i = 0; i < noffs; ++i) { 8894049ce86SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 89010337b07SRichard Fitzgerald if (ret) { 89120e00db2SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 89210337b07SRichard Fitzgerald return; 89310337b07SRichard Fitzgerald } 89420e00db2SRichard Fitzgerald } 8954049ce86SCharles Keepax } 8964049ce86SCharles Keepax 8974049ce86SCharles Keepax static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 8984049ce86SCharles Keepax { 8994049ce86SCharles Keepax unsigned int offs[] = { 9004049ce86SCharles Keepax ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 9014049ce86SCharles Keepax }; 9024049ce86SCharles Keepax 9034049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 90410337b07SRichard Fitzgerald 90510337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9064049ce86SCharles Keepax offs[0], offs[1], offs[2], offs[3]); 90710337b07SRichard Fitzgerald } 90810337b07SRichard Fitzgerald 909e1ea1879SRichard Fitzgerald static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) 910e1ea1879SRichard Fitzgerald { 9114049ce86SCharles Keepax unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 912e1ea1879SRichard Fitzgerald 9134049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 914e1ea1879SRichard Fitzgerald 915e1ea1879SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9164049ce86SCharles Keepax offs[0] & 0xFFFF, offs[0] >> 16, 9174049ce86SCharles Keepax offs[1] & 0xFFFF, offs[1] >> 16); 918e1ea1879SRichard Fitzgerald } 919e1ea1879SRichard Fitzgerald 920170b1e12SWen Shi static void wm_halo_show_fw_status(struct wm_adsp *dsp) 921170b1e12SWen Shi { 922170b1e12SWen Shi unsigned int offs[] = { 923170b1e12SWen Shi HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, 924170b1e12SWen Shi }; 925170b1e12SWen Shi 926170b1e12SWen Shi wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 927170b1e12SWen Shi 928170b1e12SWen Shi adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 929170b1e12SWen Shi offs[0], offs[1], offs[2], offs[3]); 930170b1e12SWen Shi } 931170b1e12SWen Shi 9329ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 9339ee78757SCharles Keepax { 9349ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 9359ee78757SCharles Keepax } 9369ee78757SCharles Keepax 937b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 938b396ebcaSRichard Fitzgerald { 939b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 940b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 941b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 942b396ebcaSRichard Fitzgerald 943b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 944b396ebcaSRichard Fitzgerald if (!mem) { 945b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 946b396ebcaSRichard Fitzgerald alg_region->type); 947b396ebcaSRichard Fitzgerald return -EINVAL; 948b396ebcaSRichard Fitzgerald } 949b396ebcaSRichard Fitzgerald 950170b1e12SWen Shi *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); 951b396ebcaSRichard Fitzgerald 952b396ebcaSRichard Fitzgerald return 0; 953b396ebcaSRichard Fitzgerald } 954b396ebcaSRichard Fitzgerald 9557585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 9566ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 9576ab2b7b4SDimitris Papastamos { 9589ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9599ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9609ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9616ab2b7b4SDimitris Papastamos 962a23ebba8SRichard Fitzgerald switch (ctl->type) { 963a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 964a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 965a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 966a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 967a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 968a23ebba8SRichard Fitzgerald uinfo->count = 1; 969a23ebba8SRichard Fitzgerald break; 970a23ebba8SRichard Fitzgerald default: 9716ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 9726ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 973a23ebba8SRichard Fitzgerald break; 974a23ebba8SRichard Fitzgerald } 975a23ebba8SRichard Fitzgerald 9766ab2b7b4SDimitris Papastamos return 0; 9776ab2b7b4SDimitris Papastamos } 9786ab2b7b4SDimitris Papastamos 979f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 980f4f0c4c6SRichard Fitzgerald unsigned int event_id) 981f4f0c4c6SRichard Fitzgerald { 982f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 983a0b653e8SRichard Fitzgerald __be32 val = cpu_to_be32(event_id); 984f4f0c4c6SRichard Fitzgerald unsigned int reg; 985f4f0c4c6SRichard Fitzgerald int i, ret; 986f4f0c4c6SRichard Fitzgerald 987f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 988f4f0c4c6SRichard Fitzgerald if (ret) 989f4f0c4c6SRichard Fitzgerald return ret; 990f4f0c4c6SRichard Fitzgerald 991f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 992f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 993f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 994f4f0c4c6SRichard Fitzgerald 995f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 996f4f0c4c6SRichard Fitzgerald if (ret) { 997f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 998f4f0c4c6SRichard Fitzgerald return ret; 999f4f0c4c6SRichard Fitzgerald } 1000f4f0c4c6SRichard Fitzgerald 1001f4f0c4c6SRichard Fitzgerald /* 1002f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 1003f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 1004f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 1005f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 1006f4f0c4c6SRichard Fitzgerald */ 1007f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 1008f4f0c4c6SRichard Fitzgerald switch (i) { 1009f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 1010f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 1011f4f0c4c6SRichard Fitzgerald i++; 1012f4f0c4c6SRichard Fitzgerald break; 1013f4f0c4c6SRichard Fitzgerald default: 1014f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 1015f4f0c4c6SRichard Fitzgerald i += 10; 1016f4f0c4c6SRichard Fitzgerald break; 1017f4f0c4c6SRichard Fitzgerald } 1018f4f0c4c6SRichard Fitzgerald 1019f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1020f4f0c4c6SRichard Fitzgerald if (ret) { 1021f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 1022f4f0c4c6SRichard Fitzgerald return ret; 1023f4f0c4c6SRichard Fitzgerald } 1024f4f0c4c6SRichard Fitzgerald 1025f4f0c4c6SRichard Fitzgerald if (val == 0) { 1026f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 1027f4f0c4c6SRichard Fitzgerald return 0; 1028f4f0c4c6SRichard Fitzgerald } 1029f4f0c4c6SRichard Fitzgerald } 1030f4f0c4c6SRichard Fitzgerald 1031f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 1032f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 1033f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 1034f4f0c4c6SRichard Fitzgerald ctl->offset); 1035f4f0c4c6SRichard Fitzgerald 1036f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 1037f4f0c4c6SRichard Fitzgerald } 1038f4f0c4c6SRichard Fitzgerald 103973ecf1a6SCharles Keepax static int wm_coeff_write_ctrl_raw(struct wm_coeff_ctl *ctl, 10406ab2b7b4SDimitris Papastamos const void *buf, size_t len) 10416ab2b7b4SDimitris Papastamos { 10423809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 10436ab2b7b4SDimitris Papastamos void *scratch; 10446ab2b7b4SDimitris Papastamos int ret; 10456ab2b7b4SDimitris Papastamos unsigned int reg; 10466ab2b7b4SDimitris Papastamos 1047b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1048b396ebcaSRichard Fitzgerald if (ret) 1049b396ebcaSRichard Fitzgerald return ret; 10506ab2b7b4SDimitris Papastamos 10514f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 10526ab2b7b4SDimitris Papastamos if (!scratch) 10536ab2b7b4SDimitris Papastamos return -ENOMEM; 10546ab2b7b4SDimitris Papastamos 10553809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 10564f8ea6d7SCharles Keepax len); 10576ab2b7b4SDimitris Papastamos if (ret) { 10583809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 10594f8ea6d7SCharles Keepax len, reg, ret); 10606ab2b7b4SDimitris Papastamos kfree(scratch); 10616ab2b7b4SDimitris Papastamos return ret; 10626ab2b7b4SDimitris Papastamos } 10634f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 10646ab2b7b4SDimitris Papastamos 10656ab2b7b4SDimitris Papastamos kfree(scratch); 10666ab2b7b4SDimitris Papastamos 10676ab2b7b4SDimitris Papastamos return 0; 10686ab2b7b4SDimitris Papastamos } 10696ab2b7b4SDimitris Papastamos 107073ecf1a6SCharles Keepax static int wm_coeff_write_ctrl(struct wm_coeff_ctl *ctl, 107173ecf1a6SCharles Keepax const void *buf, size_t len) 107273ecf1a6SCharles Keepax { 107373ecf1a6SCharles Keepax int ret = 0; 107473ecf1a6SCharles Keepax 107573ecf1a6SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 107673ecf1a6SCharles Keepax ret = -EPERM; 107773ecf1a6SCharles Keepax else if (buf != ctl->cache) 107873ecf1a6SCharles Keepax memcpy(ctl->cache, buf, len); 107973ecf1a6SCharles Keepax 108073ecf1a6SCharles Keepax ctl->set = 1; 108173ecf1a6SCharles Keepax if (ctl->enabled && ctl->dsp->running) 108273ecf1a6SCharles Keepax ret = wm_coeff_write_ctrl_raw(ctl, buf, len); 108373ecf1a6SCharles Keepax 108473ecf1a6SCharles Keepax return ret; 108573ecf1a6SCharles Keepax } 108673ecf1a6SCharles Keepax 10877585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 10886ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 10896ab2b7b4SDimitris Papastamos { 10909ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10919ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10929ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10936ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1094168d10e7SCharles Keepax int ret = 0; 1095168d10e7SCharles Keepax 1096168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 109773ecf1a6SCharles Keepax ret = wm_coeff_write_ctrl(ctl, p, ctl->len); 1098168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 1099168d10e7SCharles Keepax 1100168d10e7SCharles Keepax return ret; 11016ab2b7b4SDimitris Papastamos } 11026ab2b7b4SDimitris Papastamos 11039ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 11049ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 11059ee78757SCharles Keepax { 11069ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11079ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11089ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11099ee78757SCharles Keepax int ret = 0; 11109ee78757SCharles Keepax 11119ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 11129ee78757SCharles Keepax 111373ecf1a6SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) 11149ee78757SCharles Keepax ret = -EFAULT; 111573ecf1a6SCharles Keepax else 111673ecf1a6SCharles Keepax ret = wm_coeff_write_ctrl(ctl, ctl->cache, size); 11179ee78757SCharles Keepax 11189ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 11199ee78757SCharles Keepax 11209ee78757SCharles Keepax return ret; 11219ee78757SCharles Keepax } 11229ee78757SCharles Keepax 1123a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 1124a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1125a23ebba8SRichard Fitzgerald { 1126a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 1127a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 1128a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 1129a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 1130a23ebba8SRichard Fitzgerald int ret; 1131a23ebba8SRichard Fitzgerald 1132a23ebba8SRichard Fitzgerald if (val == 0) 1133a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 1134a23ebba8SRichard Fitzgerald 1135a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 1136a23ebba8SRichard Fitzgerald 11377b4af793SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1138a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 1139a23ebba8SRichard Fitzgerald else 1140a23ebba8SRichard Fitzgerald ret = -EPERM; 1141a23ebba8SRichard Fitzgerald 1142a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 1143a23ebba8SRichard Fitzgerald 1144a23ebba8SRichard Fitzgerald return ret; 1145a23ebba8SRichard Fitzgerald } 1146a23ebba8SRichard Fitzgerald 114773ecf1a6SCharles Keepax static int wm_coeff_read_ctrl_raw(struct wm_coeff_ctl *ctl, 11486ab2b7b4SDimitris Papastamos void *buf, size_t len) 11496ab2b7b4SDimitris Papastamos { 11503809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 11516ab2b7b4SDimitris Papastamos void *scratch; 11526ab2b7b4SDimitris Papastamos int ret; 11536ab2b7b4SDimitris Papastamos unsigned int reg; 11546ab2b7b4SDimitris Papastamos 1155b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1156b396ebcaSRichard Fitzgerald if (ret) 1157b396ebcaSRichard Fitzgerald return ret; 11586ab2b7b4SDimitris Papastamos 11594f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 11606ab2b7b4SDimitris Papastamos if (!scratch) 11616ab2b7b4SDimitris Papastamos return -ENOMEM; 11626ab2b7b4SDimitris Papastamos 11634f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 11646ab2b7b4SDimitris Papastamos if (ret) { 11653809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 11665602a643SCharles Keepax len, reg, ret); 11676ab2b7b4SDimitris Papastamos kfree(scratch); 11686ab2b7b4SDimitris Papastamos return ret; 11696ab2b7b4SDimitris Papastamos } 11704f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 11716ab2b7b4SDimitris Papastamos 11724f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 11736ab2b7b4SDimitris Papastamos kfree(scratch); 11746ab2b7b4SDimitris Papastamos 11756ab2b7b4SDimitris Papastamos return 0; 11766ab2b7b4SDimitris Papastamos } 11776ab2b7b4SDimitris Papastamos 117873ecf1a6SCharles Keepax static int wm_coeff_read_ctrl(struct wm_coeff_ctl *ctl, void *buf, size_t len) 117973ecf1a6SCharles Keepax { 118073ecf1a6SCharles Keepax int ret = 0; 118173ecf1a6SCharles Keepax 118273ecf1a6SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 118373ecf1a6SCharles Keepax if (ctl->enabled && ctl->dsp->running) 118473ecf1a6SCharles Keepax return wm_coeff_read_ctrl_raw(ctl, buf, len); 118573ecf1a6SCharles Keepax else 118673ecf1a6SCharles Keepax return -EPERM; 118773ecf1a6SCharles Keepax } else { 118873ecf1a6SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 118973ecf1a6SCharles Keepax ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 119073ecf1a6SCharles Keepax 119173ecf1a6SCharles Keepax if (buf != ctl->cache) 119273ecf1a6SCharles Keepax memcpy(buf, ctl->cache, len); 119373ecf1a6SCharles Keepax } 119473ecf1a6SCharles Keepax 119573ecf1a6SCharles Keepax return ret; 119673ecf1a6SCharles Keepax } 119773ecf1a6SCharles Keepax 11987585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 11996ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 12006ab2b7b4SDimitris Papastamos { 12019ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 12029ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 12039ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12046ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 120573ecf1a6SCharles Keepax int ret; 1206168d10e7SCharles Keepax 1207168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 120873ecf1a6SCharles Keepax ret = wm_coeff_read_ctrl(ctl, p, ctl->len); 1209168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 121026c22a19SCharles Keepax 1211168d10e7SCharles Keepax return ret; 12126ab2b7b4SDimitris Papastamos } 12136ab2b7b4SDimitris Papastamos 12149ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 12159ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 12169ee78757SCharles Keepax { 12179ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 12189ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 12199ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12209ee78757SCharles Keepax int ret = 0; 12219ee78757SCharles Keepax 12229ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 12239ee78757SCharles Keepax 122473ecf1a6SCharles Keepax ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, size); 12259ee78757SCharles Keepax 12269ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 12279ee78757SCharles Keepax ret = -EFAULT; 12289ee78757SCharles Keepax 12299ee78757SCharles Keepax mutex_unlock(&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 12496ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 12503809f001SCharles Keepax struct wm_adsp *dsp; 12516ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 12526ab2b7b4SDimitris Papastamos struct work_struct work; 12536ab2b7b4SDimitris Papastamos }; 12546ab2b7b4SDimitris Papastamos 12559ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 12569ee78757SCharles Keepax { 12579ee78757SCharles Keepax unsigned int out, rd, wr, vol; 12589ee78757SCharles Keepax 12599ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 12609ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 12619ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 12629ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12639ee78757SCharles Keepax 12649ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 12659ee78757SCharles Keepax } else { 12669ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 12679ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 12689ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12699ee78757SCharles Keepax 12709ee78757SCharles Keepax out = 0; 12719ee78757SCharles Keepax } 12729ee78757SCharles Keepax 12739ee78757SCharles Keepax if (in) { 12749ee78757SCharles Keepax out |= rd; 12759ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 12769ee78757SCharles Keepax out |= wr; 12779ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 12789ee78757SCharles Keepax out |= vol; 12799ee78757SCharles Keepax } else { 12809ee78757SCharles Keepax out |= rd | wr | vol; 12819ee78757SCharles Keepax } 12829ee78757SCharles Keepax 12839ee78757SCharles Keepax return out; 12849ee78757SCharles Keepax } 12859ee78757SCharles Keepax 12863809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 12876ab2b7b4SDimitris Papastamos { 12886ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 12896ab2b7b4SDimitris Papastamos int ret; 12906ab2b7b4SDimitris Papastamos 129192bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 12926ab2b7b4SDimitris Papastamos return -EINVAL; 12936ab2b7b4SDimitris Papastamos 12946ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 12956ab2b7b4SDimitris Papastamos if (!kcontrol) 12966ab2b7b4SDimitris Papastamos return -ENOMEM; 12976ab2b7b4SDimitris Papastamos 12986ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 12996ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 13009ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 13019ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 13029ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1303a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1304a23ebba8SRichard Fitzgerald 1305a23ebba8SRichard Fitzgerald switch (ctl->type) { 1306a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1307a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1308a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1309a23ebba8SRichard Fitzgerald break; 1310a23ebba8SRichard Fitzgerald default: 1311d7789f5bSRichard Fitzgerald if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 13129ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 13139ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 13149ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1315d7789f5bSRichard Fitzgerald } else { 1316d7789f5bSRichard Fitzgerald kcontrol->get = wm_coeff_get; 1317d7789f5bSRichard Fitzgerald kcontrol->put = wm_coeff_put; 1318d7789f5bSRichard Fitzgerald } 1319a23ebba8SRichard Fitzgerald break; 1320a23ebba8SRichard Fitzgerald } 132126c22a19SCharles Keepax 13220fe1daa6SKuninori Morimoto ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); 13236ab2b7b4SDimitris Papastamos if (ret < 0) 13246ab2b7b4SDimitris Papastamos goto err_kcontrol; 13256ab2b7b4SDimitris Papastamos 13266ab2b7b4SDimitris Papastamos kfree(kcontrol); 13276ab2b7b4SDimitris Papastamos 13286ab2b7b4SDimitris Papastamos return 0; 13296ab2b7b4SDimitris Papastamos 13306ab2b7b4SDimitris Papastamos err_kcontrol: 13316ab2b7b4SDimitris Papastamos kfree(kcontrol); 13326ab2b7b4SDimitris Papastamos return ret; 13336ab2b7b4SDimitris Papastamos } 13346ab2b7b4SDimitris Papastamos 1335b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1336b21acc1cSCharles Keepax { 1337b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1338b21acc1cSCharles Keepax int ret; 1339b21acc1cSCharles Keepax 1340b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1341b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1342b21acc1cSCharles Keepax continue; 134326c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 134426c22a19SCharles Keepax continue; 134526c22a19SCharles Keepax 134604ff40a9SRichard Fitzgerald /* 134704ff40a9SRichard Fitzgerald * For readable controls populate the cache from the DSP memory. 134804ff40a9SRichard Fitzgerald * For non-readable controls the cache was zero-filled when 134904ff40a9SRichard Fitzgerald * created so we don't need to do anything. 135004ff40a9SRichard Fitzgerald */ 135104ff40a9SRichard Fitzgerald if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 135273ecf1a6SCharles Keepax ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 1353b21acc1cSCharles Keepax if (ret < 0) 1354b21acc1cSCharles Keepax return ret; 1355b21acc1cSCharles Keepax } 135604ff40a9SRichard Fitzgerald } 1357b21acc1cSCharles Keepax 1358b21acc1cSCharles Keepax return 0; 1359b21acc1cSCharles Keepax } 1360b21acc1cSCharles Keepax 1361b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1362b21acc1cSCharles Keepax { 1363b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1364b21acc1cSCharles Keepax int ret; 1365b21acc1cSCharles Keepax 1366b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1367b21acc1cSCharles Keepax if (!ctl->enabled) 1368b21acc1cSCharles Keepax continue; 136926c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 137073ecf1a6SCharles Keepax ret = wm_coeff_write_ctrl_raw(ctl, ctl->cache, 137173ecf1a6SCharles Keepax ctl->len); 1372b21acc1cSCharles Keepax if (ret < 0) 1373b21acc1cSCharles Keepax return ret; 1374b21acc1cSCharles Keepax } 1375b21acc1cSCharles Keepax } 1376b21acc1cSCharles Keepax 1377b21acc1cSCharles Keepax return 0; 1378b21acc1cSCharles Keepax } 1379b21acc1cSCharles Keepax 1380f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1381f4f0c4c6SRichard Fitzgerald unsigned int event) 1382f4f0c4c6SRichard Fitzgerald { 1383f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1384f4f0c4c6SRichard Fitzgerald int ret; 1385f4f0c4c6SRichard Fitzgerald 1386f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1387f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1388f4f0c4c6SRichard Fitzgerald continue; 1389f4f0c4c6SRichard Fitzgerald 139087aa6374SCharles Keepax if (!ctl->enabled) 139187aa6374SCharles Keepax continue; 139287aa6374SCharles Keepax 1393f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1394f4f0c4c6SRichard Fitzgerald if (ret) 1395f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1396f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1397f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1398f4f0c4c6SRichard Fitzgerald } 1399f4f0c4c6SRichard Fitzgerald } 1400f4f0c4c6SRichard Fitzgerald 1401b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1402b21acc1cSCharles Keepax { 1403b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1404b21acc1cSCharles Keepax struct wmfw_ctl_work, 1405b21acc1cSCharles Keepax work); 1406b21acc1cSCharles Keepax 1407b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1408b21acc1cSCharles Keepax kfree(ctl_work); 1409b21acc1cSCharles Keepax } 1410b21acc1cSCharles Keepax 141166225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 141266225e98SRichard Fitzgerald { 141366225e98SRichard Fitzgerald kfree(ctl->cache); 141466225e98SRichard Fitzgerald kfree(ctl->name); 1415eb65ccdbSLi Xu kfree(ctl->subname); 141666225e98SRichard Fitzgerald kfree(ctl); 141766225e98SRichard Fitzgerald } 141866225e98SRichard Fitzgerald 1419b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1420b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 14212323736dSCharles Keepax unsigned int offset, unsigned int len, 142226c22a19SCharles Keepax const char *subname, unsigned int subname_len, 1423*f6212e0aSRichard Fitzgerald unsigned int flags, snd_ctl_elem_type_t type) 1424b21acc1cSCharles Keepax { 1425b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1426b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1427b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 14289ce5e6e6SRichard Fitzgerald const char *region_name; 1429b21acc1cSCharles Keepax int ret; 1430b21acc1cSCharles Keepax 14319ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 14329ce5e6e6SRichard Fitzgerald if (!region_name) { 14332323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1434b21acc1cSCharles Keepax return -EINVAL; 1435b21acc1cSCharles Keepax } 1436b21acc1cSCharles Keepax 1437cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1438cb5b57a9SCharles Keepax case 0: 1439cb5b57a9SCharles Keepax case 1: 1440605391d0SRichard Fitzgerald snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", 1441605391d0SRichard Fitzgerald dsp->name, region_name, alg_region->alg); 1442170b1e12SWen Shi subname = NULL; /* don't append subname */ 1443cb5b57a9SCharles Keepax break; 1444170b1e12SWen Shi case 2: 144557819429STakashi Iwai ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1446605391d0SRichard Fitzgerald "%s%c %.12s %x", dsp->name, *region_name, 1447cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1448170b1e12SWen Shi break; 1449170b1e12SWen Shi default: 145057819429STakashi Iwai ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1451170b1e12SWen Shi "%s %.12s %x", dsp->name, 1452170b1e12SWen Shi wm_adsp_fw_text[dsp->fw], alg_region->alg); 1453170b1e12SWen Shi break; 1454170b1e12SWen Shi } 1455cb5b57a9SCharles Keepax 1456cb5b57a9SCharles Keepax if (subname) { 1457cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1458cb5b57a9SCharles Keepax int skip = 0; 1459cb5b57a9SCharles Keepax 1460b7ede5afSCharles Keepax if (dsp->component->name_prefix) 1461b7ede5afSCharles Keepax avail -= strlen(dsp->component->name_prefix) + 1; 1462b7ede5afSCharles Keepax 1463170b1e12SWen Shi /* Truncate the subname from the start if it is too long */ 1464cb5b57a9SCharles Keepax if (subname_len > avail) 1465cb5b57a9SCharles Keepax skip = subname_len - avail; 1466cb5b57a9SCharles Keepax 1467170b1e12SWen Shi snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, 1468170b1e12SWen Shi " %.*s", subname_len - skip, subname + skip); 1469cb5b57a9SCharles Keepax } 1470b21acc1cSCharles Keepax 14717585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1472b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1473b21acc1cSCharles Keepax if (!ctl->enabled) 1474b21acc1cSCharles Keepax ctl->enabled = 1; 1475b21acc1cSCharles Keepax return 0; 1476b21acc1cSCharles Keepax } 1477b21acc1cSCharles Keepax } 1478b21acc1cSCharles Keepax 1479b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1480b21acc1cSCharles Keepax if (!ctl) 1481b21acc1cSCharles Keepax return -ENOMEM; 14822323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1483b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1484b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1485b21acc1cSCharles Keepax if (!ctl->name) { 1486b21acc1cSCharles Keepax ret = -ENOMEM; 1487b21acc1cSCharles Keepax goto err_ctl; 1488b21acc1cSCharles Keepax } 1489eb65ccdbSLi Xu if (subname) { 1490eb65ccdbSLi Xu ctl->subname_len = subname_len; 1491eb65ccdbSLi Xu ctl->subname = kmemdup(subname, 1492eb65ccdbSLi Xu strlen(subname) + 1, GFP_KERNEL); 1493eb65ccdbSLi Xu if (!ctl->subname) { 1494eb65ccdbSLi Xu ret = -ENOMEM; 1495eb65ccdbSLi Xu goto err_ctl_name; 1496eb65ccdbSLi Xu } 1497eb65ccdbSLi Xu } 1498b21acc1cSCharles Keepax ctl->enabled = 1; 1499b21acc1cSCharles Keepax ctl->set = 0; 1500b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1501b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1502b21acc1cSCharles Keepax ctl->dsp = dsp; 1503b21acc1cSCharles Keepax 150426c22a19SCharles Keepax ctl->flags = flags; 15058eb084d0SStuart Henderson ctl->type = type; 15062323736dSCharles Keepax ctl->offset = offset; 1507b21acc1cSCharles Keepax ctl->len = len; 1508b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1509b21acc1cSCharles Keepax if (!ctl->cache) { 1510b21acc1cSCharles Keepax ret = -ENOMEM; 1511eb65ccdbSLi Xu goto err_ctl_subname; 1512b21acc1cSCharles Keepax } 1513b21acc1cSCharles Keepax 15142323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 15152323736dSCharles Keepax 15168eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 15178eb084d0SStuart Henderson return 0; 15188eb084d0SStuart Henderson 1519b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1520b21acc1cSCharles Keepax if (!ctl_work) { 1521b21acc1cSCharles Keepax ret = -ENOMEM; 152285a75555SDan Carpenter goto err_list_del; 1523b21acc1cSCharles Keepax } 1524b21acc1cSCharles Keepax 1525b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1526b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1527b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1528b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1529b21acc1cSCharles Keepax 1530b21acc1cSCharles Keepax return 0; 1531b21acc1cSCharles Keepax 153285a75555SDan Carpenter err_list_del: 153385a75555SDan Carpenter list_del(&ctl->list); 1534b21acc1cSCharles Keepax kfree(ctl->cache); 1535eb65ccdbSLi Xu err_ctl_subname: 1536eb65ccdbSLi Xu kfree(ctl->subname); 1537b21acc1cSCharles Keepax err_ctl_name: 1538b21acc1cSCharles Keepax kfree(ctl->name); 1539b21acc1cSCharles Keepax err_ctl: 1540b21acc1cSCharles Keepax kfree(ctl); 1541b21acc1cSCharles Keepax 1542b21acc1cSCharles Keepax return ret; 1543b21acc1cSCharles Keepax } 1544b21acc1cSCharles Keepax 15452323736dSCharles Keepax struct wm_coeff_parsed_alg { 15462323736dSCharles Keepax int id; 15472323736dSCharles Keepax const u8 *name; 15482323736dSCharles Keepax int name_len; 15492323736dSCharles Keepax int ncoeff; 15502323736dSCharles Keepax }; 15512323736dSCharles Keepax 15522323736dSCharles Keepax struct wm_coeff_parsed_coeff { 15532323736dSCharles Keepax int offset; 15542323736dSCharles Keepax int mem_type; 15552323736dSCharles Keepax const u8 *name; 15562323736dSCharles Keepax int name_len; 1557*f6212e0aSRichard Fitzgerald snd_ctl_elem_type_t ctl_type; 15582323736dSCharles Keepax int flags; 15592323736dSCharles Keepax int len; 15602323736dSCharles Keepax }; 15612323736dSCharles Keepax 1562cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1563cb5b57a9SCharles Keepax { 1564cb5b57a9SCharles Keepax int length; 1565cb5b57a9SCharles Keepax 1566cb5b57a9SCharles Keepax switch (bytes) { 1567cb5b57a9SCharles Keepax case 1: 1568cb5b57a9SCharles Keepax length = **pos; 1569cb5b57a9SCharles Keepax break; 1570cb5b57a9SCharles Keepax case 2: 15718299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1572cb5b57a9SCharles Keepax break; 1573cb5b57a9SCharles Keepax default: 1574cb5b57a9SCharles Keepax return 0; 1575cb5b57a9SCharles Keepax } 1576cb5b57a9SCharles Keepax 1577cb5b57a9SCharles Keepax if (str) 1578cb5b57a9SCharles Keepax *str = *pos + bytes; 1579cb5b57a9SCharles Keepax 1580cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1581cb5b57a9SCharles Keepax 1582cb5b57a9SCharles Keepax return length; 1583cb5b57a9SCharles Keepax } 1584cb5b57a9SCharles Keepax 1585cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1586cb5b57a9SCharles Keepax { 1587cb5b57a9SCharles Keepax int val = 0; 1588cb5b57a9SCharles Keepax 1589cb5b57a9SCharles Keepax switch (bytes) { 1590cb5b57a9SCharles Keepax case 2: 15918299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1592cb5b57a9SCharles Keepax break; 1593cb5b57a9SCharles Keepax case 4: 15948299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1595cb5b57a9SCharles Keepax break; 1596cb5b57a9SCharles Keepax default: 1597cb5b57a9SCharles Keepax break; 1598cb5b57a9SCharles Keepax } 1599cb5b57a9SCharles Keepax 1600cb5b57a9SCharles Keepax *pos += bytes; 1601cb5b57a9SCharles Keepax 1602cb5b57a9SCharles Keepax return val; 1603cb5b57a9SCharles Keepax } 1604cb5b57a9SCharles Keepax 16052323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 16062323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 16072323736dSCharles Keepax { 16082323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 16092323736dSCharles Keepax 1610cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1611cb5b57a9SCharles Keepax case 0: 1612cb5b57a9SCharles Keepax case 1: 16132323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 16142323736dSCharles Keepax *data = raw->data; 16152323736dSCharles Keepax 16162323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 16172323736dSCharles Keepax blk->name = raw->name; 16182323736dSCharles Keepax blk->name_len = strlen(raw->name); 16192323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1620cb5b57a9SCharles Keepax break; 1621cb5b57a9SCharles Keepax default: 1622cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1623cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1624cb5b57a9SCharles Keepax &blk->name); 1625cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1626cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1627cb5b57a9SCharles Keepax break; 1628cb5b57a9SCharles Keepax } 16292323736dSCharles Keepax 16302323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 16312323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 16322323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 16332323736dSCharles Keepax } 16342323736dSCharles Keepax 16352323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 16362323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 16372323736dSCharles Keepax { 16382323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1639cb5b57a9SCharles Keepax const u8 *tmp; 1640cb5b57a9SCharles Keepax int length; 16412323736dSCharles Keepax 1642cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1643cb5b57a9SCharles Keepax case 0: 1644cb5b57a9SCharles Keepax case 1: 16452323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 16462323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 16472323736dSCharles Keepax 16482323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 16492323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 16502323736dSCharles Keepax blk->name = raw->name; 16512323736dSCharles Keepax blk->name_len = strlen(raw->name); 1652*f6212e0aSRichard Fitzgerald blk->ctl_type = (__force snd_ctl_elem_type_t)le16_to_cpu(raw->ctl_type); 16532323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 16542323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1655cb5b57a9SCharles Keepax break; 1656cb5b57a9SCharles Keepax default: 1657cb5b57a9SCharles Keepax tmp = *data; 1658cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1659cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1660cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1661cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1662cb5b57a9SCharles Keepax &blk->name); 1663cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1664cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1665*f6212e0aSRichard Fitzgerald blk->ctl_type = 1666*f6212e0aSRichard Fitzgerald (__force snd_ctl_elem_type_t)wm_coeff_parse_int(sizeof(raw->ctl_type), 1667*f6212e0aSRichard Fitzgerald &tmp); 1668cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1669cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1670cb5b57a9SCharles Keepax 1671cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1672cb5b57a9SCharles Keepax break; 1673cb5b57a9SCharles Keepax } 16742323736dSCharles Keepax 16752323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 16762323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 16772323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 16782323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 16792323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 16802323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 16812323736dSCharles Keepax } 16822323736dSCharles Keepax 1683f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1684f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1685f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1686f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1687f4f0c4c6SRichard Fitzgerald { 1688f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1689f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1690f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1691f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1692f4f0c4c6SRichard Fitzgerald return -EINVAL; 1693f4f0c4c6SRichard Fitzgerald } 1694f4f0c4c6SRichard Fitzgerald 1695f4f0c4c6SRichard Fitzgerald return 0; 1696f4f0c4c6SRichard Fitzgerald } 1697f4f0c4c6SRichard Fitzgerald 16982323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 16992323736dSCharles Keepax const struct wmfw_region *region) 17002323736dSCharles Keepax { 17012323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 17022323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 17032323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 17042323736dSCharles Keepax const u8 *data = region->data; 17052323736dSCharles Keepax int i, ret; 17062323736dSCharles Keepax 17072323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 17082323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 17092323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 17102323736dSCharles Keepax 17112323736dSCharles Keepax switch (coeff_blk.ctl_type) { 17122323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 17132323736dSCharles Keepax break; 1714a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1715a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1716a23ebba8SRichard Fitzgerald continue; /* ignore */ 1717a23ebba8SRichard Fitzgerald 1718a23ebba8SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1719a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1720a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1721a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1722a23ebba8SRichard Fitzgerald 0); 1723a23ebba8SRichard Fitzgerald if (ret) 1724a23ebba8SRichard Fitzgerald return -EINVAL; 1725a23ebba8SRichard Fitzgerald break; 1726f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1727f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1728f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1729f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1730f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1731f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1732f4f0c4c6SRichard Fitzgerald 0); 1733f4f0c4c6SRichard Fitzgerald if (ret) 1734f4f0c4c6SRichard Fitzgerald return -EINVAL; 1735f4f0c4c6SRichard Fitzgerald break; 1736d52ed4b0SRichard Fitzgerald case WMFW_CTL_TYPE_HOST_BUFFER: 1737d52ed4b0SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1738d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1739d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1740d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1741d52ed4b0SRichard Fitzgerald 0); 1742d52ed4b0SRichard Fitzgerald if (ret) 1743d52ed4b0SRichard Fitzgerald return -EINVAL; 1744d52ed4b0SRichard Fitzgerald break; 17452323736dSCharles Keepax default: 17462323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 17472323736dSCharles Keepax coeff_blk.ctl_type); 17482323736dSCharles Keepax return -EINVAL; 17492323736dSCharles Keepax } 17502323736dSCharles Keepax 17512323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 17522323736dSCharles Keepax alg_region.alg = alg_blk.id; 17532323736dSCharles Keepax 17542323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 17552323736dSCharles Keepax coeff_blk.offset, 17562323736dSCharles Keepax coeff_blk.len, 17572323736dSCharles Keepax coeff_blk.name, 175826c22a19SCharles Keepax coeff_blk.name_len, 17598eb084d0SStuart Henderson coeff_blk.flags, 17608eb084d0SStuart Henderson coeff_blk.ctl_type); 17612323736dSCharles Keepax if (ret < 0) 17622323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 17632323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 17642323736dSCharles Keepax } 17652323736dSCharles Keepax 17662323736dSCharles Keepax return 0; 17672323736dSCharles Keepax } 17682323736dSCharles Keepax 17694e08d50dSCharles Keepax static unsigned int wm_adsp1_parse_sizes(struct wm_adsp *dsp, 17704e08d50dSCharles Keepax const char * const file, 17714e08d50dSCharles Keepax unsigned int pos, 17724e08d50dSCharles Keepax const struct firmware *firmware) 17734e08d50dSCharles Keepax { 17744e08d50dSCharles Keepax const struct wmfw_adsp1_sizes *adsp1_sizes; 17754e08d50dSCharles Keepax 17764e08d50dSCharles Keepax adsp1_sizes = (void *)&firmware->data[pos]; 17774e08d50dSCharles Keepax 17784e08d50dSCharles Keepax adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 17794e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 17804e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->zm)); 17814e08d50dSCharles Keepax 17824e08d50dSCharles Keepax return pos + sizeof(*adsp1_sizes); 17834e08d50dSCharles Keepax } 17844e08d50dSCharles Keepax 17854e08d50dSCharles Keepax static unsigned int wm_adsp2_parse_sizes(struct wm_adsp *dsp, 17864e08d50dSCharles Keepax const char * const file, 17874e08d50dSCharles Keepax unsigned int pos, 17884e08d50dSCharles Keepax const struct firmware *firmware) 17894e08d50dSCharles Keepax { 17904e08d50dSCharles Keepax const struct wmfw_adsp2_sizes *adsp2_sizes; 17914e08d50dSCharles Keepax 17924e08d50dSCharles Keepax adsp2_sizes = (void *)&firmware->data[pos]; 17934e08d50dSCharles Keepax 17944e08d50dSCharles Keepax adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 17954e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 17964e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 17974e08d50dSCharles Keepax 17984e08d50dSCharles Keepax return pos + sizeof(*adsp2_sizes); 17994e08d50dSCharles Keepax } 18004e08d50dSCharles Keepax 18014e08d50dSCharles Keepax static bool wm_adsp_validate_version(struct wm_adsp *dsp, unsigned int version) 18024e08d50dSCharles Keepax { 18034e08d50dSCharles Keepax switch (version) { 18044e08d50dSCharles Keepax case 0: 18054e08d50dSCharles Keepax adsp_warn(dsp, "Deprecated file format %d\n", version); 18064e08d50dSCharles Keepax return true; 18074e08d50dSCharles Keepax case 1: 18084e08d50dSCharles Keepax case 2: 18094e08d50dSCharles Keepax return true; 18104e08d50dSCharles Keepax default: 18114e08d50dSCharles Keepax return false; 18124e08d50dSCharles Keepax } 18134e08d50dSCharles Keepax } 18144e08d50dSCharles Keepax 1815170b1e12SWen Shi static bool wm_halo_validate_version(struct wm_adsp *dsp, unsigned int version) 1816170b1e12SWen Shi { 1817170b1e12SWen Shi switch (version) { 1818170b1e12SWen Shi case 3: 1819170b1e12SWen Shi return true; 1820170b1e12SWen Shi default: 1821170b1e12SWen Shi return false; 1822170b1e12SWen Shi } 1823170b1e12SWen Shi } 1824170b1e12SWen Shi 18252159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 18262159ad93SMark Brown { 1827cf17c83cSMark Brown LIST_HEAD(buf_list); 18282159ad93SMark Brown const struct firmware *firmware; 18292159ad93SMark Brown struct regmap *regmap = dsp->regmap; 18302159ad93SMark Brown unsigned int pos = 0; 18312159ad93SMark Brown const struct wmfw_header *header; 18322159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 18332159ad93SMark Brown const struct wmfw_footer *footer; 18342159ad93SMark Brown const struct wmfw_region *region; 18352159ad93SMark Brown const struct wm_adsp_region *mem; 18362159ad93SMark Brown const char *region_name; 18371cab2a84SRichard Fitzgerald char *file, *text = NULL; 1838cf17c83cSMark Brown struct wm_adsp_buf *buf; 18392159ad93SMark Brown unsigned int reg; 18402159ad93SMark Brown int regions = 0; 18414e08d50dSCharles Keepax int ret, offset, type; 18422159ad93SMark Brown 18432159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 18442159ad93SMark Brown if (file == NULL) 18452159ad93SMark Brown return -ENOMEM; 18462159ad93SMark Brown 1847605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, 18481023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 18492159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 18502159ad93SMark Brown 18512159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 18522159ad93SMark Brown if (ret != 0) { 18532159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 18542159ad93SMark Brown goto out; 18552159ad93SMark Brown } 18562159ad93SMark Brown ret = -EINVAL; 18572159ad93SMark Brown 18582159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 18592159ad93SMark Brown if (pos >= firmware->size) { 18602159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 18612159ad93SMark Brown file, firmware->size); 18622159ad93SMark Brown goto out_fw; 18632159ad93SMark Brown } 18642159ad93SMark Brown 18652159ad93SMark Brown header = (void *)&firmware->data[0]; 18662159ad93SMark Brown 18672159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 18682159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 18692159ad93SMark Brown goto out_fw; 18702159ad93SMark Brown } 18712159ad93SMark Brown 18724e08d50dSCharles Keepax if (!dsp->ops->validate_version(dsp, header->ver)) { 18732159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 18742159ad93SMark Brown file, header->ver); 18752159ad93SMark Brown goto out_fw; 18762159ad93SMark Brown } 18772323736dSCharles Keepax 18783626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 18792323736dSCharles Keepax dsp->fw_ver = header->ver; 18802159ad93SMark Brown 18812159ad93SMark Brown if (header->core != dsp->type) { 18822159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 18832159ad93SMark Brown file, header->core, dsp->type); 18842159ad93SMark Brown goto out_fw; 18852159ad93SMark Brown } 18862159ad93SMark Brown 18874e08d50dSCharles Keepax pos = sizeof(*header); 18884e08d50dSCharles Keepax pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 18892159ad93SMark Brown 18904e08d50dSCharles Keepax footer = (void *)&firmware->data[pos]; 18914e08d50dSCharles Keepax pos += sizeof(*footer); 18922159ad93SMark Brown 18934e08d50dSCharles Keepax if (le32_to_cpu(header->len) != pos) { 18942159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 18952159ad93SMark Brown file, le32_to_cpu(header->len)); 18962159ad93SMark Brown goto out_fw; 18972159ad93SMark Brown } 18982159ad93SMark Brown 18992159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 19002159ad93SMark Brown le64_to_cpu(footer->timestamp)); 19012159ad93SMark Brown 19022159ad93SMark Brown while (pos < firmware->size && 190350dd2ea8SBen Hutchings sizeof(*region) < firmware->size - pos) { 19042159ad93SMark Brown region = (void *)&(firmware->data[pos]); 19052159ad93SMark Brown region_name = "Unknown"; 19062159ad93SMark Brown reg = 0; 19072159ad93SMark Brown text = NULL; 19082159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 19092159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 19102159ad93SMark Brown 19112159ad93SMark Brown switch (type) { 19122159ad93SMark Brown case WMFW_NAME_TEXT: 19132159ad93SMark Brown region_name = "Firmware name"; 19142159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 19152159ad93SMark Brown GFP_KERNEL); 19162159ad93SMark Brown break; 19172323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 19182323736dSCharles Keepax region_name = "Algorithm"; 19192323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 19202323736dSCharles Keepax if (ret != 0) 19212323736dSCharles Keepax goto out_fw; 19222323736dSCharles Keepax break; 19232159ad93SMark Brown case WMFW_INFO_TEXT: 19242159ad93SMark Brown region_name = "Information"; 19252159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 19262159ad93SMark Brown GFP_KERNEL); 19272159ad93SMark Brown break; 19282159ad93SMark Brown case WMFW_ABSOLUTE: 19292159ad93SMark Brown region_name = "Absolute"; 19302159ad93SMark Brown reg = offset; 19312159ad93SMark Brown break; 19322159ad93SMark Brown case WMFW_ADSP1_PM: 19332159ad93SMark Brown case WMFW_ADSP1_DM: 19342159ad93SMark Brown case WMFW_ADSP2_XM: 19352159ad93SMark Brown case WMFW_ADSP2_YM: 19362159ad93SMark Brown case WMFW_ADSP1_ZM: 1937170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 1938170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 1939170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 1940170b1e12SWen Shi mem = wm_adsp_find_region(dsp, type); 1941170b1e12SWen Shi if (!mem) { 1942170b1e12SWen Shi adsp_err(dsp, "No region of type: %x\n", type); 19433fba05a2SLuo Meng ret = -EINVAL; 1944170b1e12SWen Shi goto out_fw; 1945170b1e12SWen Shi } 1946170b1e12SWen Shi 19479ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 1948170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, offset); 19492159ad93SMark Brown break; 19502159ad93SMark Brown default: 19512159ad93SMark Brown adsp_warn(dsp, 19522159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 19532159ad93SMark Brown file, regions, type, pos, pos); 19542159ad93SMark Brown break; 19552159ad93SMark Brown } 19562159ad93SMark Brown 19572159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 19582159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 19592159ad93SMark Brown region_name); 19602159ad93SMark Brown 196150dd2ea8SBen Hutchings if (le32_to_cpu(region->len) > 196250dd2ea8SBen Hutchings firmware->size - pos - sizeof(*region)) { 19631cab2a84SRichard Fitzgerald adsp_err(dsp, 19641cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 19651cab2a84SRichard Fitzgerald file, regions, region_name, 19661cab2a84SRichard Fitzgerald le32_to_cpu(region->len), firmware->size); 19671cab2a84SRichard Fitzgerald ret = -EINVAL; 19681cab2a84SRichard Fitzgerald goto out_fw; 19691cab2a84SRichard Fitzgerald } 19701cab2a84SRichard Fitzgerald 19712159ad93SMark Brown if (text) { 19722159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 19732159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 19742159ad93SMark Brown kfree(text); 19751cab2a84SRichard Fitzgerald text = NULL; 19762159ad93SMark Brown } 19772159ad93SMark Brown 19782159ad93SMark Brown if (reg) { 1979cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1980cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1981cf17c83cSMark Brown &buf_list); 1982a76fefabSMark Brown if (!buf) { 1983a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 19847328823dSDimitris Papastamos ret = -ENOMEM; 19857328823dSDimitris Papastamos goto out_fw; 1986a76fefabSMark Brown } 1987a76fefabSMark Brown 1988cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1989cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 19902159ad93SMark Brown if (ret != 0) { 19912159ad93SMark Brown adsp_err(dsp, 1992cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 19932159ad93SMark Brown file, regions, 1994cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 19952159ad93SMark Brown region_name, ret); 19962159ad93SMark Brown goto out_fw; 19972159ad93SMark Brown } 19982159ad93SMark Brown } 19992159ad93SMark Brown 20002159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 20012159ad93SMark Brown regions++; 20022159ad93SMark Brown } 20032159ad93SMark Brown 2004cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2005cf17c83cSMark Brown if (ret != 0) { 2006cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2007cf17c83cSMark Brown goto out_fw; 2008cf17c83cSMark Brown } 2009cf17c83cSMark Brown 20102159ad93SMark Brown if (pos > firmware->size) 20112159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 20122159ad93SMark Brown file, regions, pos - firmware->size); 20132159ad93SMark Brown 2014f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 2015f9f55e31SRichard Fitzgerald 20162159ad93SMark Brown out_fw: 2017cf17c83cSMark Brown regmap_async_complete(regmap); 2018cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 20192159ad93SMark Brown release_firmware(firmware); 20201cab2a84SRichard Fitzgerald kfree(text); 20212159ad93SMark Brown out: 20222159ad93SMark Brown kfree(file); 20232159ad93SMark Brown 20242159ad93SMark Brown return ret; 20252159ad93SMark Brown } 20262159ad93SMark Brown 2027eb65ccdbSLi Xu /* 2028eb65ccdbSLi Xu * Find wm_coeff_ctl with input name as its subname 2029eb65ccdbSLi Xu * If not found, return NULL 2030eb65ccdbSLi Xu */ 2031eb65ccdbSLi Xu static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp, 2032eb65ccdbSLi Xu const char *name, int type, 2033eb65ccdbSLi Xu unsigned int alg) 2034eb65ccdbSLi Xu { 2035eb65ccdbSLi Xu struct wm_coeff_ctl *pos, *rslt = NULL; 2036eb65ccdbSLi Xu 2037eb65ccdbSLi Xu list_for_each_entry(pos, &dsp->ctl_list, list) { 2038eb65ccdbSLi Xu if (!pos->subname) 2039eb65ccdbSLi Xu continue; 2040eb65ccdbSLi Xu if (strncmp(pos->subname, name, pos->subname_len) == 0 && 2041eb65ccdbSLi Xu pos->alg_region.alg == alg && 2042eb65ccdbSLi Xu pos->alg_region.type == type) { 2043eb65ccdbSLi Xu rslt = pos; 2044eb65ccdbSLi Xu break; 2045eb65ccdbSLi Xu } 2046eb65ccdbSLi Xu } 2047eb65ccdbSLi Xu 2048eb65ccdbSLi Xu return rslt; 2049eb65ccdbSLi Xu } 2050eb65ccdbSLi Xu 2051eb65ccdbSLi Xu int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, 2052eb65ccdbSLi Xu unsigned int alg, void *buf, size_t len) 2053eb65ccdbSLi Xu { 2054eb65ccdbSLi Xu struct wm_coeff_ctl *ctl; 2055eb65ccdbSLi Xu struct snd_kcontrol *kcontrol; 205620441614SAdam Brickman char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 2057eb65ccdbSLi Xu int ret; 2058eb65ccdbSLi Xu 2059eb65ccdbSLi Xu ctl = wm_adsp_get_ctl(dsp, name, type, alg); 2060eb65ccdbSLi Xu if (!ctl) 2061eb65ccdbSLi Xu return -EINVAL; 2062eb65ccdbSLi Xu 2063eb65ccdbSLi Xu if (len > ctl->len) 2064eb65ccdbSLi Xu return -EINVAL; 2065eb65ccdbSLi Xu 206673ecf1a6SCharles Keepax ret = wm_coeff_write_ctrl(ctl, buf, len); 206720441614SAdam Brickman if (ret) 206820441614SAdam Brickman return ret; 2069eb65ccdbSLi Xu 207020441614SAdam Brickman if (ctl->flags & WMFW_CTL_FLAG_SYS) 207120441614SAdam Brickman return 0; 207220441614SAdam Brickman 207320441614SAdam Brickman if (dsp->component->name_prefix) 207420441614SAdam Brickman snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", 207520441614SAdam Brickman dsp->component->name_prefix, ctl->name); 207620441614SAdam Brickman else 207720441614SAdam Brickman snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", 207820441614SAdam Brickman ctl->name); 207920441614SAdam Brickman 208020441614SAdam Brickman kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); 208120441614SAdam Brickman if (!kcontrol) { 208220441614SAdam Brickman adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); 208320441614SAdam Brickman return -EINVAL; 208420441614SAdam Brickman } 208520441614SAdam Brickman 2086eb65ccdbSLi Xu snd_ctl_notify(dsp->component->card->snd_card, 2087eb65ccdbSLi Xu SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); 2088eb65ccdbSLi Xu 2089eb65ccdbSLi Xu return ret; 2090eb65ccdbSLi Xu } 2091eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); 2092eb65ccdbSLi Xu 2093eb65ccdbSLi Xu int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, 2094eb65ccdbSLi Xu unsigned int alg, void *buf, size_t len) 2095eb65ccdbSLi Xu { 2096eb65ccdbSLi Xu struct wm_coeff_ctl *ctl; 2097eb65ccdbSLi Xu 2098eb65ccdbSLi Xu ctl = wm_adsp_get_ctl(dsp, name, type, alg); 2099eb65ccdbSLi Xu if (!ctl) 2100eb65ccdbSLi Xu return -EINVAL; 2101eb65ccdbSLi Xu 2102eb65ccdbSLi Xu if (len > ctl->len) 2103eb65ccdbSLi Xu return -EINVAL; 2104eb65ccdbSLi Xu 210573ecf1a6SCharles Keepax return wm_coeff_read_ctrl(ctl, buf, len); 2106eb65ccdbSLi Xu } 2107eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); 2108eb65ccdbSLi Xu 21092323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 21102323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 21112323736dSCharles Keepax { 21122323736dSCharles Keepax struct wm_coeff_ctl *ctl; 21132323736dSCharles Keepax 21142323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 21152323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 21162323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 21172323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 21182323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 21192323736dSCharles Keepax } 21202323736dSCharles Keepax } 21212323736dSCharles Keepax } 21222323736dSCharles Keepax 21233809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 21247f7cca08SCharles Keepax const struct wm_adsp_region *mem, 2125b618a185SCharles Keepax unsigned int pos, unsigned int len) 2126db40517cSMark Brown { 2127b618a185SCharles Keepax void *alg; 21287f7cca08SCharles Keepax unsigned int reg; 2129b618a185SCharles Keepax int ret; 2130db40517cSMark Brown __be32 val; 2131db40517cSMark Brown 21323809f001SCharles Keepax if (n_algs == 0) { 2133b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 2134b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2135db40517cSMark Brown } 2136db40517cSMark Brown 21373809f001SCharles Keepax if (n_algs > 1024) { 21383809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 2139b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2140b618a185SCharles Keepax } 2141b618a185SCharles Keepax 2142b618a185SCharles Keepax /* Read the terminator first to validate the length */ 2143170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos + len); 21447f7cca08SCharles Keepax 21457f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 2146b618a185SCharles Keepax if (ret != 0) { 2147b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 2148b618a185SCharles Keepax ret); 2149b618a185SCharles Keepax return ERR_PTR(ret); 2150b618a185SCharles Keepax } 2151b618a185SCharles Keepax 2152b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 2153503ada8aSRichard Fitzgerald adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 21547f7cca08SCharles Keepax reg, be32_to_cpu(val)); 21557f7cca08SCharles Keepax 21567f7cca08SCharles Keepax /* Convert length from DSP words to bytes */ 21577f7cca08SCharles Keepax len *= sizeof(u32); 2158b618a185SCharles Keepax 2159517ee74eSCharles Keepax alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 2160b618a185SCharles Keepax if (!alg) 2161b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 2162b618a185SCharles Keepax 2163170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos); 21647f7cca08SCharles Keepax 21657f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, alg, len); 2166b618a185SCharles Keepax if (ret != 0) { 21677d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 2168b618a185SCharles Keepax kfree(alg); 2169b618a185SCharles Keepax return ERR_PTR(ret); 2170b618a185SCharles Keepax } 2171b618a185SCharles Keepax 2172b618a185SCharles Keepax return alg; 2173b618a185SCharles Keepax } 2174b618a185SCharles Keepax 217514197095SCharles Keepax static struct wm_adsp_alg_region * 217614197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 217714197095SCharles Keepax { 217814197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 217914197095SCharles Keepax 218014197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 218114197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 218214197095SCharles Keepax return alg_region; 218314197095SCharles Keepax } 218414197095SCharles Keepax 218514197095SCharles Keepax return NULL; 218614197095SCharles Keepax } 218714197095SCharles Keepax 2188d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 2189d9d20e17SCharles Keepax int type, __be32 id, 2190d9d20e17SCharles Keepax __be32 base) 2191d9d20e17SCharles Keepax { 2192d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 2193d9d20e17SCharles Keepax 2194d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 2195d9d20e17SCharles Keepax if (!alg_region) 2196d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 2197d9d20e17SCharles Keepax 2198d9d20e17SCharles Keepax alg_region->type = type; 2199d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 2200d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 2201d9d20e17SCharles Keepax 2202d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 2203d9d20e17SCharles Keepax 22042323736dSCharles Keepax if (dsp->fw_ver > 0) 22052323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 22062323736dSCharles Keepax 2207d9d20e17SCharles Keepax return alg_region; 2208d9d20e17SCharles Keepax } 2209d9d20e17SCharles Keepax 221056574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 221156574d54SRichard Fitzgerald { 221256574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 221356574d54SRichard Fitzgerald 221456574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 221556574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 221656574d54SRichard Fitzgerald struct wm_adsp_alg_region, 221756574d54SRichard Fitzgerald list); 221856574d54SRichard Fitzgerald list_del(&alg_region->list); 221956574d54SRichard Fitzgerald kfree(alg_region); 222056574d54SRichard Fitzgerald } 222156574d54SRichard Fitzgerald } 222256574d54SRichard Fitzgerald 2223a5dcb24dSCharles Keepax static void wmfw_parse_id_header(struct wm_adsp *dsp, 2224a5dcb24dSCharles Keepax struct wmfw_id_hdr *fw, int nalgs) 2225a5dcb24dSCharles Keepax { 2226a5dcb24dSCharles Keepax dsp->fw_id = be32_to_cpu(fw->id); 2227a5dcb24dSCharles Keepax dsp->fw_id_version = be32_to_cpu(fw->ver); 2228a5dcb24dSCharles Keepax 2229cd537873SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 2230a5dcb24dSCharles Keepax dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 2231a5dcb24dSCharles Keepax (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2232a5dcb24dSCharles Keepax nalgs); 2233a5dcb24dSCharles Keepax } 2234a5dcb24dSCharles Keepax 2235170b1e12SWen Shi static void wmfw_v3_parse_id_header(struct wm_adsp *dsp, 2236170b1e12SWen Shi struct wmfw_v3_id_hdr *fw, int nalgs) 2237170b1e12SWen Shi { 2238170b1e12SWen Shi dsp->fw_id = be32_to_cpu(fw->id); 2239170b1e12SWen Shi dsp->fw_id_version = be32_to_cpu(fw->ver); 2240170b1e12SWen Shi dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 2241170b1e12SWen Shi 2242cd537873SCharles Keepax adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 2243170b1e12SWen Shi dsp->fw_id, dsp->fw_vendor_id, 2244170b1e12SWen Shi (dsp->fw_id_version & 0xff0000) >> 16, 2245170b1e12SWen Shi (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2246170b1e12SWen Shi nalgs); 2247170b1e12SWen Shi } 2248170b1e12SWen Shi 2249170b1e12SWen Shi static int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions, 2250170b1e12SWen Shi int *type, __be32 *base) 2251170b1e12SWen Shi { 2252170b1e12SWen Shi struct wm_adsp_alg_region *alg_region; 2253170b1e12SWen Shi int i; 2254170b1e12SWen Shi 2255170b1e12SWen Shi for (i = 0; i < nregions; i++) { 2256170b1e12SWen Shi alg_region = wm_adsp_create_region(dsp, type[i], id, base[i]); 2257170b1e12SWen Shi if (IS_ERR(alg_region)) 2258170b1e12SWen Shi return PTR_ERR(alg_region); 2259170b1e12SWen Shi } 2260170b1e12SWen Shi 2261170b1e12SWen Shi return 0; 2262170b1e12SWen Shi } 2263170b1e12SWen Shi 2264b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 2265b618a185SCharles Keepax { 2266b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 2267b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 22683809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2269b618a185SCharles Keepax const struct wm_adsp_region *mem; 2270b618a185SCharles Keepax unsigned int pos, len; 22713809f001SCharles Keepax size_t n_algs; 2272b618a185SCharles Keepax int i, ret; 2273b618a185SCharles Keepax 2274b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 22756c452bdaSTakashi Iwai if (WARN_ON(!mem)) 2276db40517cSMark Brown return -EINVAL; 2277db40517cSMark Brown 2278b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 2279db40517cSMark Brown sizeof(adsp1_id)); 2280db40517cSMark Brown if (ret != 0) { 2281db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 2282db40517cSMark Brown ret); 2283db40517cSMark Brown return ret; 2284db40517cSMark Brown } 2285db40517cSMark Brown 22863809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 2287a5dcb24dSCharles Keepax 2288a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs); 2289db40517cSMark Brown 2290d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2291d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 2292d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2293d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2294ac50009fSMark Brown 2295d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2296d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 2297d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2298d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2299ac50009fSMark Brown 23007f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 23017f7cca08SCharles Keepax pos = sizeof(adsp1_id) / sizeof(u32); 23027f7cca08SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 2303db40517cSMark Brown 23047f7cca08SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2305b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 2306b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 2307db40517cSMark Brown 23083809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2309471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 2310db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 2311db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 2312db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 2313471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 2314471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 2315471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 2316471f4885SMark Brown 2317d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2318d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2319d9d20e17SCharles Keepax adsp1_alg[i].dm); 2320d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2321d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2322d6d52179SJS Park goto out; 2323d6d52179SJS Park } 23242323736dSCharles Keepax if (dsp->fw_ver == 0) { 23253809f001SCharles Keepax if (i + 1 < n_algs) { 23266958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 23276958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 23286958eb2aSCharles Keepax len *= 4; 23292323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23308eb084d0SStuart Henderson len, NULL, 0, 0, 23318eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23326ab2b7b4SDimitris Papastamos } else { 23336ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 23346ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 23356ab2b7b4SDimitris Papastamos } 23362323736dSCharles Keepax } 2337471f4885SMark Brown 2338d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2339d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2340d9d20e17SCharles Keepax adsp1_alg[i].zm); 2341d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2342d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2343d6d52179SJS Park goto out; 2344d6d52179SJS Park } 23452323736dSCharles Keepax if (dsp->fw_ver == 0) { 23463809f001SCharles Keepax if (i + 1 < n_algs) { 23476958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 23486958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 23496958eb2aSCharles Keepax len *= 4; 23502323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23518eb084d0SStuart Henderson len, NULL, 0, 0, 23528eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23536ab2b7b4SDimitris Papastamos } else { 23546ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 23556ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 23566ab2b7b4SDimitris Papastamos } 2357b618a185SCharles Keepax } 23582323736dSCharles Keepax } 2359db40517cSMark Brown 2360b618a185SCharles Keepax out: 2361b618a185SCharles Keepax kfree(adsp1_alg); 2362b618a185SCharles Keepax return ret; 2363b618a185SCharles Keepax } 2364b618a185SCharles Keepax 2365b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 2366b618a185SCharles Keepax { 2367b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 2368b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 23693809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2370b618a185SCharles Keepax const struct wm_adsp_region *mem; 2371b618a185SCharles Keepax unsigned int pos, len; 23723809f001SCharles Keepax size_t n_algs; 2373b618a185SCharles Keepax int i, ret; 2374b618a185SCharles Keepax 2375b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2376b618a185SCharles Keepax if (WARN_ON(!mem)) 2377b618a185SCharles Keepax return -EINVAL; 2378b618a185SCharles Keepax 2379b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 2380b618a185SCharles Keepax sizeof(adsp2_id)); 2381b618a185SCharles Keepax if (ret != 0) { 2382b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 2383b618a185SCharles Keepax ret); 2384b618a185SCharles Keepax return ret; 2385b618a185SCharles Keepax } 2386b618a185SCharles Keepax 23873809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 2388a5dcb24dSCharles Keepax 2389a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs); 2390b618a185SCharles Keepax 2391d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2392d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2393d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2394d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2395b618a185SCharles Keepax 2396d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2397d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2398d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2399d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2400b618a185SCharles Keepax 2401d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2402d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2403d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2404d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2405b618a185SCharles Keepax 24067f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 24077f7cca08SCharles Keepax pos = sizeof(adsp2_id) / sizeof(u32); 24087f7cca08SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 2409b618a185SCharles Keepax 24107f7cca08SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2411b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2412b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2413b618a185SCharles Keepax 24143809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2415471f4885SMark Brown adsp_info(dsp, 2416471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2417db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2418db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2419db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2420471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2421471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2422471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2423471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2424471f4885SMark Brown 2425d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2426d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2427d9d20e17SCharles Keepax adsp2_alg[i].xm); 2428d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2429d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2430d6d52179SJS Park goto out; 2431d6d52179SJS Park } 24322323736dSCharles Keepax if (dsp->fw_ver == 0) { 24333809f001SCharles Keepax if (i + 1 < n_algs) { 24346958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 24356958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 24366958eb2aSCharles Keepax len *= 4; 24372323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 24388eb084d0SStuart Henderson len, NULL, 0, 0, 24398eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 24406ab2b7b4SDimitris Papastamos } else { 24416ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 24426ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 24436ab2b7b4SDimitris Papastamos } 24442323736dSCharles Keepax } 2445471f4885SMark Brown 2446d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2447d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2448d9d20e17SCharles Keepax adsp2_alg[i].ym); 2449d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2450d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2451d6d52179SJS Park goto out; 2452d6d52179SJS Park } 24532323736dSCharles Keepax if (dsp->fw_ver == 0) { 24543809f001SCharles Keepax if (i + 1 < n_algs) { 24556958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 24566958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 24576958eb2aSCharles Keepax len *= 4; 24582323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 24598eb084d0SStuart Henderson len, NULL, 0, 0, 24608eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 24616ab2b7b4SDimitris Papastamos } else { 24626ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 24636ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 24646ab2b7b4SDimitris Papastamos } 24652323736dSCharles Keepax } 2466471f4885SMark Brown 2467d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2468d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2469d9d20e17SCharles Keepax adsp2_alg[i].zm); 2470d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2471d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2472d6d52179SJS Park goto out; 2473d6d52179SJS Park } 24742323736dSCharles Keepax if (dsp->fw_ver == 0) { 24753809f001SCharles Keepax if (i + 1 < n_algs) { 24766958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 24776958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 24786958eb2aSCharles Keepax len *= 4; 24792323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 24808eb084d0SStuart Henderson len, NULL, 0, 0, 24818eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 24826ab2b7b4SDimitris Papastamos } else { 24836ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 24846ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 24856ab2b7b4SDimitris Papastamos } 2486db40517cSMark Brown } 24872323736dSCharles Keepax } 2488db40517cSMark Brown 2489db40517cSMark Brown out: 2490b618a185SCharles Keepax kfree(adsp2_alg); 2491db40517cSMark Brown return ret; 2492db40517cSMark Brown } 2493db40517cSMark Brown 2494170b1e12SWen Shi static int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id, 2495170b1e12SWen Shi __be32 xm_base, __be32 ym_base) 2496170b1e12SWen Shi { 2497170b1e12SWen Shi int types[] = { 2498170b1e12SWen Shi WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 2499170b1e12SWen Shi WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 2500170b1e12SWen Shi }; 2501170b1e12SWen Shi __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 2502170b1e12SWen Shi 2503170b1e12SWen Shi return wm_adsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); 2504170b1e12SWen Shi } 2505170b1e12SWen Shi 2506170b1e12SWen Shi static int wm_halo_setup_algs(struct wm_adsp *dsp) 2507170b1e12SWen Shi { 2508170b1e12SWen Shi struct wmfw_halo_id_hdr halo_id; 2509170b1e12SWen Shi struct wmfw_halo_alg_hdr *halo_alg; 2510170b1e12SWen Shi const struct wm_adsp_region *mem; 2511170b1e12SWen Shi unsigned int pos, len; 2512170b1e12SWen Shi size_t n_algs; 2513170b1e12SWen Shi int i, ret; 2514170b1e12SWen Shi 2515170b1e12SWen Shi mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2516170b1e12SWen Shi if (WARN_ON(!mem)) 2517170b1e12SWen Shi return -EINVAL; 2518170b1e12SWen Shi 2519170b1e12SWen Shi ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 2520170b1e12SWen Shi sizeof(halo_id)); 2521170b1e12SWen Shi if (ret != 0) { 2522170b1e12SWen Shi adsp_err(dsp, "Failed to read algorithm info: %d\n", 2523170b1e12SWen Shi ret); 2524170b1e12SWen Shi return ret; 2525170b1e12SWen Shi } 2526170b1e12SWen Shi 2527170b1e12SWen Shi n_algs = be32_to_cpu(halo_id.n_algs); 2528170b1e12SWen Shi 2529170b1e12SWen Shi wmfw_v3_parse_id_header(dsp, &halo_id.fw, n_algs); 2530170b1e12SWen Shi 2531170b1e12SWen Shi ret = wm_halo_create_regions(dsp, halo_id.fw.id, 2532b75a9799SCharles Keepax halo_id.xm_base, halo_id.ym_base); 2533170b1e12SWen Shi if (ret) 2534170b1e12SWen Shi return ret; 2535170b1e12SWen Shi 2536170b1e12SWen Shi /* Calculate offset and length in DSP words */ 2537170b1e12SWen Shi pos = sizeof(halo_id) / sizeof(u32); 2538170b1e12SWen Shi len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 2539170b1e12SWen Shi 2540170b1e12SWen Shi halo_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2541170b1e12SWen Shi if (IS_ERR(halo_alg)) 2542170b1e12SWen Shi return PTR_ERR(halo_alg); 2543170b1e12SWen Shi 2544170b1e12SWen Shi for (i = 0; i < n_algs; i++) { 2545170b1e12SWen Shi adsp_info(dsp, 2546170b1e12SWen Shi "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 2547170b1e12SWen Shi i, be32_to_cpu(halo_alg[i].alg.id), 2548170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 2549170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 2550170b1e12SWen Shi be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 2551170b1e12SWen Shi be32_to_cpu(halo_alg[i].xm_base), 2552170b1e12SWen Shi be32_to_cpu(halo_alg[i].ym_base)); 2553170b1e12SWen Shi 2554170b1e12SWen Shi ret = wm_halo_create_regions(dsp, halo_alg[i].alg.id, 2555170b1e12SWen Shi halo_alg[i].xm_base, 2556170b1e12SWen Shi halo_alg[i].ym_base); 2557170b1e12SWen Shi if (ret) 2558170b1e12SWen Shi goto out; 2559170b1e12SWen Shi } 2560170b1e12SWen Shi 2561170b1e12SWen Shi out: 2562170b1e12SWen Shi kfree(halo_alg); 2563170b1e12SWen Shi return ret; 2564170b1e12SWen Shi } 2565170b1e12SWen Shi 25662159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 25672159ad93SMark Brown { 2568cf17c83cSMark Brown LIST_HEAD(buf_list); 25692159ad93SMark Brown struct regmap *regmap = dsp->regmap; 25702159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 25712159ad93SMark Brown struct wmfw_coeff_item *blk; 25722159ad93SMark Brown const struct firmware *firmware; 2573471f4885SMark Brown const struct wm_adsp_region *mem; 2574471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 25752159ad93SMark Brown const char *region_name; 25762159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 25772159ad93SMark Brown char *file; 2578cf17c83cSMark Brown struct wm_adsp_buf *buf; 25792159ad93SMark Brown 25802159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 25812159ad93SMark Brown if (file == NULL) 25822159ad93SMark Brown return -ENOMEM; 25832159ad93SMark Brown 2584605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, 25851023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 25862159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 25872159ad93SMark Brown 25882159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 25892159ad93SMark Brown if (ret != 0) { 25902159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 25912159ad93SMark Brown ret = 0; 25922159ad93SMark Brown goto out; 25932159ad93SMark Brown } 25942159ad93SMark Brown ret = -EINVAL; 25952159ad93SMark Brown 25962159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 25972159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 25982159ad93SMark Brown file, firmware->size); 25992159ad93SMark Brown goto out_fw; 26002159ad93SMark Brown } 26012159ad93SMark Brown 26022159ad93SMark Brown hdr = (void *)&firmware->data[0]; 26032159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 26042159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2605a4cdbec7SCharles Keepax goto out_fw; 26062159ad93SMark Brown } 26072159ad93SMark Brown 2608c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2609c712326dSMark Brown case 1: 2610c712326dSMark Brown break; 2611c712326dSMark Brown default: 2612c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2613c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2614c712326dSMark Brown ret = -EINVAL; 2615c712326dSMark Brown goto out_fw; 2616c712326dSMark Brown } 2617c712326dSMark Brown 26182159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 26192159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 26202159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 26212159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 26222159ad93SMark Brown 26232159ad93SMark Brown pos = le32_to_cpu(hdr->len); 26242159ad93SMark Brown 26252159ad93SMark Brown blocks = 0; 26262159ad93SMark Brown while (pos < firmware->size && 262750dd2ea8SBen Hutchings sizeof(*blk) < firmware->size - pos) { 26282159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 26292159ad93SMark Brown 2630c712326dSMark Brown type = le16_to_cpu(blk->type); 2631c712326dSMark Brown offset = le16_to_cpu(blk->offset); 26322159ad93SMark Brown 26332159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 26342159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 26352159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 26362159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 26372159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 26382159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 26392159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 26402159ad93SMark Brown 26412159ad93SMark Brown reg = 0; 26422159ad93SMark Brown region_name = "Unknown"; 26432159ad93SMark Brown switch (type) { 2644c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2645c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 2646779bedffSJames Schulman case (WMFW_METADATA << 8): 26472159ad93SMark Brown break; 2648c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2649f395a218SMark Brown /* 2650f395a218SMark Brown * Old files may use this for global 2651f395a218SMark Brown * coefficients. 2652f395a218SMark Brown */ 2653f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2654f395a218SMark Brown offset == 0) { 2655f395a218SMark Brown region_name = "global coefficients"; 2656f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2657f395a218SMark Brown if (!mem) { 2658f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2659f395a218SMark Brown break; 2660f395a218SMark Brown } 2661170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, 0); 2662f395a218SMark Brown 2663f395a218SMark Brown } else { 26642159ad93SMark Brown region_name = "register"; 26652159ad93SMark Brown reg = offset; 2666f395a218SMark Brown } 26672159ad93SMark Brown break; 2668471f4885SMark Brown 2669471f4885SMark Brown case WMFW_ADSP1_DM: 2670471f4885SMark Brown case WMFW_ADSP1_ZM: 2671471f4885SMark Brown case WMFW_ADSP2_XM: 2672471f4885SMark Brown case WMFW_ADSP2_YM: 2673170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 2674170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 2675170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 2676471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2677471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2678471f4885SMark Brown type, le32_to_cpu(blk->id)); 2679471f4885SMark Brown 2680471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2681471f4885SMark Brown if (!mem) { 2682471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2683471f4885SMark Brown break; 2684471f4885SMark Brown } 2685471f4885SMark Brown 268614197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 268714197095SCharles Keepax le32_to_cpu(blk->id)); 268814197095SCharles Keepax if (alg_region) { 2689338c5188SMark Brown reg = alg_region->base; 2690170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, reg); 2691338c5188SMark Brown reg += offset; 269214197095SCharles Keepax } else { 2693471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2694471f4885SMark Brown type, le32_to_cpu(blk->id)); 269514197095SCharles Keepax } 2696471f4885SMark Brown break; 2697471f4885SMark Brown 26982159ad93SMark Brown default: 269925c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 270025c62f7eSMark Brown file, blocks, type, pos); 27012159ad93SMark Brown break; 27022159ad93SMark Brown } 27032159ad93SMark Brown 27042159ad93SMark Brown if (reg) { 270550dd2ea8SBen Hutchings if (le32_to_cpu(blk->len) > 270650dd2ea8SBen Hutchings firmware->size - pos - sizeof(*blk)) { 27071cab2a84SRichard Fitzgerald adsp_err(dsp, 27081cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 27091cab2a84SRichard Fitzgerald file, blocks, region_name, 27101cab2a84SRichard Fitzgerald le32_to_cpu(blk->len), 27111cab2a84SRichard Fitzgerald firmware->size); 27121cab2a84SRichard Fitzgerald ret = -EINVAL; 27131cab2a84SRichard Fitzgerald goto out_fw; 27141cab2a84SRichard Fitzgerald } 27151cab2a84SRichard Fitzgerald 2716cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2717cf17c83cSMark Brown le32_to_cpu(blk->len), 2718cf17c83cSMark Brown &buf_list); 2719a76fefabSMark Brown if (!buf) { 2720a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2721f4b82812SWei Yongjun ret = -ENOMEM; 2722f4b82812SWei Yongjun goto out_fw; 2723a76fefabSMark Brown } 2724a76fefabSMark Brown 272520da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 272620da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 272720da6d5aSMark Brown reg); 2728cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 27292159ad93SMark Brown le32_to_cpu(blk->len)); 27302159ad93SMark Brown if (ret != 0) { 27312159ad93SMark Brown adsp_err(dsp, 273243bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 273343bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 27342159ad93SMark Brown } 27352159ad93SMark Brown } 27362159ad93SMark Brown 2737be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 27382159ad93SMark Brown blocks++; 27392159ad93SMark Brown } 27402159ad93SMark Brown 2741cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2742cf17c83cSMark Brown if (ret != 0) 2743cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2744cf17c83cSMark Brown 27452159ad93SMark Brown if (pos > firmware->size) 27462159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 27472159ad93SMark Brown file, blocks, pos - firmware->size); 27482159ad93SMark Brown 2749f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2750f9f55e31SRichard Fitzgerald 27512159ad93SMark Brown out_fw: 27529da7a5a9SCharles Keepax regmap_async_complete(regmap); 27532159ad93SMark Brown release_firmware(firmware); 2754cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 27552159ad93SMark Brown out: 27562159ad93SMark Brown kfree(file); 2757f4b82812SWei Yongjun return ret; 27582159ad93SMark Brown } 27592159ad93SMark Brown 2760605391d0SRichard Fitzgerald static int wm_adsp_create_name(struct wm_adsp *dsp) 2761605391d0SRichard Fitzgerald { 2762605391d0SRichard Fitzgerald char *p; 2763605391d0SRichard Fitzgerald 2764605391d0SRichard Fitzgerald if (!dsp->name) { 2765605391d0SRichard Fitzgerald dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2766605391d0SRichard Fitzgerald dsp->num); 2767605391d0SRichard Fitzgerald if (!dsp->name) 2768605391d0SRichard Fitzgerald return -ENOMEM; 2769605391d0SRichard Fitzgerald } 2770605391d0SRichard Fitzgerald 2771605391d0SRichard Fitzgerald if (!dsp->fwf_name) { 2772605391d0SRichard Fitzgerald p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); 2773605391d0SRichard Fitzgerald if (!p) 2774605391d0SRichard Fitzgerald return -ENOMEM; 2775605391d0SRichard Fitzgerald 2776605391d0SRichard Fitzgerald dsp->fwf_name = p; 2777605391d0SRichard Fitzgerald for (; *p != 0; ++p) 2778605391d0SRichard Fitzgerald *p = tolower(*p); 2779605391d0SRichard Fitzgerald } 2780605391d0SRichard Fitzgerald 2781605391d0SRichard Fitzgerald return 0; 2782605391d0SRichard Fitzgerald } 2783605391d0SRichard Fitzgerald 2784dcad34f8SRichard Fitzgerald static int wm_adsp_common_init(struct wm_adsp *dsp) 27855e7a7a22SMark Brown { 2786605391d0SRichard Fitzgerald int ret; 2787605391d0SRichard Fitzgerald 2788605391d0SRichard Fitzgerald ret = wm_adsp_create_name(dsp); 2789605391d0SRichard Fitzgerald if (ret) 2790605391d0SRichard Fitzgerald return ret; 2791605391d0SRichard Fitzgerald 27923809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 2793dcad34f8SRichard Fitzgerald INIT_LIST_HEAD(&dsp->ctl_list); 27944f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->compr_list); 27954f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->buffer_list); 27965e7a7a22SMark Brown 2797078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2798078e7183SCharles Keepax 27995e7a7a22SMark Brown return 0; 28005e7a7a22SMark Brown } 2801dcad34f8SRichard Fitzgerald 2802dcad34f8SRichard Fitzgerald int wm_adsp1_init(struct wm_adsp *dsp) 2803dcad34f8SRichard Fitzgerald { 28044e08d50dSCharles Keepax dsp->ops = &wm_adsp1_ops; 28054e08d50dSCharles Keepax 2806dcad34f8SRichard Fitzgerald return wm_adsp_common_init(dsp); 2807dcad34f8SRichard Fitzgerald } 28085e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 28095e7a7a22SMark Brown 28102159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 28112159ad93SMark Brown struct snd_kcontrol *kcontrol, 28122159ad93SMark Brown int event) 28132159ad93SMark Brown { 28140fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 28150fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 28162159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 28176ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 28182159ad93SMark Brown int ret; 28197585a5b0SCharles Keepax unsigned int val; 28202159ad93SMark Brown 28210fe1daa6SKuninori Morimoto dsp->component = component; 282292bb4c32SDimitris Papastamos 2823078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2824078e7183SCharles Keepax 28252159ad93SMark Brown switch (event) { 28262159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 28272159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28282159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 28292159ad93SMark Brown 283094e205bfSChris Rattray /* 283194e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 283294e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 283394e205bfSChris Rattray */ 283494e205bfSChris Rattray if (dsp->sysclk_reg) { 283594e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 283694e205bfSChris Rattray if (ret != 0) { 283794e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 283894e205bfSChris Rattray ret); 2839078e7183SCharles Keepax goto err_mutex; 284094e205bfSChris Rattray } 284194e205bfSChris Rattray 28427d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 284394e205bfSChris Rattray 284494e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 284594e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 284694e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 284794e205bfSChris Rattray if (ret != 0) { 284894e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 284994e205bfSChris Rattray ret); 2850078e7183SCharles Keepax goto err_mutex; 285194e205bfSChris Rattray } 285294e205bfSChris Rattray } 285394e205bfSChris Rattray 28542159ad93SMark Brown ret = wm_adsp_load(dsp); 28552159ad93SMark Brown if (ret != 0) 2856078e7183SCharles Keepax goto err_ena; 28572159ad93SMark Brown 2858b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2859db40517cSMark Brown if (ret != 0) 2860078e7183SCharles Keepax goto err_ena; 2861db40517cSMark Brown 28622159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 28632159ad93SMark Brown if (ret != 0) 2864078e7183SCharles Keepax goto err_ena; 28652159ad93SMark Brown 28660c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 286781ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 28686ab2b7b4SDimitris Papastamos if (ret != 0) 2869078e7183SCharles Keepax goto err_ena; 28706ab2b7b4SDimitris Papastamos 28710c2e3f34SDimitris Papastamos /* Sync set controls */ 287281ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 28736ab2b7b4SDimitris Papastamos if (ret != 0) 2874078e7183SCharles Keepax goto err_ena; 28756ab2b7b4SDimitris Papastamos 287628823ebaSCharles Keepax dsp->booted = true; 287728823ebaSCharles Keepax 28782159ad93SMark Brown /* Start the core running */ 28792159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28802159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 28812159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 288228823ebaSCharles Keepax 288328823ebaSCharles Keepax dsp->running = true; 28842159ad93SMark Brown break; 28852159ad93SMark Brown 28862159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 288728823ebaSCharles Keepax dsp->running = false; 288828823ebaSCharles Keepax dsp->booted = false; 288928823ebaSCharles Keepax 28902159ad93SMark Brown /* Halt the core */ 28912159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28922159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 28932159ad93SMark Brown 28942159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 28952159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 28962159ad93SMark Brown 28972159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28982159ad93SMark Brown ADSP1_SYS_ENA, 0); 28996ab2b7b4SDimitris Papastamos 290081ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 29016ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2902b0101b4fSDimitris Papastamos 290356574d54SRichard Fitzgerald 290456574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 29052159ad93SMark Brown break; 29062159ad93SMark Brown 29072159ad93SMark Brown default: 29082159ad93SMark Brown break; 29092159ad93SMark Brown } 29102159ad93SMark Brown 2911078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2912078e7183SCharles Keepax 29132159ad93SMark Brown return 0; 29142159ad93SMark Brown 2915078e7183SCharles Keepax err_ena: 29162159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 29172159ad93SMark Brown ADSP1_SYS_ENA, 0); 2918078e7183SCharles Keepax err_mutex: 2919078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2920078e7183SCharles Keepax 29212159ad93SMark Brown return ret; 29222159ad93SMark Brown } 29232159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 29242159ad93SMark Brown 29254e08d50dSCharles Keepax static int wm_adsp2v2_enable_core(struct wm_adsp *dsp) 29262159ad93SMark Brown { 29272159ad93SMark Brown unsigned int val; 29282159ad93SMark Brown int ret, count; 29292159ad93SMark Brown 29302159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2931939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 29327d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 29332159ad93SMark Brown if (ret != 0) 29342159ad93SMark Brown return ret; 2935939fd1e8SCharles Keepax 2936939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2937939fd1e8SCharles Keepax break; 2938939fd1e8SCharles Keepax 29391fa96f3fSCharles Keepax usleep_range(250, 500); 2940939fd1e8SCharles Keepax } 29412159ad93SMark Brown 29422159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 29432159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 29442159ad93SMark Brown return -EBUSY; 29452159ad93SMark Brown } 29462159ad93SMark Brown 29472159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 29482159ad93SMark Brown 29492159ad93SMark Brown return 0; 29502159ad93SMark Brown } 29512159ad93SMark Brown 29524e08d50dSCharles Keepax static int wm_adsp2_enable_core(struct wm_adsp *dsp) 29534e08d50dSCharles Keepax { 29544e08d50dSCharles Keepax int ret; 29554e08d50dSCharles Keepax 29564e08d50dSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 29574e08d50dSCharles Keepax ADSP2_SYS_ENA, ADSP2_SYS_ENA); 29584e08d50dSCharles Keepax if (ret != 0) 29594e08d50dSCharles Keepax return ret; 29604e08d50dSCharles Keepax 29614e08d50dSCharles Keepax return wm_adsp2v2_enable_core(dsp); 29624e08d50dSCharles Keepax } 29634e08d50dSCharles Keepax 29642b0ee49fSCharles Keepax static int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 29652b0ee49fSCharles Keepax { 29662b0ee49fSCharles Keepax struct regmap *regmap = dsp->regmap; 29672b0ee49fSCharles Keepax unsigned int code0, code1, lock_reg; 29682b0ee49fSCharles Keepax 29692b0ee49fSCharles Keepax if (!(lock_regions & WM_ADSP2_REGION_ALL)) 29702b0ee49fSCharles Keepax return 0; 29712b0ee49fSCharles Keepax 29722b0ee49fSCharles Keepax lock_regions &= WM_ADSP2_REGION_ALL; 29732b0ee49fSCharles Keepax lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 29742b0ee49fSCharles Keepax 29752b0ee49fSCharles Keepax while (lock_regions) { 29762b0ee49fSCharles Keepax code0 = code1 = 0; 29772b0ee49fSCharles Keepax if (lock_regions & BIT(0)) { 29782b0ee49fSCharles Keepax code0 = ADSP2_LOCK_CODE_0; 29792b0ee49fSCharles Keepax code1 = ADSP2_LOCK_CODE_1; 29802b0ee49fSCharles Keepax } 29812b0ee49fSCharles Keepax if (lock_regions & BIT(1)) { 29822b0ee49fSCharles Keepax code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 29832b0ee49fSCharles Keepax code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 29842b0ee49fSCharles Keepax } 29852b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code0); 29862b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code1); 29872b0ee49fSCharles Keepax lock_regions >>= 2; 29882b0ee49fSCharles Keepax lock_reg += 2; 29892b0ee49fSCharles Keepax } 29902b0ee49fSCharles Keepax 29912b0ee49fSCharles Keepax return 0; 29922b0ee49fSCharles Keepax } 29932b0ee49fSCharles Keepax 29944e08d50dSCharles Keepax static int wm_adsp2_enable_memory(struct wm_adsp *dsp) 29954e08d50dSCharles Keepax { 29964e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 29974e08d50dSCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 29984e08d50dSCharles Keepax } 29994e08d50dSCharles Keepax 30004e08d50dSCharles Keepax static void wm_adsp2_disable_memory(struct wm_adsp *dsp) 30014e08d50dSCharles Keepax { 30024e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 30034e08d50dSCharles Keepax ADSP2_MEM_ENA, 0); 30044e08d50dSCharles Keepax } 30054e08d50dSCharles Keepax 30064e08d50dSCharles Keepax static void wm_adsp2_disable_core(struct wm_adsp *dsp) 30074e08d50dSCharles Keepax { 30084e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 30094e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 30104e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 30114e08d50dSCharles Keepax 30124e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 30134e08d50dSCharles Keepax ADSP2_SYS_ENA, 0); 30144e08d50dSCharles Keepax } 30154e08d50dSCharles Keepax 30164e08d50dSCharles Keepax static void wm_adsp2v2_disable_core(struct wm_adsp *dsp) 30174e08d50dSCharles Keepax { 30184e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 30194e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 30204e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 30214e08d50dSCharles Keepax } 30224e08d50dSCharles Keepax 30234e08d50dSCharles Keepax static void wm_adsp_boot_work(struct work_struct *work) 30242159ad93SMark Brown { 3025d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 3026d8a64d6aSCharles Keepax struct wm_adsp, 3027d8a64d6aSCharles Keepax boot_work); 30282159ad93SMark Brown int ret; 30292159ad93SMark Brown 3030078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 3031078e7183SCharles Keepax 30324e08d50dSCharles Keepax if (dsp->ops->enable_memory) { 30334e08d50dSCharles Keepax ret = dsp->ops->enable_memory(dsp); 303490d19ba5SCharles Keepax if (ret != 0) 303590d19ba5SCharles Keepax goto err_mutex; 30364e08d50dSCharles Keepax } 303790d19ba5SCharles Keepax 30384e08d50dSCharles Keepax if (dsp->ops->enable_core) { 30394e08d50dSCharles Keepax ret = dsp->ops->enable_core(dsp); 30402159ad93SMark Brown if (ret != 0) 3041d589d8b8SCharles Keepax goto err_mem; 30424e08d50dSCharles Keepax } 30432159ad93SMark Brown 30442159ad93SMark Brown ret = wm_adsp_load(dsp); 30452159ad93SMark Brown if (ret != 0) 3046078e7183SCharles Keepax goto err_ena; 30472159ad93SMark Brown 30484e08d50dSCharles Keepax ret = dsp->ops->setup_algs(dsp); 3049db40517cSMark Brown if (ret != 0) 3050078e7183SCharles Keepax goto err_ena; 3051db40517cSMark Brown 30522159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 30532159ad93SMark Brown if (ret != 0) 3054078e7183SCharles Keepax goto err_ena; 30552159ad93SMark Brown 30560c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 305781ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 30586ab2b7b4SDimitris Papastamos if (ret != 0) 3059078e7183SCharles Keepax goto err_ena; 30606ab2b7b4SDimitris Papastamos 30614e08d50dSCharles Keepax if (dsp->ops->disable_core) 30624e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 306390d19ba5SCharles Keepax 3064e779974bSCharles Keepax dsp->booted = true; 3065e779974bSCharles Keepax 3066078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3067078e7183SCharles Keepax 3068d8a64d6aSCharles Keepax return; 3069d8a64d6aSCharles Keepax 3070078e7183SCharles Keepax err_ena: 30714e08d50dSCharles Keepax if (dsp->ops->disable_core) 30724e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 3073d589d8b8SCharles Keepax err_mem: 30744e08d50dSCharles Keepax if (dsp->ops->disable_memory) 30754e08d50dSCharles Keepax dsp->ops->disable_memory(dsp); 3076078e7183SCharles Keepax err_mutex: 3077078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3078d8a64d6aSCharles Keepax } 3079d8a64d6aSCharles Keepax 3080170b1e12SWen Shi static int wm_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions) 3081170b1e12SWen Shi { 3082170b1e12SWen Shi struct reg_sequence config[] = { 3083170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 3084170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 3085170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 3086170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 3087170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 3088170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 3089170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 3090170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 3091170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 3092170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 3093170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 3094170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 3095170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 3096170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 3097170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 3098170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 3099170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 3100170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 3101170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 3102170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 3103170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 3104170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 3105170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 3106170b1e12SWen Shi }; 3107170b1e12SWen Shi 3108170b1e12SWen Shi return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 3109170b1e12SWen Shi } 3110170b1e12SWen Shi 3111b9070df4SRichard Fitzgerald int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) 3112d82d767fSCharles Keepax { 3113b9070df4SRichard Fitzgerald struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 3114b9070df4SRichard Fitzgerald struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3115b9070df4SRichard Fitzgerald struct wm_adsp *dsp = &dsps[w->shift]; 3116d82d767fSCharles Keepax int ret; 3117d82d767fSCharles Keepax 3118b9070df4SRichard Fitzgerald ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 3119d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 3120d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 3121b9070df4SRichard Fitzgerald if (ret) 3122d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 3123b9070df4SRichard Fitzgerald 3124b9070df4SRichard Fitzgerald return ret; 3125e1ea1879SRichard Fitzgerald } 3126b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); 3127d82d767fSCharles Keepax 3128af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 3129af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3130af813a6fSCharles Keepax { 31310fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3132b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3133b1470d4cSAjit Pandey struct soc_mixer_control *mc = 3134b1470d4cSAjit Pandey (struct soc_mixer_control *)kcontrol->private_value; 3135b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3136af813a6fSCharles Keepax 3137af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 3138af813a6fSCharles Keepax 3139af813a6fSCharles Keepax return 0; 3140af813a6fSCharles Keepax } 3141af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 3142af813a6fSCharles Keepax 3143af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 3144af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3145af813a6fSCharles Keepax { 31460fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3147b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 31480fe1daa6SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 3149af813a6fSCharles Keepax struct soc_mixer_control *mc = 3150af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 3151b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3152af813a6fSCharles Keepax char preload[32]; 3153af813a6fSCharles Keepax 3154605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 3155af813a6fSCharles Keepax 3156af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 3157af813a6fSCharles Keepax 3158af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 315995a594d0SCharles Keepax snd_soc_component_force_enable_pin(component, preload); 3160af813a6fSCharles Keepax else 316195a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3162af813a6fSCharles Keepax 3163af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 3164af813a6fSCharles Keepax 3165868e49a4SStuart Henderson flush_work(&dsp->boot_work); 3166868e49a4SStuart Henderson 3167af813a6fSCharles Keepax return 0; 3168af813a6fSCharles Keepax } 3169af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 3170af813a6fSCharles Keepax 317151a2c944SMayuresh Kulkarni static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) 317251a2c944SMayuresh Kulkarni { 317351a2c944SMayuresh Kulkarni regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 317451a2c944SMayuresh Kulkarni ADSP2_WDT_ENA_MASK, 0); 317551a2c944SMayuresh Kulkarni } 317651a2c944SMayuresh Kulkarni 31778bc144f9SStuart Henderson static void wm_halo_stop_watchdog(struct wm_adsp *dsp) 31788bc144f9SStuart Henderson { 31798bc144f9SStuart Henderson regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 31808bc144f9SStuart Henderson HALO_WDT_EN_MASK, 0); 31818bc144f9SStuart Henderson } 31828bc144f9SStuart Henderson 31834e08d50dSCharles Keepax int wm_adsp_early_event(struct snd_soc_dapm_widget *w, 3184b9070df4SRichard Fitzgerald struct snd_kcontrol *kcontrol, int event) 318512db5eddSCharles Keepax { 31860fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 31870fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 318812db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 318957a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 319012db5eddSCharles Keepax 319112db5eddSCharles Keepax switch (event) { 319212db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 319312db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 319412db5eddSCharles Keepax break; 319557a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 3196bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3197bb24ee41SCharles Keepax 319857a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 319957a60cc3SCharles Keepax 320057a60cc3SCharles Keepax dsp->fw_id = 0; 320157a60cc3SCharles Keepax dsp->fw_id_version = 0; 320257a60cc3SCharles Keepax 320357a60cc3SCharles Keepax dsp->booted = false; 320457a60cc3SCharles Keepax 32054e08d50dSCharles Keepax if (dsp->ops->disable_memory) 32064e08d50dSCharles Keepax dsp->ops->disable_memory(dsp); 320757a60cc3SCharles Keepax 320857a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 320957a60cc3SCharles Keepax ctl->enabled = 0; 321057a60cc3SCharles Keepax 321157a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 321257a60cc3SCharles Keepax 3213bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3214bb24ee41SCharles Keepax 321557a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 321657a60cc3SCharles Keepax break; 321712db5eddSCharles Keepax default: 321812db5eddSCharles Keepax break; 3219cab27258SCharles Keepax } 322012db5eddSCharles Keepax 322112db5eddSCharles Keepax return 0; 322212db5eddSCharles Keepax } 32234e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event); 322412db5eddSCharles Keepax 32254e08d50dSCharles Keepax static int wm_adsp2_start_core(struct wm_adsp *dsp) 32264e08d50dSCharles Keepax { 32274e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 32284e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 32294e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 32304e08d50dSCharles Keepax } 32314e08d50dSCharles Keepax 32324e08d50dSCharles Keepax static void wm_adsp2_stop_core(struct wm_adsp *dsp) 32334e08d50dSCharles Keepax { 32344e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 32354e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 32364e08d50dSCharles Keepax } 32374e08d50dSCharles Keepax 32384e08d50dSCharles Keepax int wm_adsp_event(struct snd_soc_dapm_widget *w, 3239d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 3240d8a64d6aSCharles Keepax { 32410fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 32420fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3243d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 3244d8a64d6aSCharles Keepax int ret; 3245d8a64d6aSCharles Keepax 3246d8a64d6aSCharles Keepax switch (event) { 3247d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 3248d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 3249d8a64d6aSCharles Keepax 3250bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3251bb24ee41SCharles Keepax 3252bb24ee41SCharles Keepax if (!dsp->booted) { 3253bb24ee41SCharles Keepax ret = -EIO; 3254bb24ee41SCharles Keepax goto err; 3255bb24ee41SCharles Keepax } 3256d8a64d6aSCharles Keepax 32574e08d50dSCharles Keepax if (dsp->ops->enable_core) { 32584e08d50dSCharles Keepax ret = dsp->ops->enable_core(dsp); 325990d19ba5SCharles Keepax if (ret != 0) 326090d19ba5SCharles Keepax goto err; 32614e08d50dSCharles Keepax } 326290d19ba5SCharles Keepax 3263cef45771SCharles Keepax /* Sync set controls */ 3264cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 3265cef45771SCharles Keepax if (ret != 0) 3266cef45771SCharles Keepax goto err; 3267cef45771SCharles Keepax 32684e08d50dSCharles Keepax if (dsp->ops->lock_memory) { 32694e08d50dSCharles Keepax ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 32704e08d50dSCharles Keepax if (ret != 0) { 32714e08d50dSCharles Keepax adsp_err(dsp, "Error configuring MPU: %d\n", 32724e08d50dSCharles Keepax ret); 32734e08d50dSCharles Keepax goto err; 32744e08d50dSCharles Keepax } 32754e08d50dSCharles Keepax } 327651a2c944SMayuresh Kulkarni 32774e08d50dSCharles Keepax if (dsp->ops->start_core) { 32784e08d50dSCharles Keepax ret = dsp->ops->start_core(dsp); 3279d8a64d6aSCharles Keepax if (ret != 0) 3280d8a64d6aSCharles Keepax goto err; 32814e08d50dSCharles Keepax } 32822cd19bdbSCharles Keepax 328348c2c993SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) { 32842cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 3285bb24ee41SCharles Keepax if (ret < 0) 328648c2c993SCharles Keepax goto err; 328748c2c993SCharles Keepax } 32882cd19bdbSCharles Keepax 3289e779974bSCharles Keepax dsp->running = true; 32901023dbd9SMark Brown 3291612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 32922159ad93SMark Brown break; 32932159ad93SMark Brown 32942159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 3295f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 3296f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 3297f4f0c4c6SRichard Fitzgerald 32984e08d50dSCharles Keepax if (dsp->ops->stop_watchdog) 32994e08d50dSCharles Keepax dsp->ops->stop_watchdog(dsp); 330051a2c944SMayuresh Kulkarni 330110337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 33024e08d50dSCharles Keepax if (dsp->ops->show_fw_status) 33034e08d50dSCharles Keepax dsp->ops->show_fw_status(dsp); 330410337b07SRichard Fitzgerald 3305078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 3306078e7183SCharles Keepax 33071023dbd9SMark Brown dsp->running = false; 33081023dbd9SMark Brown 33094e08d50dSCharles Keepax if (dsp->ops->stop_core) 33104e08d50dSCharles Keepax dsp->ops->stop_core(dsp); 33114e08d50dSCharles Keepax if (dsp->ops->disable_core) 33124e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 33132d30b575SMark Brown 33142cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 33152cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 33162cd19bdbSCharles Keepax 3317a2bcbc1bSCharles Keepax dsp->fatal_error = false; 3318a2bcbc1bSCharles Keepax 3319078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3320078e7183SCharles Keepax 332157a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 33222159ad93SMark Brown break; 33232159ad93SMark Brown 33242159ad93SMark Brown default: 33252159ad93SMark Brown break; 33262159ad93SMark Brown } 33272159ad93SMark Brown 33282159ad93SMark Brown return 0; 33292159ad93SMark Brown err: 33304e08d50dSCharles Keepax if (dsp->ops->stop_core) 33314e08d50dSCharles Keepax dsp->ops->stop_core(dsp); 33324e08d50dSCharles Keepax if (dsp->ops->disable_core) 33334e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 3334bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 33352159ad93SMark Brown return ret; 33362159ad93SMark Brown } 33374e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event); 3338973838a0SMark Brown 3339170b1e12SWen Shi static int wm_halo_start_core(struct wm_adsp *dsp) 3340170b1e12SWen Shi { 3341170b1e12SWen Shi return regmap_update_bits(dsp->regmap, 3342170b1e12SWen Shi dsp->base + HALO_CCM_CORE_CONTROL, 3343170b1e12SWen Shi HALO_CORE_EN, HALO_CORE_EN); 3344170b1e12SWen Shi } 3345170b1e12SWen Shi 3346170b1e12SWen Shi static void wm_halo_stop_core(struct wm_adsp *dsp) 3347170b1e12SWen Shi { 3348170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 3349170b1e12SWen Shi HALO_CORE_EN, 0); 3350170b1e12SWen Shi 3351809589a9SCharles Keepax /* reset halo core with CORE_SOFT_RESET */ 3352170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 3353170b1e12SWen Shi HALO_CORE_SOFT_RESET_MASK, 1); 3354170b1e12SWen Shi } 3355170b1e12SWen Shi 33560fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 3357f5e2ce92SRichard Fitzgerald { 3358af813a6fSCharles Keepax char preload[32]; 3359af813a6fSCharles Keepax 3360605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 336195a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3362685f51a5SRichard Fitzgerald 33630fe1daa6SKuninori Morimoto wm_adsp2_init_debugfs(dsp, component); 3364f9f55e31SRichard Fitzgerald 33650fe1daa6SKuninori Morimoto dsp->component = component; 3366af813a6fSCharles Keepax 33670a047f07SRichard Fitzgerald return 0; 3368f5e2ce92SRichard Fitzgerald } 33690fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 3370f5e2ce92SRichard Fitzgerald 33710fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 3372f5e2ce92SRichard Fitzgerald { 3373f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 3374f9f55e31SRichard Fitzgerald 3375f5e2ce92SRichard Fitzgerald return 0; 3376f5e2ce92SRichard Fitzgerald } 33770fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 3378f5e2ce92SRichard Fitzgerald 337981ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 3380973838a0SMark Brown { 3381973838a0SMark Brown int ret; 3382973838a0SMark Brown 3383dcad34f8SRichard Fitzgerald ret = wm_adsp_common_init(dsp); 3384605391d0SRichard Fitzgerald if (ret) 3385605391d0SRichard Fitzgerald return ret; 3386605391d0SRichard Fitzgerald 3387e1ea1879SRichard Fitzgerald switch (dsp->rev) { 3388e1ea1879SRichard Fitzgerald case 0: 338910a2b662SMark Brown /* 339010a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 339110a2b662SMark Brown * power saving. 339210a2b662SMark Brown */ 33933809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 339410a2b662SMark Brown ADSP2_MEM_ENA, 0); 3395e1ea1879SRichard Fitzgerald if (ret) { 3396e1ea1879SRichard Fitzgerald adsp_err(dsp, 3397e1ea1879SRichard Fitzgerald "Failed to clear memory retention: %d\n", ret); 339810a2b662SMark Brown return ret; 339910a2b662SMark Brown } 34004e08d50dSCharles Keepax 34014e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[0]; 34024e08d50dSCharles Keepax break; 34034e08d50dSCharles Keepax case 1: 34044e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[1]; 3405e1ea1879SRichard Fitzgerald break; 3406e1ea1879SRichard Fitzgerald default: 34074e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[2]; 3408e1ea1879SRichard Fitzgerald break; 3409e1ea1879SRichard Fitzgerald } 341010a2b662SMark Brown 34114e08d50dSCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 34126ab2b7b4SDimitris Papastamos 3413973838a0SMark Brown return 0; 3414973838a0SMark Brown } 3415973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 34160a37c6efSPraveen Diwakar 3417170b1e12SWen Shi int wm_halo_init(struct wm_adsp *dsp) 3418170b1e12SWen Shi { 3419170b1e12SWen Shi int ret; 3420170b1e12SWen Shi 3421170b1e12SWen Shi ret = wm_adsp_common_init(dsp); 3422170b1e12SWen Shi if (ret) 3423170b1e12SWen Shi return ret; 3424170b1e12SWen Shi 3425170b1e12SWen Shi dsp->ops = &wm_halo_ops; 3426170b1e12SWen Shi 3427170b1e12SWen Shi INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 3428170b1e12SWen Shi 3429170b1e12SWen Shi return 0; 3430170b1e12SWen Shi } 3431170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init); 3432170b1e12SWen Shi 343366225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 343466225e98SRichard Fitzgerald { 343566225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 343666225e98SRichard Fitzgerald 343766225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 343866225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 343966225e98SRichard Fitzgerald list); 344066225e98SRichard Fitzgerald list_del(&ctl->list); 344166225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 344266225e98SRichard Fitzgerald } 344366225e98SRichard Fitzgerald } 344466225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 344566225e98SRichard Fitzgerald 3446edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 3447edd71350SCharles Keepax { 3448edd71350SCharles Keepax return compr->buf != NULL; 3449edd71350SCharles Keepax } 3450edd71350SCharles Keepax 3451edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 3452edd71350SCharles Keepax { 34534f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf = NULL, *tmp; 34544f2d4eabSStuart Henderson 3455a2bcbc1bSCharles Keepax if (compr->dsp->fatal_error) 3456a2bcbc1bSCharles Keepax return -EINVAL; 3457a2bcbc1bSCharles Keepax 34584f2d4eabSStuart Henderson list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { 34594f2d4eabSStuart Henderson if (!tmp->name || !strcmp(compr->name, tmp->name)) { 34604f2d4eabSStuart Henderson buf = tmp; 34614f2d4eabSStuart Henderson break; 34624f2d4eabSStuart Henderson } 34634f2d4eabSStuart Henderson } 34644f2d4eabSStuart Henderson 34654f2d4eabSStuart Henderson if (!buf) 3466edd71350SCharles Keepax return -EINVAL; 3467edd71350SCharles Keepax 34684f2d4eabSStuart Henderson compr->buf = buf; 3469789b930aSCharles Keepax buf->compr = compr; 3470edd71350SCharles Keepax 3471edd71350SCharles Keepax return 0; 3472edd71350SCharles Keepax } 3473edd71350SCharles Keepax 3474721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 3475721be3beSCharles Keepax { 3476721be3beSCharles Keepax if (!compr) 3477721be3beSCharles Keepax return; 3478721be3beSCharles Keepax 3479721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 3480721be3beSCharles Keepax if (compr->stream) 3481721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 3482721be3beSCharles Keepax 3483721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 3484721be3beSCharles Keepax compr->buf->compr = NULL; 3485721be3beSCharles Keepax compr->buf = NULL; 3486721be3beSCharles Keepax } 3487721be3beSCharles Keepax } 3488721be3beSCharles Keepax 3489406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 3490406abc95SCharles Keepax { 34914f2d4eabSStuart Henderson struct wm_adsp_compr *compr, *tmp; 34924f2d4eabSStuart Henderson struct snd_soc_pcm_runtime *rtd = stream->private_data; 3493406abc95SCharles Keepax int ret = 0; 3494406abc95SCharles Keepax 3495406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3496406abc95SCharles Keepax 3497406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 34980d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support compressed API\n", 3499b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 3500406abc95SCharles Keepax ret = -ENXIO; 3501406abc95SCharles Keepax goto out; 3502406abc95SCharles Keepax } 3503406abc95SCharles Keepax 3504406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 35050d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support stream direction\n", 3506b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 3507406abc95SCharles Keepax ret = -EINVAL; 3508406abc95SCharles Keepax goto out; 3509406abc95SCharles Keepax } 3510406abc95SCharles Keepax 35114f2d4eabSStuart Henderson list_for_each_entry(tmp, &dsp->compr_list, list) { 3512b5cb8558SKuninori Morimoto if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) { 35130d3fba3eSCharles Keepax adsp_err(dsp, "%s: Only a single stream supported per dai\n", 3514b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 351595fe9597SCharles Keepax ret = -EBUSY; 351695fe9597SCharles Keepax goto out; 351795fe9597SCharles Keepax } 35184f2d4eabSStuart Henderson } 351995fe9597SCharles Keepax 3520406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 3521406abc95SCharles Keepax if (!compr) { 3522406abc95SCharles Keepax ret = -ENOMEM; 3523406abc95SCharles Keepax goto out; 3524406abc95SCharles Keepax } 3525406abc95SCharles Keepax 3526406abc95SCharles Keepax compr->dsp = dsp; 3527406abc95SCharles Keepax compr->stream = stream; 3528b5cb8558SKuninori Morimoto compr->name = asoc_rtd_to_codec(rtd, 0)->name; 3529406abc95SCharles Keepax 35304f2d4eabSStuart Henderson list_add_tail(&compr->list, &dsp->compr_list); 3531406abc95SCharles Keepax 3532406abc95SCharles Keepax stream->runtime->private_data = compr; 3533406abc95SCharles Keepax 3534406abc95SCharles Keepax out: 3535406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3536406abc95SCharles Keepax 3537406abc95SCharles Keepax return ret; 3538406abc95SCharles Keepax } 3539406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 3540406abc95SCharles Keepax 35413a5ccf25SKuninori Morimoto int wm_adsp_compr_free(struct snd_soc_component *component, 35423a5ccf25SKuninori Morimoto struct snd_compr_stream *stream) 3543406abc95SCharles Keepax { 3544406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3545406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3546406abc95SCharles Keepax 3547406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3548406abc95SCharles Keepax 3549721be3beSCharles Keepax wm_adsp_compr_detach(compr); 35504f2d4eabSStuart Henderson list_del(&compr->list); 3551406abc95SCharles Keepax 355283a40ce9SCharles Keepax kfree(compr->raw_buf); 3553406abc95SCharles Keepax kfree(compr); 3554406abc95SCharles Keepax 3555406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3556406abc95SCharles Keepax 3557406abc95SCharles Keepax return 0; 3558406abc95SCharles Keepax } 3559406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 3560406abc95SCharles Keepax 3561406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 3562406abc95SCharles Keepax struct snd_compr_params *params) 3563406abc95SCharles Keepax { 3564406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3565406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3566406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3567406abc95SCharles Keepax const struct snd_codec_desc *desc; 3568406abc95SCharles Keepax int i, j; 3569406abc95SCharles Keepax 3570406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 3571406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 3572406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 3573406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 3574406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 35750d3fba3eSCharles Keepax compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", 3576406abc95SCharles Keepax params->buffer.fragment_size, 3577406abc95SCharles Keepax params->buffer.fragments); 3578406abc95SCharles Keepax 3579406abc95SCharles Keepax return -EINVAL; 3580406abc95SCharles Keepax } 3581406abc95SCharles Keepax 3582406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 3583406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 3584406abc95SCharles Keepax desc = &caps->desc; 3585406abc95SCharles Keepax 3586406abc95SCharles Keepax if (caps->id != params->codec.id) 3587406abc95SCharles Keepax continue; 3588406abc95SCharles Keepax 3589406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 3590406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 3591406abc95SCharles Keepax continue; 3592406abc95SCharles Keepax } else { 3593406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 3594406abc95SCharles Keepax continue; 3595406abc95SCharles Keepax } 3596406abc95SCharles Keepax 3597406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 3598406abc95SCharles Keepax continue; 3599406abc95SCharles Keepax 3600406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 3601406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 3602406abc95SCharles Keepax return 0; 3603406abc95SCharles Keepax } 3604406abc95SCharles Keepax 36050d3fba3eSCharles Keepax compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 3606406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 3607406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 3608406abc95SCharles Keepax return -EINVAL; 3609406abc95SCharles Keepax } 3610406abc95SCharles Keepax 3611565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 3612565ace46SCharles Keepax { 3613565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 3614565ace46SCharles Keepax } 3615565ace46SCharles Keepax 36163a5ccf25SKuninori Morimoto int wm_adsp_compr_set_params(struct snd_soc_component *component, 36173a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 3618406abc95SCharles Keepax struct snd_compr_params *params) 3619406abc95SCharles Keepax { 3620406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 362183a40ce9SCharles Keepax unsigned int size; 3622406abc95SCharles Keepax int ret; 3623406abc95SCharles Keepax 3624406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 3625406abc95SCharles Keepax if (ret) 3626406abc95SCharles Keepax return ret; 3627406abc95SCharles Keepax 3628406abc95SCharles Keepax compr->size = params->buffer; 3629406abc95SCharles Keepax 36300d3fba3eSCharles Keepax compr_dbg(compr, "fragment_size=%d fragments=%d\n", 3631406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 3632406abc95SCharles Keepax 363383a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 363483a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 363583a40ce9SCharles Keepax if (!compr->raw_buf) 363683a40ce9SCharles Keepax return -ENOMEM; 363783a40ce9SCharles Keepax 3638da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 3639da2b3358SCharles Keepax 3640406abc95SCharles Keepax return 0; 3641406abc95SCharles Keepax } 3642406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 3643406abc95SCharles Keepax 36443a5ccf25SKuninori Morimoto int wm_adsp_compr_get_caps(struct snd_soc_component *component, 36453a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 3646406abc95SCharles Keepax struct snd_compr_caps *caps) 3647406abc95SCharles Keepax { 3648406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3649406abc95SCharles Keepax int fw = compr->dsp->fw; 3650406abc95SCharles Keepax int i; 3651406abc95SCharles Keepax 3652406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 3653406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 3654406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 3655406abc95SCharles Keepax 3656406abc95SCharles Keepax caps->num_codecs = i; 3657406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 3658406abc95SCharles Keepax 3659406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 3660406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 3661406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 3662406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 3663406abc95SCharles Keepax } 3664406abc95SCharles Keepax 3665406abc95SCharles Keepax return 0; 3666406abc95SCharles Keepax } 3667406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 3668406abc95SCharles Keepax 36697726e498SRichard Fitzgerald static int wm_adsp_read_raw_data_block(struct wm_adsp *dsp, int mem_type, 36702cd19bdbSCharles Keepax unsigned int mem_addr, 36717726e498SRichard Fitzgerald unsigned int num_words, __be32 *data) 36722cd19bdbSCharles Keepax { 36732cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 36747726e498SRichard Fitzgerald unsigned int reg; 36752cd19bdbSCharles Keepax int ret; 36762cd19bdbSCharles Keepax 36772cd19bdbSCharles Keepax if (!mem) 36782cd19bdbSCharles Keepax return -EINVAL; 36792cd19bdbSCharles Keepax 3680170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 36812cd19bdbSCharles Keepax 36822cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 36832cd19bdbSCharles Keepax sizeof(*data) * num_words); 36842cd19bdbSCharles Keepax if (ret < 0) 36852cd19bdbSCharles Keepax return ret; 36862cd19bdbSCharles Keepax 36872cd19bdbSCharles Keepax return 0; 36882cd19bdbSCharles Keepax } 36892cd19bdbSCharles Keepax 36902cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 36912cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 36922cd19bdbSCharles Keepax { 36937726e498SRichard Fitzgerald __be32 raw; 36947726e498SRichard Fitzgerald int ret; 36957726e498SRichard Fitzgerald 36967726e498SRichard Fitzgerald ret = wm_adsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw); 36977726e498SRichard Fitzgerald if (ret) 36987726e498SRichard Fitzgerald return ret; 36997726e498SRichard Fitzgerald 37007726e498SRichard Fitzgerald *data = be32_to_cpu(raw) & 0x00ffffffu; 37017726e498SRichard Fitzgerald 37027726e498SRichard Fitzgerald return 0; 37032cd19bdbSCharles Keepax } 37042cd19bdbSCharles Keepax 37052cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 37062cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 37072cd19bdbSCharles Keepax { 37082cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 3709a0b653e8SRichard Fitzgerald __be32 val = cpu_to_be32(data & 0x00ffffffu); 37102cd19bdbSCharles Keepax unsigned int reg; 37112cd19bdbSCharles Keepax 37122cd19bdbSCharles Keepax if (!mem) 37132cd19bdbSCharles Keepax return -EINVAL; 37142cd19bdbSCharles Keepax 3715170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 37162cd19bdbSCharles Keepax 3717a0b653e8SRichard Fitzgerald return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 37182cd19bdbSCharles Keepax } 37192cd19bdbSCharles Keepax 37202cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 37212cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 37222cd19bdbSCharles Keepax { 3723fb13f19dSAndrew Ford return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, 37242cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 37252cd19bdbSCharles Keepax } 37262cd19bdbSCharles Keepax 37272cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 37282cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 37292cd19bdbSCharles Keepax { 3730fb13f19dSAndrew Ford return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, 37312cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 37322cd19bdbSCharles Keepax } 37332cd19bdbSCharles Keepax 37347726e498SRichard Fitzgerald static void wm_adsp_remove_padding(u32 *buf, int nwords) 3735cc7d6ce9SCharles Keepax { 37367726e498SRichard Fitzgerald const __be32 *pack_in = (__be32 *)buf; 3737cc7d6ce9SCharles Keepax u8 *pack_out = (u8 *)buf; 37387726e498SRichard Fitzgerald int i; 3739cc7d6ce9SCharles Keepax 37407726e498SRichard Fitzgerald /* 37417726e498SRichard Fitzgerald * DSP words from the register map have pad bytes and the data bytes 37427726e498SRichard Fitzgerald * are in swapped order. This swaps back to the original little-endian 37437726e498SRichard Fitzgerald * order and strips the pad bytes. 37447726e498SRichard Fitzgerald */ 3745cc7d6ce9SCharles Keepax for (i = 0; i < nwords; i++) { 37467726e498SRichard Fitzgerald u32 word = be32_to_cpu(*pack_in++); 37477726e498SRichard Fitzgerald *pack_out++ = (u8)word; 37487726e498SRichard Fitzgerald *pack_out++ = (u8)(word >> 8); 37497726e498SRichard Fitzgerald *pack_out++ = (u8)(word >> 16); 3750cc7d6ce9SCharles Keepax } 3751cc7d6ce9SCharles Keepax } 3752cc7d6ce9SCharles Keepax 37531e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 37541e38f069SCharles Keepax { 37551e38f069SCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 37561e38f069SCharles Keepax struct wm_adsp_buffer_region *region; 37571e38f069SCharles Keepax u32 offset = 0; 37581e38f069SCharles Keepax int i, ret; 37591e38f069SCharles Keepax 3760a792af69SCharles Keepax buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), 3761a792af69SCharles Keepax GFP_KERNEL); 3762a792af69SCharles Keepax if (!buf->regions) 3763a792af69SCharles Keepax return -ENOMEM; 3764a792af69SCharles Keepax 37651e38f069SCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 37661e38f069SCharles Keepax region = &buf->regions[i]; 37671e38f069SCharles Keepax 37681e38f069SCharles Keepax region->offset = offset; 37691e38f069SCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 37701e38f069SCharles Keepax 37711e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 37721e38f069SCharles Keepax ®ion->base_addr); 37731e38f069SCharles Keepax if (ret < 0) 37741e38f069SCharles Keepax return ret; 37751e38f069SCharles Keepax 37761e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 37771e38f069SCharles Keepax &offset); 37781e38f069SCharles Keepax if (ret < 0) 37791e38f069SCharles Keepax return ret; 37801e38f069SCharles Keepax 37811e38f069SCharles Keepax region->cumulative_size = offset; 37821e38f069SCharles Keepax 37830d3fba3eSCharles Keepax compr_dbg(buf, 37841e38f069SCharles Keepax "region=%d type=%d base=%08x off=%08x size=%08x\n", 37851e38f069SCharles Keepax i, region->mem_type, region->base_addr, 37861e38f069SCharles Keepax region->offset, region->cumulative_size); 37871e38f069SCharles Keepax } 37881e38f069SCharles Keepax 37891e38f069SCharles Keepax return 0; 37901e38f069SCharles Keepax } 37911e38f069SCharles Keepax 37921e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 37931e38f069SCharles Keepax { 37941e38f069SCharles Keepax buf->irq_count = 0xFFFFFFFF; 37951e38f069SCharles Keepax buf->read_index = -1; 37961e38f069SCharles Keepax buf->avail = 0; 37971e38f069SCharles Keepax } 37981e38f069SCharles Keepax 3799a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) 3800a792af69SCharles Keepax { 3801a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3802a792af69SCharles Keepax 3803a792af69SCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3804a792af69SCharles Keepax if (!buf) 3805a792af69SCharles Keepax return NULL; 3806a792af69SCharles Keepax 3807a792af69SCharles Keepax buf->dsp = dsp; 3808a792af69SCharles Keepax 3809a792af69SCharles Keepax wm_adsp_buffer_clear(buf); 3810a792af69SCharles Keepax 38114f2d4eabSStuart Henderson list_add_tail(&buf->list, &dsp->buffer_list); 3812a792af69SCharles Keepax 3813a792af69SCharles Keepax return buf; 3814a792af69SCharles Keepax } 3815a792af69SCharles Keepax 3816a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) 38172cd19bdbSCharles Keepax { 38182cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 3819a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 38202cd19bdbSCharles Keepax u32 xmalg, addr, magic; 38212cd19bdbSCharles Keepax int i, ret; 38222cd19bdbSCharles Keepax 38239daf4fd0SLi Xu alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 38249daf4fd0SLi Xu if (!alg_region) { 38259daf4fd0SLi Xu adsp_err(dsp, "No algorithm region found\n"); 38269daf4fd0SLi Xu return -EINVAL; 38279daf4fd0SLi Xu } 38289daf4fd0SLi Xu 3829a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(dsp); 3830a792af69SCharles Keepax if (!buf) 3831a792af69SCharles Keepax return -ENOMEM; 3832a792af69SCharles Keepax 3833170b1e12SWen Shi xmalg = dsp->ops->sys_config_size / sizeof(__be32); 38342cd19bdbSCharles Keepax 38352cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 38362cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 38372cd19bdbSCharles Keepax if (ret < 0) 38382cd19bdbSCharles Keepax return ret; 38392cd19bdbSCharles Keepax 38402cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 3841a792af69SCharles Keepax return -ENODEV; 38422cd19bdbSCharles Keepax 38432cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 38442cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 38452cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 38462cd19bdbSCharles Keepax &buf->host_buf_ptr); 38472cd19bdbSCharles Keepax if (ret < 0) 38482cd19bdbSCharles Keepax return ret; 38492cd19bdbSCharles Keepax 38502cd19bdbSCharles Keepax if (buf->host_buf_ptr) 38512cd19bdbSCharles Keepax break; 38522cd19bdbSCharles Keepax 38532cd19bdbSCharles Keepax usleep_range(1000, 2000); 38542cd19bdbSCharles Keepax } 38552cd19bdbSCharles Keepax 38562cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 38572cd19bdbSCharles Keepax return -EIO; 38582cd19bdbSCharles Keepax 3859fb13f19dSAndrew Ford buf->host_buf_mem_type = WMFW_ADSP2_XM; 3860fb13f19dSAndrew Ford 3861a792af69SCharles Keepax ret = wm_adsp_buffer_populate(buf); 3862a792af69SCharles Keepax if (ret < 0) 3863a792af69SCharles Keepax return ret; 3864a792af69SCharles Keepax 38650d3fba3eSCharles Keepax compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); 38662cd19bdbSCharles Keepax 38672cd19bdbSCharles Keepax return 0; 38682cd19bdbSCharles Keepax } 38692cd19bdbSCharles Keepax 3870a792af69SCharles Keepax static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) 3871d52ed4b0SRichard Fitzgerald { 38724f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 coeff_v1; 3873a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3874a0b653e8SRichard Fitzgerald unsigned int reg, version; 3875a0b653e8SRichard Fitzgerald __be32 bufp; 3876a792af69SCharles Keepax int ret, i; 3877d52ed4b0SRichard Fitzgerald 3878d52ed4b0SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 3879d52ed4b0SRichard Fitzgerald if (ret) 3880d52ed4b0SRichard Fitzgerald return ret; 3881d52ed4b0SRichard Fitzgerald 3882d52ed4b0SRichard Fitzgerald for (i = 0; i < 5; ++i) { 3883a0b653e8SRichard Fitzgerald ret = regmap_raw_read(ctl->dsp->regmap, reg, &bufp, sizeof(bufp)); 3884d52ed4b0SRichard Fitzgerald if (ret < 0) 3885d52ed4b0SRichard Fitzgerald return ret; 3886d52ed4b0SRichard Fitzgerald 3887a0b653e8SRichard Fitzgerald if (bufp) 3888d52ed4b0SRichard Fitzgerald break; 3889d52ed4b0SRichard Fitzgerald 3890d52ed4b0SRichard Fitzgerald usleep_range(1000, 2000); 3891d52ed4b0SRichard Fitzgerald } 3892d52ed4b0SRichard Fitzgerald 3893a0b653e8SRichard Fitzgerald if (!bufp) { 3894a792af69SCharles Keepax adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); 3895d52ed4b0SRichard Fitzgerald return -EIO; 3896d52ed4b0SRichard Fitzgerald } 3897d52ed4b0SRichard Fitzgerald 3898a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(ctl->dsp); 38992cd19bdbSCharles Keepax if (!buf) 39002cd19bdbSCharles Keepax return -ENOMEM; 39012cd19bdbSCharles Keepax 3902a792af69SCharles Keepax buf->host_buf_mem_type = ctl->alg_region.type; 3903a0b653e8SRichard Fitzgerald buf->host_buf_ptr = be32_to_cpu(bufp); 39042cd19bdbSCharles Keepax 39052cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 3906a792af69SCharles Keepax if (ret < 0) 3907a792af69SCharles Keepax return ret; 3908a792af69SCharles Keepax 39094f2d4eabSStuart Henderson /* 39104f2d4eabSStuart Henderson * v0 host_buffer coefficients didn't have versioning, so if the 39114f2d4eabSStuart Henderson * control is one word, assume version 0. 39124f2d4eabSStuart Henderson */ 39134f2d4eabSStuart Henderson if (ctl->len == 4) { 39140d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); 3915a792af69SCharles Keepax return 0; 39162cd19bdbSCharles Keepax } 39172cd19bdbSCharles Keepax 39184f2d4eabSStuart Henderson ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, 39194f2d4eabSStuart Henderson sizeof(coeff_v1)); 39204f2d4eabSStuart Henderson if (ret < 0) 39214f2d4eabSStuart Henderson return ret; 39224f2d4eabSStuart Henderson 3923a0b653e8SRichard Fitzgerald version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK; 3924a0b653e8SRichard Fitzgerald version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; 39254f2d4eabSStuart Henderson 3926a0b653e8SRichard Fitzgerald if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { 39274f2d4eabSStuart Henderson adsp_err(ctl->dsp, 39284f2d4eabSStuart Henderson "Host buffer coeff ver %u > supported version %u\n", 3929a0b653e8SRichard Fitzgerald version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); 39304f2d4eabSStuart Henderson return -EINVAL; 39314f2d4eabSStuart Henderson } 39324f2d4eabSStuart Henderson 39337726e498SRichard Fitzgerald wm_adsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); 39344f2d4eabSStuart Henderson 39354f2d4eabSStuart Henderson buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, 39364f2d4eabSStuart Henderson (char *)&coeff_v1.name); 39374f2d4eabSStuart Henderson 39380d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", 3939a0b653e8SRichard Fitzgerald buf->host_buf_ptr, version); 39404f2d4eabSStuart Henderson 3941a0b653e8SRichard Fitzgerald return version; 39424f2d4eabSStuart Henderson } 39434f2d4eabSStuart Henderson 3944a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 3945a792af69SCharles Keepax { 3946a792af69SCharles Keepax struct wm_coeff_ctl *ctl; 3947a792af69SCharles Keepax int ret; 3948a792af69SCharles Keepax 3949a792af69SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 3950a792af69SCharles Keepax if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 3951a792af69SCharles Keepax continue; 3952a792af69SCharles Keepax 3953a792af69SCharles Keepax if (!ctl->enabled) 3954a792af69SCharles Keepax continue; 3955a792af69SCharles Keepax 3956a792af69SCharles Keepax ret = wm_adsp_buffer_parse_coeff(ctl); 3957a792af69SCharles Keepax if (ret < 0) { 3958a792af69SCharles Keepax adsp_err(dsp, "Failed to parse coeff: %d\n", ret); 3959a792af69SCharles Keepax goto error; 39604f2d4eabSStuart Henderson } else if (ret == 0) { 39614f2d4eabSStuart Henderson /* Only one buffer supported for version 0 */ 3962a792af69SCharles Keepax return 0; 3963a792af69SCharles Keepax } 39644f2d4eabSStuart Henderson } 3965a792af69SCharles Keepax 39664f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 3967a792af69SCharles Keepax /* Fall back to legacy support */ 3968a792af69SCharles Keepax ret = wm_adsp_buffer_parse_legacy(dsp); 3969a792af69SCharles Keepax if (ret) { 3970a792af69SCharles Keepax adsp_err(dsp, "Failed to parse legacy: %d\n", ret); 3971a792af69SCharles Keepax goto error; 3972a792af69SCharles Keepax } 3973a792af69SCharles Keepax } 39742cd19bdbSCharles Keepax 39752cd19bdbSCharles Keepax return 0; 39762cd19bdbSCharles Keepax 3977a792af69SCharles Keepax error: 3978a792af69SCharles Keepax wm_adsp_buffer_free(dsp); 39792cd19bdbSCharles Keepax return ret; 39802cd19bdbSCharles Keepax } 39812cd19bdbSCharles Keepax 39822cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 39832cd19bdbSCharles Keepax { 39844f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf, *tmp; 3985721be3beSCharles Keepax 39864f2d4eabSStuart Henderson list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { 39874f2d4eabSStuart Henderson wm_adsp_compr_detach(buf->compr); 39882cd19bdbSCharles Keepax 39894f2d4eabSStuart Henderson kfree(buf->name); 39904f2d4eabSStuart Henderson kfree(buf->regions); 39914f2d4eabSStuart Henderson list_del(&buf->list); 39924f2d4eabSStuart Henderson kfree(buf); 39932cd19bdbSCharles Keepax } 39942cd19bdbSCharles Keepax 39952cd19bdbSCharles Keepax return 0; 39962cd19bdbSCharles Keepax } 39972cd19bdbSCharles Keepax 3998f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 3999f938f348SStuart Henderson { 4000f938f348SStuart Henderson int ret; 4001f938f348SStuart Henderson 4002f938f348SStuart Henderson ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 4003f938f348SStuart Henderson if (ret < 0) { 400448ead31cSCharles Keepax compr_err(buf, "Failed to check buffer error: %d\n", ret); 4005f938f348SStuart Henderson return ret; 4006f938f348SStuart Henderson } 4007f938f348SStuart Henderson if (buf->error != 0) { 400848ead31cSCharles Keepax compr_err(buf, "Buffer error occurred: %d\n", buf->error); 4009f938f348SStuart Henderson return -EIO; 4010f938f348SStuart Henderson } 4011f938f348SStuart Henderson 4012f938f348SStuart Henderson return 0; 4013f938f348SStuart Henderson } 4014f938f348SStuart Henderson 40153a5ccf25SKuninori Morimoto int wm_adsp_compr_trigger(struct snd_soc_component *component, 40163a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, int cmd) 401795fe9597SCharles Keepax { 401895fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 401995fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 402095fe9597SCharles Keepax int ret = 0; 402195fe9597SCharles Keepax 40220d3fba3eSCharles Keepax compr_dbg(compr, "Trigger: %d\n", cmd); 402395fe9597SCharles Keepax 402495fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 402595fe9597SCharles Keepax 402695fe9597SCharles Keepax switch (cmd) { 402795fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 402861fc060cSCharles Keepax if (!wm_adsp_compr_attached(compr)) { 402995fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 403095fe9597SCharles Keepax if (ret < 0) { 40310d3fba3eSCharles Keepax compr_err(compr, "Failed to link buffer and stream: %d\n", 403295fe9597SCharles Keepax ret); 403395fe9597SCharles Keepax break; 403495fe9597SCharles Keepax } 403561fc060cSCharles Keepax } 403661fc060cSCharles Keepax 4037f938f348SStuart Henderson ret = wm_adsp_buffer_get_error(compr->buf); 4038f938f348SStuart Henderson if (ret < 0) 4039f938f348SStuart Henderson break; 4040f938f348SStuart Henderson 4041565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 4042565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 4043565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 4044565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 4045565ace46SCharles Keepax if (ret < 0) { 40460d3fba3eSCharles Keepax compr_err(compr, "Failed to set high water mark: %d\n", 4047565ace46SCharles Keepax ret); 4048565ace46SCharles Keepax break; 4049565ace46SCharles Keepax } 405095fe9597SCharles Keepax break; 405195fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 405243d147beSCharles Keepax if (wm_adsp_compr_attached(compr)) 4053639e5eb3SCharles Keepax wm_adsp_buffer_clear(compr->buf); 405495fe9597SCharles Keepax break; 405595fe9597SCharles Keepax default: 405695fe9597SCharles Keepax ret = -EINVAL; 405795fe9597SCharles Keepax break; 405895fe9597SCharles Keepax } 405995fe9597SCharles Keepax 406095fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 406195fe9597SCharles Keepax 406295fe9597SCharles Keepax return ret; 406395fe9597SCharles Keepax } 406495fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 406595fe9597SCharles Keepax 4066565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 4067565ace46SCharles Keepax { 4068565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 4069565ace46SCharles Keepax 4070565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 4071565ace46SCharles Keepax } 4072565ace46SCharles Keepax 4073565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 4074565ace46SCharles Keepax { 4075565ace46SCharles Keepax u32 next_read_index, next_write_index; 4076565ace46SCharles Keepax int write_index, read_index, avail; 4077565ace46SCharles Keepax int ret; 4078565ace46SCharles Keepax 4079565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 4080565ace46SCharles Keepax if (buf->read_index < 0) { 4081565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 4082565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 4083565ace46SCharles Keepax &next_read_index); 4084565ace46SCharles Keepax if (ret < 0) 4085565ace46SCharles Keepax return ret; 4086565ace46SCharles Keepax 4087565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 4088565ace46SCharles Keepax 4089565ace46SCharles Keepax if (read_index < 0) { 40900d3fba3eSCharles Keepax compr_dbg(buf, "Avail check on unstarted stream\n"); 4091565ace46SCharles Keepax return 0; 4092565ace46SCharles Keepax } 4093565ace46SCharles Keepax 4094565ace46SCharles Keepax buf->read_index = read_index; 4095565ace46SCharles Keepax } 4096565ace46SCharles Keepax 4097565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 4098565ace46SCharles Keepax &next_write_index); 4099565ace46SCharles Keepax if (ret < 0) 4100565ace46SCharles Keepax return ret; 4101565ace46SCharles Keepax 4102565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 4103565ace46SCharles Keepax 4104565ace46SCharles Keepax avail = write_index - buf->read_index; 4105565ace46SCharles Keepax if (avail < 0) 4106565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 4107565ace46SCharles Keepax 41080d3fba3eSCharles Keepax compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 410933d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 4110565ace46SCharles Keepax 4111565ace46SCharles Keepax buf->avail = avail; 4112565ace46SCharles Keepax 4113565ace46SCharles Keepax return 0; 4114565ace46SCharles Keepax } 4115565ace46SCharles Keepax 4116565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 4117565ace46SCharles Keepax { 4118612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 4119612047f0SCharles Keepax struct wm_adsp_compr *compr; 4120565ace46SCharles Keepax int ret = 0; 4121565ace46SCharles Keepax 4122565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 4123565ace46SCharles Keepax 41244f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 4125565ace46SCharles Keepax ret = -ENODEV; 4126565ace46SCharles Keepax goto out; 4127565ace46SCharles Keepax } 41280d3fba3eSCharles Keepax 4129565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 4130565ace46SCharles Keepax 41314f2d4eabSStuart Henderson list_for_each_entry(buf, &dsp->buffer_list, list) { 41324f2d4eabSStuart Henderson compr = buf->compr; 41334f2d4eabSStuart Henderson 41349771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 41359771b18aSCharles Keepax if (ret < 0) 41365847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 4137565ace46SCharles Keepax 4138565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 4139565ace46SCharles Keepax &buf->irq_count); 4140565ace46SCharles Keepax if (ret < 0) { 41410d3fba3eSCharles Keepax compr_err(buf, "Failed to get irq_count: %d\n", ret); 4142565ace46SCharles Keepax goto out; 4143565ace46SCharles Keepax } 4144565ace46SCharles Keepax 4145565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4146565ace46SCharles Keepax if (ret < 0) { 41470d3fba3eSCharles Keepax compr_err(buf, "Error reading avail: %d\n", ret); 4148565ace46SCharles Keepax goto out; 4149565ace46SCharles Keepax } 4150565ace46SCharles Keepax 415120b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 415220b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 415320b7f7c5SCharles Keepax 41545847609eSCharles Keepax out_notify: 4155c7dae7c4SCharles Keepax if (compr && compr->stream) 415683a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 41574f2d4eabSStuart Henderson } 415883a40ce9SCharles Keepax 4159565ace46SCharles Keepax out: 4160565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4161565ace46SCharles Keepax 4162565ace46SCharles Keepax return ret; 4163565ace46SCharles Keepax } 4164565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 4165565ace46SCharles Keepax 4166565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 4167565ace46SCharles Keepax { 4168565ace46SCharles Keepax if (buf->irq_count & 0x01) 4169565ace46SCharles Keepax return 0; 4170565ace46SCharles Keepax 41710d3fba3eSCharles Keepax compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); 4172565ace46SCharles Keepax 4173565ace46SCharles Keepax buf->irq_count |= 0x01; 4174565ace46SCharles Keepax 4175565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 4176565ace46SCharles Keepax buf->irq_count); 4177565ace46SCharles Keepax } 4178565ace46SCharles Keepax 41793a5ccf25SKuninori Morimoto int wm_adsp_compr_pointer(struct snd_soc_component *component, 41803a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 4181565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 4182565ace46SCharles Keepax { 4183565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 4184565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 4185612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 4186565ace46SCharles Keepax int ret = 0; 4187565ace46SCharles Keepax 41880d3fba3eSCharles Keepax compr_dbg(compr, "Pointer request\n"); 4189565ace46SCharles Keepax 4190565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 4191565ace46SCharles Keepax 4192612047f0SCharles Keepax buf = compr->buf; 4193612047f0SCharles Keepax 4194aa612f2bSCharles Keepax if (dsp->fatal_error || !buf || buf->error) { 41958d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 4196565ace46SCharles Keepax ret = -EIO; 4197565ace46SCharles Keepax goto out; 4198565ace46SCharles Keepax } 4199565ace46SCharles Keepax 4200565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 4201565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4202565ace46SCharles Keepax if (ret < 0) { 42030d3fba3eSCharles Keepax compr_err(compr, "Error reading avail: %d\n", ret); 4204565ace46SCharles Keepax goto out; 4205565ace46SCharles Keepax } 4206565ace46SCharles Keepax 4207565ace46SCharles Keepax /* 4208565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 4209565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 4210565ace46SCharles Keepax */ 4211565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 42125847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 42138d280664SCharles Keepax if (ret < 0) { 4214789b930aSCharles Keepax if (buf->error) 42158d280664SCharles Keepax snd_compr_stop_error(stream, 42168d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 42175847609eSCharles Keepax goto out; 42188d280664SCharles Keepax } 42195847609eSCharles Keepax 4220565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 4221565ace46SCharles Keepax if (ret < 0) { 42220d3fba3eSCharles Keepax compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", 4223565ace46SCharles Keepax ret); 4224565ace46SCharles Keepax goto out; 4225565ace46SCharles Keepax } 4226565ace46SCharles Keepax } 4227565ace46SCharles Keepax } 4228565ace46SCharles Keepax 4229565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 4230565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 4231da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 4232565ace46SCharles Keepax 4233565ace46SCharles Keepax out: 4234565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4235565ace46SCharles Keepax 4236565ace46SCharles Keepax return ret; 4237565ace46SCharles Keepax } 4238565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 4239565ace46SCharles Keepax 424083a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 424183a40ce9SCharles Keepax { 424283a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 424383a40ce9SCharles Keepax unsigned int adsp_addr; 424483a40ce9SCharles Keepax int mem_type, nwords, max_read; 4245cc7d6ce9SCharles Keepax int i, ret; 424683a40ce9SCharles Keepax 424783a40ce9SCharles Keepax /* Calculate read parameters */ 424883a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 424983a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 425083a40ce9SCharles Keepax break; 425183a40ce9SCharles Keepax 425283a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 425383a40ce9SCharles Keepax return -EINVAL; 425483a40ce9SCharles Keepax 425583a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 425683a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 425783a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 425883a40ce9SCharles Keepax 425983a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 426083a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 426183a40ce9SCharles Keepax 426283a40ce9SCharles Keepax if (nwords > target) 426383a40ce9SCharles Keepax nwords = target; 426483a40ce9SCharles Keepax if (nwords > buf->avail) 426583a40ce9SCharles Keepax nwords = buf->avail; 426683a40ce9SCharles Keepax if (nwords > max_read) 426783a40ce9SCharles Keepax nwords = max_read; 426883a40ce9SCharles Keepax if (!nwords) 426983a40ce9SCharles Keepax return 0; 427083a40ce9SCharles Keepax 427183a40ce9SCharles Keepax /* Read data from DSP */ 42727726e498SRichard Fitzgerald ret = wm_adsp_read_raw_data_block(buf->dsp, mem_type, adsp_addr, 4273a0b653e8SRichard Fitzgerald nwords, (__be32 *)compr->raw_buf); 427483a40ce9SCharles Keepax if (ret < 0) 427583a40ce9SCharles Keepax return ret; 427683a40ce9SCharles Keepax 42777726e498SRichard Fitzgerald wm_adsp_remove_padding(compr->raw_buf, nwords); 427883a40ce9SCharles Keepax 427983a40ce9SCharles Keepax /* update read index to account for words read */ 428083a40ce9SCharles Keepax buf->read_index += nwords; 428183a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 428283a40ce9SCharles Keepax buf->read_index = 0; 428383a40ce9SCharles Keepax 428483a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 428583a40ce9SCharles Keepax buf->read_index); 428683a40ce9SCharles Keepax if (ret < 0) 428783a40ce9SCharles Keepax return ret; 428883a40ce9SCharles Keepax 428983a40ce9SCharles Keepax /* update avail to account for words read */ 429083a40ce9SCharles Keepax buf->avail -= nwords; 429183a40ce9SCharles Keepax 429283a40ce9SCharles Keepax return nwords; 429383a40ce9SCharles Keepax } 429483a40ce9SCharles Keepax 429583a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 429683a40ce9SCharles Keepax char __user *buf, size_t count) 429783a40ce9SCharles Keepax { 4298aa612f2bSCharles Keepax struct wm_adsp *dsp = compr->dsp; 429983a40ce9SCharles Keepax int ntotal = 0; 430083a40ce9SCharles Keepax int nwords, nbytes; 430183a40ce9SCharles Keepax 43020d3fba3eSCharles Keepax compr_dbg(compr, "Requested read of %zu bytes\n", count); 430383a40ce9SCharles Keepax 4304aa612f2bSCharles Keepax if (dsp->fatal_error || !compr->buf || compr->buf->error) { 43058d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 430683a40ce9SCharles Keepax return -EIO; 43078d280664SCharles Keepax } 430883a40ce9SCharles Keepax 430983a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 431083a40ce9SCharles Keepax 431183a40ce9SCharles Keepax do { 431283a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 431383a40ce9SCharles Keepax if (nwords < 0) { 43140d3fba3eSCharles Keepax compr_err(compr, "Failed to capture block: %d\n", 43150d3fba3eSCharles Keepax nwords); 431683a40ce9SCharles Keepax return nwords; 431783a40ce9SCharles Keepax } 431883a40ce9SCharles Keepax 431983a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 432083a40ce9SCharles Keepax 43210d3fba3eSCharles Keepax compr_dbg(compr, "Read %d bytes\n", nbytes); 432283a40ce9SCharles Keepax 432383a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 43240d3fba3eSCharles Keepax compr_err(compr, "Failed to copy data to user: %d, %d\n", 432583a40ce9SCharles Keepax ntotal, nbytes); 432683a40ce9SCharles Keepax return -EFAULT; 432783a40ce9SCharles Keepax } 432883a40ce9SCharles Keepax 432983a40ce9SCharles Keepax count -= nwords; 433083a40ce9SCharles Keepax ntotal += nbytes; 433183a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 433283a40ce9SCharles Keepax 433383a40ce9SCharles Keepax compr->copied_total += ntotal; 433483a40ce9SCharles Keepax 433583a40ce9SCharles Keepax return ntotal; 433683a40ce9SCharles Keepax } 433783a40ce9SCharles Keepax 43383a5ccf25SKuninori Morimoto int wm_adsp_compr_copy(struct snd_soc_component *component, 43393a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, char __user *buf, 434083a40ce9SCharles Keepax size_t count) 434183a40ce9SCharles Keepax { 434283a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 434383a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 434483a40ce9SCharles Keepax int ret; 434583a40ce9SCharles Keepax 434683a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 434783a40ce9SCharles Keepax 434883a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 434983a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 435083a40ce9SCharles Keepax else 435183a40ce9SCharles Keepax ret = -ENOTSUPP; 435283a40ce9SCharles Keepax 435383a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 435483a40ce9SCharles Keepax 435583a40ce9SCharles Keepax return ret; 435683a40ce9SCharles Keepax } 435783a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 435883a40ce9SCharles Keepax 4359a2bcbc1bSCharles Keepax static void wm_adsp_fatal_error(struct wm_adsp *dsp) 4360a2bcbc1bSCharles Keepax { 4361a2bcbc1bSCharles Keepax struct wm_adsp_compr *compr; 4362a2bcbc1bSCharles Keepax 4363a2bcbc1bSCharles Keepax dsp->fatal_error = true; 4364a2bcbc1bSCharles Keepax 4365a2bcbc1bSCharles Keepax list_for_each_entry(compr, &dsp->compr_list, list) { 4366aa612f2bSCharles Keepax if (compr->stream) 4367a2bcbc1bSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 4368a2bcbc1bSCharles Keepax } 4369a2bcbc1bSCharles Keepax } 4370a2bcbc1bSCharles Keepax 437101ec57a4SCharles Keepax irqreturn_t wm_adsp2_bus_error(int irq, void *data) 437251a2c944SMayuresh Kulkarni { 437301ec57a4SCharles Keepax struct wm_adsp *dsp = (struct wm_adsp *)data; 437451a2c944SMayuresh Kulkarni unsigned int val; 437551a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 437651a2c944SMayuresh Kulkarni int ret = 0; 437751a2c944SMayuresh Kulkarni 4378a2225a6dSCharles Keepax mutex_lock(&dsp->pwr_lock); 4379a2225a6dSCharles Keepax 438051a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 438151a2c944SMayuresh Kulkarni if (ret) { 438251a2c944SMayuresh Kulkarni adsp_err(dsp, 438351a2c944SMayuresh Kulkarni "Failed to read Region Lock Ctrl register: %d\n", ret); 4384a2225a6dSCharles Keepax goto error; 438551a2c944SMayuresh Kulkarni } 438651a2c944SMayuresh Kulkarni 438751a2c944SMayuresh Kulkarni if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 438851a2c944SMayuresh Kulkarni adsp_err(dsp, "watchdog timeout error\n"); 438981ed8845SCharles Keepax dsp->ops->stop_watchdog(dsp); 4390a2bcbc1bSCharles Keepax wm_adsp_fatal_error(dsp); 439151a2c944SMayuresh Kulkarni } 439251a2c944SMayuresh Kulkarni 4393a4d328efSCharles Keepax if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 4394a4d328efSCharles Keepax if (val & ADSP2_ADDR_ERR_MASK) 4395a4d328efSCharles Keepax adsp_err(dsp, "bus error: address error\n"); 439651a2c944SMayuresh Kulkarni else 439751a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: region lock error\n"); 439851a2c944SMayuresh Kulkarni 439951a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 440051a2c944SMayuresh Kulkarni if (ret) { 440151a2c944SMayuresh Kulkarni adsp_err(dsp, 440251a2c944SMayuresh Kulkarni "Failed to read Bus Err Addr register: %d\n", 440351a2c944SMayuresh Kulkarni ret); 4404a2225a6dSCharles Keepax goto error; 440551a2c944SMayuresh Kulkarni } 440651a2c944SMayuresh Kulkarni 440751a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error address = 0x%x\n", 440851a2c944SMayuresh Kulkarni val & ADSP2_BUS_ERR_ADDR_MASK); 440951a2c944SMayuresh Kulkarni 441051a2c944SMayuresh Kulkarni ret = regmap_read(regmap, 441151a2c944SMayuresh Kulkarni dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 441251a2c944SMayuresh Kulkarni &val); 441351a2c944SMayuresh Kulkarni if (ret) { 441451a2c944SMayuresh Kulkarni adsp_err(dsp, 441551a2c944SMayuresh Kulkarni "Failed to read Pmem Xmem Err Addr register: %d\n", 441651a2c944SMayuresh Kulkarni ret); 4417a2225a6dSCharles Keepax goto error; 441851a2c944SMayuresh Kulkarni } 441951a2c944SMayuresh Kulkarni 442051a2c944SMayuresh Kulkarni adsp_err(dsp, "xmem error address = 0x%x\n", 442151a2c944SMayuresh Kulkarni val & ADSP2_XMEM_ERR_ADDR_MASK); 442251a2c944SMayuresh Kulkarni adsp_err(dsp, "pmem error address = 0x%x\n", 442351a2c944SMayuresh Kulkarni (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 442451a2c944SMayuresh Kulkarni ADSP2_PMEM_ERR_ADDR_SHIFT); 442551a2c944SMayuresh Kulkarni } 442651a2c944SMayuresh Kulkarni 442751a2c944SMayuresh Kulkarni regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 442851a2c944SMayuresh Kulkarni ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 442951a2c944SMayuresh Kulkarni 4430a2225a6dSCharles Keepax error: 4431a2225a6dSCharles Keepax mutex_unlock(&dsp->pwr_lock); 4432a2225a6dSCharles Keepax 443351a2c944SMayuresh Kulkarni return IRQ_HANDLED; 443451a2c944SMayuresh Kulkarni } 443551a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 443651a2c944SMayuresh Kulkarni 443701ec57a4SCharles Keepax irqreturn_t wm_halo_bus_error(int irq, void *data) 44382ae58138SRichard Fitzgerald { 443901ec57a4SCharles Keepax struct wm_adsp *dsp = (struct wm_adsp *)data; 44402ae58138SRichard Fitzgerald struct regmap *regmap = dsp->regmap; 44412ae58138SRichard Fitzgerald unsigned int fault[6]; 44422ae58138SRichard Fitzgerald struct reg_sequence clear[] = { 44432ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 44442ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 44452ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 44462ae58138SRichard Fitzgerald }; 44472ae58138SRichard Fitzgerald int ret; 44482ae58138SRichard Fitzgerald 44492ae58138SRichard Fitzgerald mutex_lock(&dsp->pwr_lock); 44502ae58138SRichard Fitzgerald 44512ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 44522ae58138SRichard Fitzgerald fault); 44532ae58138SRichard Fitzgerald if (ret) { 44542ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 44552ae58138SRichard Fitzgerald goto exit_unlock; 44562ae58138SRichard Fitzgerald } 44572ae58138SRichard Fitzgerald 44582ae58138SRichard Fitzgerald adsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 44592ae58138SRichard Fitzgerald *fault & HALO_AHBM_FLAGS_ERR_MASK, 44602ae58138SRichard Fitzgerald (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 44612ae58138SRichard Fitzgerald HALO_AHBM_CORE_ERR_ADDR_SHIFT); 44622ae58138SRichard Fitzgerald 44632ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 44642ae58138SRichard Fitzgerald fault); 44652ae58138SRichard Fitzgerald if (ret) { 44662ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 44672ae58138SRichard Fitzgerald goto exit_unlock; 44682ae58138SRichard Fitzgerald } 44692ae58138SRichard Fitzgerald 44702ae58138SRichard Fitzgerald adsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 44712ae58138SRichard Fitzgerald 44722ae58138SRichard Fitzgerald ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 44732ae58138SRichard Fitzgerald fault, ARRAY_SIZE(fault)); 44742ae58138SRichard Fitzgerald if (ret) { 44752ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 44762ae58138SRichard Fitzgerald goto exit_unlock; 44772ae58138SRichard Fitzgerald } 44782ae58138SRichard Fitzgerald 44792ae58138SRichard Fitzgerald adsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 44802ae58138SRichard Fitzgerald adsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 44812ae58138SRichard Fitzgerald adsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 44822ae58138SRichard Fitzgerald 44832ae58138SRichard Fitzgerald ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 44842ae58138SRichard Fitzgerald if (ret) 44852ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 44862ae58138SRichard Fitzgerald 44872ae58138SRichard Fitzgerald exit_unlock: 44882ae58138SRichard Fitzgerald mutex_unlock(&dsp->pwr_lock); 44892ae58138SRichard Fitzgerald 44902ae58138SRichard Fitzgerald return IRQ_HANDLED; 44912ae58138SRichard Fitzgerald } 44922ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error); 44932ae58138SRichard Fitzgerald 44948bc144f9SStuart Henderson irqreturn_t wm_halo_wdt_expire(int irq, void *data) 44958bc144f9SStuart Henderson { 44968bc144f9SStuart Henderson struct wm_adsp *dsp = data; 44978bc144f9SStuart Henderson 44988bc144f9SStuart Henderson mutex_lock(&dsp->pwr_lock); 44998bc144f9SStuart Henderson 45008bc144f9SStuart Henderson adsp_warn(dsp, "WDT Expiry Fault\n"); 450181ed8845SCharles Keepax dsp->ops->stop_watchdog(dsp); 45028bc144f9SStuart Henderson wm_adsp_fatal_error(dsp); 45038bc144f9SStuart Henderson 45048bc144f9SStuart Henderson mutex_unlock(&dsp->pwr_lock); 45058bc144f9SStuart Henderson 45068bc144f9SStuart Henderson return IRQ_HANDLED; 45078bc144f9SStuart Henderson } 45088bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); 45098bc144f9SStuart Henderson 4510cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp1_ops = { 45114e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 45124e08d50dSCharles Keepax .parse_sizes = wm_adsp1_parse_sizes, 4513170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 45144e08d50dSCharles Keepax }; 45154e08d50dSCharles Keepax 4516cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp2_ops[] = { 45174e08d50dSCharles Keepax { 4518170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 45194e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 45204e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 45214e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4522170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 45234e08d50dSCharles Keepax 45244e08d50dSCharles Keepax .show_fw_status = wm_adsp2_show_fw_status, 45254e08d50dSCharles Keepax 45264e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 45274e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 45284e08d50dSCharles Keepax 45294e08d50dSCharles Keepax .enable_core = wm_adsp2_enable_core, 45304e08d50dSCharles Keepax .disable_core = wm_adsp2_disable_core, 45314e08d50dSCharles Keepax 45324e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 45334e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 45344e08d50dSCharles Keepax 45354e08d50dSCharles Keepax }, 45364e08d50dSCharles Keepax { 4537170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 45384e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 45394e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 45404e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4541170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 45424e08d50dSCharles Keepax 45434e08d50dSCharles Keepax .show_fw_status = wm_adsp2v2_show_fw_status, 45444e08d50dSCharles Keepax 45454e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 45464e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 45474e08d50dSCharles Keepax .lock_memory = wm_adsp2_lock, 45484e08d50dSCharles Keepax 45494e08d50dSCharles Keepax .enable_core = wm_adsp2v2_enable_core, 45504e08d50dSCharles Keepax .disable_core = wm_adsp2v2_disable_core, 45514e08d50dSCharles Keepax 45524e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 45534e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 45544e08d50dSCharles Keepax }, 45554e08d50dSCharles Keepax { 4556170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 45574e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 45584e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 45594e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4560170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 45614e08d50dSCharles Keepax 45624e08d50dSCharles Keepax .show_fw_status = wm_adsp2v2_show_fw_status, 45634e08d50dSCharles Keepax .stop_watchdog = wm_adsp_stop_watchdog, 45644e08d50dSCharles Keepax 45654e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 45664e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 45674e08d50dSCharles Keepax .lock_memory = wm_adsp2_lock, 45684e08d50dSCharles Keepax 45694e08d50dSCharles Keepax .enable_core = wm_adsp2v2_enable_core, 45704e08d50dSCharles Keepax .disable_core = wm_adsp2v2_disable_core, 45714e08d50dSCharles Keepax 45724e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 45734e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 45744e08d50dSCharles Keepax }, 45754e08d50dSCharles Keepax }; 45764e08d50dSCharles Keepax 4577cd537873SCharles Keepax static struct wm_adsp_ops wm_halo_ops = { 4578170b1e12SWen Shi .sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr), 4579170b1e12SWen Shi .parse_sizes = wm_adsp2_parse_sizes, 4580170b1e12SWen Shi .validate_version = wm_halo_validate_version, 4581170b1e12SWen Shi .setup_algs = wm_halo_setup_algs, 4582170b1e12SWen Shi .region_to_reg = wm_halo_region_to_reg, 4583170b1e12SWen Shi 4584170b1e12SWen Shi .show_fw_status = wm_halo_show_fw_status, 45858bc144f9SStuart Henderson .stop_watchdog = wm_halo_stop_watchdog, 4586170b1e12SWen Shi 4587170b1e12SWen Shi .lock_memory = wm_halo_configure_mpu, 4588170b1e12SWen Shi 4589170b1e12SWen Shi .start_core = wm_halo_start_core, 4590170b1e12SWen Shi .stop_core = wm_halo_stop_core, 4591170b1e12SWen Shi }; 4592170b1e12SWen Shi 45930a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 4594