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 20251a2c944SMayuresh Kulkarni #define ADSP2_SLAVE_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 35804d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 35904d1300fSCharles Keepax 36004d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 361dd84f925SMark Brown 3621023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 363dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 36404d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 365dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 366dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 36704d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 368dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 36904d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 37004d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 37104d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 37204d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 37304d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 3741023dbd9SMark Brown }; 3751023dbd9SMark Brown 3762cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 3772cd19bdbSCharles Keepax __be32 sys_enable; 3782cd19bdbSCharles Keepax __be32 fw_id; 3792cd19bdbSCharles Keepax __be32 fw_rev; 3802cd19bdbSCharles Keepax __be32 boot_status; 3812cd19bdbSCharles Keepax __be32 watchdog; 3822cd19bdbSCharles Keepax __be32 dma_buffer_size; 3832cd19bdbSCharles Keepax __be32 rdma[6]; 3842cd19bdbSCharles Keepax __be32 wdma[8]; 3852cd19bdbSCharles Keepax __be32 build_job_name[3]; 3862cd19bdbSCharles Keepax __be32 build_job_number; 3872cd19bdbSCharles Keepax }; 3882cd19bdbSCharles Keepax 389170b1e12SWen Shi struct wm_halo_system_config_xm_hdr { 390170b1e12SWen Shi __be32 halo_heartbeat; 391170b1e12SWen Shi __be32 build_job_name[3]; 392170b1e12SWen Shi __be32 build_job_number; 393170b1e12SWen Shi }; 394170b1e12SWen Shi 3952cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 3962cd19bdbSCharles Keepax __be32 magic; 3972cd19bdbSCharles Keepax __be32 smoothing; 3982cd19bdbSCharles Keepax __be32 threshold; 3992cd19bdbSCharles Keepax __be32 host_buf_ptr; 4002cd19bdbSCharles Keepax __be32 start_seq; 4012cd19bdbSCharles Keepax __be32 high_water_mark; 4022cd19bdbSCharles Keepax __be32 low_water_mark; 4032cd19bdbSCharles Keepax __be64 smoothed_power; 4042cd19bdbSCharles Keepax }; 4052cd19bdbSCharles Keepax 4064f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 { 4074f2d4eabSStuart Henderson __be32 host_buf_ptr; /* Host buffer pointer */ 4084f2d4eabSStuart Henderson __be32 versions; /* Version numbers */ 4094f2d4eabSStuart Henderson __be32 name[4]; /* The buffer name */ 4104f2d4eabSStuart Henderson }; 4114f2d4eabSStuart Henderson 4122cd19bdbSCharles Keepax struct wm_adsp_buffer { 4132a2aefa4SRichard Fitzgerald __be32 buf1_base; /* Base addr of first buffer area */ 4142a2aefa4SRichard Fitzgerald __be32 buf1_size; /* Size of buf1 area in DSP words */ 4152a2aefa4SRichard Fitzgerald __be32 buf2_base; /* Base addr of 2nd buffer area */ 4162a2aefa4SRichard Fitzgerald __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 4172a2aefa4SRichard Fitzgerald __be32 buf3_base; /* Base addr of buf3 area */ 4182a2aefa4SRichard Fitzgerald __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 4192cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 4202cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 4212cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 4222cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 4232cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 4242cd19bdbSCharles Keepax __be32 error; /* error if any */ 4252cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 4262cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 4272cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 4282cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 4292cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 4302cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 4312cd19bdbSCharles Keepax }; 4322cd19bdbSCharles Keepax 433721be3beSCharles Keepax struct wm_adsp_compr; 434721be3beSCharles Keepax 4352cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 4364f2d4eabSStuart Henderson struct list_head list; 4372cd19bdbSCharles Keepax struct wm_adsp *dsp; 438721be3beSCharles Keepax struct wm_adsp_compr *compr; 4392cd19bdbSCharles Keepax 4402cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 4412cd19bdbSCharles Keepax u32 host_buf_ptr; 442565ace46SCharles Keepax 443565ace46SCharles Keepax u32 error; 444565ace46SCharles Keepax u32 irq_count; 445565ace46SCharles Keepax int read_index; 446565ace46SCharles Keepax int avail; 447fb13f19dSAndrew Ford int host_buf_mem_type; 4484f2d4eabSStuart Henderson 4494f2d4eabSStuart Henderson char *name; 4502cd19bdbSCharles Keepax }; 4512cd19bdbSCharles Keepax 452406abc95SCharles Keepax struct wm_adsp_compr { 4534f2d4eabSStuart Henderson struct list_head list; 454406abc95SCharles Keepax struct wm_adsp *dsp; 45595fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 456406abc95SCharles Keepax 457406abc95SCharles Keepax struct snd_compr_stream *stream; 458406abc95SCharles Keepax struct snd_compressed_buffer size; 459565ace46SCharles Keepax 46083a40ce9SCharles Keepax u32 *raw_buf; 461565ace46SCharles Keepax unsigned int copied_total; 462da2b3358SCharles Keepax 463da2b3358SCharles Keepax unsigned int sample_rate; 4644f2d4eabSStuart Henderson 4654f2d4eabSStuart Henderson const char *name; 466406abc95SCharles Keepax }; 467406abc95SCharles Keepax 468406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 469406abc95SCharles Keepax 470406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 471406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 472406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 473406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 474406abc95SCharles Keepax 4752cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 4762cd19bdbSCharles Keepax 4772cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 4782cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 4792cd19bdbSCharles Keepax 4802cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 4812cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 4822cd19bdbSCharles Keepax 4834f2d4eabSStuart Henderson #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 4844f2d4eabSStuart Henderson 4854f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 4864f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 4874f2d4eabSStuart Henderson 4882cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 4892cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 4902cd19bdbSCharles Keepax 4912cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 4922cd19bdbSCharles Keepax unsigned int offset; 4932cd19bdbSCharles Keepax unsigned int cumulative_size; 4942cd19bdbSCharles Keepax unsigned int mem_type; 4952cd19bdbSCharles Keepax unsigned int base_addr; 4962cd19bdbSCharles Keepax }; 4972cd19bdbSCharles Keepax 4982cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 4992cd19bdbSCharles Keepax unsigned int mem_type; 5002cd19bdbSCharles Keepax unsigned int base_offset; 5012cd19bdbSCharles Keepax unsigned int size_offset; 5022cd19bdbSCharles Keepax }; 5032cd19bdbSCharles Keepax 5043a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 5052cd19bdbSCharles Keepax { 5062cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5072a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf1_base), 5082a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_size), 5092cd19bdbSCharles Keepax }, 5102cd19bdbSCharles Keepax { 5112cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5122a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf2_base), 5132a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 5142cd19bdbSCharles Keepax }, 5152cd19bdbSCharles Keepax { 5162cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 5172a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf3_base), 5182a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf_total_size), 5192cd19bdbSCharles Keepax }, 5202cd19bdbSCharles Keepax }; 5212cd19bdbSCharles Keepax 522406abc95SCharles Keepax struct wm_adsp_fw_caps { 523406abc95SCharles Keepax u32 id; 524406abc95SCharles Keepax struct snd_codec_desc desc; 5252cd19bdbSCharles Keepax int num_regions; 5263a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 527406abc95SCharles Keepax }; 528406abc95SCharles Keepax 529e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 530406abc95SCharles Keepax { 531406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 532406abc95SCharles Keepax .desc = { 5333bbc2705SRichard Fitzgerald .max_ch = 8, 534406abc95SCharles Keepax .sample_rates = { 16000 }, 535406abc95SCharles Keepax .num_sample_rates = 1, 536406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 537406abc95SCharles Keepax }, 538e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 539e6d00f34SCharles Keepax .region_defs = default_regions, 540406abc95SCharles Keepax }, 541406abc95SCharles Keepax }; 542406abc95SCharles Keepax 5437ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 5447ce4283cSCharles Keepax { 5457ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 5467ce4283cSCharles Keepax .desc = { 5477ce4283cSCharles Keepax .max_ch = 8, 5487ce4283cSCharles Keepax .sample_rates = { 5497ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 5507ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 5517ce4283cSCharles Keepax 96000, 176400, 192000 5527ce4283cSCharles Keepax }, 5537ce4283cSCharles Keepax .num_sample_rates = 15, 5547ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 5557ce4283cSCharles Keepax }, 5567ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 5577ce4283cSCharles Keepax .region_defs = default_regions, 558406abc95SCharles Keepax }, 559406abc95SCharles Keepax }; 560406abc95SCharles Keepax 561406abc95SCharles Keepax static const struct { 5621023dbd9SMark Brown const char *file; 563406abc95SCharles Keepax int compr_direction; 564406abc95SCharles Keepax int num_caps; 565406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 56620b7f7c5SCharles Keepax bool voice_trigger; 5671023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 568dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 56904d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 570dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 571dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 57204d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 573dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 574406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 575406abc95SCharles Keepax .file = "ctrl", 576406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 577e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 578e6d00f34SCharles Keepax .caps = ctrl_caps, 57920b7f7c5SCharles Keepax .voice_trigger = true, 580406abc95SCharles Keepax }, 58104d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 5827ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 5837ce4283cSCharles Keepax .file = "trace", 5847ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 5857ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 5867ce4283cSCharles Keepax .caps = trace_caps, 5877ce4283cSCharles Keepax }, 58804d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 58904d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 5901023dbd9SMark Brown }; 5911023dbd9SMark Brown 5926ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 5936ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 5946ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 5956ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 5966ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 5976ab2b7b4SDimitris Papastamos }; 5986ab2b7b4SDimitris Papastamos 5996ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 6006ab2b7b4SDimitris Papastamos const char *name; 6012323736dSCharles Keepax const char *fw_name; 6023809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 6036ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 6043809f001SCharles Keepax struct wm_adsp *dsp; 6056ab2b7b4SDimitris Papastamos unsigned int enabled:1; 6066ab2b7b4SDimitris Papastamos struct list_head list; 6076ab2b7b4SDimitris Papastamos void *cache; 6082323736dSCharles Keepax unsigned int offset; 6096ab2b7b4SDimitris Papastamos size_t len; 6100c2e3f34SDimitris Papastamos unsigned int set:1; 6119ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 61226c22a19SCharles Keepax unsigned int flags; 6138eb084d0SStuart Henderson unsigned int type; 6146ab2b7b4SDimitris Papastamos }; 6156ab2b7b4SDimitris Papastamos 6169ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 6179ce5e6e6SRichard Fitzgerald { 6189ce5e6e6SRichard Fitzgerald switch (type) { 6199ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 6209ce5e6e6SRichard Fitzgerald return "PM"; 621170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 622170b1e12SWen Shi return "PM_PACKED"; 6239ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 6249ce5e6e6SRichard Fitzgerald return "DM"; 6259ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 6269ce5e6e6SRichard Fitzgerald return "XM"; 627170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 628170b1e12SWen Shi return "XM_PACKED"; 6299ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 6309ce5e6e6SRichard Fitzgerald return "YM"; 631170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 632170b1e12SWen Shi return "YM_PACKED"; 6339ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 6349ce5e6e6SRichard Fitzgerald return "ZM"; 6359ce5e6e6SRichard Fitzgerald default: 6369ce5e6e6SRichard Fitzgerald return NULL; 6379ce5e6e6SRichard Fitzgerald } 6389ce5e6e6SRichard Fitzgerald } 6399ce5e6e6SRichard Fitzgerald 640f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 641f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 642f9f55e31SRichard Fitzgerald { 643f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 644f9f55e31SRichard Fitzgerald 645f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 646f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 647f9f55e31SRichard Fitzgerald } 648f9f55e31SRichard Fitzgerald 649f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 650f9f55e31SRichard Fitzgerald { 651f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 652f9f55e31SRichard Fitzgerald 653f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 654f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 655f9f55e31SRichard Fitzgerald } 656f9f55e31SRichard Fitzgerald 657f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 658f9f55e31SRichard Fitzgerald { 659f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 660f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 661f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 662f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 663f9f55e31SRichard Fitzgerald } 664f9f55e31SRichard Fitzgerald 665f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 666f9f55e31SRichard Fitzgerald char __user *user_buf, 667f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 668f9f55e31SRichard Fitzgerald { 669f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 670f9f55e31SRichard Fitzgerald ssize_t ret; 671f9f55e31SRichard Fitzgerald 672078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 673f9f55e31SRichard Fitzgerald 67428823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 675f9f55e31SRichard Fitzgerald ret = 0; 676f9f55e31SRichard Fitzgerald else 677f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 678f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 679f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 680f9f55e31SRichard Fitzgerald 681078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 682f9f55e31SRichard Fitzgerald return ret; 683f9f55e31SRichard Fitzgerald } 684f9f55e31SRichard Fitzgerald 685f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 686f9f55e31SRichard Fitzgerald char __user *user_buf, 687f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 688f9f55e31SRichard Fitzgerald { 689f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 690f9f55e31SRichard Fitzgerald ssize_t ret; 691f9f55e31SRichard Fitzgerald 692078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 693f9f55e31SRichard Fitzgerald 69428823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 695f9f55e31SRichard Fitzgerald ret = 0; 696f9f55e31SRichard Fitzgerald else 697f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 698f9f55e31SRichard Fitzgerald dsp->bin_file_name, 699f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 700f9f55e31SRichard Fitzgerald 701078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 702f9f55e31SRichard Fitzgerald return ret; 703f9f55e31SRichard Fitzgerald } 704f9f55e31SRichard Fitzgerald 705f9f55e31SRichard Fitzgerald static const struct { 706f9f55e31SRichard Fitzgerald const char *name; 707f9f55e31SRichard Fitzgerald const struct file_operations fops; 708f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 709f9f55e31SRichard Fitzgerald { 710f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 711f9f55e31SRichard Fitzgerald .fops = { 712f9f55e31SRichard Fitzgerald .open = simple_open, 713f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 714f9f55e31SRichard Fitzgerald }, 715f9f55e31SRichard Fitzgerald }, 716f9f55e31SRichard Fitzgerald { 717f9f55e31SRichard Fitzgerald .name = "bin_file_name", 718f9f55e31SRichard Fitzgerald .fops = { 719f9f55e31SRichard Fitzgerald .open = simple_open, 720f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 721f9f55e31SRichard Fitzgerald }, 722f9f55e31SRichard Fitzgerald }, 723f9f55e31SRichard Fitzgerald }; 724f9f55e31SRichard Fitzgerald 725f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7260fe1daa6SKuninori Morimoto struct snd_soc_component *component) 727f9f55e31SRichard Fitzgerald { 728f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 729f9f55e31SRichard Fitzgerald int i; 730f9f55e31SRichard Fitzgerald 731605391d0SRichard Fitzgerald root = debugfs_create_dir(dsp->name, component->debugfs_root); 732f9f55e31SRichard Fitzgerald 7337f807f28SGreg Kroah-Hartman debugfs_create_bool("booted", 0444, root, &dsp->booted); 7347f807f28SGreg Kroah-Hartman debugfs_create_bool("running", 0444, root, &dsp->running); 7357f807f28SGreg Kroah-Hartman debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); 7367f807f28SGreg Kroah-Hartman debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); 737f9f55e31SRichard Fitzgerald 7387f807f28SGreg Kroah-Hartman for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) 7397f807f28SGreg Kroah-Hartman debugfs_create_file(wm_adsp_debugfs_fops[i].name, 0444, root, 7407f807f28SGreg Kroah-Hartman dsp, &wm_adsp_debugfs_fops[i].fops); 741f9f55e31SRichard Fitzgerald 742f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 743f9f55e31SRichard Fitzgerald } 744f9f55e31SRichard Fitzgerald 745f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 746f9f55e31SRichard Fitzgerald { 747f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 748f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 749f9f55e31SRichard Fitzgerald } 750f9f55e31SRichard Fitzgerald #else 751f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7520fe1daa6SKuninori Morimoto struct snd_soc_component *component) 753f9f55e31SRichard Fitzgerald { 754f9f55e31SRichard Fitzgerald } 755f9f55e31SRichard Fitzgerald 756f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 757f9f55e31SRichard Fitzgerald { 758f9f55e31SRichard Fitzgerald } 759f9f55e31SRichard Fitzgerald 760f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 761f9f55e31SRichard Fitzgerald const char *s) 762f9f55e31SRichard Fitzgerald { 763f9f55e31SRichard Fitzgerald } 764f9f55e31SRichard Fitzgerald 765f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 766f9f55e31SRichard Fitzgerald const char *s) 767f9f55e31SRichard Fitzgerald { 768f9f55e31SRichard Fitzgerald } 769f9f55e31SRichard Fitzgerald 770f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 771f9f55e31SRichard Fitzgerald { 772f9f55e31SRichard Fitzgerald } 773f9f55e31SRichard Fitzgerald #endif 774f9f55e31SRichard Fitzgerald 7750a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 7761023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7771023dbd9SMark Brown { 7780fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7791023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7800fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 7811023dbd9SMark Brown 78215c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 7831023dbd9SMark Brown 7841023dbd9SMark Brown return 0; 7851023dbd9SMark Brown } 7860a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get); 7871023dbd9SMark Brown 7880a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 7891023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7901023dbd9SMark Brown { 7910fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7921023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7930fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 794d27c5e15SCharles Keepax int ret = 0; 7951023dbd9SMark Brown 79615c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 7971023dbd9SMark Brown return 0; 7981023dbd9SMark Brown 79915c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 8001023dbd9SMark Brown return -EINVAL; 8011023dbd9SMark Brown 802d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 8031023dbd9SMark Brown 8044f2d4eabSStuart Henderson if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) 805d27c5e15SCharles Keepax ret = -EBUSY; 806d27c5e15SCharles Keepax else 80715c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 8081023dbd9SMark Brown 809d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 810d27c5e15SCharles Keepax 811d27c5e15SCharles Keepax return ret; 8121023dbd9SMark Brown } 8130a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put); 8141023dbd9SMark Brown 8150a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = { 8161023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8171023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8181023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8191023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 820e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 821e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 822e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8231023dbd9SMark Brown }; 8240a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 8252159ad93SMark Brown 8262159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 8272159ad93SMark Brown int type) 8282159ad93SMark Brown { 8292159ad93SMark Brown int i; 8302159ad93SMark Brown 8312159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 8322159ad93SMark Brown if (dsp->mem[i].type == type) 8332159ad93SMark Brown return &dsp->mem[i]; 8342159ad93SMark Brown 8352159ad93SMark Brown return NULL; 8362159ad93SMark Brown } 8372159ad93SMark Brown 8383809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 83945b9ee72SMark Brown unsigned int offset) 84045b9ee72SMark Brown { 8413809f001SCharles Keepax switch (mem->type) { 84245b9ee72SMark Brown case WMFW_ADSP1_PM: 8433809f001SCharles Keepax return mem->base + (offset * 3); 84445b9ee72SMark Brown case WMFW_ADSP1_DM: 84545b9ee72SMark Brown case WMFW_ADSP2_XM: 84645b9ee72SMark Brown case WMFW_ADSP2_YM: 84745b9ee72SMark Brown case WMFW_ADSP1_ZM: 8483809f001SCharles Keepax return mem->base + (offset * 2); 84945b9ee72SMark Brown default: 8506c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 85145b9ee72SMark Brown return offset; 85245b9ee72SMark Brown } 85345b9ee72SMark Brown } 85445b9ee72SMark Brown 855170b1e12SWen Shi static unsigned int wm_halo_region_to_reg(struct wm_adsp_region const *mem, 856170b1e12SWen Shi unsigned int offset) 857170b1e12SWen Shi { 858170b1e12SWen Shi switch (mem->type) { 859170b1e12SWen Shi case WMFW_ADSP2_XM: 860170b1e12SWen Shi case WMFW_ADSP2_YM: 861170b1e12SWen Shi return mem->base + (offset * 4); 862170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 863170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 864170b1e12SWen Shi return (mem->base + (offset * 3)) & ~0x3; 865170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 866170b1e12SWen Shi return mem->base + (offset * 5); 867170b1e12SWen Shi default: 868170b1e12SWen Shi WARN(1, "Unknown memory region type"); 869170b1e12SWen Shi return offset; 870170b1e12SWen Shi } 871170b1e12SWen Shi } 872170b1e12SWen Shi 8734049ce86SCharles Keepax static void wm_adsp_read_fw_status(struct wm_adsp *dsp, 8744049ce86SCharles Keepax int noffs, unsigned int *offs) 87510337b07SRichard Fitzgerald { 87620e00db2SRichard Fitzgerald unsigned int i; 87710337b07SRichard Fitzgerald int ret; 87810337b07SRichard Fitzgerald 8794049ce86SCharles Keepax for (i = 0; i < noffs; ++i) { 8804049ce86SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 88110337b07SRichard Fitzgerald if (ret) { 88220e00db2SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 88310337b07SRichard Fitzgerald return; 88410337b07SRichard Fitzgerald } 88520e00db2SRichard Fitzgerald } 8864049ce86SCharles Keepax } 8874049ce86SCharles Keepax 8884049ce86SCharles Keepax static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 8894049ce86SCharles Keepax { 8904049ce86SCharles Keepax unsigned int offs[] = { 8914049ce86SCharles Keepax ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 8924049ce86SCharles Keepax }; 8934049ce86SCharles Keepax 8944049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 89510337b07SRichard Fitzgerald 89610337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 8974049ce86SCharles Keepax offs[0], offs[1], offs[2], offs[3]); 89810337b07SRichard Fitzgerald } 89910337b07SRichard Fitzgerald 900e1ea1879SRichard Fitzgerald static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) 901e1ea1879SRichard Fitzgerald { 9024049ce86SCharles Keepax unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 903e1ea1879SRichard Fitzgerald 9044049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 905e1ea1879SRichard Fitzgerald 906e1ea1879SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9074049ce86SCharles Keepax offs[0] & 0xFFFF, offs[0] >> 16, 9084049ce86SCharles Keepax offs[1] & 0xFFFF, offs[1] >> 16); 909e1ea1879SRichard Fitzgerald } 910e1ea1879SRichard Fitzgerald 911170b1e12SWen Shi static void wm_halo_show_fw_status(struct wm_adsp *dsp) 912170b1e12SWen Shi { 913170b1e12SWen Shi unsigned int offs[] = { 914170b1e12SWen Shi HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, 915170b1e12SWen Shi }; 916170b1e12SWen Shi 917170b1e12SWen Shi wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 918170b1e12SWen Shi 919170b1e12SWen Shi adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 920170b1e12SWen Shi offs[0], offs[1], offs[2], offs[3]); 921170b1e12SWen Shi } 922170b1e12SWen Shi 9239ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 9249ee78757SCharles Keepax { 9259ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 9269ee78757SCharles Keepax } 9279ee78757SCharles Keepax 928b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 929b396ebcaSRichard Fitzgerald { 930b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 931b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 932b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 933b396ebcaSRichard Fitzgerald 934b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 935b396ebcaSRichard Fitzgerald if (!mem) { 936b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 937b396ebcaSRichard Fitzgerald alg_region->type); 938b396ebcaSRichard Fitzgerald return -EINVAL; 939b396ebcaSRichard Fitzgerald } 940b396ebcaSRichard Fitzgerald 941170b1e12SWen Shi *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); 942b396ebcaSRichard Fitzgerald 943b396ebcaSRichard Fitzgerald return 0; 944b396ebcaSRichard Fitzgerald } 945b396ebcaSRichard Fitzgerald 9467585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 9476ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 9486ab2b7b4SDimitris Papastamos { 9499ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9509ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9519ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9526ab2b7b4SDimitris Papastamos 953a23ebba8SRichard Fitzgerald switch (ctl->type) { 954a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 955a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 956a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 957a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 958a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 959a23ebba8SRichard Fitzgerald uinfo->count = 1; 960a23ebba8SRichard Fitzgerald break; 961a23ebba8SRichard Fitzgerald default: 9626ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 9636ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 964a23ebba8SRichard Fitzgerald break; 965a23ebba8SRichard Fitzgerald } 966a23ebba8SRichard Fitzgerald 9676ab2b7b4SDimitris Papastamos return 0; 9686ab2b7b4SDimitris Papastamos } 9696ab2b7b4SDimitris Papastamos 970f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 971f4f0c4c6SRichard Fitzgerald unsigned int event_id) 972f4f0c4c6SRichard Fitzgerald { 973f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 974f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 975f4f0c4c6SRichard Fitzgerald unsigned int reg; 976f4f0c4c6SRichard Fitzgerald int i, ret; 977f4f0c4c6SRichard Fitzgerald 978f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 979f4f0c4c6SRichard Fitzgerald if (ret) 980f4f0c4c6SRichard Fitzgerald return ret; 981f4f0c4c6SRichard Fitzgerald 982f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 983f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 984f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 985f4f0c4c6SRichard Fitzgerald 986f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 987f4f0c4c6SRichard Fitzgerald if (ret) { 988f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 989f4f0c4c6SRichard Fitzgerald return ret; 990f4f0c4c6SRichard Fitzgerald } 991f4f0c4c6SRichard Fitzgerald 992f4f0c4c6SRichard Fitzgerald /* 993f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 994f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 995f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 996f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 997f4f0c4c6SRichard Fitzgerald */ 998f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 999f4f0c4c6SRichard Fitzgerald switch (i) { 1000f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 1001f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 1002f4f0c4c6SRichard Fitzgerald i++; 1003f4f0c4c6SRichard Fitzgerald break; 1004f4f0c4c6SRichard Fitzgerald default: 1005f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 1006f4f0c4c6SRichard Fitzgerald i += 10; 1007f4f0c4c6SRichard Fitzgerald break; 1008f4f0c4c6SRichard Fitzgerald } 1009f4f0c4c6SRichard Fitzgerald 1010f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1011f4f0c4c6SRichard Fitzgerald if (ret) { 1012f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 1013f4f0c4c6SRichard Fitzgerald return ret; 1014f4f0c4c6SRichard Fitzgerald } 1015f4f0c4c6SRichard Fitzgerald 1016f4f0c4c6SRichard Fitzgerald if (val == 0) { 1017f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 1018f4f0c4c6SRichard Fitzgerald return 0; 1019f4f0c4c6SRichard Fitzgerald } 1020f4f0c4c6SRichard Fitzgerald } 1021f4f0c4c6SRichard Fitzgerald 1022f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 1023f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 1024f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 1025f4f0c4c6SRichard Fitzgerald ctl->offset); 1026f4f0c4c6SRichard Fitzgerald 1027f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 1028f4f0c4c6SRichard Fitzgerald } 1029f4f0c4c6SRichard Fitzgerald 1030c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 10316ab2b7b4SDimitris Papastamos const void *buf, size_t len) 10326ab2b7b4SDimitris Papastamos { 10333809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 10346ab2b7b4SDimitris Papastamos void *scratch; 10356ab2b7b4SDimitris Papastamos int ret; 10366ab2b7b4SDimitris Papastamos unsigned int reg; 10376ab2b7b4SDimitris Papastamos 1038b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1039b396ebcaSRichard Fitzgerald if (ret) 1040b396ebcaSRichard Fitzgerald return ret; 10416ab2b7b4SDimitris Papastamos 10424f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 10436ab2b7b4SDimitris Papastamos if (!scratch) 10446ab2b7b4SDimitris Papastamos return -ENOMEM; 10456ab2b7b4SDimitris Papastamos 10463809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 10474f8ea6d7SCharles Keepax len); 10486ab2b7b4SDimitris Papastamos if (ret) { 10493809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 10504f8ea6d7SCharles Keepax len, reg, ret); 10516ab2b7b4SDimitris Papastamos kfree(scratch); 10526ab2b7b4SDimitris Papastamos return ret; 10536ab2b7b4SDimitris Papastamos } 10544f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 10556ab2b7b4SDimitris Papastamos 10566ab2b7b4SDimitris Papastamos kfree(scratch); 10576ab2b7b4SDimitris Papastamos 10586ab2b7b4SDimitris Papastamos return 0; 10596ab2b7b4SDimitris Papastamos } 10606ab2b7b4SDimitris Papastamos 10617585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 10626ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 10636ab2b7b4SDimitris Papastamos { 10649ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10659ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10669ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10676ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1068168d10e7SCharles Keepax int ret = 0; 1069168d10e7SCharles Keepax 1070168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10716ab2b7b4SDimitris Papastamos 107267430a39SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 107367430a39SCharles Keepax ret = -EPERM; 107467430a39SCharles Keepax else 10756ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 10766ab2b7b4SDimitris Papastamos 10770c2e3f34SDimitris Papastamos ctl->set = 1; 1078cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1079168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 10806ab2b7b4SDimitris Papastamos 1081168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 1082168d10e7SCharles Keepax 1083168d10e7SCharles Keepax return ret; 10846ab2b7b4SDimitris Papastamos } 10856ab2b7b4SDimitris Papastamos 10869ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 10879ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 10889ee78757SCharles Keepax { 10899ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10909ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10919ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10929ee78757SCharles Keepax int ret = 0; 10939ee78757SCharles Keepax 10949ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10959ee78757SCharles Keepax 10969ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 10979ee78757SCharles Keepax ret = -EFAULT; 10989ee78757SCharles Keepax } else { 10999ee78757SCharles Keepax ctl->set = 1; 1100cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 11019ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 110267430a39SCharles Keepax else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 110367430a39SCharles Keepax ret = -EPERM; 11049ee78757SCharles Keepax } 11059ee78757SCharles Keepax 11069ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 11079ee78757SCharles Keepax 11089ee78757SCharles Keepax return ret; 11099ee78757SCharles Keepax } 11109ee78757SCharles Keepax 1111a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 1112a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1113a23ebba8SRichard Fitzgerald { 1114a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 1115a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 1116a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 1117a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 1118a23ebba8SRichard Fitzgerald int ret; 1119a23ebba8SRichard Fitzgerald 1120a23ebba8SRichard Fitzgerald if (val == 0) 1121a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 1122a23ebba8SRichard Fitzgerald 1123a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 1124a23ebba8SRichard Fitzgerald 11257b4af793SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1126a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 1127a23ebba8SRichard Fitzgerald else 1128a23ebba8SRichard Fitzgerald ret = -EPERM; 1129a23ebba8SRichard Fitzgerald 1130a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 1131a23ebba8SRichard Fitzgerald 1132a23ebba8SRichard Fitzgerald return ret; 1133a23ebba8SRichard Fitzgerald } 1134a23ebba8SRichard Fitzgerald 1135c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 11366ab2b7b4SDimitris Papastamos void *buf, size_t len) 11376ab2b7b4SDimitris Papastamos { 11383809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 11396ab2b7b4SDimitris Papastamos void *scratch; 11406ab2b7b4SDimitris Papastamos int ret; 11416ab2b7b4SDimitris Papastamos unsigned int reg; 11426ab2b7b4SDimitris Papastamos 1143b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1144b396ebcaSRichard Fitzgerald if (ret) 1145b396ebcaSRichard Fitzgerald return ret; 11466ab2b7b4SDimitris Papastamos 11474f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 11486ab2b7b4SDimitris Papastamos if (!scratch) 11496ab2b7b4SDimitris Papastamos return -ENOMEM; 11506ab2b7b4SDimitris Papastamos 11514f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 11526ab2b7b4SDimitris Papastamos if (ret) { 11533809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 11545602a643SCharles Keepax len, reg, ret); 11556ab2b7b4SDimitris Papastamos kfree(scratch); 11566ab2b7b4SDimitris Papastamos return ret; 11576ab2b7b4SDimitris Papastamos } 11584f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 11596ab2b7b4SDimitris Papastamos 11604f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 11616ab2b7b4SDimitris Papastamos kfree(scratch); 11626ab2b7b4SDimitris Papastamos 11636ab2b7b4SDimitris Papastamos return 0; 11646ab2b7b4SDimitris Papastamos } 11656ab2b7b4SDimitris Papastamos 11667585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 11676ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 11686ab2b7b4SDimitris Papastamos { 11699ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11709ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11719ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11726ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1173168d10e7SCharles Keepax int ret = 0; 1174168d10e7SCharles Keepax 1175168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 11766ab2b7b4SDimitris Papastamos 117726c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1178cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1179168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 118026c22a19SCharles Keepax else 1181168d10e7SCharles Keepax ret = -EPERM; 1182168d10e7SCharles Keepax } else { 1183cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 1184bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1185bc1765d6SCharles Keepax 1186168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 118726c22a19SCharles Keepax } 118826c22a19SCharles Keepax 1189168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 119026c22a19SCharles Keepax 1191168d10e7SCharles Keepax return ret; 11926ab2b7b4SDimitris Papastamos } 11936ab2b7b4SDimitris Papastamos 11949ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 11959ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 11969ee78757SCharles Keepax { 11979ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11989ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11999ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12009ee78757SCharles Keepax int ret = 0; 12019ee78757SCharles Keepax 12029ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 12039ee78757SCharles Keepax 12049ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1205cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 12069ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 12079ee78757SCharles Keepax else 12089ee78757SCharles Keepax ret = -EPERM; 12099ee78757SCharles Keepax } else { 1210cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 12119ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 12129ee78757SCharles Keepax } 12139ee78757SCharles Keepax 12149ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 12159ee78757SCharles Keepax ret = -EFAULT; 12169ee78757SCharles Keepax 12179ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 12189ee78757SCharles Keepax 12199ee78757SCharles Keepax return ret; 12209ee78757SCharles Keepax } 12219ee78757SCharles Keepax 1222a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1223a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1224a23ebba8SRichard Fitzgerald { 1225a23ebba8SRichard Fitzgerald /* 1226a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1227a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1228a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1229a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1230a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1231a23ebba8SRichard Fitzgerald */ 1232a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1233a23ebba8SRichard Fitzgerald 1234a23ebba8SRichard Fitzgerald return 0; 1235a23ebba8SRichard Fitzgerald } 1236a23ebba8SRichard Fitzgerald 12376ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 12383809f001SCharles Keepax struct wm_adsp *dsp; 12396ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 12406ab2b7b4SDimitris Papastamos struct work_struct work; 12416ab2b7b4SDimitris Papastamos }; 12426ab2b7b4SDimitris Papastamos 12439ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 12449ee78757SCharles Keepax { 12459ee78757SCharles Keepax unsigned int out, rd, wr, vol; 12469ee78757SCharles Keepax 12479ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 12489ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 12499ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 12509ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12519ee78757SCharles Keepax 12529ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 12539ee78757SCharles Keepax } else { 12549ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 12559ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 12569ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12579ee78757SCharles Keepax 12589ee78757SCharles Keepax out = 0; 12599ee78757SCharles Keepax } 12609ee78757SCharles Keepax 12619ee78757SCharles Keepax if (in) { 12629ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 12639ee78757SCharles Keepax out |= rd; 12649ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 12659ee78757SCharles Keepax out |= wr; 12669ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 12679ee78757SCharles Keepax out |= vol; 12689ee78757SCharles Keepax } else { 12699ee78757SCharles Keepax out |= rd | wr | vol; 12709ee78757SCharles Keepax } 12719ee78757SCharles Keepax 12729ee78757SCharles Keepax return out; 12739ee78757SCharles Keepax } 12749ee78757SCharles Keepax 12753809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 12766ab2b7b4SDimitris Papastamos { 12776ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 12786ab2b7b4SDimitris Papastamos int ret; 12796ab2b7b4SDimitris Papastamos 128092bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 12816ab2b7b4SDimitris Papastamos return -EINVAL; 12826ab2b7b4SDimitris Papastamos 12836ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 12846ab2b7b4SDimitris Papastamos if (!kcontrol) 12856ab2b7b4SDimitris Papastamos return -ENOMEM; 12866ab2b7b4SDimitris Papastamos 12876ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 12886ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 12899ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 12909ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 12919ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1292a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1293a23ebba8SRichard Fitzgerald 1294a23ebba8SRichard Fitzgerald switch (ctl->type) { 1295a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1296a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1297a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1298a23ebba8SRichard Fitzgerald break; 1299a23ebba8SRichard Fitzgerald default: 1300d7789f5bSRichard Fitzgerald if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 13019ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 13029ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 13039ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1304d7789f5bSRichard Fitzgerald } else { 1305d7789f5bSRichard Fitzgerald kcontrol->get = wm_coeff_get; 1306d7789f5bSRichard Fitzgerald kcontrol->put = wm_coeff_put; 1307d7789f5bSRichard Fitzgerald } 1308a23ebba8SRichard Fitzgerald break; 1309a23ebba8SRichard Fitzgerald } 131026c22a19SCharles Keepax 13110fe1daa6SKuninori Morimoto ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); 13126ab2b7b4SDimitris Papastamos if (ret < 0) 13136ab2b7b4SDimitris Papastamos goto err_kcontrol; 13146ab2b7b4SDimitris Papastamos 13156ab2b7b4SDimitris Papastamos kfree(kcontrol); 13166ab2b7b4SDimitris Papastamos 13176ab2b7b4SDimitris Papastamos return 0; 13186ab2b7b4SDimitris Papastamos 13196ab2b7b4SDimitris Papastamos err_kcontrol: 13206ab2b7b4SDimitris Papastamos kfree(kcontrol); 13216ab2b7b4SDimitris Papastamos return ret; 13226ab2b7b4SDimitris Papastamos } 13236ab2b7b4SDimitris Papastamos 1324b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1325b21acc1cSCharles Keepax { 1326b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1327b21acc1cSCharles Keepax int ret; 1328b21acc1cSCharles Keepax 1329b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1330b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1331b21acc1cSCharles Keepax continue; 133226c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 133326c22a19SCharles Keepax continue; 133426c22a19SCharles Keepax 133504ff40a9SRichard Fitzgerald /* 133604ff40a9SRichard Fitzgerald * For readable controls populate the cache from the DSP memory. 133704ff40a9SRichard Fitzgerald * For non-readable controls the cache was zero-filled when 133804ff40a9SRichard Fitzgerald * created so we don't need to do anything. 133904ff40a9SRichard Fitzgerald */ 134004ff40a9SRichard Fitzgerald if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 13417d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1342b21acc1cSCharles Keepax if (ret < 0) 1343b21acc1cSCharles Keepax return ret; 1344b21acc1cSCharles Keepax } 134504ff40a9SRichard Fitzgerald } 1346b21acc1cSCharles Keepax 1347b21acc1cSCharles Keepax return 0; 1348b21acc1cSCharles Keepax } 1349b21acc1cSCharles Keepax 1350b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1351b21acc1cSCharles Keepax { 1352b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1353b21acc1cSCharles Keepax int ret; 1354b21acc1cSCharles Keepax 1355b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1356b21acc1cSCharles Keepax if (!ctl->enabled) 1357b21acc1cSCharles Keepax continue; 135826c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 13597d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1360b21acc1cSCharles Keepax if (ret < 0) 1361b21acc1cSCharles Keepax return ret; 1362b21acc1cSCharles Keepax } 1363b21acc1cSCharles Keepax } 1364b21acc1cSCharles Keepax 1365b21acc1cSCharles Keepax return 0; 1366b21acc1cSCharles Keepax } 1367b21acc1cSCharles Keepax 1368f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1369f4f0c4c6SRichard Fitzgerald unsigned int event) 1370f4f0c4c6SRichard Fitzgerald { 1371f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1372f4f0c4c6SRichard Fitzgerald int ret; 1373f4f0c4c6SRichard Fitzgerald 1374f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1375f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1376f4f0c4c6SRichard Fitzgerald continue; 1377f4f0c4c6SRichard Fitzgerald 137887aa6374SCharles Keepax if (!ctl->enabled) 137987aa6374SCharles Keepax continue; 138087aa6374SCharles Keepax 1381f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1382f4f0c4c6SRichard Fitzgerald if (ret) 1383f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1384f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1385f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1386f4f0c4c6SRichard Fitzgerald } 1387f4f0c4c6SRichard Fitzgerald } 1388f4f0c4c6SRichard Fitzgerald 1389b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1390b21acc1cSCharles Keepax { 1391b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1392b21acc1cSCharles Keepax struct wmfw_ctl_work, 1393b21acc1cSCharles Keepax work); 1394b21acc1cSCharles Keepax 1395b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1396b21acc1cSCharles Keepax kfree(ctl_work); 1397b21acc1cSCharles Keepax } 1398b21acc1cSCharles Keepax 139966225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 140066225e98SRichard Fitzgerald { 140166225e98SRichard Fitzgerald kfree(ctl->cache); 140266225e98SRichard Fitzgerald kfree(ctl->name); 140366225e98SRichard Fitzgerald kfree(ctl); 140466225e98SRichard Fitzgerald } 140566225e98SRichard Fitzgerald 1406b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1407b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 14082323736dSCharles Keepax unsigned int offset, unsigned int len, 140926c22a19SCharles Keepax const char *subname, unsigned int subname_len, 14108eb084d0SStuart Henderson unsigned int flags, unsigned int type) 1411b21acc1cSCharles Keepax { 1412b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1413b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1414b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 14159ce5e6e6SRichard Fitzgerald const char *region_name; 1416b21acc1cSCharles Keepax int ret; 1417b21acc1cSCharles Keepax 14189ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 14199ce5e6e6SRichard Fitzgerald if (!region_name) { 14202323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1421b21acc1cSCharles Keepax return -EINVAL; 1422b21acc1cSCharles Keepax } 1423b21acc1cSCharles Keepax 1424cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1425cb5b57a9SCharles Keepax case 0: 1426cb5b57a9SCharles Keepax case 1: 1427605391d0SRichard Fitzgerald snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", 1428605391d0SRichard Fitzgerald dsp->name, region_name, alg_region->alg); 1429170b1e12SWen Shi subname = NULL; /* don't append subname */ 1430cb5b57a9SCharles Keepax break; 1431170b1e12SWen Shi case 2: 1432cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1433605391d0SRichard Fitzgerald "%s%c %.12s %x", dsp->name, *region_name, 1434cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1435170b1e12SWen Shi break; 1436170b1e12SWen Shi default: 1437170b1e12SWen Shi ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1438170b1e12SWen Shi "%s %.12s %x", dsp->name, 1439170b1e12SWen Shi wm_adsp_fw_text[dsp->fw], alg_region->alg); 1440170b1e12SWen Shi break; 1441170b1e12SWen Shi } 1442cb5b57a9SCharles Keepax 1443cb5b57a9SCharles Keepax if (subname) { 1444cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1445cb5b57a9SCharles Keepax int skip = 0; 1446cb5b57a9SCharles Keepax 1447b7ede5afSCharles Keepax if (dsp->component->name_prefix) 1448b7ede5afSCharles Keepax avail -= strlen(dsp->component->name_prefix) + 1; 1449b7ede5afSCharles Keepax 1450170b1e12SWen Shi /* Truncate the subname from the start if it is too long */ 1451cb5b57a9SCharles Keepax if (subname_len > avail) 1452cb5b57a9SCharles Keepax skip = subname_len - avail; 1453cb5b57a9SCharles Keepax 1454170b1e12SWen Shi snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, 1455170b1e12SWen Shi " %.*s", subname_len - skip, subname + skip); 1456cb5b57a9SCharles Keepax } 1457b21acc1cSCharles Keepax 14587585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1459b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1460b21acc1cSCharles Keepax if (!ctl->enabled) 1461b21acc1cSCharles Keepax ctl->enabled = 1; 1462b21acc1cSCharles Keepax return 0; 1463b21acc1cSCharles Keepax } 1464b21acc1cSCharles Keepax } 1465b21acc1cSCharles Keepax 1466b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1467b21acc1cSCharles Keepax if (!ctl) 1468b21acc1cSCharles Keepax return -ENOMEM; 14692323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1470b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1471b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1472b21acc1cSCharles Keepax if (!ctl->name) { 1473b21acc1cSCharles Keepax ret = -ENOMEM; 1474b21acc1cSCharles Keepax goto err_ctl; 1475b21acc1cSCharles Keepax } 1476b21acc1cSCharles Keepax ctl->enabled = 1; 1477b21acc1cSCharles Keepax ctl->set = 0; 1478b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1479b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1480b21acc1cSCharles Keepax ctl->dsp = dsp; 1481b21acc1cSCharles Keepax 148226c22a19SCharles Keepax ctl->flags = flags; 14838eb084d0SStuart Henderson ctl->type = type; 14842323736dSCharles Keepax ctl->offset = offset; 1485b21acc1cSCharles Keepax ctl->len = len; 1486b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1487b21acc1cSCharles Keepax if (!ctl->cache) { 1488b21acc1cSCharles Keepax ret = -ENOMEM; 1489b21acc1cSCharles Keepax goto err_ctl_name; 1490b21acc1cSCharles Keepax } 1491b21acc1cSCharles Keepax 14922323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 14932323736dSCharles Keepax 14948eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 14958eb084d0SStuart Henderson return 0; 14968eb084d0SStuart Henderson 1497b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1498b21acc1cSCharles Keepax if (!ctl_work) { 1499b21acc1cSCharles Keepax ret = -ENOMEM; 1500b21acc1cSCharles Keepax goto err_ctl_cache; 1501b21acc1cSCharles Keepax } 1502b21acc1cSCharles Keepax 1503b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1504b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1505b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1506b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1507b21acc1cSCharles Keepax 1508b21acc1cSCharles Keepax return 0; 1509b21acc1cSCharles Keepax 1510b21acc1cSCharles Keepax err_ctl_cache: 1511b21acc1cSCharles Keepax kfree(ctl->cache); 1512b21acc1cSCharles Keepax err_ctl_name: 1513b21acc1cSCharles Keepax kfree(ctl->name); 1514b21acc1cSCharles Keepax err_ctl: 1515b21acc1cSCharles Keepax kfree(ctl); 1516b21acc1cSCharles Keepax 1517b21acc1cSCharles Keepax return ret; 1518b21acc1cSCharles Keepax } 1519b21acc1cSCharles Keepax 15202323736dSCharles Keepax struct wm_coeff_parsed_alg { 15212323736dSCharles Keepax int id; 15222323736dSCharles Keepax const u8 *name; 15232323736dSCharles Keepax int name_len; 15242323736dSCharles Keepax int ncoeff; 15252323736dSCharles Keepax }; 15262323736dSCharles Keepax 15272323736dSCharles Keepax struct wm_coeff_parsed_coeff { 15282323736dSCharles Keepax int offset; 15292323736dSCharles Keepax int mem_type; 15302323736dSCharles Keepax const u8 *name; 15312323736dSCharles Keepax int name_len; 15322323736dSCharles Keepax int ctl_type; 15332323736dSCharles Keepax int flags; 15342323736dSCharles Keepax int len; 15352323736dSCharles Keepax }; 15362323736dSCharles Keepax 1537cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1538cb5b57a9SCharles Keepax { 1539cb5b57a9SCharles Keepax int length; 1540cb5b57a9SCharles Keepax 1541cb5b57a9SCharles Keepax switch (bytes) { 1542cb5b57a9SCharles Keepax case 1: 1543cb5b57a9SCharles Keepax length = **pos; 1544cb5b57a9SCharles Keepax break; 1545cb5b57a9SCharles Keepax case 2: 15468299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1547cb5b57a9SCharles Keepax break; 1548cb5b57a9SCharles Keepax default: 1549cb5b57a9SCharles Keepax return 0; 1550cb5b57a9SCharles Keepax } 1551cb5b57a9SCharles Keepax 1552cb5b57a9SCharles Keepax if (str) 1553cb5b57a9SCharles Keepax *str = *pos + bytes; 1554cb5b57a9SCharles Keepax 1555cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1556cb5b57a9SCharles Keepax 1557cb5b57a9SCharles Keepax return length; 1558cb5b57a9SCharles Keepax } 1559cb5b57a9SCharles Keepax 1560cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1561cb5b57a9SCharles Keepax { 1562cb5b57a9SCharles Keepax int val = 0; 1563cb5b57a9SCharles Keepax 1564cb5b57a9SCharles Keepax switch (bytes) { 1565cb5b57a9SCharles Keepax case 2: 15668299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1567cb5b57a9SCharles Keepax break; 1568cb5b57a9SCharles Keepax case 4: 15698299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1570cb5b57a9SCharles Keepax break; 1571cb5b57a9SCharles Keepax default: 1572cb5b57a9SCharles Keepax break; 1573cb5b57a9SCharles Keepax } 1574cb5b57a9SCharles Keepax 1575cb5b57a9SCharles Keepax *pos += bytes; 1576cb5b57a9SCharles Keepax 1577cb5b57a9SCharles Keepax return val; 1578cb5b57a9SCharles Keepax } 1579cb5b57a9SCharles Keepax 15802323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 15812323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 15822323736dSCharles Keepax { 15832323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 15842323736dSCharles Keepax 1585cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1586cb5b57a9SCharles Keepax case 0: 1587cb5b57a9SCharles Keepax case 1: 15882323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 15892323736dSCharles Keepax *data = raw->data; 15902323736dSCharles Keepax 15912323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 15922323736dSCharles Keepax blk->name = raw->name; 15932323736dSCharles Keepax blk->name_len = strlen(raw->name); 15942323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1595cb5b57a9SCharles Keepax break; 1596cb5b57a9SCharles Keepax default: 1597cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1598cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1599cb5b57a9SCharles Keepax &blk->name); 1600cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1601cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1602cb5b57a9SCharles Keepax break; 1603cb5b57a9SCharles Keepax } 16042323736dSCharles Keepax 16052323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 16062323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 16072323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 16082323736dSCharles Keepax } 16092323736dSCharles Keepax 16102323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 16112323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 16122323736dSCharles Keepax { 16132323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1614cb5b57a9SCharles Keepax const u8 *tmp; 1615cb5b57a9SCharles Keepax int length; 16162323736dSCharles Keepax 1617cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1618cb5b57a9SCharles Keepax case 0: 1619cb5b57a9SCharles Keepax case 1: 16202323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 16212323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 16222323736dSCharles Keepax 16232323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 16242323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 16252323736dSCharles Keepax blk->name = raw->name; 16262323736dSCharles Keepax blk->name_len = strlen(raw->name); 16272323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 16282323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 16292323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1630cb5b57a9SCharles Keepax break; 1631cb5b57a9SCharles Keepax default: 1632cb5b57a9SCharles Keepax tmp = *data; 1633cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1634cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1635cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1636cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1637cb5b57a9SCharles Keepax &blk->name); 1638cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1639cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1640cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1641cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1642cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1643cb5b57a9SCharles Keepax 1644cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1645cb5b57a9SCharles Keepax break; 1646cb5b57a9SCharles Keepax } 16472323736dSCharles Keepax 16482323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 16492323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 16502323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 16512323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 16522323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 16532323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 16542323736dSCharles Keepax } 16552323736dSCharles Keepax 1656f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1657f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1658f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1659f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1660f4f0c4c6SRichard Fitzgerald { 1661f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1662f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1663f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1664f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1665f4f0c4c6SRichard Fitzgerald return -EINVAL; 1666f4f0c4c6SRichard Fitzgerald } 1667f4f0c4c6SRichard Fitzgerald 1668f4f0c4c6SRichard Fitzgerald return 0; 1669f4f0c4c6SRichard Fitzgerald } 1670f4f0c4c6SRichard Fitzgerald 16712323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 16722323736dSCharles Keepax const struct wmfw_region *region) 16732323736dSCharles Keepax { 16742323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 16752323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 16762323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 16772323736dSCharles Keepax const u8 *data = region->data; 16782323736dSCharles Keepax int i, ret; 16792323736dSCharles Keepax 16802323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 16812323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 16822323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 16832323736dSCharles Keepax 16842323736dSCharles Keepax switch (coeff_blk.ctl_type) { 16852323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 16862323736dSCharles Keepax break; 1687a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1688a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1689a23ebba8SRichard Fitzgerald continue; /* ignore */ 1690a23ebba8SRichard Fitzgerald 1691a23ebba8SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1692a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1693a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1694a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1695a23ebba8SRichard Fitzgerald 0); 1696a23ebba8SRichard Fitzgerald if (ret) 1697a23ebba8SRichard Fitzgerald return -EINVAL; 1698a23ebba8SRichard Fitzgerald break; 1699f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1700f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1701f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1702f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1703f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1704f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1705f4f0c4c6SRichard Fitzgerald 0); 1706f4f0c4c6SRichard Fitzgerald if (ret) 1707f4f0c4c6SRichard Fitzgerald return -EINVAL; 1708f4f0c4c6SRichard Fitzgerald break; 1709d52ed4b0SRichard Fitzgerald case WMFW_CTL_TYPE_HOST_BUFFER: 1710d52ed4b0SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1711d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1712d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1713d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1714d52ed4b0SRichard Fitzgerald 0); 1715d52ed4b0SRichard Fitzgerald if (ret) 1716d52ed4b0SRichard Fitzgerald return -EINVAL; 1717d52ed4b0SRichard Fitzgerald break; 17182323736dSCharles Keepax default: 17192323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 17202323736dSCharles Keepax coeff_blk.ctl_type); 17212323736dSCharles Keepax return -EINVAL; 17222323736dSCharles Keepax } 17232323736dSCharles Keepax 17242323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 17252323736dSCharles Keepax alg_region.alg = alg_blk.id; 17262323736dSCharles Keepax 17272323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 17282323736dSCharles Keepax coeff_blk.offset, 17292323736dSCharles Keepax coeff_blk.len, 17302323736dSCharles Keepax coeff_blk.name, 173126c22a19SCharles Keepax coeff_blk.name_len, 17328eb084d0SStuart Henderson coeff_blk.flags, 17338eb084d0SStuart Henderson coeff_blk.ctl_type); 17342323736dSCharles Keepax if (ret < 0) 17352323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 17362323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 17372323736dSCharles Keepax } 17382323736dSCharles Keepax 17392323736dSCharles Keepax return 0; 17402323736dSCharles Keepax } 17412323736dSCharles Keepax 17424e08d50dSCharles Keepax static unsigned int wm_adsp1_parse_sizes(struct wm_adsp *dsp, 17434e08d50dSCharles Keepax const char * const file, 17444e08d50dSCharles Keepax unsigned int pos, 17454e08d50dSCharles Keepax const struct firmware *firmware) 17464e08d50dSCharles Keepax { 17474e08d50dSCharles Keepax const struct wmfw_adsp1_sizes *adsp1_sizes; 17484e08d50dSCharles Keepax 17494e08d50dSCharles Keepax adsp1_sizes = (void *)&firmware->data[pos]; 17504e08d50dSCharles Keepax 17514e08d50dSCharles Keepax adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 17524e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 17534e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->zm)); 17544e08d50dSCharles Keepax 17554e08d50dSCharles Keepax return pos + sizeof(*adsp1_sizes); 17564e08d50dSCharles Keepax } 17574e08d50dSCharles Keepax 17584e08d50dSCharles Keepax static unsigned int wm_adsp2_parse_sizes(struct wm_adsp *dsp, 17594e08d50dSCharles Keepax const char * const file, 17604e08d50dSCharles Keepax unsigned int pos, 17614e08d50dSCharles Keepax const struct firmware *firmware) 17624e08d50dSCharles Keepax { 17634e08d50dSCharles Keepax const struct wmfw_adsp2_sizes *adsp2_sizes; 17644e08d50dSCharles Keepax 17654e08d50dSCharles Keepax adsp2_sizes = (void *)&firmware->data[pos]; 17664e08d50dSCharles Keepax 17674e08d50dSCharles Keepax adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 17684e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 17694e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 17704e08d50dSCharles Keepax 17714e08d50dSCharles Keepax return pos + sizeof(*adsp2_sizes); 17724e08d50dSCharles Keepax } 17734e08d50dSCharles Keepax 17744e08d50dSCharles Keepax static bool wm_adsp_validate_version(struct wm_adsp *dsp, unsigned int version) 17754e08d50dSCharles Keepax { 17764e08d50dSCharles Keepax switch (version) { 17774e08d50dSCharles Keepax case 0: 17784e08d50dSCharles Keepax adsp_warn(dsp, "Deprecated file format %d\n", version); 17794e08d50dSCharles Keepax return true; 17804e08d50dSCharles Keepax case 1: 17814e08d50dSCharles Keepax case 2: 17824e08d50dSCharles Keepax return true; 17834e08d50dSCharles Keepax default: 17844e08d50dSCharles Keepax return false; 17854e08d50dSCharles Keepax } 17864e08d50dSCharles Keepax } 17874e08d50dSCharles Keepax 1788170b1e12SWen Shi static bool wm_halo_validate_version(struct wm_adsp *dsp, unsigned int version) 1789170b1e12SWen Shi { 1790170b1e12SWen Shi switch (version) { 1791170b1e12SWen Shi case 3: 1792170b1e12SWen Shi return true; 1793170b1e12SWen Shi default: 1794170b1e12SWen Shi return false; 1795170b1e12SWen Shi } 1796170b1e12SWen Shi } 1797170b1e12SWen Shi 17982159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 17992159ad93SMark Brown { 1800cf17c83cSMark Brown LIST_HEAD(buf_list); 18012159ad93SMark Brown const struct firmware *firmware; 18022159ad93SMark Brown struct regmap *regmap = dsp->regmap; 18032159ad93SMark Brown unsigned int pos = 0; 18042159ad93SMark Brown const struct wmfw_header *header; 18052159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 18062159ad93SMark Brown const struct wmfw_footer *footer; 18072159ad93SMark Brown const struct wmfw_region *region; 18082159ad93SMark Brown const struct wm_adsp_region *mem; 18092159ad93SMark Brown const char *region_name; 18101cab2a84SRichard Fitzgerald char *file, *text = NULL; 1811cf17c83cSMark Brown struct wm_adsp_buf *buf; 18122159ad93SMark Brown unsigned int reg; 18132159ad93SMark Brown int regions = 0; 18144e08d50dSCharles Keepax int ret, offset, type; 18152159ad93SMark Brown 18162159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 18172159ad93SMark Brown if (file == NULL) 18182159ad93SMark Brown return -ENOMEM; 18192159ad93SMark Brown 1820605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, 18211023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 18222159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 18232159ad93SMark Brown 18242159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 18252159ad93SMark Brown if (ret != 0) { 18262159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 18272159ad93SMark Brown goto out; 18282159ad93SMark Brown } 18292159ad93SMark Brown ret = -EINVAL; 18302159ad93SMark Brown 18312159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 18322159ad93SMark Brown if (pos >= firmware->size) { 18332159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 18342159ad93SMark Brown file, firmware->size); 18352159ad93SMark Brown goto out_fw; 18362159ad93SMark Brown } 18372159ad93SMark Brown 18382159ad93SMark Brown header = (void *)&firmware->data[0]; 18392159ad93SMark Brown 18402159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 18412159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 18422159ad93SMark Brown goto out_fw; 18432159ad93SMark Brown } 18442159ad93SMark Brown 18454e08d50dSCharles Keepax if (!dsp->ops->validate_version(dsp, header->ver)) { 18462159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 18472159ad93SMark Brown file, header->ver); 18482159ad93SMark Brown goto out_fw; 18492159ad93SMark Brown } 18502323736dSCharles Keepax 18513626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 18522323736dSCharles Keepax dsp->fw_ver = header->ver; 18532159ad93SMark Brown 18542159ad93SMark Brown if (header->core != dsp->type) { 18552159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 18562159ad93SMark Brown file, header->core, dsp->type); 18572159ad93SMark Brown goto out_fw; 18582159ad93SMark Brown } 18592159ad93SMark Brown 18604e08d50dSCharles Keepax pos = sizeof(*header); 18614e08d50dSCharles Keepax pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 18622159ad93SMark Brown 18634e08d50dSCharles Keepax footer = (void *)&firmware->data[pos]; 18644e08d50dSCharles Keepax pos += sizeof(*footer); 18652159ad93SMark Brown 18664e08d50dSCharles Keepax if (le32_to_cpu(header->len) != pos) { 18672159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 18682159ad93SMark Brown file, le32_to_cpu(header->len)); 18692159ad93SMark Brown goto out_fw; 18702159ad93SMark Brown } 18712159ad93SMark Brown 18722159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 18732159ad93SMark Brown le64_to_cpu(footer->timestamp)); 18742159ad93SMark Brown 18752159ad93SMark Brown while (pos < firmware->size && 187650dd2ea8SBen Hutchings sizeof(*region) < firmware->size - pos) { 18772159ad93SMark Brown region = (void *)&(firmware->data[pos]); 18782159ad93SMark Brown region_name = "Unknown"; 18792159ad93SMark Brown reg = 0; 18802159ad93SMark Brown text = NULL; 18812159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 18822159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 18832159ad93SMark Brown 18842159ad93SMark Brown switch (type) { 18852159ad93SMark Brown case WMFW_NAME_TEXT: 18862159ad93SMark Brown region_name = "Firmware name"; 18872159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 18882159ad93SMark Brown GFP_KERNEL); 18892159ad93SMark Brown break; 18902323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 18912323736dSCharles Keepax region_name = "Algorithm"; 18922323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 18932323736dSCharles Keepax if (ret != 0) 18942323736dSCharles Keepax goto out_fw; 18952323736dSCharles Keepax break; 18962159ad93SMark Brown case WMFW_INFO_TEXT: 18972159ad93SMark Brown region_name = "Information"; 18982159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 18992159ad93SMark Brown GFP_KERNEL); 19002159ad93SMark Brown break; 19012159ad93SMark Brown case WMFW_ABSOLUTE: 19022159ad93SMark Brown region_name = "Absolute"; 19032159ad93SMark Brown reg = offset; 19042159ad93SMark Brown break; 19052159ad93SMark Brown case WMFW_ADSP1_PM: 19062159ad93SMark Brown case WMFW_ADSP1_DM: 19072159ad93SMark Brown case WMFW_ADSP2_XM: 19082159ad93SMark Brown case WMFW_ADSP2_YM: 19092159ad93SMark Brown case WMFW_ADSP1_ZM: 1910170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 1911170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 1912170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 1913170b1e12SWen Shi mem = wm_adsp_find_region(dsp, type); 1914170b1e12SWen Shi if (!mem) { 1915170b1e12SWen Shi adsp_err(dsp, "No region of type: %x\n", type); 1916170b1e12SWen Shi goto out_fw; 1917170b1e12SWen Shi } 1918170b1e12SWen Shi 19199ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 1920170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, offset); 19212159ad93SMark Brown break; 19222159ad93SMark Brown default: 19232159ad93SMark Brown adsp_warn(dsp, 19242159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 19252159ad93SMark Brown file, regions, type, pos, pos); 19262159ad93SMark Brown break; 19272159ad93SMark Brown } 19282159ad93SMark Brown 19292159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 19302159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 19312159ad93SMark Brown region_name); 19322159ad93SMark Brown 193350dd2ea8SBen Hutchings if (le32_to_cpu(region->len) > 193450dd2ea8SBen Hutchings firmware->size - pos - sizeof(*region)) { 19351cab2a84SRichard Fitzgerald adsp_err(dsp, 19361cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 19371cab2a84SRichard Fitzgerald file, regions, region_name, 19381cab2a84SRichard Fitzgerald le32_to_cpu(region->len), firmware->size); 19391cab2a84SRichard Fitzgerald ret = -EINVAL; 19401cab2a84SRichard Fitzgerald goto out_fw; 19411cab2a84SRichard Fitzgerald } 19421cab2a84SRichard Fitzgerald 19432159ad93SMark Brown if (text) { 19442159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 19452159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 19462159ad93SMark Brown kfree(text); 19471cab2a84SRichard Fitzgerald text = NULL; 19482159ad93SMark Brown } 19492159ad93SMark Brown 19502159ad93SMark Brown if (reg) { 1951cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1952cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1953cf17c83cSMark Brown &buf_list); 1954a76fefabSMark Brown if (!buf) { 1955a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 19567328823dSDimitris Papastamos ret = -ENOMEM; 19577328823dSDimitris Papastamos goto out_fw; 1958a76fefabSMark Brown } 1959a76fefabSMark Brown 1960cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1961cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 19622159ad93SMark Brown if (ret != 0) { 19632159ad93SMark Brown adsp_err(dsp, 1964cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 19652159ad93SMark Brown file, regions, 1966cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 19672159ad93SMark Brown region_name, ret); 19682159ad93SMark Brown goto out_fw; 19692159ad93SMark Brown } 19702159ad93SMark Brown } 19712159ad93SMark Brown 19722159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 19732159ad93SMark Brown regions++; 19742159ad93SMark Brown } 19752159ad93SMark Brown 1976cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1977cf17c83cSMark Brown if (ret != 0) { 1978cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1979cf17c83cSMark Brown goto out_fw; 1980cf17c83cSMark Brown } 1981cf17c83cSMark Brown 19822159ad93SMark Brown if (pos > firmware->size) 19832159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 19842159ad93SMark Brown file, regions, pos - firmware->size); 19852159ad93SMark Brown 1986f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1987f9f55e31SRichard Fitzgerald 19882159ad93SMark Brown out_fw: 1989cf17c83cSMark Brown regmap_async_complete(regmap); 1990cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 19912159ad93SMark Brown release_firmware(firmware); 19921cab2a84SRichard Fitzgerald kfree(text); 19932159ad93SMark Brown out: 19942159ad93SMark Brown kfree(file); 19952159ad93SMark Brown 19962159ad93SMark Brown return ret; 19972159ad93SMark Brown } 19982159ad93SMark Brown 19992323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 20002323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 20012323736dSCharles Keepax { 20022323736dSCharles Keepax struct wm_coeff_ctl *ctl; 20032323736dSCharles Keepax 20042323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 20052323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 20062323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 20072323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 20082323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 20092323736dSCharles Keepax } 20102323736dSCharles Keepax } 20112323736dSCharles Keepax } 20122323736dSCharles Keepax 20133809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 20147f7cca08SCharles Keepax const struct wm_adsp_region *mem, 2015b618a185SCharles Keepax unsigned int pos, unsigned int len) 2016db40517cSMark Brown { 2017b618a185SCharles Keepax void *alg; 20187f7cca08SCharles Keepax unsigned int reg; 2019b618a185SCharles Keepax int ret; 2020db40517cSMark Brown __be32 val; 2021db40517cSMark Brown 20223809f001SCharles Keepax if (n_algs == 0) { 2023b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 2024b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2025db40517cSMark Brown } 2026db40517cSMark Brown 20273809f001SCharles Keepax if (n_algs > 1024) { 20283809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 2029b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2030b618a185SCharles Keepax } 2031b618a185SCharles Keepax 2032b618a185SCharles Keepax /* Read the terminator first to validate the length */ 2033170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos + len); 20347f7cca08SCharles Keepax 20357f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 2036b618a185SCharles Keepax if (ret != 0) { 2037b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 2038b618a185SCharles Keepax ret); 2039b618a185SCharles Keepax return ERR_PTR(ret); 2040b618a185SCharles Keepax } 2041b618a185SCharles Keepax 2042b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 2043503ada8aSRichard Fitzgerald adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 20447f7cca08SCharles Keepax reg, be32_to_cpu(val)); 20457f7cca08SCharles Keepax 20467f7cca08SCharles Keepax /* Convert length from DSP words to bytes */ 20477f7cca08SCharles Keepax len *= sizeof(u32); 2048b618a185SCharles Keepax 2049517ee74eSCharles Keepax alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 2050b618a185SCharles Keepax if (!alg) 2051b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 2052b618a185SCharles Keepax 2053170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos); 20547f7cca08SCharles Keepax 20557f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, alg, len); 2056b618a185SCharles Keepax if (ret != 0) { 20577d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 2058b618a185SCharles Keepax kfree(alg); 2059b618a185SCharles Keepax return ERR_PTR(ret); 2060b618a185SCharles Keepax } 2061b618a185SCharles Keepax 2062b618a185SCharles Keepax return alg; 2063b618a185SCharles Keepax } 2064b618a185SCharles Keepax 206514197095SCharles Keepax static struct wm_adsp_alg_region * 206614197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 206714197095SCharles Keepax { 206814197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 206914197095SCharles Keepax 207014197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 207114197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 207214197095SCharles Keepax return alg_region; 207314197095SCharles Keepax } 207414197095SCharles Keepax 207514197095SCharles Keepax return NULL; 207614197095SCharles Keepax } 207714197095SCharles Keepax 2078d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 2079d9d20e17SCharles Keepax int type, __be32 id, 2080d9d20e17SCharles Keepax __be32 base) 2081d9d20e17SCharles Keepax { 2082d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 2083d9d20e17SCharles Keepax 2084d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 2085d9d20e17SCharles Keepax if (!alg_region) 2086d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 2087d9d20e17SCharles Keepax 2088d9d20e17SCharles Keepax alg_region->type = type; 2089d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 2090d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 2091d9d20e17SCharles Keepax 2092d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 2093d9d20e17SCharles Keepax 20942323736dSCharles Keepax if (dsp->fw_ver > 0) 20952323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 20962323736dSCharles Keepax 2097d9d20e17SCharles Keepax return alg_region; 2098d9d20e17SCharles Keepax } 2099d9d20e17SCharles Keepax 210056574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 210156574d54SRichard Fitzgerald { 210256574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 210356574d54SRichard Fitzgerald 210456574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 210556574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 210656574d54SRichard Fitzgerald struct wm_adsp_alg_region, 210756574d54SRichard Fitzgerald list); 210856574d54SRichard Fitzgerald list_del(&alg_region->list); 210956574d54SRichard Fitzgerald kfree(alg_region); 211056574d54SRichard Fitzgerald } 211156574d54SRichard Fitzgerald } 211256574d54SRichard Fitzgerald 2113a5dcb24dSCharles Keepax static void wmfw_parse_id_header(struct wm_adsp *dsp, 2114a5dcb24dSCharles Keepax struct wmfw_id_hdr *fw, int nalgs) 2115a5dcb24dSCharles Keepax { 2116a5dcb24dSCharles Keepax dsp->fw_id = be32_to_cpu(fw->id); 2117a5dcb24dSCharles Keepax dsp->fw_id_version = be32_to_cpu(fw->ver); 2118a5dcb24dSCharles Keepax 2119cd537873SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 2120a5dcb24dSCharles Keepax dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 2121a5dcb24dSCharles Keepax (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2122a5dcb24dSCharles Keepax nalgs); 2123a5dcb24dSCharles Keepax } 2124a5dcb24dSCharles Keepax 2125170b1e12SWen Shi static void wmfw_v3_parse_id_header(struct wm_adsp *dsp, 2126170b1e12SWen Shi struct wmfw_v3_id_hdr *fw, int nalgs) 2127170b1e12SWen Shi { 2128170b1e12SWen Shi dsp->fw_id = be32_to_cpu(fw->id); 2129170b1e12SWen Shi dsp->fw_id_version = be32_to_cpu(fw->ver); 2130170b1e12SWen Shi dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 2131170b1e12SWen Shi 2132cd537873SCharles Keepax adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 2133170b1e12SWen Shi dsp->fw_id, dsp->fw_vendor_id, 2134170b1e12SWen Shi (dsp->fw_id_version & 0xff0000) >> 16, 2135170b1e12SWen Shi (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2136170b1e12SWen Shi nalgs); 2137170b1e12SWen Shi } 2138170b1e12SWen Shi 2139170b1e12SWen Shi static int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions, 2140170b1e12SWen Shi int *type, __be32 *base) 2141170b1e12SWen Shi { 2142170b1e12SWen Shi struct wm_adsp_alg_region *alg_region; 2143170b1e12SWen Shi int i; 2144170b1e12SWen Shi 2145170b1e12SWen Shi for (i = 0; i < nregions; i++) { 2146170b1e12SWen Shi alg_region = wm_adsp_create_region(dsp, type[i], id, base[i]); 2147170b1e12SWen Shi if (IS_ERR(alg_region)) 2148170b1e12SWen Shi return PTR_ERR(alg_region); 2149170b1e12SWen Shi } 2150170b1e12SWen Shi 2151170b1e12SWen Shi return 0; 2152170b1e12SWen Shi } 2153170b1e12SWen Shi 2154b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 2155b618a185SCharles Keepax { 2156b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 2157b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 21583809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2159b618a185SCharles Keepax const struct wm_adsp_region *mem; 2160b618a185SCharles Keepax unsigned int pos, len; 21613809f001SCharles Keepax size_t n_algs; 2162b618a185SCharles Keepax int i, ret; 2163b618a185SCharles Keepax 2164b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 21656c452bdaSTakashi Iwai if (WARN_ON(!mem)) 2166db40517cSMark Brown return -EINVAL; 2167db40517cSMark Brown 2168b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 2169db40517cSMark Brown sizeof(adsp1_id)); 2170db40517cSMark Brown if (ret != 0) { 2171db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 2172db40517cSMark Brown ret); 2173db40517cSMark Brown return ret; 2174db40517cSMark Brown } 2175db40517cSMark Brown 21763809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 2177a5dcb24dSCharles Keepax 2178a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs); 2179db40517cSMark Brown 2180d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2181d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 2182d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2183d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2184ac50009fSMark Brown 2185d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2186d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 2187d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2188d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2189ac50009fSMark Brown 21907f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 21917f7cca08SCharles Keepax pos = sizeof(adsp1_id) / sizeof(u32); 21927f7cca08SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 2193db40517cSMark Brown 21947f7cca08SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2195b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 2196b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 2197db40517cSMark Brown 21983809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2199471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 2200db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 2201db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 2202db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 2203471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 2204471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 2205471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 2206471f4885SMark Brown 2207d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2208d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2209d9d20e17SCharles Keepax adsp1_alg[i].dm); 2210d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2211d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2212d6d52179SJS Park goto out; 2213d6d52179SJS Park } 22142323736dSCharles Keepax if (dsp->fw_ver == 0) { 22153809f001SCharles Keepax if (i + 1 < n_algs) { 22166958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 22176958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 22186958eb2aSCharles Keepax len *= 4; 22192323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 22208eb084d0SStuart Henderson len, NULL, 0, 0, 22218eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 22226ab2b7b4SDimitris Papastamos } else { 22236ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 22246ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 22256ab2b7b4SDimitris Papastamos } 22262323736dSCharles Keepax } 2227471f4885SMark Brown 2228d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2229d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2230d9d20e17SCharles Keepax adsp1_alg[i].zm); 2231d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2232d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2233d6d52179SJS Park goto out; 2234d6d52179SJS Park } 22352323736dSCharles Keepax if (dsp->fw_ver == 0) { 22363809f001SCharles Keepax if (i + 1 < n_algs) { 22376958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 22386958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 22396958eb2aSCharles Keepax len *= 4; 22402323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 22418eb084d0SStuart Henderson len, NULL, 0, 0, 22428eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 22436ab2b7b4SDimitris Papastamos } else { 22446ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 22456ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 22466ab2b7b4SDimitris Papastamos } 2247b618a185SCharles Keepax } 22482323736dSCharles Keepax } 2249db40517cSMark Brown 2250b618a185SCharles Keepax out: 2251b618a185SCharles Keepax kfree(adsp1_alg); 2252b618a185SCharles Keepax return ret; 2253b618a185SCharles Keepax } 2254b618a185SCharles Keepax 2255b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 2256b618a185SCharles Keepax { 2257b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 2258b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 22593809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2260b618a185SCharles Keepax const struct wm_adsp_region *mem; 2261b618a185SCharles Keepax unsigned int pos, len; 22623809f001SCharles Keepax size_t n_algs; 2263b618a185SCharles Keepax int i, ret; 2264b618a185SCharles Keepax 2265b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2266b618a185SCharles Keepax if (WARN_ON(!mem)) 2267b618a185SCharles Keepax return -EINVAL; 2268b618a185SCharles Keepax 2269b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 2270b618a185SCharles Keepax sizeof(adsp2_id)); 2271b618a185SCharles Keepax if (ret != 0) { 2272b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 2273b618a185SCharles Keepax ret); 2274b618a185SCharles Keepax return ret; 2275b618a185SCharles Keepax } 2276b618a185SCharles Keepax 22773809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 2278a5dcb24dSCharles Keepax 2279a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs); 2280b618a185SCharles Keepax 2281d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2282d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2283d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2284d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2285b618a185SCharles Keepax 2286d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2287d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2288d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2289d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2290b618a185SCharles Keepax 2291d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2292d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2293d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2294d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2295b618a185SCharles Keepax 22967f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 22977f7cca08SCharles Keepax pos = sizeof(adsp2_id) / sizeof(u32); 22987f7cca08SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 2299b618a185SCharles Keepax 23007f7cca08SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2301b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2302b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2303b618a185SCharles Keepax 23043809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2305471f4885SMark Brown adsp_info(dsp, 2306471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2307db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2308db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2309db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2310471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2311471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2312471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2313471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2314471f4885SMark Brown 2315d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2316d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2317d9d20e17SCharles Keepax adsp2_alg[i].xm); 2318d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2319d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2320d6d52179SJS Park goto out; 2321d6d52179SJS Park } 23222323736dSCharles Keepax if (dsp->fw_ver == 0) { 23233809f001SCharles Keepax if (i + 1 < n_algs) { 23246958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 23256958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 23266958eb2aSCharles Keepax len *= 4; 23272323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23288eb084d0SStuart Henderson len, NULL, 0, 0, 23298eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23306ab2b7b4SDimitris Papastamos } else { 23316ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 23326ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 23336ab2b7b4SDimitris Papastamos } 23342323736dSCharles Keepax } 2335471f4885SMark Brown 2336d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2337d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2338d9d20e17SCharles Keepax adsp2_alg[i].ym); 2339d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2340d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2341d6d52179SJS Park goto out; 2342d6d52179SJS Park } 23432323736dSCharles Keepax if (dsp->fw_ver == 0) { 23443809f001SCharles Keepax if (i + 1 < n_algs) { 23456958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 23466958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 23476958eb2aSCharles Keepax len *= 4; 23482323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23498eb084d0SStuart Henderson len, NULL, 0, 0, 23508eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23516ab2b7b4SDimitris Papastamos } else { 23526ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 23536ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 23546ab2b7b4SDimitris Papastamos } 23552323736dSCharles Keepax } 2356471f4885SMark Brown 2357d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2358d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2359d9d20e17SCharles Keepax adsp2_alg[i].zm); 2360d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2361d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2362d6d52179SJS Park goto out; 2363d6d52179SJS Park } 23642323736dSCharles Keepax if (dsp->fw_ver == 0) { 23653809f001SCharles Keepax if (i + 1 < n_algs) { 23666958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 23676958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 23686958eb2aSCharles Keepax len *= 4; 23692323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23708eb084d0SStuart Henderson len, NULL, 0, 0, 23718eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23726ab2b7b4SDimitris Papastamos } else { 23736ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 23746ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 23756ab2b7b4SDimitris Papastamos } 2376db40517cSMark Brown } 23772323736dSCharles Keepax } 2378db40517cSMark Brown 2379db40517cSMark Brown out: 2380b618a185SCharles Keepax kfree(adsp2_alg); 2381db40517cSMark Brown return ret; 2382db40517cSMark Brown } 2383db40517cSMark Brown 2384170b1e12SWen Shi static int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id, 2385170b1e12SWen Shi __be32 xm_base, __be32 ym_base) 2386170b1e12SWen Shi { 2387170b1e12SWen Shi int types[] = { 2388170b1e12SWen Shi WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 2389170b1e12SWen Shi WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 2390170b1e12SWen Shi }; 2391170b1e12SWen Shi __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 2392170b1e12SWen Shi 2393170b1e12SWen Shi return wm_adsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); 2394170b1e12SWen Shi } 2395170b1e12SWen Shi 2396170b1e12SWen Shi static int wm_halo_setup_algs(struct wm_adsp *dsp) 2397170b1e12SWen Shi { 2398170b1e12SWen Shi struct wmfw_halo_id_hdr halo_id; 2399170b1e12SWen Shi struct wmfw_halo_alg_hdr *halo_alg; 2400170b1e12SWen Shi const struct wm_adsp_region *mem; 2401170b1e12SWen Shi unsigned int pos, len; 2402170b1e12SWen Shi size_t n_algs; 2403170b1e12SWen Shi int i, ret; 2404170b1e12SWen Shi 2405170b1e12SWen Shi mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2406170b1e12SWen Shi if (WARN_ON(!mem)) 2407170b1e12SWen Shi return -EINVAL; 2408170b1e12SWen Shi 2409170b1e12SWen Shi ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 2410170b1e12SWen Shi sizeof(halo_id)); 2411170b1e12SWen Shi if (ret != 0) { 2412170b1e12SWen Shi adsp_err(dsp, "Failed to read algorithm info: %d\n", 2413170b1e12SWen Shi ret); 2414170b1e12SWen Shi return ret; 2415170b1e12SWen Shi } 2416170b1e12SWen Shi 2417170b1e12SWen Shi n_algs = be32_to_cpu(halo_id.n_algs); 2418170b1e12SWen Shi 2419170b1e12SWen Shi wmfw_v3_parse_id_header(dsp, &halo_id.fw, n_algs); 2420170b1e12SWen Shi 2421170b1e12SWen Shi ret = wm_halo_create_regions(dsp, halo_id.fw.id, 2422b75a9799SCharles Keepax halo_id.xm_base, halo_id.ym_base); 2423170b1e12SWen Shi if (ret) 2424170b1e12SWen Shi return ret; 2425170b1e12SWen Shi 2426170b1e12SWen Shi /* Calculate offset and length in DSP words */ 2427170b1e12SWen Shi pos = sizeof(halo_id) / sizeof(u32); 2428170b1e12SWen Shi len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 2429170b1e12SWen Shi 2430170b1e12SWen Shi halo_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2431170b1e12SWen Shi if (IS_ERR(halo_alg)) 2432170b1e12SWen Shi return PTR_ERR(halo_alg); 2433170b1e12SWen Shi 2434170b1e12SWen Shi for (i = 0; i < n_algs; i++) { 2435170b1e12SWen Shi adsp_info(dsp, 2436170b1e12SWen Shi "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 2437170b1e12SWen Shi i, be32_to_cpu(halo_alg[i].alg.id), 2438170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 2439170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 2440170b1e12SWen Shi be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 2441170b1e12SWen Shi be32_to_cpu(halo_alg[i].xm_base), 2442170b1e12SWen Shi be32_to_cpu(halo_alg[i].ym_base)); 2443170b1e12SWen Shi 2444170b1e12SWen Shi ret = wm_halo_create_regions(dsp, halo_alg[i].alg.id, 2445170b1e12SWen Shi halo_alg[i].xm_base, 2446170b1e12SWen Shi halo_alg[i].ym_base); 2447170b1e12SWen Shi if (ret) 2448170b1e12SWen Shi goto out; 2449170b1e12SWen Shi } 2450170b1e12SWen Shi 2451170b1e12SWen Shi out: 2452170b1e12SWen Shi kfree(halo_alg); 2453170b1e12SWen Shi return ret; 2454170b1e12SWen Shi } 2455170b1e12SWen Shi 24562159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 24572159ad93SMark Brown { 2458cf17c83cSMark Brown LIST_HEAD(buf_list); 24592159ad93SMark Brown struct regmap *regmap = dsp->regmap; 24602159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 24612159ad93SMark Brown struct wmfw_coeff_item *blk; 24622159ad93SMark Brown const struct firmware *firmware; 2463471f4885SMark Brown const struct wm_adsp_region *mem; 2464471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 24652159ad93SMark Brown const char *region_name; 24662159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 24672159ad93SMark Brown char *file; 2468cf17c83cSMark Brown struct wm_adsp_buf *buf; 24692159ad93SMark Brown 24702159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 24712159ad93SMark Brown if (file == NULL) 24722159ad93SMark Brown return -ENOMEM; 24732159ad93SMark Brown 2474605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, 24751023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 24762159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 24772159ad93SMark Brown 24782159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 24792159ad93SMark Brown if (ret != 0) { 24802159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 24812159ad93SMark Brown ret = 0; 24822159ad93SMark Brown goto out; 24832159ad93SMark Brown } 24842159ad93SMark Brown ret = -EINVAL; 24852159ad93SMark Brown 24862159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 24872159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 24882159ad93SMark Brown file, firmware->size); 24892159ad93SMark Brown goto out_fw; 24902159ad93SMark Brown } 24912159ad93SMark Brown 24922159ad93SMark Brown hdr = (void *)&firmware->data[0]; 24932159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 24942159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2495a4cdbec7SCharles Keepax goto out_fw; 24962159ad93SMark Brown } 24972159ad93SMark Brown 2498c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2499c712326dSMark Brown case 1: 2500c712326dSMark Brown break; 2501c712326dSMark Brown default: 2502c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2503c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2504c712326dSMark Brown ret = -EINVAL; 2505c712326dSMark Brown goto out_fw; 2506c712326dSMark Brown } 2507c712326dSMark Brown 25082159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 25092159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 25102159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 25112159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 25122159ad93SMark Brown 25132159ad93SMark Brown pos = le32_to_cpu(hdr->len); 25142159ad93SMark Brown 25152159ad93SMark Brown blocks = 0; 25162159ad93SMark Brown while (pos < firmware->size && 251750dd2ea8SBen Hutchings sizeof(*blk) < firmware->size - pos) { 25182159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 25192159ad93SMark Brown 2520c712326dSMark Brown type = le16_to_cpu(blk->type); 2521c712326dSMark Brown offset = le16_to_cpu(blk->offset); 25222159ad93SMark Brown 25232159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 25242159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 25252159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 25262159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 25272159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 25282159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 25292159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 25302159ad93SMark Brown 25312159ad93SMark Brown reg = 0; 25322159ad93SMark Brown region_name = "Unknown"; 25332159ad93SMark Brown switch (type) { 2534c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2535c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 25362159ad93SMark Brown break; 2537c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2538f395a218SMark Brown /* 2539f395a218SMark Brown * Old files may use this for global 2540f395a218SMark Brown * coefficients. 2541f395a218SMark Brown */ 2542f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2543f395a218SMark Brown offset == 0) { 2544f395a218SMark Brown region_name = "global coefficients"; 2545f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2546f395a218SMark Brown if (!mem) { 2547f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2548f395a218SMark Brown break; 2549f395a218SMark Brown } 2550170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, 0); 2551f395a218SMark Brown 2552f395a218SMark Brown } else { 25532159ad93SMark Brown region_name = "register"; 25542159ad93SMark Brown reg = offset; 2555f395a218SMark Brown } 25562159ad93SMark Brown break; 2557471f4885SMark Brown 2558471f4885SMark Brown case WMFW_ADSP1_DM: 2559471f4885SMark Brown case WMFW_ADSP1_ZM: 2560471f4885SMark Brown case WMFW_ADSP2_XM: 2561471f4885SMark Brown case WMFW_ADSP2_YM: 2562170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 2563170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 2564170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 2565471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2566471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2567471f4885SMark Brown type, le32_to_cpu(blk->id)); 2568471f4885SMark Brown 2569471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2570471f4885SMark Brown if (!mem) { 2571471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2572471f4885SMark Brown break; 2573471f4885SMark Brown } 2574471f4885SMark Brown 257514197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 257614197095SCharles Keepax le32_to_cpu(blk->id)); 257714197095SCharles Keepax if (alg_region) { 2578338c5188SMark Brown reg = alg_region->base; 2579170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, reg); 2580338c5188SMark Brown reg += offset; 258114197095SCharles Keepax } else { 2582471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2583471f4885SMark Brown type, le32_to_cpu(blk->id)); 258414197095SCharles Keepax } 2585471f4885SMark Brown break; 2586471f4885SMark Brown 25872159ad93SMark Brown default: 258825c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 258925c62f7eSMark Brown file, blocks, type, pos); 25902159ad93SMark Brown break; 25912159ad93SMark Brown } 25922159ad93SMark Brown 25932159ad93SMark Brown if (reg) { 259450dd2ea8SBen Hutchings if (le32_to_cpu(blk->len) > 259550dd2ea8SBen Hutchings firmware->size - pos - sizeof(*blk)) { 25961cab2a84SRichard Fitzgerald adsp_err(dsp, 25971cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 25981cab2a84SRichard Fitzgerald file, blocks, region_name, 25991cab2a84SRichard Fitzgerald le32_to_cpu(blk->len), 26001cab2a84SRichard Fitzgerald firmware->size); 26011cab2a84SRichard Fitzgerald ret = -EINVAL; 26021cab2a84SRichard Fitzgerald goto out_fw; 26031cab2a84SRichard Fitzgerald } 26041cab2a84SRichard Fitzgerald 2605cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2606cf17c83cSMark Brown le32_to_cpu(blk->len), 2607cf17c83cSMark Brown &buf_list); 2608a76fefabSMark Brown if (!buf) { 2609a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2610f4b82812SWei Yongjun ret = -ENOMEM; 2611f4b82812SWei Yongjun goto out_fw; 2612a76fefabSMark Brown } 2613a76fefabSMark Brown 261420da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 261520da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 261620da6d5aSMark Brown reg); 2617cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 26182159ad93SMark Brown le32_to_cpu(blk->len)); 26192159ad93SMark Brown if (ret != 0) { 26202159ad93SMark Brown adsp_err(dsp, 262143bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 262243bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 26232159ad93SMark Brown } 26242159ad93SMark Brown } 26252159ad93SMark Brown 2626be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 26272159ad93SMark Brown blocks++; 26282159ad93SMark Brown } 26292159ad93SMark Brown 2630cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2631cf17c83cSMark Brown if (ret != 0) 2632cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2633cf17c83cSMark Brown 26342159ad93SMark Brown if (pos > firmware->size) 26352159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 26362159ad93SMark Brown file, blocks, pos - firmware->size); 26372159ad93SMark Brown 2638f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2639f9f55e31SRichard Fitzgerald 26402159ad93SMark Brown out_fw: 26419da7a5a9SCharles Keepax regmap_async_complete(regmap); 26422159ad93SMark Brown release_firmware(firmware); 2643cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 26442159ad93SMark Brown out: 26452159ad93SMark Brown kfree(file); 2646f4b82812SWei Yongjun return ret; 26472159ad93SMark Brown } 26482159ad93SMark Brown 2649605391d0SRichard Fitzgerald static int wm_adsp_create_name(struct wm_adsp *dsp) 2650605391d0SRichard Fitzgerald { 2651605391d0SRichard Fitzgerald char *p; 2652605391d0SRichard Fitzgerald 2653605391d0SRichard Fitzgerald if (!dsp->name) { 2654605391d0SRichard Fitzgerald dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2655605391d0SRichard Fitzgerald dsp->num); 2656605391d0SRichard Fitzgerald if (!dsp->name) 2657605391d0SRichard Fitzgerald return -ENOMEM; 2658605391d0SRichard Fitzgerald } 2659605391d0SRichard Fitzgerald 2660605391d0SRichard Fitzgerald if (!dsp->fwf_name) { 2661605391d0SRichard Fitzgerald p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); 2662605391d0SRichard Fitzgerald if (!p) 2663605391d0SRichard Fitzgerald return -ENOMEM; 2664605391d0SRichard Fitzgerald 2665605391d0SRichard Fitzgerald dsp->fwf_name = p; 2666605391d0SRichard Fitzgerald for (; *p != 0; ++p) 2667605391d0SRichard Fitzgerald *p = tolower(*p); 2668605391d0SRichard Fitzgerald } 2669605391d0SRichard Fitzgerald 2670605391d0SRichard Fitzgerald return 0; 2671605391d0SRichard Fitzgerald } 2672605391d0SRichard Fitzgerald 2673dcad34f8SRichard Fitzgerald static int wm_adsp_common_init(struct wm_adsp *dsp) 26745e7a7a22SMark Brown { 2675605391d0SRichard Fitzgerald int ret; 2676605391d0SRichard Fitzgerald 2677605391d0SRichard Fitzgerald ret = wm_adsp_create_name(dsp); 2678605391d0SRichard Fitzgerald if (ret) 2679605391d0SRichard Fitzgerald return ret; 2680605391d0SRichard Fitzgerald 26813809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 2682dcad34f8SRichard Fitzgerald INIT_LIST_HEAD(&dsp->ctl_list); 26834f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->compr_list); 26844f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->buffer_list); 26855e7a7a22SMark Brown 2686078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2687078e7183SCharles Keepax 26885e7a7a22SMark Brown return 0; 26895e7a7a22SMark Brown } 2690dcad34f8SRichard Fitzgerald 2691dcad34f8SRichard Fitzgerald int wm_adsp1_init(struct wm_adsp *dsp) 2692dcad34f8SRichard Fitzgerald { 26934e08d50dSCharles Keepax dsp->ops = &wm_adsp1_ops; 26944e08d50dSCharles Keepax 2695dcad34f8SRichard Fitzgerald return wm_adsp_common_init(dsp); 2696dcad34f8SRichard Fitzgerald } 26975e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 26985e7a7a22SMark Brown 26992159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 27002159ad93SMark Brown struct snd_kcontrol *kcontrol, 27012159ad93SMark Brown int event) 27022159ad93SMark Brown { 27030fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 27040fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 27052159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 27066ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 27072159ad93SMark Brown int ret; 27087585a5b0SCharles Keepax unsigned int val; 27092159ad93SMark Brown 27100fe1daa6SKuninori Morimoto dsp->component = component; 271192bb4c32SDimitris Papastamos 2712078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2713078e7183SCharles Keepax 27142159ad93SMark Brown switch (event) { 27152159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 27162159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 27172159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 27182159ad93SMark Brown 271994e205bfSChris Rattray /* 272094e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 272194e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 272294e205bfSChris Rattray */ 272394e205bfSChris Rattray if (dsp->sysclk_reg) { 272494e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 272594e205bfSChris Rattray if (ret != 0) { 272694e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 272794e205bfSChris Rattray ret); 2728078e7183SCharles Keepax goto err_mutex; 272994e205bfSChris Rattray } 273094e205bfSChris Rattray 27317d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 273294e205bfSChris Rattray 273394e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 273494e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 273594e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 273694e205bfSChris Rattray if (ret != 0) { 273794e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 273894e205bfSChris Rattray ret); 2739078e7183SCharles Keepax goto err_mutex; 274094e205bfSChris Rattray } 274194e205bfSChris Rattray } 274294e205bfSChris Rattray 27432159ad93SMark Brown ret = wm_adsp_load(dsp); 27442159ad93SMark Brown if (ret != 0) 2745078e7183SCharles Keepax goto err_ena; 27462159ad93SMark Brown 2747b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2748db40517cSMark Brown if (ret != 0) 2749078e7183SCharles Keepax goto err_ena; 2750db40517cSMark Brown 27512159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 27522159ad93SMark Brown if (ret != 0) 2753078e7183SCharles Keepax goto err_ena; 27542159ad93SMark Brown 27550c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 275681ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 27576ab2b7b4SDimitris Papastamos if (ret != 0) 2758078e7183SCharles Keepax goto err_ena; 27596ab2b7b4SDimitris Papastamos 27600c2e3f34SDimitris Papastamos /* Sync set controls */ 276181ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 27626ab2b7b4SDimitris Papastamos if (ret != 0) 2763078e7183SCharles Keepax goto err_ena; 27646ab2b7b4SDimitris Papastamos 276528823ebaSCharles Keepax dsp->booted = true; 276628823ebaSCharles Keepax 27672159ad93SMark Brown /* Start the core running */ 27682159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 27692159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 27702159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 277128823ebaSCharles Keepax 277228823ebaSCharles Keepax dsp->running = true; 27732159ad93SMark Brown break; 27742159ad93SMark Brown 27752159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 277628823ebaSCharles Keepax dsp->running = false; 277728823ebaSCharles Keepax dsp->booted = false; 277828823ebaSCharles Keepax 27792159ad93SMark Brown /* Halt the core */ 27802159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 27812159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 27822159ad93SMark Brown 27832159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 27842159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 27852159ad93SMark Brown 27862159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 27872159ad93SMark Brown ADSP1_SYS_ENA, 0); 27886ab2b7b4SDimitris Papastamos 278981ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 27906ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2791b0101b4fSDimitris Papastamos 279256574d54SRichard Fitzgerald 279356574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 27942159ad93SMark Brown break; 27952159ad93SMark Brown 27962159ad93SMark Brown default: 27972159ad93SMark Brown break; 27982159ad93SMark Brown } 27992159ad93SMark Brown 2800078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2801078e7183SCharles Keepax 28022159ad93SMark Brown return 0; 28032159ad93SMark Brown 2804078e7183SCharles Keepax err_ena: 28052159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28062159ad93SMark Brown ADSP1_SYS_ENA, 0); 2807078e7183SCharles Keepax err_mutex: 2808078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2809078e7183SCharles Keepax 28102159ad93SMark Brown return ret; 28112159ad93SMark Brown } 28122159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 28132159ad93SMark Brown 28144e08d50dSCharles Keepax static int wm_adsp2v2_enable_core(struct wm_adsp *dsp) 28152159ad93SMark Brown { 28162159ad93SMark Brown unsigned int val; 28172159ad93SMark Brown int ret, count; 28182159ad93SMark Brown 28192159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2820939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 28217d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 28222159ad93SMark Brown if (ret != 0) 28232159ad93SMark Brown return ret; 2824939fd1e8SCharles Keepax 2825939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2826939fd1e8SCharles Keepax break; 2827939fd1e8SCharles Keepax 28281fa96f3fSCharles Keepax usleep_range(250, 500); 2829939fd1e8SCharles Keepax } 28302159ad93SMark Brown 28312159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 28322159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 28332159ad93SMark Brown return -EBUSY; 28342159ad93SMark Brown } 28352159ad93SMark Brown 28362159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 28372159ad93SMark Brown 28382159ad93SMark Brown return 0; 28392159ad93SMark Brown } 28402159ad93SMark Brown 28414e08d50dSCharles Keepax static int wm_adsp2_enable_core(struct wm_adsp *dsp) 28424e08d50dSCharles Keepax { 28434e08d50dSCharles Keepax int ret; 28444e08d50dSCharles Keepax 28454e08d50dSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 28464e08d50dSCharles Keepax ADSP2_SYS_ENA, ADSP2_SYS_ENA); 28474e08d50dSCharles Keepax if (ret != 0) 28484e08d50dSCharles Keepax return ret; 28494e08d50dSCharles Keepax 28504e08d50dSCharles Keepax return wm_adsp2v2_enable_core(dsp); 28514e08d50dSCharles Keepax } 28524e08d50dSCharles Keepax 28532b0ee49fSCharles Keepax static int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 28542b0ee49fSCharles Keepax { 28552b0ee49fSCharles Keepax struct regmap *regmap = dsp->regmap; 28562b0ee49fSCharles Keepax unsigned int code0, code1, lock_reg; 28572b0ee49fSCharles Keepax 28582b0ee49fSCharles Keepax if (!(lock_regions & WM_ADSP2_REGION_ALL)) 28592b0ee49fSCharles Keepax return 0; 28602b0ee49fSCharles Keepax 28612b0ee49fSCharles Keepax lock_regions &= WM_ADSP2_REGION_ALL; 28622b0ee49fSCharles Keepax lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 28632b0ee49fSCharles Keepax 28642b0ee49fSCharles Keepax while (lock_regions) { 28652b0ee49fSCharles Keepax code0 = code1 = 0; 28662b0ee49fSCharles Keepax if (lock_regions & BIT(0)) { 28672b0ee49fSCharles Keepax code0 = ADSP2_LOCK_CODE_0; 28682b0ee49fSCharles Keepax code1 = ADSP2_LOCK_CODE_1; 28692b0ee49fSCharles Keepax } 28702b0ee49fSCharles Keepax if (lock_regions & BIT(1)) { 28712b0ee49fSCharles Keepax code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 28722b0ee49fSCharles Keepax code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 28732b0ee49fSCharles Keepax } 28742b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code0); 28752b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code1); 28762b0ee49fSCharles Keepax lock_regions >>= 2; 28772b0ee49fSCharles Keepax lock_reg += 2; 28782b0ee49fSCharles Keepax } 28792b0ee49fSCharles Keepax 28802b0ee49fSCharles Keepax return 0; 28812b0ee49fSCharles Keepax } 28822b0ee49fSCharles Keepax 28834e08d50dSCharles Keepax static int wm_adsp2_enable_memory(struct wm_adsp *dsp) 28844e08d50dSCharles Keepax { 28854e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 28864e08d50dSCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 28874e08d50dSCharles Keepax } 28884e08d50dSCharles Keepax 28894e08d50dSCharles Keepax static void wm_adsp2_disable_memory(struct wm_adsp *dsp) 28904e08d50dSCharles Keepax { 28914e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 28924e08d50dSCharles Keepax ADSP2_MEM_ENA, 0); 28934e08d50dSCharles Keepax } 28944e08d50dSCharles Keepax 28954e08d50dSCharles Keepax static void wm_adsp2_disable_core(struct wm_adsp *dsp) 28964e08d50dSCharles Keepax { 28974e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 28984e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 28994e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 29004e08d50dSCharles Keepax 29014e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 29024e08d50dSCharles Keepax ADSP2_SYS_ENA, 0); 29034e08d50dSCharles Keepax } 29044e08d50dSCharles Keepax 29054e08d50dSCharles Keepax static void wm_adsp2v2_disable_core(struct wm_adsp *dsp) 29064e08d50dSCharles Keepax { 29074e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 29084e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 29094e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 29104e08d50dSCharles Keepax } 29114e08d50dSCharles Keepax 29124e08d50dSCharles Keepax static void wm_adsp_boot_work(struct work_struct *work) 29132159ad93SMark Brown { 2914d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2915d8a64d6aSCharles Keepax struct wm_adsp, 2916d8a64d6aSCharles Keepax boot_work); 29172159ad93SMark Brown int ret; 29182159ad93SMark Brown 2919078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2920078e7183SCharles Keepax 29214e08d50dSCharles Keepax if (dsp->ops->enable_memory) { 29224e08d50dSCharles Keepax ret = dsp->ops->enable_memory(dsp); 292390d19ba5SCharles Keepax if (ret != 0) 292490d19ba5SCharles Keepax goto err_mutex; 29254e08d50dSCharles Keepax } 292690d19ba5SCharles Keepax 29274e08d50dSCharles Keepax if (dsp->ops->enable_core) { 29284e08d50dSCharles Keepax ret = dsp->ops->enable_core(dsp); 29292159ad93SMark Brown if (ret != 0) 2930d589d8b8SCharles Keepax goto err_mem; 29314e08d50dSCharles Keepax } 29322159ad93SMark Brown 29332159ad93SMark Brown ret = wm_adsp_load(dsp); 29342159ad93SMark Brown if (ret != 0) 2935078e7183SCharles Keepax goto err_ena; 29362159ad93SMark Brown 29374e08d50dSCharles Keepax ret = dsp->ops->setup_algs(dsp); 2938db40517cSMark Brown if (ret != 0) 2939078e7183SCharles Keepax goto err_ena; 2940db40517cSMark Brown 29412159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 29422159ad93SMark Brown if (ret != 0) 2943078e7183SCharles Keepax goto err_ena; 29442159ad93SMark Brown 29450c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 294681ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 29476ab2b7b4SDimitris Papastamos if (ret != 0) 2948078e7183SCharles Keepax goto err_ena; 29496ab2b7b4SDimitris Papastamos 29504e08d50dSCharles Keepax if (dsp->ops->disable_core) 29514e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 295290d19ba5SCharles Keepax 2953e779974bSCharles Keepax dsp->booted = true; 2954e779974bSCharles Keepax 2955078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2956078e7183SCharles Keepax 2957d8a64d6aSCharles Keepax return; 2958d8a64d6aSCharles Keepax 2959078e7183SCharles Keepax err_ena: 29604e08d50dSCharles Keepax if (dsp->ops->disable_core) 29614e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 2962d589d8b8SCharles Keepax err_mem: 29634e08d50dSCharles Keepax if (dsp->ops->disable_memory) 29644e08d50dSCharles Keepax dsp->ops->disable_memory(dsp); 2965078e7183SCharles Keepax err_mutex: 2966078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2967d8a64d6aSCharles Keepax } 2968d8a64d6aSCharles Keepax 2969170b1e12SWen Shi static int wm_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions) 2970170b1e12SWen Shi { 2971170b1e12SWen Shi struct reg_sequence config[] = { 2972170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 2973170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 2974170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 2975170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 2976170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 2977170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 2978170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 2979170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 2980170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 2981170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 2982170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 2983170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 2984170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 2985170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 2986170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 2987170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 2988170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 2989170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 2990170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 2991170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 2992170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 2993170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 2994170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 2995170b1e12SWen Shi }; 2996170b1e12SWen Shi 2997170b1e12SWen Shi return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 2998170b1e12SWen Shi } 2999170b1e12SWen Shi 3000b9070df4SRichard Fitzgerald int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) 3001d82d767fSCharles Keepax { 3002b9070df4SRichard Fitzgerald struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 3003b9070df4SRichard Fitzgerald struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3004b9070df4SRichard Fitzgerald struct wm_adsp *dsp = &dsps[w->shift]; 3005d82d767fSCharles Keepax int ret; 3006d82d767fSCharles Keepax 3007b9070df4SRichard Fitzgerald ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 3008d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 3009d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 3010b9070df4SRichard Fitzgerald if (ret) 3011d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 3012b9070df4SRichard Fitzgerald 3013b9070df4SRichard Fitzgerald return ret; 3014e1ea1879SRichard Fitzgerald } 3015b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); 3016d82d767fSCharles Keepax 3017af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 3018af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3019af813a6fSCharles Keepax { 30200fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3021b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3022b1470d4cSAjit Pandey struct soc_mixer_control *mc = 3023b1470d4cSAjit Pandey (struct soc_mixer_control *)kcontrol->private_value; 3024b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3025af813a6fSCharles Keepax 3026af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 3027af813a6fSCharles Keepax 3028af813a6fSCharles Keepax return 0; 3029af813a6fSCharles Keepax } 3030af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 3031af813a6fSCharles Keepax 3032af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 3033af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3034af813a6fSCharles Keepax { 30350fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3036b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 30370fe1daa6SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 3038af813a6fSCharles Keepax struct soc_mixer_control *mc = 3039af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 3040b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3041af813a6fSCharles Keepax char preload[32]; 3042af813a6fSCharles Keepax 3043605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 3044af813a6fSCharles Keepax 3045af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 3046af813a6fSCharles Keepax 3047af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 304895a594d0SCharles Keepax snd_soc_component_force_enable_pin(component, preload); 3049af813a6fSCharles Keepax else 305095a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3051af813a6fSCharles Keepax 3052af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 3053af813a6fSCharles Keepax 3054868e49a4SStuart Henderson flush_work(&dsp->boot_work); 3055868e49a4SStuart Henderson 3056af813a6fSCharles Keepax return 0; 3057af813a6fSCharles Keepax } 3058af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 3059af813a6fSCharles Keepax 306051a2c944SMayuresh Kulkarni static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) 306151a2c944SMayuresh Kulkarni { 306251a2c944SMayuresh Kulkarni regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 306351a2c944SMayuresh Kulkarni ADSP2_WDT_ENA_MASK, 0); 306451a2c944SMayuresh Kulkarni } 306551a2c944SMayuresh Kulkarni 30668bc144f9SStuart Henderson static void wm_halo_stop_watchdog(struct wm_adsp *dsp) 30678bc144f9SStuart Henderson { 30688bc144f9SStuart Henderson regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 30698bc144f9SStuart Henderson HALO_WDT_EN_MASK, 0); 30708bc144f9SStuart Henderson } 30718bc144f9SStuart Henderson 30724e08d50dSCharles Keepax int wm_adsp_early_event(struct snd_soc_dapm_widget *w, 3073b9070df4SRichard Fitzgerald struct snd_kcontrol *kcontrol, int event) 307412db5eddSCharles Keepax { 30750fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 30760fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 307712db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 307857a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 307912db5eddSCharles Keepax 308012db5eddSCharles Keepax switch (event) { 308112db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 308212db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 308312db5eddSCharles Keepax break; 308457a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 3085bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3086bb24ee41SCharles Keepax 308757a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 308857a60cc3SCharles Keepax 308957a60cc3SCharles Keepax dsp->fw_id = 0; 309057a60cc3SCharles Keepax dsp->fw_id_version = 0; 309157a60cc3SCharles Keepax 309257a60cc3SCharles Keepax dsp->booted = false; 309357a60cc3SCharles Keepax 30944e08d50dSCharles Keepax if (dsp->ops->disable_memory) 30954e08d50dSCharles Keepax dsp->ops->disable_memory(dsp); 309657a60cc3SCharles Keepax 309757a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 309857a60cc3SCharles Keepax ctl->enabled = 0; 309957a60cc3SCharles Keepax 310057a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 310157a60cc3SCharles Keepax 3102bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3103bb24ee41SCharles Keepax 310457a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 310557a60cc3SCharles Keepax break; 310612db5eddSCharles Keepax default: 310712db5eddSCharles Keepax break; 3108cab27258SCharles Keepax } 310912db5eddSCharles Keepax 311012db5eddSCharles Keepax return 0; 311112db5eddSCharles Keepax } 31124e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event); 311312db5eddSCharles Keepax 31144e08d50dSCharles Keepax static int wm_adsp2_start_core(struct wm_adsp *dsp) 31154e08d50dSCharles Keepax { 31164e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 31174e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 31184e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 31194e08d50dSCharles Keepax } 31204e08d50dSCharles Keepax 31214e08d50dSCharles Keepax static void wm_adsp2_stop_core(struct wm_adsp *dsp) 31224e08d50dSCharles Keepax { 31234e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 31244e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 31254e08d50dSCharles Keepax } 31264e08d50dSCharles Keepax 31274e08d50dSCharles Keepax int wm_adsp_event(struct snd_soc_dapm_widget *w, 3128d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 3129d8a64d6aSCharles Keepax { 31300fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 31310fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3132d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 3133d8a64d6aSCharles Keepax int ret; 3134d8a64d6aSCharles Keepax 3135d8a64d6aSCharles Keepax switch (event) { 3136d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 3137d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 3138d8a64d6aSCharles Keepax 3139bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3140bb24ee41SCharles Keepax 3141bb24ee41SCharles Keepax if (!dsp->booted) { 3142bb24ee41SCharles Keepax ret = -EIO; 3143bb24ee41SCharles Keepax goto err; 3144bb24ee41SCharles Keepax } 3145d8a64d6aSCharles Keepax 31464e08d50dSCharles Keepax if (dsp->ops->enable_core) { 31474e08d50dSCharles Keepax ret = dsp->ops->enable_core(dsp); 314890d19ba5SCharles Keepax if (ret != 0) 314990d19ba5SCharles Keepax goto err; 31504e08d50dSCharles Keepax } 315190d19ba5SCharles Keepax 3152cef45771SCharles Keepax /* Sync set controls */ 3153cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 3154cef45771SCharles Keepax if (ret != 0) 3155cef45771SCharles Keepax goto err; 3156cef45771SCharles Keepax 31574e08d50dSCharles Keepax if (dsp->ops->lock_memory) { 31584e08d50dSCharles Keepax ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 31594e08d50dSCharles Keepax if (ret != 0) { 31604e08d50dSCharles Keepax adsp_err(dsp, "Error configuring MPU: %d\n", 31614e08d50dSCharles Keepax ret); 31624e08d50dSCharles Keepax goto err; 31634e08d50dSCharles Keepax } 31644e08d50dSCharles Keepax } 316551a2c944SMayuresh Kulkarni 31664e08d50dSCharles Keepax if (dsp->ops->start_core) { 31674e08d50dSCharles Keepax ret = dsp->ops->start_core(dsp); 3168d8a64d6aSCharles Keepax if (ret != 0) 3169d8a64d6aSCharles Keepax goto err; 31704e08d50dSCharles Keepax } 31712cd19bdbSCharles Keepax 317248c2c993SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) { 31732cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 3174bb24ee41SCharles Keepax if (ret < 0) 317548c2c993SCharles Keepax goto err; 317648c2c993SCharles Keepax } 31772cd19bdbSCharles Keepax 3178e779974bSCharles Keepax dsp->running = true; 31791023dbd9SMark Brown 3180612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 31812159ad93SMark Brown break; 31822159ad93SMark Brown 31832159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 3184f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 3185f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 3186f4f0c4c6SRichard Fitzgerald 31874e08d50dSCharles Keepax if (dsp->ops->stop_watchdog) 31884e08d50dSCharles Keepax dsp->ops->stop_watchdog(dsp); 318951a2c944SMayuresh Kulkarni 319010337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 31914e08d50dSCharles Keepax if (dsp->ops->show_fw_status) 31924e08d50dSCharles Keepax dsp->ops->show_fw_status(dsp); 319310337b07SRichard Fitzgerald 3194078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 3195078e7183SCharles Keepax 31961023dbd9SMark Brown dsp->running = false; 31971023dbd9SMark Brown 31984e08d50dSCharles Keepax if (dsp->ops->stop_core) 31994e08d50dSCharles Keepax dsp->ops->stop_core(dsp); 32004e08d50dSCharles Keepax if (dsp->ops->disable_core) 32014e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 32022d30b575SMark Brown 32032cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 32042cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 32052cd19bdbSCharles Keepax 3206a2bcbc1bSCharles Keepax dsp->fatal_error = false; 3207a2bcbc1bSCharles Keepax 3208078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3209078e7183SCharles Keepax 321057a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 32112159ad93SMark Brown break; 32122159ad93SMark Brown 32132159ad93SMark Brown default: 32142159ad93SMark Brown break; 32152159ad93SMark Brown } 32162159ad93SMark Brown 32172159ad93SMark Brown return 0; 32182159ad93SMark Brown err: 32194e08d50dSCharles Keepax if (dsp->ops->stop_core) 32204e08d50dSCharles Keepax dsp->ops->stop_core(dsp); 32214e08d50dSCharles Keepax if (dsp->ops->disable_core) 32224e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 3223bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 32242159ad93SMark Brown return ret; 32252159ad93SMark Brown } 32264e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event); 3227973838a0SMark Brown 3228170b1e12SWen Shi static int wm_halo_start_core(struct wm_adsp *dsp) 3229170b1e12SWen Shi { 3230170b1e12SWen Shi return regmap_update_bits(dsp->regmap, 3231170b1e12SWen Shi dsp->base + HALO_CCM_CORE_CONTROL, 3232170b1e12SWen Shi HALO_CORE_EN, HALO_CORE_EN); 3233170b1e12SWen Shi } 3234170b1e12SWen Shi 3235170b1e12SWen Shi static void wm_halo_stop_core(struct wm_adsp *dsp) 3236170b1e12SWen Shi { 3237170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 3238170b1e12SWen Shi HALO_CORE_EN, 0); 3239170b1e12SWen Shi 3240809589a9SCharles Keepax /* reset halo core with CORE_SOFT_RESET */ 3241170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 3242170b1e12SWen Shi HALO_CORE_SOFT_RESET_MASK, 1); 3243170b1e12SWen Shi } 3244170b1e12SWen Shi 32450fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 3246f5e2ce92SRichard Fitzgerald { 3247af813a6fSCharles Keepax char preload[32]; 3248af813a6fSCharles Keepax 3249605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 325095a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3251685f51a5SRichard Fitzgerald 32520fe1daa6SKuninori Morimoto wm_adsp2_init_debugfs(dsp, component); 3253f9f55e31SRichard Fitzgerald 32540fe1daa6SKuninori Morimoto dsp->component = component; 3255af813a6fSCharles Keepax 32560a047f07SRichard Fitzgerald return 0; 3257f5e2ce92SRichard Fitzgerald } 32580fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 3259f5e2ce92SRichard Fitzgerald 32600fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 3261f5e2ce92SRichard Fitzgerald { 3262f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 3263f9f55e31SRichard Fitzgerald 3264f5e2ce92SRichard Fitzgerald return 0; 3265f5e2ce92SRichard Fitzgerald } 32660fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 3267f5e2ce92SRichard Fitzgerald 326881ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 3269973838a0SMark Brown { 3270973838a0SMark Brown int ret; 3271973838a0SMark Brown 3272dcad34f8SRichard Fitzgerald ret = wm_adsp_common_init(dsp); 3273605391d0SRichard Fitzgerald if (ret) 3274605391d0SRichard Fitzgerald return ret; 3275605391d0SRichard Fitzgerald 3276e1ea1879SRichard Fitzgerald switch (dsp->rev) { 3277e1ea1879SRichard Fitzgerald case 0: 327810a2b662SMark Brown /* 327910a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 328010a2b662SMark Brown * power saving. 328110a2b662SMark Brown */ 32823809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 328310a2b662SMark Brown ADSP2_MEM_ENA, 0); 3284e1ea1879SRichard Fitzgerald if (ret) { 3285e1ea1879SRichard Fitzgerald adsp_err(dsp, 3286e1ea1879SRichard Fitzgerald "Failed to clear memory retention: %d\n", ret); 328710a2b662SMark Brown return ret; 328810a2b662SMark Brown } 32894e08d50dSCharles Keepax 32904e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[0]; 32914e08d50dSCharles Keepax break; 32924e08d50dSCharles Keepax case 1: 32934e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[1]; 3294e1ea1879SRichard Fitzgerald break; 3295e1ea1879SRichard Fitzgerald default: 32964e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[2]; 3297e1ea1879SRichard Fitzgerald break; 3298e1ea1879SRichard Fitzgerald } 329910a2b662SMark Brown 33004e08d50dSCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 33016ab2b7b4SDimitris Papastamos 3302973838a0SMark Brown return 0; 3303973838a0SMark Brown } 3304973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 33050a37c6efSPraveen Diwakar 3306170b1e12SWen Shi int wm_halo_init(struct wm_adsp *dsp) 3307170b1e12SWen Shi { 3308170b1e12SWen Shi int ret; 3309170b1e12SWen Shi 3310170b1e12SWen Shi ret = wm_adsp_common_init(dsp); 3311170b1e12SWen Shi if (ret) 3312170b1e12SWen Shi return ret; 3313170b1e12SWen Shi 3314170b1e12SWen Shi dsp->ops = &wm_halo_ops; 3315170b1e12SWen Shi 3316170b1e12SWen Shi INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 3317170b1e12SWen Shi 3318170b1e12SWen Shi return 0; 3319170b1e12SWen Shi } 3320170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init); 3321170b1e12SWen Shi 332266225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 332366225e98SRichard Fitzgerald { 332466225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 332566225e98SRichard Fitzgerald 332666225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 332766225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 332866225e98SRichard Fitzgerald list); 332966225e98SRichard Fitzgerald list_del(&ctl->list); 333066225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 333166225e98SRichard Fitzgerald } 333266225e98SRichard Fitzgerald } 333366225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 333466225e98SRichard Fitzgerald 3335edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 3336edd71350SCharles Keepax { 3337edd71350SCharles Keepax return compr->buf != NULL; 3338edd71350SCharles Keepax } 3339edd71350SCharles Keepax 3340edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 3341edd71350SCharles Keepax { 33424f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf = NULL, *tmp; 33434f2d4eabSStuart Henderson 3344a2bcbc1bSCharles Keepax if (compr->dsp->fatal_error) 3345a2bcbc1bSCharles Keepax return -EINVAL; 3346a2bcbc1bSCharles Keepax 33474f2d4eabSStuart Henderson list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { 33484f2d4eabSStuart Henderson if (!tmp->name || !strcmp(compr->name, tmp->name)) { 33494f2d4eabSStuart Henderson buf = tmp; 33504f2d4eabSStuart Henderson break; 33514f2d4eabSStuart Henderson } 33524f2d4eabSStuart Henderson } 33534f2d4eabSStuart Henderson 33544f2d4eabSStuart Henderson if (!buf) 3355edd71350SCharles Keepax return -EINVAL; 3356edd71350SCharles Keepax 33574f2d4eabSStuart Henderson compr->buf = buf; 3358789b930aSCharles Keepax buf->compr = compr; 3359edd71350SCharles Keepax 3360edd71350SCharles Keepax return 0; 3361edd71350SCharles Keepax } 3362edd71350SCharles Keepax 3363721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 3364721be3beSCharles Keepax { 3365721be3beSCharles Keepax if (!compr) 3366721be3beSCharles Keepax return; 3367721be3beSCharles Keepax 3368721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 3369721be3beSCharles Keepax if (compr->stream) 3370721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 3371721be3beSCharles Keepax 3372721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 3373721be3beSCharles Keepax compr->buf->compr = NULL; 3374721be3beSCharles Keepax compr->buf = NULL; 3375721be3beSCharles Keepax } 3376721be3beSCharles Keepax } 3377721be3beSCharles Keepax 3378406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 3379406abc95SCharles Keepax { 33804f2d4eabSStuart Henderson struct wm_adsp_compr *compr, *tmp; 33814f2d4eabSStuart Henderson struct snd_soc_pcm_runtime *rtd = stream->private_data; 3382406abc95SCharles Keepax int ret = 0; 3383406abc95SCharles Keepax 3384406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3385406abc95SCharles Keepax 3386406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 33870d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support compressed API\n", 33880d3fba3eSCharles Keepax rtd->codec_dai->name); 3389406abc95SCharles Keepax ret = -ENXIO; 3390406abc95SCharles Keepax goto out; 3391406abc95SCharles Keepax } 3392406abc95SCharles Keepax 3393406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 33940d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support stream direction\n", 33950d3fba3eSCharles Keepax rtd->codec_dai->name); 3396406abc95SCharles Keepax ret = -EINVAL; 3397406abc95SCharles Keepax goto out; 3398406abc95SCharles Keepax } 3399406abc95SCharles Keepax 34004f2d4eabSStuart Henderson list_for_each_entry(tmp, &dsp->compr_list, list) { 34014f2d4eabSStuart Henderson if (!strcmp(tmp->name, rtd->codec_dai->name)) { 34020d3fba3eSCharles Keepax adsp_err(dsp, "%s: Only a single stream supported per dai\n", 34030d3fba3eSCharles Keepax rtd->codec_dai->name); 340495fe9597SCharles Keepax ret = -EBUSY; 340595fe9597SCharles Keepax goto out; 340695fe9597SCharles Keepax } 34074f2d4eabSStuart Henderson } 340895fe9597SCharles Keepax 3409406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 3410406abc95SCharles Keepax if (!compr) { 3411406abc95SCharles Keepax ret = -ENOMEM; 3412406abc95SCharles Keepax goto out; 3413406abc95SCharles Keepax } 3414406abc95SCharles Keepax 3415406abc95SCharles Keepax compr->dsp = dsp; 3416406abc95SCharles Keepax compr->stream = stream; 34174f2d4eabSStuart Henderson compr->name = rtd->codec_dai->name; 3418406abc95SCharles Keepax 34194f2d4eabSStuart Henderson list_add_tail(&compr->list, &dsp->compr_list); 3420406abc95SCharles Keepax 3421406abc95SCharles Keepax stream->runtime->private_data = compr; 3422406abc95SCharles Keepax 3423406abc95SCharles Keepax out: 3424406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3425406abc95SCharles Keepax 3426406abc95SCharles Keepax return ret; 3427406abc95SCharles Keepax } 3428406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 3429406abc95SCharles Keepax 3430406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 3431406abc95SCharles Keepax { 3432406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3433406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3434406abc95SCharles Keepax 3435406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3436406abc95SCharles Keepax 3437721be3beSCharles Keepax wm_adsp_compr_detach(compr); 34384f2d4eabSStuart Henderson list_del(&compr->list); 3439406abc95SCharles Keepax 344083a40ce9SCharles Keepax kfree(compr->raw_buf); 3441406abc95SCharles Keepax kfree(compr); 3442406abc95SCharles Keepax 3443406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3444406abc95SCharles Keepax 3445406abc95SCharles Keepax return 0; 3446406abc95SCharles Keepax } 3447406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 3448406abc95SCharles Keepax 3449406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 3450406abc95SCharles Keepax struct snd_compr_params *params) 3451406abc95SCharles Keepax { 3452406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3453406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3454406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3455406abc95SCharles Keepax const struct snd_codec_desc *desc; 3456406abc95SCharles Keepax int i, j; 3457406abc95SCharles Keepax 3458406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 3459406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 3460406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 3461406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 3462406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 34630d3fba3eSCharles Keepax compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", 3464406abc95SCharles Keepax params->buffer.fragment_size, 3465406abc95SCharles Keepax params->buffer.fragments); 3466406abc95SCharles Keepax 3467406abc95SCharles Keepax return -EINVAL; 3468406abc95SCharles Keepax } 3469406abc95SCharles Keepax 3470406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 3471406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 3472406abc95SCharles Keepax desc = &caps->desc; 3473406abc95SCharles Keepax 3474406abc95SCharles Keepax if (caps->id != params->codec.id) 3475406abc95SCharles Keepax continue; 3476406abc95SCharles Keepax 3477406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 3478406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 3479406abc95SCharles Keepax continue; 3480406abc95SCharles Keepax } else { 3481406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 3482406abc95SCharles Keepax continue; 3483406abc95SCharles Keepax } 3484406abc95SCharles Keepax 3485406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 3486406abc95SCharles Keepax continue; 3487406abc95SCharles Keepax 3488406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 3489406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 3490406abc95SCharles Keepax return 0; 3491406abc95SCharles Keepax } 3492406abc95SCharles Keepax 34930d3fba3eSCharles Keepax compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 3494406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 3495406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 3496406abc95SCharles Keepax return -EINVAL; 3497406abc95SCharles Keepax } 3498406abc95SCharles Keepax 3499565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 3500565ace46SCharles Keepax { 3501565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 3502565ace46SCharles Keepax } 3503565ace46SCharles Keepax 3504406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 3505406abc95SCharles Keepax struct snd_compr_params *params) 3506406abc95SCharles Keepax { 3507406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 350883a40ce9SCharles Keepax unsigned int size; 3509406abc95SCharles Keepax int ret; 3510406abc95SCharles Keepax 3511406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 3512406abc95SCharles Keepax if (ret) 3513406abc95SCharles Keepax return ret; 3514406abc95SCharles Keepax 3515406abc95SCharles Keepax compr->size = params->buffer; 3516406abc95SCharles Keepax 35170d3fba3eSCharles Keepax compr_dbg(compr, "fragment_size=%d fragments=%d\n", 3518406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 3519406abc95SCharles Keepax 352083a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 352183a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 352283a40ce9SCharles Keepax if (!compr->raw_buf) 352383a40ce9SCharles Keepax return -ENOMEM; 352483a40ce9SCharles Keepax 3525da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 3526da2b3358SCharles Keepax 3527406abc95SCharles Keepax return 0; 3528406abc95SCharles Keepax } 3529406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 3530406abc95SCharles Keepax 3531406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 3532406abc95SCharles Keepax struct snd_compr_caps *caps) 3533406abc95SCharles Keepax { 3534406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3535406abc95SCharles Keepax int fw = compr->dsp->fw; 3536406abc95SCharles Keepax int i; 3537406abc95SCharles Keepax 3538406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 3539406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 3540406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 3541406abc95SCharles Keepax 3542406abc95SCharles Keepax caps->num_codecs = i; 3543406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 3544406abc95SCharles Keepax 3545406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 3546406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 3547406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 3548406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 3549406abc95SCharles Keepax } 3550406abc95SCharles Keepax 3551406abc95SCharles Keepax return 0; 3552406abc95SCharles Keepax } 3553406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 3554406abc95SCharles Keepax 35552cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 35562cd19bdbSCharles Keepax unsigned int mem_addr, 35572cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 35582cd19bdbSCharles Keepax { 35592cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 35602cd19bdbSCharles Keepax unsigned int i, reg; 35612cd19bdbSCharles Keepax int ret; 35622cd19bdbSCharles Keepax 35632cd19bdbSCharles Keepax if (!mem) 35642cd19bdbSCharles Keepax return -EINVAL; 35652cd19bdbSCharles Keepax 3566170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 35672cd19bdbSCharles Keepax 35682cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 35692cd19bdbSCharles Keepax sizeof(*data) * num_words); 35702cd19bdbSCharles Keepax if (ret < 0) 35712cd19bdbSCharles Keepax return ret; 35722cd19bdbSCharles Keepax 35732cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 35742cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 35752cd19bdbSCharles Keepax 35762cd19bdbSCharles Keepax return 0; 35772cd19bdbSCharles Keepax } 35782cd19bdbSCharles Keepax 35792cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 35802cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 35812cd19bdbSCharles Keepax { 35822cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 35832cd19bdbSCharles Keepax } 35842cd19bdbSCharles Keepax 35852cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 35862cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 35872cd19bdbSCharles Keepax { 35882cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 35892cd19bdbSCharles Keepax unsigned int reg; 35902cd19bdbSCharles Keepax 35912cd19bdbSCharles Keepax if (!mem) 35922cd19bdbSCharles Keepax return -EINVAL; 35932cd19bdbSCharles Keepax 3594170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 35952cd19bdbSCharles Keepax 35962cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 35972cd19bdbSCharles Keepax 35982cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 35992cd19bdbSCharles Keepax } 36002cd19bdbSCharles Keepax 36012cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 36022cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 36032cd19bdbSCharles Keepax { 3604fb13f19dSAndrew Ford return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, 36052cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 36062cd19bdbSCharles Keepax } 36072cd19bdbSCharles Keepax 36082cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 36092cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 36102cd19bdbSCharles Keepax { 3611fb13f19dSAndrew Ford return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, 36122cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 36132cd19bdbSCharles Keepax } 36142cd19bdbSCharles Keepax 3615cc7d6ce9SCharles Keepax static void wm_adsp_remove_padding(u32 *buf, int nwords, int data_word_size) 3616cc7d6ce9SCharles Keepax { 3617cc7d6ce9SCharles Keepax u8 *pack_in = (u8 *)buf; 3618cc7d6ce9SCharles Keepax u8 *pack_out = (u8 *)buf; 3619cc7d6ce9SCharles Keepax int i, j; 3620cc7d6ce9SCharles Keepax 3621cc7d6ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 3622cc7d6ce9SCharles Keepax for (i = 0; i < nwords; i++) { 3623cc7d6ce9SCharles Keepax for (j = 0; j < data_word_size; j++) 3624cc7d6ce9SCharles Keepax *pack_out++ = *pack_in++; 3625cc7d6ce9SCharles Keepax 3626cc7d6ce9SCharles Keepax pack_in += sizeof(*buf) - data_word_size; 3627cc7d6ce9SCharles Keepax } 3628cc7d6ce9SCharles Keepax } 3629cc7d6ce9SCharles Keepax 36301e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 36311e38f069SCharles Keepax { 36321e38f069SCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 36331e38f069SCharles Keepax struct wm_adsp_buffer_region *region; 36341e38f069SCharles Keepax u32 offset = 0; 36351e38f069SCharles Keepax int i, ret; 36361e38f069SCharles Keepax 3637a792af69SCharles Keepax buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), 3638a792af69SCharles Keepax GFP_KERNEL); 3639a792af69SCharles Keepax if (!buf->regions) 3640a792af69SCharles Keepax return -ENOMEM; 3641a792af69SCharles Keepax 36421e38f069SCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 36431e38f069SCharles Keepax region = &buf->regions[i]; 36441e38f069SCharles Keepax 36451e38f069SCharles Keepax region->offset = offset; 36461e38f069SCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 36471e38f069SCharles Keepax 36481e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 36491e38f069SCharles Keepax ®ion->base_addr); 36501e38f069SCharles Keepax if (ret < 0) 36511e38f069SCharles Keepax return ret; 36521e38f069SCharles Keepax 36531e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 36541e38f069SCharles Keepax &offset); 36551e38f069SCharles Keepax if (ret < 0) 36561e38f069SCharles Keepax return ret; 36571e38f069SCharles Keepax 36581e38f069SCharles Keepax region->cumulative_size = offset; 36591e38f069SCharles Keepax 36600d3fba3eSCharles Keepax compr_dbg(buf, 36611e38f069SCharles Keepax "region=%d type=%d base=%08x off=%08x size=%08x\n", 36621e38f069SCharles Keepax i, region->mem_type, region->base_addr, 36631e38f069SCharles Keepax region->offset, region->cumulative_size); 36641e38f069SCharles Keepax } 36651e38f069SCharles Keepax 36661e38f069SCharles Keepax return 0; 36671e38f069SCharles Keepax } 36681e38f069SCharles Keepax 36691e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 36701e38f069SCharles Keepax { 36711e38f069SCharles Keepax buf->irq_count = 0xFFFFFFFF; 36721e38f069SCharles Keepax buf->read_index = -1; 36731e38f069SCharles Keepax buf->avail = 0; 36741e38f069SCharles Keepax } 36751e38f069SCharles Keepax 3676a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) 3677a792af69SCharles Keepax { 3678a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3679a792af69SCharles Keepax 3680a792af69SCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3681a792af69SCharles Keepax if (!buf) 3682a792af69SCharles Keepax return NULL; 3683a792af69SCharles Keepax 3684a792af69SCharles Keepax buf->dsp = dsp; 3685a792af69SCharles Keepax 3686a792af69SCharles Keepax wm_adsp_buffer_clear(buf); 3687a792af69SCharles Keepax 36884f2d4eabSStuart Henderson list_add_tail(&buf->list, &dsp->buffer_list); 3689a792af69SCharles Keepax 3690a792af69SCharles Keepax return buf; 3691a792af69SCharles Keepax } 3692a792af69SCharles Keepax 3693a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) 36942cd19bdbSCharles Keepax { 36952cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 3696a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 36972cd19bdbSCharles Keepax u32 xmalg, addr, magic; 36982cd19bdbSCharles Keepax int i, ret; 36992cd19bdbSCharles Keepax 3700a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(dsp); 3701a792af69SCharles Keepax if (!buf) 3702a792af69SCharles Keepax return -ENOMEM; 3703a792af69SCharles Keepax 37042cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 3705170b1e12SWen Shi xmalg = dsp->ops->sys_config_size / sizeof(__be32); 37062cd19bdbSCharles Keepax 37072cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 37082cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 37092cd19bdbSCharles Keepax if (ret < 0) 37102cd19bdbSCharles Keepax return ret; 37112cd19bdbSCharles Keepax 37122cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 3713a792af69SCharles Keepax return -ENODEV; 37142cd19bdbSCharles Keepax 37152cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 37162cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 37172cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 37182cd19bdbSCharles Keepax &buf->host_buf_ptr); 37192cd19bdbSCharles Keepax if (ret < 0) 37202cd19bdbSCharles Keepax return ret; 37212cd19bdbSCharles Keepax 37222cd19bdbSCharles Keepax if (buf->host_buf_ptr) 37232cd19bdbSCharles Keepax break; 37242cd19bdbSCharles Keepax 37252cd19bdbSCharles Keepax usleep_range(1000, 2000); 37262cd19bdbSCharles Keepax } 37272cd19bdbSCharles Keepax 37282cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 37292cd19bdbSCharles Keepax return -EIO; 37302cd19bdbSCharles Keepax 3731fb13f19dSAndrew Ford buf->host_buf_mem_type = WMFW_ADSP2_XM; 3732fb13f19dSAndrew Ford 3733a792af69SCharles Keepax ret = wm_adsp_buffer_populate(buf); 3734a792af69SCharles Keepax if (ret < 0) 3735a792af69SCharles Keepax return ret; 3736a792af69SCharles Keepax 37370d3fba3eSCharles Keepax compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); 37382cd19bdbSCharles Keepax 37392cd19bdbSCharles Keepax return 0; 37402cd19bdbSCharles Keepax } 37412cd19bdbSCharles Keepax 3742a792af69SCharles Keepax static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) 3743d52ed4b0SRichard Fitzgerald { 37444f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 coeff_v1; 3745a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3746a792af69SCharles Keepax unsigned int val, reg; 3747a792af69SCharles Keepax int ret, i; 3748d52ed4b0SRichard Fitzgerald 3749d52ed4b0SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 3750d52ed4b0SRichard Fitzgerald if (ret) 3751d52ed4b0SRichard Fitzgerald return ret; 3752d52ed4b0SRichard Fitzgerald 3753d52ed4b0SRichard Fitzgerald for (i = 0; i < 5; ++i) { 3754a792af69SCharles Keepax ret = regmap_raw_read(ctl->dsp->regmap, reg, &val, sizeof(val)); 3755d52ed4b0SRichard Fitzgerald if (ret < 0) 3756d52ed4b0SRichard Fitzgerald return ret; 3757d52ed4b0SRichard Fitzgerald 3758d52ed4b0SRichard Fitzgerald if (val) 3759d52ed4b0SRichard Fitzgerald break; 3760d52ed4b0SRichard Fitzgerald 3761d52ed4b0SRichard Fitzgerald usleep_range(1000, 2000); 3762d52ed4b0SRichard Fitzgerald } 3763d52ed4b0SRichard Fitzgerald 3764a792af69SCharles Keepax if (!val) { 3765a792af69SCharles Keepax adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); 3766d52ed4b0SRichard Fitzgerald return -EIO; 3767d52ed4b0SRichard Fitzgerald } 3768d52ed4b0SRichard Fitzgerald 3769a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(ctl->dsp); 37702cd19bdbSCharles Keepax if (!buf) 37712cd19bdbSCharles Keepax return -ENOMEM; 37722cd19bdbSCharles Keepax 3773a792af69SCharles Keepax buf->host_buf_mem_type = ctl->alg_region.type; 3774a792af69SCharles Keepax buf->host_buf_ptr = be32_to_cpu(val); 37752cd19bdbSCharles Keepax 37762cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 3777a792af69SCharles Keepax if (ret < 0) 3778a792af69SCharles Keepax return ret; 3779a792af69SCharles Keepax 37804f2d4eabSStuart Henderson /* 37814f2d4eabSStuart Henderson * v0 host_buffer coefficients didn't have versioning, so if the 37824f2d4eabSStuart Henderson * control is one word, assume version 0. 37834f2d4eabSStuart Henderson */ 37844f2d4eabSStuart Henderson if (ctl->len == 4) { 37850d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); 3786a792af69SCharles Keepax return 0; 37872cd19bdbSCharles Keepax } 37882cd19bdbSCharles Keepax 37894f2d4eabSStuart Henderson ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, 37904f2d4eabSStuart Henderson sizeof(coeff_v1)); 37914f2d4eabSStuart Henderson if (ret < 0) 37924f2d4eabSStuart Henderson return ret; 37934f2d4eabSStuart Henderson 37944f2d4eabSStuart Henderson coeff_v1.versions = be32_to_cpu(coeff_v1.versions); 37954f2d4eabSStuart Henderson val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK; 37964f2d4eabSStuart Henderson val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; 37974f2d4eabSStuart Henderson 37984f2d4eabSStuart Henderson if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { 37994f2d4eabSStuart Henderson adsp_err(ctl->dsp, 38004f2d4eabSStuart Henderson "Host buffer coeff ver %u > supported version %u\n", 38014f2d4eabSStuart Henderson val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); 38024f2d4eabSStuart Henderson return -EINVAL; 38034f2d4eabSStuart Henderson } 38044f2d4eabSStuart Henderson 38054f2d4eabSStuart Henderson for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++) 38064f2d4eabSStuart Henderson coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]); 38074f2d4eabSStuart Henderson 38084f2d4eabSStuart Henderson wm_adsp_remove_padding((u32 *)&coeff_v1.name, 38094f2d4eabSStuart Henderson ARRAY_SIZE(coeff_v1.name), 38104f2d4eabSStuart Henderson WM_ADSP_DATA_WORD_SIZE); 38114f2d4eabSStuart Henderson 38124f2d4eabSStuart Henderson buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, 38134f2d4eabSStuart Henderson (char *)&coeff_v1.name); 38144f2d4eabSStuart Henderson 38150d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", 38164f2d4eabSStuart Henderson buf->host_buf_ptr, val); 38174f2d4eabSStuart Henderson 38184f2d4eabSStuart Henderson return val; 38194f2d4eabSStuart Henderson } 38204f2d4eabSStuart Henderson 3821a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 3822a792af69SCharles Keepax { 3823a792af69SCharles Keepax struct wm_coeff_ctl *ctl; 3824a792af69SCharles Keepax int ret; 3825a792af69SCharles Keepax 3826a792af69SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 3827a792af69SCharles Keepax if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 3828a792af69SCharles Keepax continue; 3829a792af69SCharles Keepax 3830a792af69SCharles Keepax if (!ctl->enabled) 3831a792af69SCharles Keepax continue; 3832a792af69SCharles Keepax 3833a792af69SCharles Keepax ret = wm_adsp_buffer_parse_coeff(ctl); 3834a792af69SCharles Keepax if (ret < 0) { 3835a792af69SCharles Keepax adsp_err(dsp, "Failed to parse coeff: %d\n", ret); 3836a792af69SCharles Keepax goto error; 38374f2d4eabSStuart Henderson } else if (ret == 0) { 38384f2d4eabSStuart Henderson /* Only one buffer supported for version 0 */ 3839a792af69SCharles Keepax return 0; 3840a792af69SCharles Keepax } 38414f2d4eabSStuart Henderson } 3842a792af69SCharles Keepax 38434f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 3844a792af69SCharles Keepax /* Fall back to legacy support */ 3845a792af69SCharles Keepax ret = wm_adsp_buffer_parse_legacy(dsp); 3846a792af69SCharles Keepax if (ret) { 3847a792af69SCharles Keepax adsp_err(dsp, "Failed to parse legacy: %d\n", ret); 3848a792af69SCharles Keepax goto error; 3849a792af69SCharles Keepax } 3850a792af69SCharles Keepax } 38512cd19bdbSCharles Keepax 38522cd19bdbSCharles Keepax return 0; 38532cd19bdbSCharles Keepax 3854a792af69SCharles Keepax error: 3855a792af69SCharles Keepax wm_adsp_buffer_free(dsp); 38562cd19bdbSCharles Keepax return ret; 38572cd19bdbSCharles Keepax } 38582cd19bdbSCharles Keepax 38592cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 38602cd19bdbSCharles Keepax { 38614f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf, *tmp; 3862721be3beSCharles Keepax 38634f2d4eabSStuart Henderson list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { 38644f2d4eabSStuart Henderson wm_adsp_compr_detach(buf->compr); 38652cd19bdbSCharles Keepax 38664f2d4eabSStuart Henderson kfree(buf->name); 38674f2d4eabSStuart Henderson kfree(buf->regions); 38684f2d4eabSStuart Henderson list_del(&buf->list); 38694f2d4eabSStuart Henderson kfree(buf); 38702cd19bdbSCharles Keepax } 38712cd19bdbSCharles Keepax 38722cd19bdbSCharles Keepax return 0; 38732cd19bdbSCharles Keepax } 38742cd19bdbSCharles Keepax 3875f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 3876f938f348SStuart Henderson { 3877f938f348SStuart Henderson int ret; 3878f938f348SStuart Henderson 3879f938f348SStuart Henderson ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 3880f938f348SStuart Henderson if (ret < 0) { 388148ead31cSCharles Keepax compr_err(buf, "Failed to check buffer error: %d\n", ret); 3882f938f348SStuart Henderson return ret; 3883f938f348SStuart Henderson } 3884f938f348SStuart Henderson if (buf->error != 0) { 388548ead31cSCharles Keepax compr_err(buf, "Buffer error occurred: %d\n", buf->error); 3886f938f348SStuart Henderson return -EIO; 3887f938f348SStuart Henderson } 3888f938f348SStuart Henderson 3889f938f348SStuart Henderson return 0; 3890f938f348SStuart Henderson } 3891f938f348SStuart Henderson 389295fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 389395fe9597SCharles Keepax { 389495fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 389595fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 389695fe9597SCharles Keepax int ret = 0; 389795fe9597SCharles Keepax 38980d3fba3eSCharles Keepax compr_dbg(compr, "Trigger: %d\n", cmd); 389995fe9597SCharles Keepax 390095fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 390195fe9597SCharles Keepax 390295fe9597SCharles Keepax switch (cmd) { 390395fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 390461fc060cSCharles Keepax if (!wm_adsp_compr_attached(compr)) { 390595fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 390695fe9597SCharles Keepax if (ret < 0) { 39070d3fba3eSCharles Keepax compr_err(compr, "Failed to link buffer and stream: %d\n", 390895fe9597SCharles Keepax ret); 390995fe9597SCharles Keepax break; 391095fe9597SCharles Keepax } 391161fc060cSCharles Keepax } 391261fc060cSCharles Keepax 3913f938f348SStuart Henderson ret = wm_adsp_buffer_get_error(compr->buf); 3914f938f348SStuart Henderson if (ret < 0) 3915f938f348SStuart Henderson break; 3916f938f348SStuart Henderson 3917565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3918565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3919565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3920565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3921565ace46SCharles Keepax if (ret < 0) { 39220d3fba3eSCharles Keepax compr_err(compr, "Failed to set high water mark: %d\n", 3923565ace46SCharles Keepax ret); 3924565ace46SCharles Keepax break; 3925565ace46SCharles Keepax } 392695fe9597SCharles Keepax break; 392795fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 392843d147beSCharles Keepax if (wm_adsp_compr_attached(compr)) 3929639e5eb3SCharles Keepax wm_adsp_buffer_clear(compr->buf); 393095fe9597SCharles Keepax break; 393195fe9597SCharles Keepax default: 393295fe9597SCharles Keepax ret = -EINVAL; 393395fe9597SCharles Keepax break; 393495fe9597SCharles Keepax } 393595fe9597SCharles Keepax 393695fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 393795fe9597SCharles Keepax 393895fe9597SCharles Keepax return ret; 393995fe9597SCharles Keepax } 394095fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 394195fe9597SCharles Keepax 3942565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3943565ace46SCharles Keepax { 3944565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3945565ace46SCharles Keepax 3946565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3947565ace46SCharles Keepax } 3948565ace46SCharles Keepax 3949565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3950565ace46SCharles Keepax { 3951565ace46SCharles Keepax u32 next_read_index, next_write_index; 3952565ace46SCharles Keepax int write_index, read_index, avail; 3953565ace46SCharles Keepax int ret; 3954565ace46SCharles Keepax 3955565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3956565ace46SCharles Keepax if (buf->read_index < 0) { 3957565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3958565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3959565ace46SCharles Keepax &next_read_index); 3960565ace46SCharles Keepax if (ret < 0) 3961565ace46SCharles Keepax return ret; 3962565ace46SCharles Keepax 3963565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3964565ace46SCharles Keepax 3965565ace46SCharles Keepax if (read_index < 0) { 39660d3fba3eSCharles Keepax compr_dbg(buf, "Avail check on unstarted stream\n"); 3967565ace46SCharles Keepax return 0; 3968565ace46SCharles Keepax } 3969565ace46SCharles Keepax 3970565ace46SCharles Keepax buf->read_index = read_index; 3971565ace46SCharles Keepax } 3972565ace46SCharles Keepax 3973565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 3974565ace46SCharles Keepax &next_write_index); 3975565ace46SCharles Keepax if (ret < 0) 3976565ace46SCharles Keepax return ret; 3977565ace46SCharles Keepax 3978565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 3979565ace46SCharles Keepax 3980565ace46SCharles Keepax avail = write_index - buf->read_index; 3981565ace46SCharles Keepax if (avail < 0) 3982565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 3983565ace46SCharles Keepax 39840d3fba3eSCharles Keepax compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 398533d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 3986565ace46SCharles Keepax 3987565ace46SCharles Keepax buf->avail = avail; 3988565ace46SCharles Keepax 3989565ace46SCharles Keepax return 0; 3990565ace46SCharles Keepax } 3991565ace46SCharles Keepax 3992565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 3993565ace46SCharles Keepax { 3994612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3995612047f0SCharles Keepax struct wm_adsp_compr *compr; 3996565ace46SCharles Keepax int ret = 0; 3997565ace46SCharles Keepax 3998565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3999565ace46SCharles Keepax 40004f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 4001565ace46SCharles Keepax ret = -ENODEV; 4002565ace46SCharles Keepax goto out; 4003565ace46SCharles Keepax } 40040d3fba3eSCharles Keepax 4005565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 4006565ace46SCharles Keepax 40074f2d4eabSStuart Henderson list_for_each_entry(buf, &dsp->buffer_list, list) { 40084f2d4eabSStuart Henderson compr = buf->compr; 40094f2d4eabSStuart Henderson 40109771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 40119771b18aSCharles Keepax if (ret < 0) 40125847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 4013565ace46SCharles Keepax 4014565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 4015565ace46SCharles Keepax &buf->irq_count); 4016565ace46SCharles Keepax if (ret < 0) { 40170d3fba3eSCharles Keepax compr_err(buf, "Failed to get irq_count: %d\n", ret); 4018565ace46SCharles Keepax goto out; 4019565ace46SCharles Keepax } 4020565ace46SCharles Keepax 4021565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4022565ace46SCharles Keepax if (ret < 0) { 40230d3fba3eSCharles Keepax compr_err(buf, "Error reading avail: %d\n", ret); 4024565ace46SCharles Keepax goto out; 4025565ace46SCharles Keepax } 4026565ace46SCharles Keepax 402720b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 402820b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 402920b7f7c5SCharles Keepax 40305847609eSCharles Keepax out_notify: 4031c7dae7c4SCharles Keepax if (compr && compr->stream) 403283a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 40334f2d4eabSStuart Henderson } 403483a40ce9SCharles Keepax 4035565ace46SCharles Keepax out: 4036565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4037565ace46SCharles Keepax 4038565ace46SCharles Keepax return ret; 4039565ace46SCharles Keepax } 4040565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 4041565ace46SCharles Keepax 4042565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 4043565ace46SCharles Keepax { 4044565ace46SCharles Keepax if (buf->irq_count & 0x01) 4045565ace46SCharles Keepax return 0; 4046565ace46SCharles Keepax 40470d3fba3eSCharles Keepax compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); 4048565ace46SCharles Keepax 4049565ace46SCharles Keepax buf->irq_count |= 0x01; 4050565ace46SCharles Keepax 4051565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 4052565ace46SCharles Keepax buf->irq_count); 4053565ace46SCharles Keepax } 4054565ace46SCharles Keepax 4055565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 4056565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 4057565ace46SCharles Keepax { 4058565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 4059565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 4060612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 4061565ace46SCharles Keepax int ret = 0; 4062565ace46SCharles Keepax 40630d3fba3eSCharles Keepax compr_dbg(compr, "Pointer request\n"); 4064565ace46SCharles Keepax 4065565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 4066565ace46SCharles Keepax 4067612047f0SCharles Keepax buf = compr->buf; 4068612047f0SCharles Keepax 4069aa612f2bSCharles Keepax if (dsp->fatal_error || !buf || buf->error) { 40708d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 4071565ace46SCharles Keepax ret = -EIO; 4072565ace46SCharles Keepax goto out; 4073565ace46SCharles Keepax } 4074565ace46SCharles Keepax 4075565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 4076565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4077565ace46SCharles Keepax if (ret < 0) { 40780d3fba3eSCharles Keepax compr_err(compr, "Error reading avail: %d\n", ret); 4079565ace46SCharles Keepax goto out; 4080565ace46SCharles Keepax } 4081565ace46SCharles Keepax 4082565ace46SCharles Keepax /* 4083565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 4084565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 4085565ace46SCharles Keepax */ 4086565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 40875847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 40888d280664SCharles Keepax if (ret < 0) { 4089789b930aSCharles Keepax if (buf->error) 40908d280664SCharles Keepax snd_compr_stop_error(stream, 40918d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 40925847609eSCharles Keepax goto out; 40938d280664SCharles Keepax } 40945847609eSCharles Keepax 4095565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 4096565ace46SCharles Keepax if (ret < 0) { 40970d3fba3eSCharles Keepax compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", 4098565ace46SCharles Keepax ret); 4099565ace46SCharles Keepax goto out; 4100565ace46SCharles Keepax } 4101565ace46SCharles Keepax } 4102565ace46SCharles Keepax } 4103565ace46SCharles Keepax 4104565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 4105565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 4106da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 4107565ace46SCharles Keepax 4108565ace46SCharles Keepax out: 4109565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4110565ace46SCharles Keepax 4111565ace46SCharles Keepax return ret; 4112565ace46SCharles Keepax } 4113565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 4114565ace46SCharles Keepax 411583a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 411683a40ce9SCharles Keepax { 411783a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 411883a40ce9SCharles Keepax unsigned int adsp_addr; 411983a40ce9SCharles Keepax int mem_type, nwords, max_read; 4120cc7d6ce9SCharles Keepax int i, ret; 412183a40ce9SCharles Keepax 412283a40ce9SCharles Keepax /* Calculate read parameters */ 412383a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 412483a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 412583a40ce9SCharles Keepax break; 412683a40ce9SCharles Keepax 412783a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 412883a40ce9SCharles Keepax return -EINVAL; 412983a40ce9SCharles Keepax 413083a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 413183a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 413283a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 413383a40ce9SCharles Keepax 413483a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 413583a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 413683a40ce9SCharles Keepax 413783a40ce9SCharles Keepax if (nwords > target) 413883a40ce9SCharles Keepax nwords = target; 413983a40ce9SCharles Keepax if (nwords > buf->avail) 414083a40ce9SCharles Keepax nwords = buf->avail; 414183a40ce9SCharles Keepax if (nwords > max_read) 414283a40ce9SCharles Keepax nwords = max_read; 414383a40ce9SCharles Keepax if (!nwords) 414483a40ce9SCharles Keepax return 0; 414583a40ce9SCharles Keepax 414683a40ce9SCharles Keepax /* Read data from DSP */ 414783a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 414883a40ce9SCharles Keepax nwords, compr->raw_buf); 414983a40ce9SCharles Keepax if (ret < 0) 415083a40ce9SCharles Keepax return ret; 415183a40ce9SCharles Keepax 4152cc7d6ce9SCharles Keepax wm_adsp_remove_padding(compr->raw_buf, nwords, WM_ADSP_DATA_WORD_SIZE); 415383a40ce9SCharles Keepax 415483a40ce9SCharles Keepax /* update read index to account for words read */ 415583a40ce9SCharles Keepax buf->read_index += nwords; 415683a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 415783a40ce9SCharles Keepax buf->read_index = 0; 415883a40ce9SCharles Keepax 415983a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 416083a40ce9SCharles Keepax buf->read_index); 416183a40ce9SCharles Keepax if (ret < 0) 416283a40ce9SCharles Keepax return ret; 416383a40ce9SCharles Keepax 416483a40ce9SCharles Keepax /* update avail to account for words read */ 416583a40ce9SCharles Keepax buf->avail -= nwords; 416683a40ce9SCharles Keepax 416783a40ce9SCharles Keepax return nwords; 416883a40ce9SCharles Keepax } 416983a40ce9SCharles Keepax 417083a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 417183a40ce9SCharles Keepax char __user *buf, size_t count) 417283a40ce9SCharles Keepax { 4173aa612f2bSCharles Keepax struct wm_adsp *dsp = compr->dsp; 417483a40ce9SCharles Keepax int ntotal = 0; 417583a40ce9SCharles Keepax int nwords, nbytes; 417683a40ce9SCharles Keepax 41770d3fba3eSCharles Keepax compr_dbg(compr, "Requested read of %zu bytes\n", count); 417883a40ce9SCharles Keepax 4179aa612f2bSCharles Keepax if (dsp->fatal_error || !compr->buf || compr->buf->error) { 41808d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 418183a40ce9SCharles Keepax return -EIO; 41828d280664SCharles Keepax } 418383a40ce9SCharles Keepax 418483a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 418583a40ce9SCharles Keepax 418683a40ce9SCharles Keepax do { 418783a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 418883a40ce9SCharles Keepax if (nwords < 0) { 41890d3fba3eSCharles Keepax compr_err(compr, "Failed to capture block: %d\n", 41900d3fba3eSCharles Keepax nwords); 419183a40ce9SCharles Keepax return nwords; 419283a40ce9SCharles Keepax } 419383a40ce9SCharles Keepax 419483a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 419583a40ce9SCharles Keepax 41960d3fba3eSCharles Keepax compr_dbg(compr, "Read %d bytes\n", nbytes); 419783a40ce9SCharles Keepax 419883a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 41990d3fba3eSCharles Keepax compr_err(compr, "Failed to copy data to user: %d, %d\n", 420083a40ce9SCharles Keepax ntotal, nbytes); 420183a40ce9SCharles Keepax return -EFAULT; 420283a40ce9SCharles Keepax } 420383a40ce9SCharles Keepax 420483a40ce9SCharles Keepax count -= nwords; 420583a40ce9SCharles Keepax ntotal += nbytes; 420683a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 420783a40ce9SCharles Keepax 420883a40ce9SCharles Keepax compr->copied_total += ntotal; 420983a40ce9SCharles Keepax 421083a40ce9SCharles Keepax return ntotal; 421183a40ce9SCharles Keepax } 421283a40ce9SCharles Keepax 421383a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 421483a40ce9SCharles Keepax size_t count) 421583a40ce9SCharles Keepax { 421683a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 421783a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 421883a40ce9SCharles Keepax int ret; 421983a40ce9SCharles Keepax 422083a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 422183a40ce9SCharles Keepax 422283a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 422383a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 422483a40ce9SCharles Keepax else 422583a40ce9SCharles Keepax ret = -ENOTSUPP; 422683a40ce9SCharles Keepax 422783a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 422883a40ce9SCharles Keepax 422983a40ce9SCharles Keepax return ret; 423083a40ce9SCharles Keepax } 423183a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 423283a40ce9SCharles Keepax 4233a2bcbc1bSCharles Keepax static void wm_adsp_fatal_error(struct wm_adsp *dsp) 4234a2bcbc1bSCharles Keepax { 4235a2bcbc1bSCharles Keepax struct wm_adsp_compr *compr; 4236a2bcbc1bSCharles Keepax 4237a2bcbc1bSCharles Keepax dsp->fatal_error = true; 4238a2bcbc1bSCharles Keepax 4239a2bcbc1bSCharles Keepax list_for_each_entry(compr, &dsp->compr_list, list) { 4240aa612f2bSCharles Keepax if (compr->stream) 4241a2bcbc1bSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 4242a2bcbc1bSCharles Keepax } 4243a2bcbc1bSCharles Keepax } 4244a2bcbc1bSCharles Keepax 424501ec57a4SCharles Keepax irqreturn_t wm_adsp2_bus_error(int irq, void *data) 424651a2c944SMayuresh Kulkarni { 424701ec57a4SCharles Keepax struct wm_adsp *dsp = (struct wm_adsp *)data; 424851a2c944SMayuresh Kulkarni unsigned int val; 424951a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 425051a2c944SMayuresh Kulkarni int ret = 0; 425151a2c944SMayuresh Kulkarni 4252a2225a6dSCharles Keepax mutex_lock(&dsp->pwr_lock); 4253a2225a6dSCharles Keepax 425451a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 425551a2c944SMayuresh Kulkarni if (ret) { 425651a2c944SMayuresh Kulkarni adsp_err(dsp, 425751a2c944SMayuresh Kulkarni "Failed to read Region Lock Ctrl register: %d\n", ret); 4258a2225a6dSCharles Keepax goto error; 425951a2c944SMayuresh Kulkarni } 426051a2c944SMayuresh Kulkarni 426151a2c944SMayuresh Kulkarni if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 426251a2c944SMayuresh Kulkarni adsp_err(dsp, "watchdog timeout error\n"); 426381ed8845SCharles Keepax dsp->ops->stop_watchdog(dsp); 4264a2bcbc1bSCharles Keepax wm_adsp_fatal_error(dsp); 426551a2c944SMayuresh Kulkarni } 426651a2c944SMayuresh Kulkarni 426751a2c944SMayuresh Kulkarni if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 426851a2c944SMayuresh Kulkarni if (val & ADSP2_SLAVE_ERR_MASK) 426951a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: slave error\n"); 427051a2c944SMayuresh Kulkarni else 427151a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: region lock error\n"); 427251a2c944SMayuresh Kulkarni 427351a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 427451a2c944SMayuresh Kulkarni if (ret) { 427551a2c944SMayuresh Kulkarni adsp_err(dsp, 427651a2c944SMayuresh Kulkarni "Failed to read Bus Err Addr register: %d\n", 427751a2c944SMayuresh Kulkarni ret); 4278a2225a6dSCharles Keepax goto error; 427951a2c944SMayuresh Kulkarni } 428051a2c944SMayuresh Kulkarni 428151a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error address = 0x%x\n", 428251a2c944SMayuresh Kulkarni val & ADSP2_BUS_ERR_ADDR_MASK); 428351a2c944SMayuresh Kulkarni 428451a2c944SMayuresh Kulkarni ret = regmap_read(regmap, 428551a2c944SMayuresh Kulkarni dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 428651a2c944SMayuresh Kulkarni &val); 428751a2c944SMayuresh Kulkarni if (ret) { 428851a2c944SMayuresh Kulkarni adsp_err(dsp, 428951a2c944SMayuresh Kulkarni "Failed to read Pmem Xmem Err Addr register: %d\n", 429051a2c944SMayuresh Kulkarni ret); 4291a2225a6dSCharles Keepax goto error; 429251a2c944SMayuresh Kulkarni } 429351a2c944SMayuresh Kulkarni 429451a2c944SMayuresh Kulkarni adsp_err(dsp, "xmem error address = 0x%x\n", 429551a2c944SMayuresh Kulkarni val & ADSP2_XMEM_ERR_ADDR_MASK); 429651a2c944SMayuresh Kulkarni adsp_err(dsp, "pmem error address = 0x%x\n", 429751a2c944SMayuresh Kulkarni (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 429851a2c944SMayuresh Kulkarni ADSP2_PMEM_ERR_ADDR_SHIFT); 429951a2c944SMayuresh Kulkarni } 430051a2c944SMayuresh Kulkarni 430151a2c944SMayuresh Kulkarni regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 430251a2c944SMayuresh Kulkarni ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 430351a2c944SMayuresh Kulkarni 4304a2225a6dSCharles Keepax error: 4305a2225a6dSCharles Keepax mutex_unlock(&dsp->pwr_lock); 4306a2225a6dSCharles Keepax 430751a2c944SMayuresh Kulkarni return IRQ_HANDLED; 430851a2c944SMayuresh Kulkarni } 430951a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 431051a2c944SMayuresh Kulkarni 431101ec57a4SCharles Keepax irqreturn_t wm_halo_bus_error(int irq, void *data) 43122ae58138SRichard Fitzgerald { 431301ec57a4SCharles Keepax struct wm_adsp *dsp = (struct wm_adsp *)data; 43142ae58138SRichard Fitzgerald struct regmap *regmap = dsp->regmap; 43152ae58138SRichard Fitzgerald unsigned int fault[6]; 43162ae58138SRichard Fitzgerald struct reg_sequence clear[] = { 43172ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 43182ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 43192ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 43202ae58138SRichard Fitzgerald }; 43212ae58138SRichard Fitzgerald int ret; 43222ae58138SRichard Fitzgerald 43232ae58138SRichard Fitzgerald mutex_lock(&dsp->pwr_lock); 43242ae58138SRichard Fitzgerald 43252ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 43262ae58138SRichard Fitzgerald fault); 43272ae58138SRichard Fitzgerald if (ret) { 43282ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 43292ae58138SRichard Fitzgerald goto exit_unlock; 43302ae58138SRichard Fitzgerald } 43312ae58138SRichard Fitzgerald 43322ae58138SRichard Fitzgerald adsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 43332ae58138SRichard Fitzgerald *fault & HALO_AHBM_FLAGS_ERR_MASK, 43342ae58138SRichard Fitzgerald (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 43352ae58138SRichard Fitzgerald HALO_AHBM_CORE_ERR_ADDR_SHIFT); 43362ae58138SRichard Fitzgerald 43372ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 43382ae58138SRichard Fitzgerald fault); 43392ae58138SRichard Fitzgerald if (ret) { 43402ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 43412ae58138SRichard Fitzgerald goto exit_unlock; 43422ae58138SRichard Fitzgerald } 43432ae58138SRichard Fitzgerald 43442ae58138SRichard Fitzgerald adsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 43452ae58138SRichard Fitzgerald 43462ae58138SRichard Fitzgerald ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 43472ae58138SRichard Fitzgerald fault, ARRAY_SIZE(fault)); 43482ae58138SRichard Fitzgerald if (ret) { 43492ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 43502ae58138SRichard Fitzgerald goto exit_unlock; 43512ae58138SRichard Fitzgerald } 43522ae58138SRichard Fitzgerald 43532ae58138SRichard Fitzgerald adsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 43542ae58138SRichard Fitzgerald adsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 43552ae58138SRichard Fitzgerald adsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 43562ae58138SRichard Fitzgerald 43572ae58138SRichard Fitzgerald ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 43582ae58138SRichard Fitzgerald if (ret) 43592ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 43602ae58138SRichard Fitzgerald 43612ae58138SRichard Fitzgerald exit_unlock: 43622ae58138SRichard Fitzgerald mutex_unlock(&dsp->pwr_lock); 43632ae58138SRichard Fitzgerald 43642ae58138SRichard Fitzgerald return IRQ_HANDLED; 43652ae58138SRichard Fitzgerald } 43662ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error); 43672ae58138SRichard Fitzgerald 43688bc144f9SStuart Henderson irqreturn_t wm_halo_wdt_expire(int irq, void *data) 43698bc144f9SStuart Henderson { 43708bc144f9SStuart Henderson struct wm_adsp *dsp = data; 43718bc144f9SStuart Henderson 43728bc144f9SStuart Henderson mutex_lock(&dsp->pwr_lock); 43738bc144f9SStuart Henderson 43748bc144f9SStuart Henderson adsp_warn(dsp, "WDT Expiry Fault\n"); 437581ed8845SCharles Keepax dsp->ops->stop_watchdog(dsp); 43768bc144f9SStuart Henderson wm_adsp_fatal_error(dsp); 43778bc144f9SStuart Henderson 43788bc144f9SStuart Henderson mutex_unlock(&dsp->pwr_lock); 43798bc144f9SStuart Henderson 43808bc144f9SStuart Henderson return IRQ_HANDLED; 43818bc144f9SStuart Henderson } 43828bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); 43838bc144f9SStuart Henderson 4384cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp1_ops = { 43854e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 43864e08d50dSCharles Keepax .parse_sizes = wm_adsp1_parse_sizes, 4387170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 43884e08d50dSCharles Keepax }; 43894e08d50dSCharles Keepax 4390cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp2_ops[] = { 43914e08d50dSCharles Keepax { 4392170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 43934e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 43944e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 43954e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4396170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 43974e08d50dSCharles Keepax 43984e08d50dSCharles Keepax .show_fw_status = wm_adsp2_show_fw_status, 43994e08d50dSCharles Keepax 44004e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 44014e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 44024e08d50dSCharles Keepax 44034e08d50dSCharles Keepax .enable_core = wm_adsp2_enable_core, 44044e08d50dSCharles Keepax .disable_core = wm_adsp2_disable_core, 44054e08d50dSCharles Keepax 44064e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 44074e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 44084e08d50dSCharles Keepax 44094e08d50dSCharles Keepax }, 44104e08d50dSCharles Keepax { 4411170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 44124e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 44134e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 44144e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4415170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 44164e08d50dSCharles Keepax 44174e08d50dSCharles Keepax .show_fw_status = wm_adsp2v2_show_fw_status, 44184e08d50dSCharles Keepax 44194e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 44204e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 44214e08d50dSCharles Keepax .lock_memory = wm_adsp2_lock, 44224e08d50dSCharles Keepax 44234e08d50dSCharles Keepax .enable_core = wm_adsp2v2_enable_core, 44244e08d50dSCharles Keepax .disable_core = wm_adsp2v2_disable_core, 44254e08d50dSCharles Keepax 44264e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 44274e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 44284e08d50dSCharles Keepax }, 44294e08d50dSCharles Keepax { 4430170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 44314e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 44324e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 44334e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4434170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 44354e08d50dSCharles Keepax 44364e08d50dSCharles Keepax .show_fw_status = wm_adsp2v2_show_fw_status, 44374e08d50dSCharles Keepax .stop_watchdog = wm_adsp_stop_watchdog, 44384e08d50dSCharles Keepax 44394e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 44404e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 44414e08d50dSCharles Keepax .lock_memory = wm_adsp2_lock, 44424e08d50dSCharles Keepax 44434e08d50dSCharles Keepax .enable_core = wm_adsp2v2_enable_core, 44444e08d50dSCharles Keepax .disable_core = wm_adsp2v2_disable_core, 44454e08d50dSCharles Keepax 44464e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 44474e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 44484e08d50dSCharles Keepax }, 44494e08d50dSCharles Keepax }; 44504e08d50dSCharles Keepax 4451cd537873SCharles Keepax static struct wm_adsp_ops wm_halo_ops = { 4452170b1e12SWen Shi .sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr), 4453170b1e12SWen Shi .parse_sizes = wm_adsp2_parse_sizes, 4454170b1e12SWen Shi .validate_version = wm_halo_validate_version, 4455170b1e12SWen Shi .setup_algs = wm_halo_setup_algs, 4456170b1e12SWen Shi .region_to_reg = wm_halo_region_to_reg, 4457170b1e12SWen Shi 4458170b1e12SWen Shi .show_fw_status = wm_halo_show_fw_status, 44598bc144f9SStuart Henderson .stop_watchdog = wm_halo_stop_watchdog, 4460170b1e12SWen Shi 4461170b1e12SWen Shi .lock_memory = wm_halo_configure_mpu, 4462170b1e12SWen Shi 4463170b1e12SWen Shi .start_core = wm_halo_start_core, 4464170b1e12SWen Shi .stop_core = wm_halo_stop_core, 4465170b1e12SWen Shi }; 4466170b1e12SWen Shi 44670a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 4468