12159ad93SMark Brown /* 22159ad93SMark Brown * wm_adsp.c -- Wolfson ADSP support 32159ad93SMark Brown * 42159ad93SMark Brown * Copyright 2012 Wolfson Microelectronics plc 52159ad93SMark Brown * 62159ad93SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 72159ad93SMark Brown * 82159ad93SMark Brown * This program is free software; you can redistribute it and/or modify 92159ad93SMark Brown * it under the terms of the GNU General Public License version 2 as 102159ad93SMark Brown * published by the Free Software Foundation. 112159ad93SMark Brown */ 122159ad93SMark Brown 13605391d0SRichard Fitzgerald #include <linux/ctype.h> 142159ad93SMark Brown #include <linux/module.h> 152159ad93SMark Brown #include <linux/moduleparam.h> 162159ad93SMark Brown #include <linux/init.h> 172159ad93SMark Brown #include <linux/delay.h> 182159ad93SMark Brown #include <linux/firmware.h> 19cf17c83cSMark Brown #include <linux/list.h> 202159ad93SMark Brown #include <linux/pm.h> 212159ad93SMark Brown #include <linux/pm_runtime.h> 222159ad93SMark Brown #include <linux/regmap.h> 23973838a0SMark Brown #include <linux/regulator/consumer.h> 242159ad93SMark Brown #include <linux/slab.h> 25cdcd7f72SCharles Keepax #include <linux/vmalloc.h> 266ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 27f9f55e31SRichard Fitzgerald #include <linux/debugfs.h> 282159ad93SMark Brown #include <sound/core.h> 292159ad93SMark Brown #include <sound/pcm.h> 302159ad93SMark Brown #include <sound/pcm_params.h> 312159ad93SMark Brown #include <sound/soc.h> 322159ad93SMark Brown #include <sound/jack.h> 332159ad93SMark Brown #include <sound/initval.h> 342159ad93SMark Brown #include <sound/tlv.h> 352159ad93SMark Brown 362159ad93SMark Brown #include "wm_adsp.h" 372159ad93SMark Brown 382159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 39605391d0SRichard Fitzgerald dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 402159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 41605391d0SRichard Fitzgerald dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 422159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 43605391d0SRichard Fitzgerald dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 442159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 45605391d0SRichard Fitzgerald dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 462159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 47605391d0SRichard Fitzgerald dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 482159ad93SMark Brown 490d3fba3eSCharles Keepax #define compr_err(_obj, fmt, ...) \ 500d3fba3eSCharles Keepax adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 510d3fba3eSCharles Keepax ##__VA_ARGS__) 520d3fba3eSCharles Keepax #define compr_dbg(_obj, fmt, ...) \ 530d3fba3eSCharles Keepax adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 540d3fba3eSCharles Keepax ##__VA_ARGS__) 550d3fba3eSCharles Keepax 562159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 572159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 582159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 592159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 602159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 612159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 622159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 632159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 642159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 652159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 662159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 672159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 682159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 692159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 702159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 712159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 722159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 732159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 742159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 752159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 762159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 772159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 782159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 792159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 802159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 812159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 822159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 832159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 842159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 852159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 862159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 872159ad93SMark Brown 882159ad93SMark Brown /* 892159ad93SMark Brown * ADSP1 Control 19 902159ad93SMark Brown */ 912159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 922159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 932159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 942159ad93SMark Brown 952159ad93SMark Brown 962159ad93SMark Brown /* 972159ad93SMark Brown * ADSP1 Control 30 982159ad93SMark Brown */ 992159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 1002159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 1012159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 1022159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 1032159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1042159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1052159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1062159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1072159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1082159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1092159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1102159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1112159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1122159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1132159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1142159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1152159ad93SMark Brown 11694e205bfSChris Rattray /* 11794e205bfSChris Rattray * ADSP1 Control 31 11894e205bfSChris Rattray */ 11994e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 12094e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 12194e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 12294e205bfSChris Rattray 1232d30b575SMark Brown #define ADSP2_CONTROL 0x0 1242d30b575SMark Brown #define ADSP2_CLOCKING 0x1 125e1ea1879SRichard Fitzgerald #define ADSP2V2_CLOCKING 0x2 1262d30b575SMark Brown #define ADSP2_STATUS1 0x4 1272d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1282d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 129e1ea1879SRichard Fitzgerald #define ADSP2V2_WDMA_CONFIG_2 0x32 1302d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1312159ad93SMark Brown 13210337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 13310337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 13410337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 13510337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 13610337b07SRichard Fitzgerald 137e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH0_1 0x40 138e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH2_3 0x42 139e1ea1879SRichard Fitzgerald 1402159ad93SMark Brown /* 1412159ad93SMark Brown * ADSP2 Control 1422159ad93SMark Brown */ 1432159ad93SMark Brown 1442159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1452159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1462159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1472159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1482159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1492159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1502159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1512159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1522159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1532159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1542159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1552159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1562159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1572159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1582159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1592159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1602159ad93SMark Brown 1612159ad93SMark Brown /* 162973838a0SMark Brown * ADSP2 clocking 163973838a0SMark Brown */ 164973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 165973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 166973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 167973838a0SMark Brown 168973838a0SMark Brown /* 169e1ea1879SRichard Fitzgerald * ADSP2V2 clocking 170e1ea1879SRichard Fitzgerald */ 171e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 172e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 173e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 174e1ea1879SRichard Fitzgerald 175e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 176e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 177e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 178e1ea1879SRichard Fitzgerald 179e1ea1879SRichard Fitzgerald /* 1802159ad93SMark Brown * ADSP2 Status 1 1812159ad93SMark Brown */ 1822159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1832159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1842159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1852159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1862159ad93SMark Brown 18751a2c944SMayuresh Kulkarni /* 18851a2c944SMayuresh Kulkarni * ADSP2 Lock support 18951a2c944SMayuresh Kulkarni */ 19051a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_0 0x5555 19151a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_1 0xAAAA 19251a2c944SMayuresh Kulkarni 19351a2c944SMayuresh Kulkarni #define ADSP2_WATCHDOG 0x0A 19451a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR 0x52 19551a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_STATUS 0x64 19651a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 19751a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 19851a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 19951a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 20051a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 20151a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_CTRL 0x7A 20251a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 20351a2c944SMayuresh Kulkarni 20451a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 20551a2c944SMayuresh Kulkarni #define ADSP2_SLAVE_ERR_MASK 0x4000 20651a2c944SMayuresh Kulkarni #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 20751a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 20851a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_EINT 0x0001 20951a2c944SMayuresh Kulkarni 21051a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 21151a2c944SMayuresh Kulkarni #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 21251a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 21351a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 21451a2c944SMayuresh Kulkarni #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 21551a2c944SMayuresh Kulkarni 21651a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_SHIFT 16 21751a2c944SMayuresh Kulkarni 2189ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 2199ee78757SCharles Keepax 220f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 221f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 222a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MIN_VALUE 0 223a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 224f4f0c4c6SRichard Fitzgerald 225f4f0c4c6SRichard Fitzgerald /* 226f4f0c4c6SRichard Fitzgerald * Event control messages 227f4f0c4c6SRichard Fitzgerald */ 228f4f0c4c6SRichard Fitzgerald #define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 229f4f0c4c6SRichard Fitzgerald 230170b1e12SWen Shi /* 2312ae58138SRichard Fitzgerald * HALO system info 2322ae58138SRichard Fitzgerald */ 2332ae58138SRichard Fitzgerald #define HALO_AHBM_WINDOW_DEBUG_0 0x02040 2342ae58138SRichard Fitzgerald #define HALO_AHBM_WINDOW_DEBUG_1 0x02044 2352ae58138SRichard Fitzgerald 2362ae58138SRichard Fitzgerald /* 237170b1e12SWen Shi * HALO core 238170b1e12SWen Shi */ 239170b1e12SWen Shi #define HALO_SCRATCH1 0x005c0 240170b1e12SWen Shi #define HALO_SCRATCH2 0x005c8 241170b1e12SWen Shi #define HALO_SCRATCH3 0x005d0 242170b1e12SWen Shi #define HALO_SCRATCH4 0x005d8 243170b1e12SWen Shi #define HALO_CCM_CORE_CONTROL 0x41000 244170b1e12SWen Shi #define HALO_CORE_SOFT_RESET 0x00010 2458bc144f9SStuart Henderson #define HALO_WDT_CONTROL 0x47000 246170b1e12SWen Shi 247170b1e12SWen Shi /* 248170b1e12SWen Shi * HALO MPU banks 249170b1e12SWen Shi */ 250170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_0 0x43000 251170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_0 0x43004 252170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_0 0x43008 253170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_0 0x4300C 254170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_0 0x43014 255170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_1 0x43018 256170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_1 0x4301C 257170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_1 0x43020 258170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_1 0x43024 259170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_1 0x4302C 260170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_2 0x43030 261170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_2 0x43034 262170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_2 0x43038 263170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_2 0x4303C 264170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_2 0x43044 265170b1e12SWen Shi #define HALO_MPU_XMEM_ACCESS_3 0x43048 266170b1e12SWen Shi #define HALO_MPU_YMEM_ACCESS_3 0x4304C 267170b1e12SWen Shi #define HALO_MPU_WINDOW_ACCESS_3 0x43050 268170b1e12SWen Shi #define HALO_MPU_XREG_ACCESS_3 0x43054 269170b1e12SWen Shi #define HALO_MPU_YREG_ACCESS_3 0x4305C 2702ae58138SRichard Fitzgerald #define HALO_MPU_XM_VIO_ADDR 0x43100 2712ae58138SRichard Fitzgerald #define HALO_MPU_XM_VIO_STATUS 0x43104 2722ae58138SRichard Fitzgerald #define HALO_MPU_YM_VIO_ADDR 0x43108 2732ae58138SRichard Fitzgerald #define HALO_MPU_YM_VIO_STATUS 0x4310C 2742ae58138SRichard Fitzgerald #define HALO_MPU_PM_VIO_ADDR 0x43110 2752ae58138SRichard Fitzgerald #define HALO_MPU_PM_VIO_STATUS 0x43114 276170b1e12SWen Shi #define HALO_MPU_LOCK_CONFIG 0x43140 277170b1e12SWen Shi 278170b1e12SWen Shi /* 2792ae58138SRichard Fitzgerald * HALO_AHBM_WINDOW_DEBUG_1 2802ae58138SRichard Fitzgerald */ 2812ae58138SRichard Fitzgerald #define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 2822ae58138SRichard Fitzgerald #define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 2832ae58138SRichard Fitzgerald #define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff 2842ae58138SRichard Fitzgerald 2852ae58138SRichard Fitzgerald /* 286170b1e12SWen Shi * HALO_CCM_CORE_CONTROL 287170b1e12SWen Shi */ 288170b1e12SWen Shi #define HALO_CORE_EN 0x00000001 289170b1e12SWen Shi 290170b1e12SWen Shi /* 291170b1e12SWen Shi * HALO_CORE_SOFT_RESET 292170b1e12SWen Shi */ 293170b1e12SWen Shi #define HALO_CORE_SOFT_RESET_MASK 0x00000001 294170b1e12SWen Shi 2952ae58138SRichard Fitzgerald /* 2968bc144f9SStuart Henderson * HALO_WDT_CONTROL 2978bc144f9SStuart Henderson */ 2988bc144f9SStuart Henderson #define HALO_WDT_EN_MASK 0x00000001 2998bc144f9SStuart Henderson 3008bc144f9SStuart Henderson /* 3012ae58138SRichard Fitzgerald * HALO_MPU_?M_VIO_STATUS 3022ae58138SRichard Fitzgerald */ 3032ae58138SRichard Fitzgerald #define HALO_MPU_VIO_STS_MASK 0x007e0000 3042ae58138SRichard Fitzgerald #define HALO_MPU_VIO_STS_SHIFT 17 3052ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 3062ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff 3072ae58138SRichard Fitzgerald #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 3082ae58138SRichard Fitzgerald 309cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp1_ops; 310cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp2_ops[]; 311cd537873SCharles Keepax static struct wm_adsp_ops wm_halo_ops; 3124e08d50dSCharles Keepax 313cf17c83cSMark Brown struct wm_adsp_buf { 314cf17c83cSMark Brown struct list_head list; 315cf17c83cSMark Brown void *buf; 316cf17c83cSMark Brown }; 317cf17c83cSMark Brown 318cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 319cf17c83cSMark Brown struct list_head *list) 320cf17c83cSMark Brown { 321cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 322cf17c83cSMark Brown 323cf17c83cSMark Brown if (buf == NULL) 324cf17c83cSMark Brown return NULL; 325cf17c83cSMark Brown 326cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 327cf17c83cSMark Brown if (!buf->buf) { 3284d41c74dSRichard Fitzgerald kfree(buf); 329cf17c83cSMark Brown return NULL; 330cf17c83cSMark Brown } 331cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 332cf17c83cSMark Brown 333cf17c83cSMark Brown if (list) 334cf17c83cSMark Brown list_add_tail(&buf->list, list); 335cf17c83cSMark Brown 336cf17c83cSMark Brown return buf; 337cf17c83cSMark Brown } 338cf17c83cSMark Brown 339cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 340cf17c83cSMark Brown { 341cf17c83cSMark Brown while (!list_empty(list)) { 342cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 343cf17c83cSMark Brown struct wm_adsp_buf, 344cf17c83cSMark Brown list); 345cf17c83cSMark Brown list_del(&buf->list); 346cdcd7f72SCharles Keepax vfree(buf->buf); 347cf17c83cSMark Brown kfree(buf); 348cf17c83cSMark Brown } 349cf17c83cSMark Brown } 350cf17c83cSMark Brown 351dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 35204d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 35304d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 35404d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 35504d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 35604d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 35704d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 35804d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 35904d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 36004d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 36104d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 36204d1300fSCharles Keepax 36304d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 364dd84f925SMark Brown 3651023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 366dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 36704d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 368dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 369dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 37004d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 371dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 37204d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 37304d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 37404d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 37504d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 37604d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 3771023dbd9SMark Brown }; 3781023dbd9SMark Brown 3792cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 3802cd19bdbSCharles Keepax __be32 sys_enable; 3812cd19bdbSCharles Keepax __be32 fw_id; 3822cd19bdbSCharles Keepax __be32 fw_rev; 3832cd19bdbSCharles Keepax __be32 boot_status; 3842cd19bdbSCharles Keepax __be32 watchdog; 3852cd19bdbSCharles Keepax __be32 dma_buffer_size; 3862cd19bdbSCharles Keepax __be32 rdma[6]; 3872cd19bdbSCharles Keepax __be32 wdma[8]; 3882cd19bdbSCharles Keepax __be32 build_job_name[3]; 3892cd19bdbSCharles Keepax __be32 build_job_number; 3902cd19bdbSCharles Keepax }; 3912cd19bdbSCharles Keepax 392170b1e12SWen Shi struct wm_halo_system_config_xm_hdr { 393170b1e12SWen Shi __be32 halo_heartbeat; 394170b1e12SWen Shi __be32 build_job_name[3]; 395170b1e12SWen Shi __be32 build_job_number; 396170b1e12SWen Shi }; 397170b1e12SWen Shi 3982cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 3992cd19bdbSCharles Keepax __be32 magic; 4002cd19bdbSCharles Keepax __be32 smoothing; 4012cd19bdbSCharles Keepax __be32 threshold; 4022cd19bdbSCharles Keepax __be32 host_buf_ptr; 4032cd19bdbSCharles Keepax __be32 start_seq; 4042cd19bdbSCharles Keepax __be32 high_water_mark; 4052cd19bdbSCharles Keepax __be32 low_water_mark; 4062cd19bdbSCharles Keepax __be64 smoothed_power; 4072cd19bdbSCharles Keepax }; 4082cd19bdbSCharles Keepax 4094f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 { 4104f2d4eabSStuart Henderson __be32 host_buf_ptr; /* Host buffer pointer */ 4114f2d4eabSStuart Henderson __be32 versions; /* Version numbers */ 4124f2d4eabSStuart Henderson __be32 name[4]; /* The buffer name */ 4134f2d4eabSStuart Henderson }; 4144f2d4eabSStuart Henderson 4152cd19bdbSCharles Keepax struct wm_adsp_buffer { 4162a2aefa4SRichard Fitzgerald __be32 buf1_base; /* Base addr of first buffer area */ 4172a2aefa4SRichard Fitzgerald __be32 buf1_size; /* Size of buf1 area in DSP words */ 4182a2aefa4SRichard Fitzgerald __be32 buf2_base; /* Base addr of 2nd buffer area */ 4192a2aefa4SRichard Fitzgerald __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 4202a2aefa4SRichard Fitzgerald __be32 buf3_base; /* Base addr of buf3 area */ 4212a2aefa4SRichard Fitzgerald __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 4222cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 4232cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 4242cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 4252cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 4262cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 4272cd19bdbSCharles Keepax __be32 error; /* error if any */ 4282cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 4292cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 4302cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 4312cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 4322cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 4332cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 4342cd19bdbSCharles Keepax }; 4352cd19bdbSCharles Keepax 436721be3beSCharles Keepax struct wm_adsp_compr; 437721be3beSCharles Keepax 4382cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 4394f2d4eabSStuart Henderson struct list_head list; 4402cd19bdbSCharles Keepax struct wm_adsp *dsp; 441721be3beSCharles Keepax struct wm_adsp_compr *compr; 4422cd19bdbSCharles Keepax 4432cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 4442cd19bdbSCharles Keepax u32 host_buf_ptr; 445565ace46SCharles Keepax 446565ace46SCharles Keepax u32 error; 447565ace46SCharles Keepax u32 irq_count; 448565ace46SCharles Keepax int read_index; 449565ace46SCharles Keepax int avail; 450fb13f19dSAndrew Ford int host_buf_mem_type; 4514f2d4eabSStuart Henderson 4524f2d4eabSStuart Henderson char *name; 4532cd19bdbSCharles Keepax }; 4542cd19bdbSCharles Keepax 455406abc95SCharles Keepax struct wm_adsp_compr { 4564f2d4eabSStuart Henderson struct list_head list; 457406abc95SCharles Keepax struct wm_adsp *dsp; 45895fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 459406abc95SCharles Keepax 460406abc95SCharles Keepax struct snd_compr_stream *stream; 461406abc95SCharles Keepax struct snd_compressed_buffer size; 462565ace46SCharles Keepax 46383a40ce9SCharles Keepax u32 *raw_buf; 464565ace46SCharles Keepax unsigned int copied_total; 465da2b3358SCharles Keepax 466da2b3358SCharles Keepax unsigned int sample_rate; 4674f2d4eabSStuart Henderson 4684f2d4eabSStuart Henderson const char *name; 469406abc95SCharles Keepax }; 470406abc95SCharles Keepax 471406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 472406abc95SCharles Keepax 473406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 474406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 475406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 476406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 477406abc95SCharles Keepax 4782cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 4792cd19bdbSCharles Keepax 4802cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 4812cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 4822cd19bdbSCharles Keepax 4832cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 4842cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 4852cd19bdbSCharles Keepax 4864f2d4eabSStuart Henderson #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 4874f2d4eabSStuart Henderson 4884f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 4894f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 4904f2d4eabSStuart Henderson 4912cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 4922cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 4932cd19bdbSCharles Keepax 4942cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 4952cd19bdbSCharles Keepax unsigned int offset; 4962cd19bdbSCharles Keepax unsigned int cumulative_size; 4972cd19bdbSCharles Keepax unsigned int mem_type; 4982cd19bdbSCharles Keepax unsigned int base_addr; 4992cd19bdbSCharles Keepax }; 5002cd19bdbSCharles Keepax 5012cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 5022cd19bdbSCharles Keepax unsigned int mem_type; 5032cd19bdbSCharles Keepax unsigned int base_offset; 5042cd19bdbSCharles Keepax unsigned int size_offset; 5052cd19bdbSCharles Keepax }; 5062cd19bdbSCharles Keepax 5073a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 5082cd19bdbSCharles Keepax { 5092cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5102a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf1_base), 5112a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_size), 5122cd19bdbSCharles Keepax }, 5132cd19bdbSCharles Keepax { 5142cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 5152a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf2_base), 5162a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 5172cd19bdbSCharles Keepax }, 5182cd19bdbSCharles Keepax { 5192cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 5202a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf3_base), 5212a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf_total_size), 5222cd19bdbSCharles Keepax }, 5232cd19bdbSCharles Keepax }; 5242cd19bdbSCharles Keepax 525406abc95SCharles Keepax struct wm_adsp_fw_caps { 526406abc95SCharles Keepax u32 id; 527406abc95SCharles Keepax struct snd_codec_desc desc; 5282cd19bdbSCharles Keepax int num_regions; 5293a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 530406abc95SCharles Keepax }; 531406abc95SCharles Keepax 532e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 533406abc95SCharles Keepax { 534406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 535406abc95SCharles Keepax .desc = { 5363bbc2705SRichard Fitzgerald .max_ch = 8, 537406abc95SCharles Keepax .sample_rates = { 16000 }, 538406abc95SCharles Keepax .num_sample_rates = 1, 539406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 540406abc95SCharles Keepax }, 541e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 542e6d00f34SCharles Keepax .region_defs = default_regions, 543406abc95SCharles Keepax }, 544406abc95SCharles Keepax }; 545406abc95SCharles Keepax 5467ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 5477ce4283cSCharles Keepax { 5487ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 5497ce4283cSCharles Keepax .desc = { 5507ce4283cSCharles Keepax .max_ch = 8, 5517ce4283cSCharles Keepax .sample_rates = { 5527ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 5537ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 5547ce4283cSCharles Keepax 96000, 176400, 192000 5557ce4283cSCharles Keepax }, 5567ce4283cSCharles Keepax .num_sample_rates = 15, 5577ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 5587ce4283cSCharles Keepax }, 5597ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 5607ce4283cSCharles Keepax .region_defs = default_regions, 561406abc95SCharles Keepax }, 562406abc95SCharles Keepax }; 563406abc95SCharles Keepax 564406abc95SCharles Keepax static const struct { 5651023dbd9SMark Brown const char *file; 566406abc95SCharles Keepax int compr_direction; 567406abc95SCharles Keepax int num_caps; 568406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 56920b7f7c5SCharles Keepax bool voice_trigger; 5701023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 571dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 57204d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 573dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 574dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 57504d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 576dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 577406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 578406abc95SCharles Keepax .file = "ctrl", 579406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 580e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 581e6d00f34SCharles Keepax .caps = ctrl_caps, 58220b7f7c5SCharles Keepax .voice_trigger = true, 583406abc95SCharles Keepax }, 58404d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 5857ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 5867ce4283cSCharles Keepax .file = "trace", 5877ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 5887ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 5897ce4283cSCharles Keepax .caps = trace_caps, 5907ce4283cSCharles Keepax }, 59104d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 59204d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 5931023dbd9SMark Brown }; 5941023dbd9SMark Brown 5956ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 5966ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 5976ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 5986ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 5996ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 6006ab2b7b4SDimitris Papastamos }; 6016ab2b7b4SDimitris Papastamos 6026ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 6036ab2b7b4SDimitris Papastamos const char *name; 6042323736dSCharles Keepax const char *fw_name; 6053809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 6066ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 6073809f001SCharles Keepax struct wm_adsp *dsp; 6086ab2b7b4SDimitris Papastamos unsigned int enabled:1; 6096ab2b7b4SDimitris Papastamos struct list_head list; 6106ab2b7b4SDimitris Papastamos void *cache; 6112323736dSCharles Keepax unsigned int offset; 6126ab2b7b4SDimitris Papastamos size_t len; 6130c2e3f34SDimitris Papastamos unsigned int set:1; 6149ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 61526c22a19SCharles Keepax unsigned int flags; 6168eb084d0SStuart Henderson unsigned int type; 6176ab2b7b4SDimitris Papastamos }; 6186ab2b7b4SDimitris Papastamos 6199ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 6209ce5e6e6SRichard Fitzgerald { 6219ce5e6e6SRichard Fitzgerald switch (type) { 6229ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 6239ce5e6e6SRichard Fitzgerald return "PM"; 624170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 625170b1e12SWen Shi return "PM_PACKED"; 6269ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 6279ce5e6e6SRichard Fitzgerald return "DM"; 6289ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 6299ce5e6e6SRichard Fitzgerald return "XM"; 630170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 631170b1e12SWen Shi return "XM_PACKED"; 6329ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 6339ce5e6e6SRichard Fitzgerald return "YM"; 634170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 635170b1e12SWen Shi return "YM_PACKED"; 6369ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 6379ce5e6e6SRichard Fitzgerald return "ZM"; 6389ce5e6e6SRichard Fitzgerald default: 6399ce5e6e6SRichard Fitzgerald return NULL; 6409ce5e6e6SRichard Fitzgerald } 6419ce5e6e6SRichard Fitzgerald } 6429ce5e6e6SRichard Fitzgerald 643f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 644f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 645f9f55e31SRichard Fitzgerald { 646f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 647f9f55e31SRichard Fitzgerald 648f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 649f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 650f9f55e31SRichard Fitzgerald } 651f9f55e31SRichard Fitzgerald 652f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 653f9f55e31SRichard Fitzgerald { 654f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 655f9f55e31SRichard Fitzgerald 656f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 657f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 658f9f55e31SRichard Fitzgerald } 659f9f55e31SRichard Fitzgerald 660f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 661f9f55e31SRichard Fitzgerald { 662f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 663f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 664f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 665f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 666f9f55e31SRichard Fitzgerald } 667f9f55e31SRichard Fitzgerald 668f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 669f9f55e31SRichard Fitzgerald char __user *user_buf, 670f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 671f9f55e31SRichard Fitzgerald { 672f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 673f9f55e31SRichard Fitzgerald ssize_t ret; 674f9f55e31SRichard Fitzgerald 675078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 676f9f55e31SRichard Fitzgerald 67728823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 678f9f55e31SRichard Fitzgerald ret = 0; 679f9f55e31SRichard Fitzgerald else 680f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 681f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 682f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 683f9f55e31SRichard Fitzgerald 684078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 685f9f55e31SRichard Fitzgerald return ret; 686f9f55e31SRichard Fitzgerald } 687f9f55e31SRichard Fitzgerald 688f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 689f9f55e31SRichard Fitzgerald char __user *user_buf, 690f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 691f9f55e31SRichard Fitzgerald { 692f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 693f9f55e31SRichard Fitzgerald ssize_t ret; 694f9f55e31SRichard Fitzgerald 695078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 696f9f55e31SRichard Fitzgerald 69728823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 698f9f55e31SRichard Fitzgerald ret = 0; 699f9f55e31SRichard Fitzgerald else 700f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 701f9f55e31SRichard Fitzgerald dsp->bin_file_name, 702f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 703f9f55e31SRichard Fitzgerald 704078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 705f9f55e31SRichard Fitzgerald return ret; 706f9f55e31SRichard Fitzgerald } 707f9f55e31SRichard Fitzgerald 708f9f55e31SRichard Fitzgerald static const struct { 709f9f55e31SRichard Fitzgerald const char *name; 710f9f55e31SRichard Fitzgerald const struct file_operations fops; 711f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 712f9f55e31SRichard Fitzgerald { 713f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 714f9f55e31SRichard Fitzgerald .fops = { 715f9f55e31SRichard Fitzgerald .open = simple_open, 716f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 717f9f55e31SRichard Fitzgerald }, 718f9f55e31SRichard Fitzgerald }, 719f9f55e31SRichard Fitzgerald { 720f9f55e31SRichard Fitzgerald .name = "bin_file_name", 721f9f55e31SRichard Fitzgerald .fops = { 722f9f55e31SRichard Fitzgerald .open = simple_open, 723f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 724f9f55e31SRichard Fitzgerald }, 725f9f55e31SRichard Fitzgerald }, 726f9f55e31SRichard Fitzgerald }; 727f9f55e31SRichard Fitzgerald 728f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7290fe1daa6SKuninori Morimoto struct snd_soc_component *component) 730f9f55e31SRichard Fitzgerald { 731f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 732f9f55e31SRichard Fitzgerald int i; 733f9f55e31SRichard Fitzgerald 7340fe1daa6SKuninori Morimoto if (!component->debugfs_root) { 735f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 736f9f55e31SRichard Fitzgerald goto err; 737f9f55e31SRichard Fitzgerald } 738f9f55e31SRichard Fitzgerald 739605391d0SRichard Fitzgerald root = debugfs_create_dir(dsp->name, component->debugfs_root); 740f9f55e31SRichard Fitzgerald 741f9f55e31SRichard Fitzgerald if (!root) 742f9f55e31SRichard Fitzgerald goto err; 743f9f55e31SRichard Fitzgerald 7446a73cf46SJoe Perches if (!debugfs_create_bool("booted", 0444, root, &dsp->booted)) 74528823ebaSCharles Keepax goto err; 74628823ebaSCharles Keepax 7476a73cf46SJoe Perches if (!debugfs_create_bool("running", 0444, root, &dsp->running)) 748f9f55e31SRichard Fitzgerald goto err; 749f9f55e31SRichard Fitzgerald 7506a73cf46SJoe Perches if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id)) 751f9f55e31SRichard Fitzgerald goto err; 752f9f55e31SRichard Fitzgerald 7536a73cf46SJoe Perches if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version)) 754f9f55e31SRichard Fitzgerald goto err; 755f9f55e31SRichard Fitzgerald 756f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 757f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 7586a73cf46SJoe Perches 0444, root, dsp, 759f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 760f9f55e31SRichard Fitzgerald goto err; 761f9f55e31SRichard Fitzgerald } 762f9f55e31SRichard Fitzgerald 763f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 764f9f55e31SRichard Fitzgerald return; 765f9f55e31SRichard Fitzgerald 766f9f55e31SRichard Fitzgerald err: 767f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 768f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 769f9f55e31SRichard Fitzgerald } 770f9f55e31SRichard Fitzgerald 771f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 772f9f55e31SRichard Fitzgerald { 773f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 774f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 775f9f55e31SRichard Fitzgerald } 776f9f55e31SRichard Fitzgerald #else 777f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7780fe1daa6SKuninori Morimoto struct snd_soc_component *component) 779f9f55e31SRichard Fitzgerald { 780f9f55e31SRichard Fitzgerald } 781f9f55e31SRichard Fitzgerald 782f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 783f9f55e31SRichard Fitzgerald { 784f9f55e31SRichard Fitzgerald } 785f9f55e31SRichard Fitzgerald 786f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 787f9f55e31SRichard Fitzgerald const char *s) 788f9f55e31SRichard Fitzgerald { 789f9f55e31SRichard Fitzgerald } 790f9f55e31SRichard Fitzgerald 791f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 792f9f55e31SRichard Fitzgerald const char *s) 793f9f55e31SRichard Fitzgerald { 794f9f55e31SRichard Fitzgerald } 795f9f55e31SRichard Fitzgerald 796f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 797f9f55e31SRichard Fitzgerald { 798f9f55e31SRichard Fitzgerald } 799f9f55e31SRichard Fitzgerald #endif 800f9f55e31SRichard Fitzgerald 8010a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 8021023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 8031023dbd9SMark Brown { 8040fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 8051023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 8060fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 8071023dbd9SMark Brown 80815c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 8091023dbd9SMark Brown 8101023dbd9SMark Brown return 0; 8111023dbd9SMark Brown } 8120a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get); 8131023dbd9SMark Brown 8140a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 8151023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 8161023dbd9SMark Brown { 8170fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 8181023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 8190fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 820d27c5e15SCharles Keepax int ret = 0; 8211023dbd9SMark Brown 82215c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 8231023dbd9SMark Brown return 0; 8241023dbd9SMark Brown 82515c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 8261023dbd9SMark Brown return -EINVAL; 8271023dbd9SMark Brown 828d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 8291023dbd9SMark Brown 8304f2d4eabSStuart Henderson if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) 831d27c5e15SCharles Keepax ret = -EBUSY; 832d27c5e15SCharles Keepax else 83315c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 8341023dbd9SMark Brown 835d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 836d27c5e15SCharles Keepax 837d27c5e15SCharles Keepax return ret; 8381023dbd9SMark Brown } 8390a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put); 8401023dbd9SMark Brown 8410a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = { 8421023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8431023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8441023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8451023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 846e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 847e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 848e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8491023dbd9SMark Brown }; 8500a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 8512159ad93SMark Brown 8522159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 8532159ad93SMark Brown int type) 8542159ad93SMark Brown { 8552159ad93SMark Brown int i; 8562159ad93SMark Brown 8572159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 8582159ad93SMark Brown if (dsp->mem[i].type == type) 8592159ad93SMark Brown return &dsp->mem[i]; 8602159ad93SMark Brown 8612159ad93SMark Brown return NULL; 8622159ad93SMark Brown } 8632159ad93SMark Brown 8643809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 86545b9ee72SMark Brown unsigned int offset) 86645b9ee72SMark Brown { 8673809f001SCharles Keepax switch (mem->type) { 86845b9ee72SMark Brown case WMFW_ADSP1_PM: 8693809f001SCharles Keepax return mem->base + (offset * 3); 87045b9ee72SMark Brown case WMFW_ADSP1_DM: 87145b9ee72SMark Brown case WMFW_ADSP2_XM: 87245b9ee72SMark Brown case WMFW_ADSP2_YM: 87345b9ee72SMark Brown case WMFW_ADSP1_ZM: 8743809f001SCharles Keepax return mem->base + (offset * 2); 87545b9ee72SMark Brown default: 8766c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 87745b9ee72SMark Brown return offset; 87845b9ee72SMark Brown } 87945b9ee72SMark Brown } 88045b9ee72SMark Brown 881170b1e12SWen Shi static unsigned int wm_halo_region_to_reg(struct wm_adsp_region const *mem, 882170b1e12SWen Shi unsigned int offset) 883170b1e12SWen Shi { 884170b1e12SWen Shi switch (mem->type) { 885170b1e12SWen Shi case WMFW_ADSP2_XM: 886170b1e12SWen Shi case WMFW_ADSP2_YM: 887170b1e12SWen Shi return mem->base + (offset * 4); 888170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 889170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 890170b1e12SWen Shi return (mem->base + (offset * 3)) & ~0x3; 891170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 892170b1e12SWen Shi return mem->base + (offset * 5); 893170b1e12SWen Shi default: 894170b1e12SWen Shi WARN(1, "Unknown memory region type"); 895170b1e12SWen Shi return offset; 896170b1e12SWen Shi } 897170b1e12SWen Shi } 898170b1e12SWen Shi 8994049ce86SCharles Keepax static void wm_adsp_read_fw_status(struct wm_adsp *dsp, 9004049ce86SCharles Keepax int noffs, unsigned int *offs) 90110337b07SRichard Fitzgerald { 90220e00db2SRichard Fitzgerald unsigned int i; 90310337b07SRichard Fitzgerald int ret; 90410337b07SRichard Fitzgerald 9054049ce86SCharles Keepax for (i = 0; i < noffs; ++i) { 9064049ce86SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 90710337b07SRichard Fitzgerald if (ret) { 90820e00db2SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 90910337b07SRichard Fitzgerald return; 91010337b07SRichard Fitzgerald } 91120e00db2SRichard Fitzgerald } 9124049ce86SCharles Keepax } 9134049ce86SCharles Keepax 9144049ce86SCharles Keepax static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 9154049ce86SCharles Keepax { 9164049ce86SCharles Keepax unsigned int offs[] = { 9174049ce86SCharles Keepax ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 9184049ce86SCharles Keepax }; 9194049ce86SCharles Keepax 9204049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 92110337b07SRichard Fitzgerald 92210337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9234049ce86SCharles Keepax offs[0], offs[1], offs[2], offs[3]); 92410337b07SRichard Fitzgerald } 92510337b07SRichard Fitzgerald 926e1ea1879SRichard Fitzgerald static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) 927e1ea1879SRichard Fitzgerald { 9284049ce86SCharles Keepax unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 929e1ea1879SRichard Fitzgerald 9304049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 931e1ea1879SRichard Fitzgerald 932e1ea1879SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9334049ce86SCharles Keepax offs[0] & 0xFFFF, offs[0] >> 16, 9344049ce86SCharles Keepax offs[1] & 0xFFFF, offs[1] >> 16); 935e1ea1879SRichard Fitzgerald } 936e1ea1879SRichard Fitzgerald 937170b1e12SWen Shi static void wm_halo_show_fw_status(struct wm_adsp *dsp) 938170b1e12SWen Shi { 939170b1e12SWen Shi unsigned int offs[] = { 940170b1e12SWen Shi HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, 941170b1e12SWen Shi }; 942170b1e12SWen Shi 943170b1e12SWen Shi wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 944170b1e12SWen Shi 945170b1e12SWen Shi adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 946170b1e12SWen Shi offs[0], offs[1], offs[2], offs[3]); 947170b1e12SWen Shi } 948170b1e12SWen Shi 9499ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 9509ee78757SCharles Keepax { 9519ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 9529ee78757SCharles Keepax } 9539ee78757SCharles Keepax 954b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 955b396ebcaSRichard Fitzgerald { 956b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 957b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 958b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 959b396ebcaSRichard Fitzgerald 960b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 961b396ebcaSRichard Fitzgerald if (!mem) { 962b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 963b396ebcaSRichard Fitzgerald alg_region->type); 964b396ebcaSRichard Fitzgerald return -EINVAL; 965b396ebcaSRichard Fitzgerald } 966b396ebcaSRichard Fitzgerald 967170b1e12SWen Shi *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); 968b396ebcaSRichard Fitzgerald 969b396ebcaSRichard Fitzgerald return 0; 970b396ebcaSRichard Fitzgerald } 971b396ebcaSRichard Fitzgerald 9727585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 9736ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 9746ab2b7b4SDimitris Papastamos { 9759ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9769ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9779ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9786ab2b7b4SDimitris Papastamos 979a23ebba8SRichard Fitzgerald switch (ctl->type) { 980a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 981a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 982a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 983a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 984a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 985a23ebba8SRichard Fitzgerald uinfo->count = 1; 986a23ebba8SRichard Fitzgerald break; 987a23ebba8SRichard Fitzgerald default: 9886ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 9896ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 990a23ebba8SRichard Fitzgerald break; 991a23ebba8SRichard Fitzgerald } 992a23ebba8SRichard Fitzgerald 9936ab2b7b4SDimitris Papastamos return 0; 9946ab2b7b4SDimitris Papastamos } 9956ab2b7b4SDimitris Papastamos 996f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 997f4f0c4c6SRichard Fitzgerald unsigned int event_id) 998f4f0c4c6SRichard Fitzgerald { 999f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 1000f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 1001f4f0c4c6SRichard Fitzgerald unsigned int reg; 1002f4f0c4c6SRichard Fitzgerald int i, ret; 1003f4f0c4c6SRichard Fitzgerald 1004f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1005f4f0c4c6SRichard Fitzgerald if (ret) 1006f4f0c4c6SRichard Fitzgerald return ret; 1007f4f0c4c6SRichard Fitzgerald 1008f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 1009f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 1010f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 1011f4f0c4c6SRichard Fitzgerald 1012f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 1013f4f0c4c6SRichard Fitzgerald if (ret) { 1014f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 1015f4f0c4c6SRichard Fitzgerald return ret; 1016f4f0c4c6SRichard Fitzgerald } 1017f4f0c4c6SRichard Fitzgerald 1018f4f0c4c6SRichard Fitzgerald /* 1019f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 1020f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 1021f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 1022f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 1023f4f0c4c6SRichard Fitzgerald */ 1024f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 1025f4f0c4c6SRichard Fitzgerald switch (i) { 1026f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 1027f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 1028f4f0c4c6SRichard Fitzgerald i++; 1029f4f0c4c6SRichard Fitzgerald break; 1030f4f0c4c6SRichard Fitzgerald default: 1031f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 1032f4f0c4c6SRichard Fitzgerald i += 10; 1033f4f0c4c6SRichard Fitzgerald break; 1034f4f0c4c6SRichard Fitzgerald } 1035f4f0c4c6SRichard Fitzgerald 1036f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1037f4f0c4c6SRichard Fitzgerald if (ret) { 1038f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 1039f4f0c4c6SRichard Fitzgerald return ret; 1040f4f0c4c6SRichard Fitzgerald } 1041f4f0c4c6SRichard Fitzgerald 1042f4f0c4c6SRichard Fitzgerald if (val == 0) { 1043f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 1044f4f0c4c6SRichard Fitzgerald return 0; 1045f4f0c4c6SRichard Fitzgerald } 1046f4f0c4c6SRichard Fitzgerald } 1047f4f0c4c6SRichard Fitzgerald 1048f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 1049f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 1050f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 1051f4f0c4c6SRichard Fitzgerald ctl->offset); 1052f4f0c4c6SRichard Fitzgerald 1053f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 1054f4f0c4c6SRichard Fitzgerald } 1055f4f0c4c6SRichard Fitzgerald 1056c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 10576ab2b7b4SDimitris Papastamos const void *buf, size_t len) 10586ab2b7b4SDimitris Papastamos { 10593809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 10606ab2b7b4SDimitris Papastamos void *scratch; 10616ab2b7b4SDimitris Papastamos int ret; 10626ab2b7b4SDimitris Papastamos unsigned int reg; 10636ab2b7b4SDimitris Papastamos 1064b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1065b396ebcaSRichard Fitzgerald if (ret) 1066b396ebcaSRichard Fitzgerald return ret; 10676ab2b7b4SDimitris Papastamos 10684f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 10696ab2b7b4SDimitris Papastamos if (!scratch) 10706ab2b7b4SDimitris Papastamos return -ENOMEM; 10716ab2b7b4SDimitris Papastamos 10723809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 10734f8ea6d7SCharles Keepax len); 10746ab2b7b4SDimitris Papastamos if (ret) { 10753809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 10764f8ea6d7SCharles Keepax len, reg, ret); 10776ab2b7b4SDimitris Papastamos kfree(scratch); 10786ab2b7b4SDimitris Papastamos return ret; 10796ab2b7b4SDimitris Papastamos } 10804f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 10816ab2b7b4SDimitris Papastamos 10826ab2b7b4SDimitris Papastamos kfree(scratch); 10836ab2b7b4SDimitris Papastamos 10846ab2b7b4SDimitris Papastamos return 0; 10856ab2b7b4SDimitris Papastamos } 10866ab2b7b4SDimitris Papastamos 10877585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 10886ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 10896ab2b7b4SDimitris Papastamos { 10909ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10919ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10929ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10936ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1094168d10e7SCharles Keepax int ret = 0; 1095168d10e7SCharles Keepax 1096168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10976ab2b7b4SDimitris Papastamos 109867430a39SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 109967430a39SCharles Keepax ret = -EPERM; 110067430a39SCharles Keepax else 11016ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 11026ab2b7b4SDimitris Papastamos 11030c2e3f34SDimitris Papastamos ctl->set = 1; 1104cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1105168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 11066ab2b7b4SDimitris Papastamos 1107168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 1108168d10e7SCharles Keepax 1109168d10e7SCharles Keepax return ret; 11106ab2b7b4SDimitris Papastamos } 11116ab2b7b4SDimitris Papastamos 11129ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 11139ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 11149ee78757SCharles Keepax { 11159ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11169ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11179ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11189ee78757SCharles Keepax int ret = 0; 11199ee78757SCharles Keepax 11209ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 11219ee78757SCharles Keepax 11229ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 11239ee78757SCharles Keepax ret = -EFAULT; 11249ee78757SCharles Keepax } else { 11259ee78757SCharles Keepax ctl->set = 1; 1126cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 11279ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 112867430a39SCharles Keepax else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 112967430a39SCharles Keepax ret = -EPERM; 11309ee78757SCharles Keepax } 11319ee78757SCharles Keepax 11329ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 11339ee78757SCharles Keepax 11349ee78757SCharles Keepax return ret; 11359ee78757SCharles Keepax } 11369ee78757SCharles Keepax 1137a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 1138a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1139a23ebba8SRichard Fitzgerald { 1140a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 1141a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 1142a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 1143a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 1144a23ebba8SRichard Fitzgerald int ret; 1145a23ebba8SRichard Fitzgerald 1146a23ebba8SRichard Fitzgerald if (val == 0) 1147a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 1148a23ebba8SRichard Fitzgerald 1149a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 1150a23ebba8SRichard Fitzgerald 11517b4af793SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1152a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 1153a23ebba8SRichard Fitzgerald else 1154a23ebba8SRichard Fitzgerald ret = -EPERM; 1155a23ebba8SRichard Fitzgerald 1156a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 1157a23ebba8SRichard Fitzgerald 1158a23ebba8SRichard Fitzgerald return ret; 1159a23ebba8SRichard Fitzgerald } 1160a23ebba8SRichard Fitzgerald 1161c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 11626ab2b7b4SDimitris Papastamos void *buf, size_t len) 11636ab2b7b4SDimitris Papastamos { 11643809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 11656ab2b7b4SDimitris Papastamos void *scratch; 11666ab2b7b4SDimitris Papastamos int ret; 11676ab2b7b4SDimitris Papastamos unsigned int reg; 11686ab2b7b4SDimitris Papastamos 1169b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1170b396ebcaSRichard Fitzgerald if (ret) 1171b396ebcaSRichard Fitzgerald return ret; 11726ab2b7b4SDimitris Papastamos 11734f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 11746ab2b7b4SDimitris Papastamos if (!scratch) 11756ab2b7b4SDimitris Papastamos return -ENOMEM; 11766ab2b7b4SDimitris Papastamos 11774f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 11786ab2b7b4SDimitris Papastamos if (ret) { 11793809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 11805602a643SCharles Keepax len, reg, ret); 11816ab2b7b4SDimitris Papastamos kfree(scratch); 11826ab2b7b4SDimitris Papastamos return ret; 11836ab2b7b4SDimitris Papastamos } 11844f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 11856ab2b7b4SDimitris Papastamos 11864f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 11876ab2b7b4SDimitris Papastamos kfree(scratch); 11886ab2b7b4SDimitris Papastamos 11896ab2b7b4SDimitris Papastamos return 0; 11906ab2b7b4SDimitris Papastamos } 11916ab2b7b4SDimitris Papastamos 11927585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 11936ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 11946ab2b7b4SDimitris Papastamos { 11959ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11969ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11979ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11986ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1199168d10e7SCharles Keepax int ret = 0; 1200168d10e7SCharles Keepax 1201168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 12026ab2b7b4SDimitris Papastamos 120326c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1204cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1205168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 120626c22a19SCharles Keepax else 1207168d10e7SCharles Keepax ret = -EPERM; 1208168d10e7SCharles Keepax } else { 1209cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 1210bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1211bc1765d6SCharles Keepax 1212168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 121326c22a19SCharles Keepax } 121426c22a19SCharles Keepax 1215168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 121626c22a19SCharles Keepax 1217168d10e7SCharles Keepax return ret; 12186ab2b7b4SDimitris Papastamos } 12196ab2b7b4SDimitris Papastamos 12209ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 12219ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 12229ee78757SCharles Keepax { 12239ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 12249ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 12259ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12269ee78757SCharles Keepax int ret = 0; 12279ee78757SCharles Keepax 12289ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 12299ee78757SCharles Keepax 12309ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1231cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 12329ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 12339ee78757SCharles Keepax else 12349ee78757SCharles Keepax ret = -EPERM; 12359ee78757SCharles Keepax } else { 1236cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 12379ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 12389ee78757SCharles Keepax } 12399ee78757SCharles Keepax 12409ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 12419ee78757SCharles Keepax ret = -EFAULT; 12429ee78757SCharles Keepax 12439ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 12449ee78757SCharles Keepax 12459ee78757SCharles Keepax return ret; 12469ee78757SCharles Keepax } 12479ee78757SCharles Keepax 1248a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1249a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1250a23ebba8SRichard Fitzgerald { 1251a23ebba8SRichard Fitzgerald /* 1252a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1253a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1254a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1255a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1256a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1257a23ebba8SRichard Fitzgerald */ 1258a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1259a23ebba8SRichard Fitzgerald 1260a23ebba8SRichard Fitzgerald return 0; 1261a23ebba8SRichard Fitzgerald } 1262a23ebba8SRichard Fitzgerald 12636ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 12643809f001SCharles Keepax struct wm_adsp *dsp; 12656ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 12666ab2b7b4SDimitris Papastamos struct work_struct work; 12676ab2b7b4SDimitris Papastamos }; 12686ab2b7b4SDimitris Papastamos 12699ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 12709ee78757SCharles Keepax { 12719ee78757SCharles Keepax unsigned int out, rd, wr, vol; 12729ee78757SCharles Keepax 12739ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 12749ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 12759ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 12769ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12779ee78757SCharles Keepax 12789ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 12799ee78757SCharles Keepax } else { 12809ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 12819ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 12829ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12839ee78757SCharles Keepax 12849ee78757SCharles Keepax out = 0; 12859ee78757SCharles Keepax } 12869ee78757SCharles Keepax 12879ee78757SCharles Keepax if (in) { 12889ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 12899ee78757SCharles Keepax out |= rd; 12909ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 12919ee78757SCharles Keepax out |= wr; 12929ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 12939ee78757SCharles Keepax out |= vol; 12949ee78757SCharles Keepax } else { 12959ee78757SCharles Keepax out |= rd | wr | vol; 12969ee78757SCharles Keepax } 12979ee78757SCharles Keepax 12989ee78757SCharles Keepax return out; 12999ee78757SCharles Keepax } 13009ee78757SCharles Keepax 13013809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 13026ab2b7b4SDimitris Papastamos { 13036ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 13046ab2b7b4SDimitris Papastamos int ret; 13056ab2b7b4SDimitris Papastamos 130692bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 13076ab2b7b4SDimitris Papastamos return -EINVAL; 13086ab2b7b4SDimitris Papastamos 13096ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 13106ab2b7b4SDimitris Papastamos if (!kcontrol) 13116ab2b7b4SDimitris Papastamos return -ENOMEM; 13126ab2b7b4SDimitris Papastamos 13136ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 13146ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 13159ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 13169ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 13179ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1318a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1319a23ebba8SRichard Fitzgerald 1320a23ebba8SRichard Fitzgerald switch (ctl->type) { 1321a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1322a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1323a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1324a23ebba8SRichard Fitzgerald break; 1325a23ebba8SRichard Fitzgerald default: 1326d7789f5bSRichard Fitzgerald if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 13279ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 13289ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 13299ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1330d7789f5bSRichard Fitzgerald } else { 1331d7789f5bSRichard Fitzgerald kcontrol->get = wm_coeff_get; 1332d7789f5bSRichard Fitzgerald kcontrol->put = wm_coeff_put; 1333d7789f5bSRichard Fitzgerald } 1334a23ebba8SRichard Fitzgerald break; 1335a23ebba8SRichard Fitzgerald } 133626c22a19SCharles Keepax 13370fe1daa6SKuninori Morimoto ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); 13386ab2b7b4SDimitris Papastamos if (ret < 0) 13396ab2b7b4SDimitris Papastamos goto err_kcontrol; 13406ab2b7b4SDimitris Papastamos 13416ab2b7b4SDimitris Papastamos kfree(kcontrol); 13426ab2b7b4SDimitris Papastamos 13436ab2b7b4SDimitris Papastamos return 0; 13446ab2b7b4SDimitris Papastamos 13456ab2b7b4SDimitris Papastamos err_kcontrol: 13466ab2b7b4SDimitris Papastamos kfree(kcontrol); 13476ab2b7b4SDimitris Papastamos return ret; 13486ab2b7b4SDimitris Papastamos } 13496ab2b7b4SDimitris Papastamos 1350b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(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 || ctl->set) 1357b21acc1cSCharles Keepax continue; 135826c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 135926c22a19SCharles Keepax continue; 136026c22a19SCharles Keepax 136104ff40a9SRichard Fitzgerald /* 136204ff40a9SRichard Fitzgerald * For readable controls populate the cache from the DSP memory. 136304ff40a9SRichard Fitzgerald * For non-readable controls the cache was zero-filled when 136404ff40a9SRichard Fitzgerald * created so we don't need to do anything. 136504ff40a9SRichard Fitzgerald */ 136604ff40a9SRichard Fitzgerald if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 13677d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1368b21acc1cSCharles Keepax if (ret < 0) 1369b21acc1cSCharles Keepax return ret; 1370b21acc1cSCharles Keepax } 137104ff40a9SRichard Fitzgerald } 1372b21acc1cSCharles Keepax 1373b21acc1cSCharles Keepax return 0; 1374b21acc1cSCharles Keepax } 1375b21acc1cSCharles Keepax 1376b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1377b21acc1cSCharles Keepax { 1378b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1379b21acc1cSCharles Keepax int ret; 1380b21acc1cSCharles Keepax 1381b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1382b21acc1cSCharles Keepax if (!ctl->enabled) 1383b21acc1cSCharles Keepax continue; 138426c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 13857d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1386b21acc1cSCharles Keepax if (ret < 0) 1387b21acc1cSCharles Keepax return ret; 1388b21acc1cSCharles Keepax } 1389b21acc1cSCharles Keepax } 1390b21acc1cSCharles Keepax 1391b21acc1cSCharles Keepax return 0; 1392b21acc1cSCharles Keepax } 1393b21acc1cSCharles Keepax 1394f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1395f4f0c4c6SRichard Fitzgerald unsigned int event) 1396f4f0c4c6SRichard Fitzgerald { 1397f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1398f4f0c4c6SRichard Fitzgerald int ret; 1399f4f0c4c6SRichard Fitzgerald 1400f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1401f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1402f4f0c4c6SRichard Fitzgerald continue; 1403f4f0c4c6SRichard Fitzgerald 140487aa6374SCharles Keepax if (!ctl->enabled) 140587aa6374SCharles Keepax continue; 140687aa6374SCharles Keepax 1407f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1408f4f0c4c6SRichard Fitzgerald if (ret) 1409f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1410f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1411f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1412f4f0c4c6SRichard Fitzgerald } 1413f4f0c4c6SRichard Fitzgerald } 1414f4f0c4c6SRichard Fitzgerald 1415b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1416b21acc1cSCharles Keepax { 1417b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1418b21acc1cSCharles Keepax struct wmfw_ctl_work, 1419b21acc1cSCharles Keepax work); 1420b21acc1cSCharles Keepax 1421b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1422b21acc1cSCharles Keepax kfree(ctl_work); 1423b21acc1cSCharles Keepax } 1424b21acc1cSCharles Keepax 142566225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 142666225e98SRichard Fitzgerald { 142766225e98SRichard Fitzgerald kfree(ctl->cache); 142866225e98SRichard Fitzgerald kfree(ctl->name); 142966225e98SRichard Fitzgerald kfree(ctl); 143066225e98SRichard Fitzgerald } 143166225e98SRichard Fitzgerald 1432b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1433b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 14342323736dSCharles Keepax unsigned int offset, unsigned int len, 143526c22a19SCharles Keepax const char *subname, unsigned int subname_len, 14368eb084d0SStuart Henderson unsigned int flags, unsigned int type) 1437b21acc1cSCharles Keepax { 1438b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1439b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1440b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 14419ce5e6e6SRichard Fitzgerald const char *region_name; 1442b21acc1cSCharles Keepax int ret; 1443b21acc1cSCharles Keepax 14449ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 14459ce5e6e6SRichard Fitzgerald if (!region_name) { 14462323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1447b21acc1cSCharles Keepax return -EINVAL; 1448b21acc1cSCharles Keepax } 1449b21acc1cSCharles Keepax 1450cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1451cb5b57a9SCharles Keepax case 0: 1452cb5b57a9SCharles Keepax case 1: 1453605391d0SRichard Fitzgerald snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", 1454605391d0SRichard Fitzgerald dsp->name, region_name, alg_region->alg); 1455170b1e12SWen Shi subname = NULL; /* don't append subname */ 1456cb5b57a9SCharles Keepax break; 1457170b1e12SWen Shi case 2: 1458cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1459605391d0SRichard Fitzgerald "%s%c %.12s %x", dsp->name, *region_name, 1460cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1461170b1e12SWen Shi break; 1462170b1e12SWen Shi default: 1463170b1e12SWen Shi ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1464170b1e12SWen Shi "%s %.12s %x", dsp->name, 1465170b1e12SWen Shi wm_adsp_fw_text[dsp->fw], alg_region->alg); 1466170b1e12SWen Shi break; 1467170b1e12SWen Shi } 1468cb5b57a9SCharles Keepax 1469cb5b57a9SCharles Keepax if (subname) { 1470cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1471cb5b57a9SCharles Keepax int skip = 0; 1472cb5b57a9SCharles Keepax 1473b7ede5afSCharles Keepax if (dsp->component->name_prefix) 1474b7ede5afSCharles Keepax avail -= strlen(dsp->component->name_prefix) + 1; 1475b7ede5afSCharles Keepax 1476170b1e12SWen Shi /* Truncate the subname from the start if it is too long */ 1477cb5b57a9SCharles Keepax if (subname_len > avail) 1478cb5b57a9SCharles Keepax skip = subname_len - avail; 1479cb5b57a9SCharles Keepax 1480170b1e12SWen Shi snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, 1481170b1e12SWen Shi " %.*s", subname_len - skip, subname + skip); 1482cb5b57a9SCharles Keepax } 1483b21acc1cSCharles Keepax 14847585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1485b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1486b21acc1cSCharles Keepax if (!ctl->enabled) 1487b21acc1cSCharles Keepax ctl->enabled = 1; 1488b21acc1cSCharles Keepax return 0; 1489b21acc1cSCharles Keepax } 1490b21acc1cSCharles Keepax } 1491b21acc1cSCharles Keepax 1492b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1493b21acc1cSCharles Keepax if (!ctl) 1494b21acc1cSCharles Keepax return -ENOMEM; 14952323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1496b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1497b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1498b21acc1cSCharles Keepax if (!ctl->name) { 1499b21acc1cSCharles Keepax ret = -ENOMEM; 1500b21acc1cSCharles Keepax goto err_ctl; 1501b21acc1cSCharles Keepax } 1502b21acc1cSCharles Keepax ctl->enabled = 1; 1503b21acc1cSCharles Keepax ctl->set = 0; 1504b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1505b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1506b21acc1cSCharles Keepax ctl->dsp = dsp; 1507b21acc1cSCharles Keepax 150826c22a19SCharles Keepax ctl->flags = flags; 15098eb084d0SStuart Henderson ctl->type = type; 15102323736dSCharles Keepax ctl->offset = offset; 1511b21acc1cSCharles Keepax ctl->len = len; 1512b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1513b21acc1cSCharles Keepax if (!ctl->cache) { 1514b21acc1cSCharles Keepax ret = -ENOMEM; 1515b21acc1cSCharles Keepax goto err_ctl_name; 1516b21acc1cSCharles Keepax } 1517b21acc1cSCharles Keepax 15182323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 15192323736dSCharles Keepax 15208eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 15218eb084d0SStuart Henderson return 0; 15228eb084d0SStuart Henderson 1523b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1524b21acc1cSCharles Keepax if (!ctl_work) { 1525b21acc1cSCharles Keepax ret = -ENOMEM; 1526b21acc1cSCharles Keepax goto err_ctl_cache; 1527b21acc1cSCharles Keepax } 1528b21acc1cSCharles Keepax 1529b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1530b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1531b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1532b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1533b21acc1cSCharles Keepax 1534b21acc1cSCharles Keepax return 0; 1535b21acc1cSCharles Keepax 1536b21acc1cSCharles Keepax err_ctl_cache: 1537b21acc1cSCharles Keepax kfree(ctl->cache); 1538b21acc1cSCharles Keepax err_ctl_name: 1539b21acc1cSCharles Keepax kfree(ctl->name); 1540b21acc1cSCharles Keepax err_ctl: 1541b21acc1cSCharles Keepax kfree(ctl); 1542b21acc1cSCharles Keepax 1543b21acc1cSCharles Keepax return ret; 1544b21acc1cSCharles Keepax } 1545b21acc1cSCharles Keepax 15462323736dSCharles Keepax struct wm_coeff_parsed_alg { 15472323736dSCharles Keepax int id; 15482323736dSCharles Keepax const u8 *name; 15492323736dSCharles Keepax int name_len; 15502323736dSCharles Keepax int ncoeff; 15512323736dSCharles Keepax }; 15522323736dSCharles Keepax 15532323736dSCharles Keepax struct wm_coeff_parsed_coeff { 15542323736dSCharles Keepax int offset; 15552323736dSCharles Keepax int mem_type; 15562323736dSCharles Keepax const u8 *name; 15572323736dSCharles Keepax int name_len; 15582323736dSCharles Keepax int ctl_type; 15592323736dSCharles Keepax int flags; 15602323736dSCharles Keepax int len; 15612323736dSCharles Keepax }; 15622323736dSCharles Keepax 1563cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1564cb5b57a9SCharles Keepax { 1565cb5b57a9SCharles Keepax int length; 1566cb5b57a9SCharles Keepax 1567cb5b57a9SCharles Keepax switch (bytes) { 1568cb5b57a9SCharles Keepax case 1: 1569cb5b57a9SCharles Keepax length = **pos; 1570cb5b57a9SCharles Keepax break; 1571cb5b57a9SCharles Keepax case 2: 15728299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1573cb5b57a9SCharles Keepax break; 1574cb5b57a9SCharles Keepax default: 1575cb5b57a9SCharles Keepax return 0; 1576cb5b57a9SCharles Keepax } 1577cb5b57a9SCharles Keepax 1578cb5b57a9SCharles Keepax if (str) 1579cb5b57a9SCharles Keepax *str = *pos + bytes; 1580cb5b57a9SCharles Keepax 1581cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1582cb5b57a9SCharles Keepax 1583cb5b57a9SCharles Keepax return length; 1584cb5b57a9SCharles Keepax } 1585cb5b57a9SCharles Keepax 1586cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1587cb5b57a9SCharles Keepax { 1588cb5b57a9SCharles Keepax int val = 0; 1589cb5b57a9SCharles Keepax 1590cb5b57a9SCharles Keepax switch (bytes) { 1591cb5b57a9SCharles Keepax case 2: 15928299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1593cb5b57a9SCharles Keepax break; 1594cb5b57a9SCharles Keepax case 4: 15958299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1596cb5b57a9SCharles Keepax break; 1597cb5b57a9SCharles Keepax default: 1598cb5b57a9SCharles Keepax break; 1599cb5b57a9SCharles Keepax } 1600cb5b57a9SCharles Keepax 1601cb5b57a9SCharles Keepax *pos += bytes; 1602cb5b57a9SCharles Keepax 1603cb5b57a9SCharles Keepax return val; 1604cb5b57a9SCharles Keepax } 1605cb5b57a9SCharles Keepax 16062323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 16072323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 16082323736dSCharles Keepax { 16092323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 16102323736dSCharles Keepax 1611cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1612cb5b57a9SCharles Keepax case 0: 1613cb5b57a9SCharles Keepax case 1: 16142323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 16152323736dSCharles Keepax *data = raw->data; 16162323736dSCharles Keepax 16172323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 16182323736dSCharles Keepax blk->name = raw->name; 16192323736dSCharles Keepax blk->name_len = strlen(raw->name); 16202323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1621cb5b57a9SCharles Keepax break; 1622cb5b57a9SCharles Keepax default: 1623cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1624cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1625cb5b57a9SCharles Keepax &blk->name); 1626cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1627cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1628cb5b57a9SCharles Keepax break; 1629cb5b57a9SCharles Keepax } 16302323736dSCharles Keepax 16312323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 16322323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 16332323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 16342323736dSCharles Keepax } 16352323736dSCharles Keepax 16362323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 16372323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 16382323736dSCharles Keepax { 16392323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1640cb5b57a9SCharles Keepax const u8 *tmp; 1641cb5b57a9SCharles Keepax int length; 16422323736dSCharles Keepax 1643cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1644cb5b57a9SCharles Keepax case 0: 1645cb5b57a9SCharles Keepax case 1: 16462323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 16472323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 16482323736dSCharles Keepax 16492323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 16502323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 16512323736dSCharles Keepax blk->name = raw->name; 16522323736dSCharles Keepax blk->name_len = strlen(raw->name); 16532323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 16542323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 16552323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1656cb5b57a9SCharles Keepax break; 1657cb5b57a9SCharles Keepax default: 1658cb5b57a9SCharles Keepax tmp = *data; 1659cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1660cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1661cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1662cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1663cb5b57a9SCharles Keepax &blk->name); 1664cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1665cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1666cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1667cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1668cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1669cb5b57a9SCharles Keepax 1670cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1671cb5b57a9SCharles Keepax break; 1672cb5b57a9SCharles Keepax } 16732323736dSCharles Keepax 16742323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 16752323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 16762323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 16772323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 16782323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 16792323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 16802323736dSCharles Keepax } 16812323736dSCharles Keepax 1682f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1683f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1684f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1685f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1686f4f0c4c6SRichard Fitzgerald { 1687f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1688f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1689f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1690f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1691f4f0c4c6SRichard Fitzgerald return -EINVAL; 1692f4f0c4c6SRichard Fitzgerald } 1693f4f0c4c6SRichard Fitzgerald 1694f4f0c4c6SRichard Fitzgerald return 0; 1695f4f0c4c6SRichard Fitzgerald } 1696f4f0c4c6SRichard Fitzgerald 16972323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 16982323736dSCharles Keepax const struct wmfw_region *region) 16992323736dSCharles Keepax { 17002323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 17012323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 17022323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 17032323736dSCharles Keepax const u8 *data = region->data; 17042323736dSCharles Keepax int i, ret; 17052323736dSCharles Keepax 17062323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 17072323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 17082323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 17092323736dSCharles Keepax 17102323736dSCharles Keepax switch (coeff_blk.ctl_type) { 17112323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 17122323736dSCharles Keepax break; 1713a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1714a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1715a23ebba8SRichard Fitzgerald continue; /* ignore */ 1716a23ebba8SRichard Fitzgerald 1717a23ebba8SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1718a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1719a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1720a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1721a23ebba8SRichard Fitzgerald 0); 1722a23ebba8SRichard Fitzgerald if (ret) 1723a23ebba8SRichard Fitzgerald return -EINVAL; 1724a23ebba8SRichard Fitzgerald break; 1725f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1726f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1727f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1728f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1729f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1730f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1731f4f0c4c6SRichard Fitzgerald 0); 1732f4f0c4c6SRichard Fitzgerald if (ret) 1733f4f0c4c6SRichard Fitzgerald return -EINVAL; 1734f4f0c4c6SRichard Fitzgerald break; 1735d52ed4b0SRichard Fitzgerald case WMFW_CTL_TYPE_HOST_BUFFER: 1736d52ed4b0SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1737d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1738d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1739d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1740d52ed4b0SRichard Fitzgerald 0); 1741d52ed4b0SRichard Fitzgerald if (ret) 1742d52ed4b0SRichard Fitzgerald return -EINVAL; 1743d52ed4b0SRichard Fitzgerald break; 17442323736dSCharles Keepax default: 17452323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 17462323736dSCharles Keepax coeff_blk.ctl_type); 17472323736dSCharles Keepax return -EINVAL; 17482323736dSCharles Keepax } 17492323736dSCharles Keepax 17502323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 17512323736dSCharles Keepax alg_region.alg = alg_blk.id; 17522323736dSCharles Keepax 17532323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 17542323736dSCharles Keepax coeff_blk.offset, 17552323736dSCharles Keepax coeff_blk.len, 17562323736dSCharles Keepax coeff_blk.name, 175726c22a19SCharles Keepax coeff_blk.name_len, 17588eb084d0SStuart Henderson coeff_blk.flags, 17598eb084d0SStuart Henderson coeff_blk.ctl_type); 17602323736dSCharles Keepax if (ret < 0) 17612323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 17622323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 17632323736dSCharles Keepax } 17642323736dSCharles Keepax 17652323736dSCharles Keepax return 0; 17662323736dSCharles Keepax } 17672323736dSCharles Keepax 17684e08d50dSCharles Keepax static unsigned int wm_adsp1_parse_sizes(struct wm_adsp *dsp, 17694e08d50dSCharles Keepax const char * const file, 17704e08d50dSCharles Keepax unsigned int pos, 17714e08d50dSCharles Keepax const struct firmware *firmware) 17724e08d50dSCharles Keepax { 17734e08d50dSCharles Keepax const struct wmfw_adsp1_sizes *adsp1_sizes; 17744e08d50dSCharles Keepax 17754e08d50dSCharles Keepax adsp1_sizes = (void *)&firmware->data[pos]; 17764e08d50dSCharles Keepax 17774e08d50dSCharles Keepax adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 17784e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 17794e08d50dSCharles Keepax le32_to_cpu(adsp1_sizes->zm)); 17804e08d50dSCharles Keepax 17814e08d50dSCharles Keepax return pos + sizeof(*adsp1_sizes); 17824e08d50dSCharles Keepax } 17834e08d50dSCharles Keepax 17844e08d50dSCharles Keepax static unsigned int wm_adsp2_parse_sizes(struct wm_adsp *dsp, 17854e08d50dSCharles Keepax const char * const file, 17864e08d50dSCharles Keepax unsigned int pos, 17874e08d50dSCharles Keepax const struct firmware *firmware) 17884e08d50dSCharles Keepax { 17894e08d50dSCharles Keepax const struct wmfw_adsp2_sizes *adsp2_sizes; 17904e08d50dSCharles Keepax 17914e08d50dSCharles Keepax adsp2_sizes = (void *)&firmware->data[pos]; 17924e08d50dSCharles Keepax 17934e08d50dSCharles Keepax adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 17944e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 17954e08d50dSCharles Keepax le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 17964e08d50dSCharles Keepax 17974e08d50dSCharles Keepax return pos + sizeof(*adsp2_sizes); 17984e08d50dSCharles Keepax } 17994e08d50dSCharles Keepax 18004e08d50dSCharles Keepax static bool wm_adsp_validate_version(struct wm_adsp *dsp, unsigned int version) 18014e08d50dSCharles Keepax { 18024e08d50dSCharles Keepax switch (version) { 18034e08d50dSCharles Keepax case 0: 18044e08d50dSCharles Keepax adsp_warn(dsp, "Deprecated file format %d\n", version); 18054e08d50dSCharles Keepax return true; 18064e08d50dSCharles Keepax case 1: 18074e08d50dSCharles Keepax case 2: 18084e08d50dSCharles Keepax return true; 18094e08d50dSCharles Keepax default: 18104e08d50dSCharles Keepax return false; 18114e08d50dSCharles Keepax } 18124e08d50dSCharles Keepax } 18134e08d50dSCharles Keepax 1814170b1e12SWen Shi static bool wm_halo_validate_version(struct wm_adsp *dsp, unsigned int version) 1815170b1e12SWen Shi { 1816170b1e12SWen Shi switch (version) { 1817170b1e12SWen Shi case 3: 1818170b1e12SWen Shi return true; 1819170b1e12SWen Shi default: 1820170b1e12SWen Shi return false; 1821170b1e12SWen Shi } 1822170b1e12SWen Shi } 1823170b1e12SWen Shi 18242159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 18252159ad93SMark Brown { 1826cf17c83cSMark Brown LIST_HEAD(buf_list); 18272159ad93SMark Brown const struct firmware *firmware; 18282159ad93SMark Brown struct regmap *regmap = dsp->regmap; 18292159ad93SMark Brown unsigned int pos = 0; 18302159ad93SMark Brown const struct wmfw_header *header; 18312159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 18322159ad93SMark Brown const struct wmfw_footer *footer; 18332159ad93SMark Brown const struct wmfw_region *region; 18342159ad93SMark Brown const struct wm_adsp_region *mem; 18352159ad93SMark Brown const char *region_name; 18361cab2a84SRichard Fitzgerald char *file, *text = NULL; 1837cf17c83cSMark Brown struct wm_adsp_buf *buf; 18382159ad93SMark Brown unsigned int reg; 18392159ad93SMark Brown int regions = 0; 18404e08d50dSCharles Keepax int ret, offset, type; 18412159ad93SMark Brown 18422159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 18432159ad93SMark Brown if (file == NULL) 18442159ad93SMark Brown return -ENOMEM; 18452159ad93SMark Brown 1846605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, 18471023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 18482159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 18492159ad93SMark Brown 18502159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 18512159ad93SMark Brown if (ret != 0) { 18522159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 18532159ad93SMark Brown goto out; 18542159ad93SMark Brown } 18552159ad93SMark Brown ret = -EINVAL; 18562159ad93SMark Brown 18572159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 18582159ad93SMark Brown if (pos >= firmware->size) { 18592159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 18602159ad93SMark Brown file, firmware->size); 18612159ad93SMark Brown goto out_fw; 18622159ad93SMark Brown } 18632159ad93SMark Brown 18642159ad93SMark Brown header = (void *)&firmware->data[0]; 18652159ad93SMark Brown 18662159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 18672159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 18682159ad93SMark Brown goto out_fw; 18692159ad93SMark Brown } 18702159ad93SMark Brown 18714e08d50dSCharles Keepax if (!dsp->ops->validate_version(dsp, header->ver)) { 18722159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 18732159ad93SMark Brown file, header->ver); 18742159ad93SMark Brown goto out_fw; 18752159ad93SMark Brown } 18762323736dSCharles Keepax 18773626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 18782323736dSCharles Keepax dsp->fw_ver = header->ver; 18792159ad93SMark Brown 18802159ad93SMark Brown if (header->core != dsp->type) { 18812159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 18822159ad93SMark Brown file, header->core, dsp->type); 18832159ad93SMark Brown goto out_fw; 18842159ad93SMark Brown } 18852159ad93SMark Brown 18864e08d50dSCharles Keepax pos = sizeof(*header); 18874e08d50dSCharles Keepax pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 18882159ad93SMark Brown 18894e08d50dSCharles Keepax footer = (void *)&firmware->data[pos]; 18904e08d50dSCharles Keepax pos += sizeof(*footer); 18912159ad93SMark Brown 18924e08d50dSCharles Keepax if (le32_to_cpu(header->len) != pos) { 18932159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 18942159ad93SMark Brown file, le32_to_cpu(header->len)); 18952159ad93SMark Brown goto out_fw; 18962159ad93SMark Brown } 18972159ad93SMark Brown 18982159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 18992159ad93SMark Brown le64_to_cpu(footer->timestamp)); 19002159ad93SMark Brown 19012159ad93SMark Brown while (pos < firmware->size && 190250dd2ea8SBen Hutchings sizeof(*region) < firmware->size - pos) { 19032159ad93SMark Brown region = (void *)&(firmware->data[pos]); 19042159ad93SMark Brown region_name = "Unknown"; 19052159ad93SMark Brown reg = 0; 19062159ad93SMark Brown text = NULL; 19072159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 19082159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 19092159ad93SMark Brown 19102159ad93SMark Brown switch (type) { 19112159ad93SMark Brown case WMFW_NAME_TEXT: 19122159ad93SMark Brown region_name = "Firmware name"; 19132159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 19142159ad93SMark Brown GFP_KERNEL); 19152159ad93SMark Brown break; 19162323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 19172323736dSCharles Keepax region_name = "Algorithm"; 19182323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 19192323736dSCharles Keepax if (ret != 0) 19202323736dSCharles Keepax goto out_fw; 19212323736dSCharles Keepax break; 19222159ad93SMark Brown case WMFW_INFO_TEXT: 19232159ad93SMark Brown region_name = "Information"; 19242159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 19252159ad93SMark Brown GFP_KERNEL); 19262159ad93SMark Brown break; 19272159ad93SMark Brown case WMFW_ABSOLUTE: 19282159ad93SMark Brown region_name = "Absolute"; 19292159ad93SMark Brown reg = offset; 19302159ad93SMark Brown break; 19312159ad93SMark Brown case WMFW_ADSP1_PM: 19322159ad93SMark Brown case WMFW_ADSP1_DM: 19332159ad93SMark Brown case WMFW_ADSP2_XM: 19342159ad93SMark Brown case WMFW_ADSP2_YM: 19352159ad93SMark Brown case WMFW_ADSP1_ZM: 1936170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 1937170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 1938170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 1939170b1e12SWen Shi mem = wm_adsp_find_region(dsp, type); 1940170b1e12SWen Shi if (!mem) { 1941170b1e12SWen Shi adsp_err(dsp, "No region of type: %x\n", type); 1942170b1e12SWen Shi goto out_fw; 1943170b1e12SWen Shi } 1944170b1e12SWen Shi 19459ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 1946170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, offset); 19472159ad93SMark Brown break; 19482159ad93SMark Brown default: 19492159ad93SMark Brown adsp_warn(dsp, 19502159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 19512159ad93SMark Brown file, regions, type, pos, pos); 19522159ad93SMark Brown break; 19532159ad93SMark Brown } 19542159ad93SMark Brown 19552159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 19562159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 19572159ad93SMark Brown region_name); 19582159ad93SMark Brown 195950dd2ea8SBen Hutchings if (le32_to_cpu(region->len) > 196050dd2ea8SBen Hutchings firmware->size - pos - sizeof(*region)) { 19611cab2a84SRichard Fitzgerald adsp_err(dsp, 19621cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 19631cab2a84SRichard Fitzgerald file, regions, region_name, 19641cab2a84SRichard Fitzgerald le32_to_cpu(region->len), firmware->size); 19651cab2a84SRichard Fitzgerald ret = -EINVAL; 19661cab2a84SRichard Fitzgerald goto out_fw; 19671cab2a84SRichard Fitzgerald } 19681cab2a84SRichard Fitzgerald 19692159ad93SMark Brown if (text) { 19702159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 19712159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 19722159ad93SMark Brown kfree(text); 19731cab2a84SRichard Fitzgerald text = NULL; 19742159ad93SMark Brown } 19752159ad93SMark Brown 19762159ad93SMark Brown if (reg) { 1977cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1978cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1979cf17c83cSMark Brown &buf_list); 1980a76fefabSMark Brown if (!buf) { 1981a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 19827328823dSDimitris Papastamos ret = -ENOMEM; 19837328823dSDimitris Papastamos goto out_fw; 1984a76fefabSMark Brown } 1985a76fefabSMark Brown 1986cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1987cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 19882159ad93SMark Brown if (ret != 0) { 19892159ad93SMark Brown adsp_err(dsp, 1990cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 19912159ad93SMark Brown file, regions, 1992cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 19932159ad93SMark Brown region_name, ret); 19942159ad93SMark Brown goto out_fw; 19952159ad93SMark Brown } 19962159ad93SMark Brown } 19972159ad93SMark Brown 19982159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 19992159ad93SMark Brown regions++; 20002159ad93SMark Brown } 20012159ad93SMark Brown 2002cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2003cf17c83cSMark Brown if (ret != 0) { 2004cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2005cf17c83cSMark Brown goto out_fw; 2006cf17c83cSMark Brown } 2007cf17c83cSMark Brown 20082159ad93SMark Brown if (pos > firmware->size) 20092159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 20102159ad93SMark Brown file, regions, pos - firmware->size); 20112159ad93SMark Brown 2012f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 2013f9f55e31SRichard Fitzgerald 20142159ad93SMark Brown out_fw: 2015cf17c83cSMark Brown regmap_async_complete(regmap); 2016cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 20172159ad93SMark Brown release_firmware(firmware); 20181cab2a84SRichard Fitzgerald kfree(text); 20192159ad93SMark Brown out: 20202159ad93SMark Brown kfree(file); 20212159ad93SMark Brown 20222159ad93SMark Brown return ret; 20232159ad93SMark Brown } 20242159ad93SMark Brown 20252323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 20262323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 20272323736dSCharles Keepax { 20282323736dSCharles Keepax struct wm_coeff_ctl *ctl; 20292323736dSCharles Keepax 20302323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 20312323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 20322323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 20332323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 20342323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 20352323736dSCharles Keepax } 20362323736dSCharles Keepax } 20372323736dSCharles Keepax } 20382323736dSCharles Keepax 20393809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 20407f7cca08SCharles Keepax const struct wm_adsp_region *mem, 2041b618a185SCharles Keepax unsigned int pos, unsigned int len) 2042db40517cSMark Brown { 2043b618a185SCharles Keepax void *alg; 20447f7cca08SCharles Keepax unsigned int reg; 2045b618a185SCharles Keepax int ret; 2046db40517cSMark Brown __be32 val; 2047db40517cSMark Brown 20483809f001SCharles Keepax if (n_algs == 0) { 2049b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 2050b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2051db40517cSMark Brown } 2052db40517cSMark Brown 20533809f001SCharles Keepax if (n_algs > 1024) { 20543809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 2055b618a185SCharles Keepax return ERR_PTR(-EINVAL); 2056b618a185SCharles Keepax } 2057b618a185SCharles Keepax 2058b618a185SCharles Keepax /* Read the terminator first to validate the length */ 2059170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos + len); 20607f7cca08SCharles Keepax 20617f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 2062b618a185SCharles Keepax if (ret != 0) { 2063b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 2064b618a185SCharles Keepax ret); 2065b618a185SCharles Keepax return ERR_PTR(ret); 2066b618a185SCharles Keepax } 2067b618a185SCharles Keepax 2068b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 2069503ada8aSRichard Fitzgerald adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 20707f7cca08SCharles Keepax reg, be32_to_cpu(val)); 20717f7cca08SCharles Keepax 20727f7cca08SCharles Keepax /* Convert length from DSP words to bytes */ 20737f7cca08SCharles Keepax len *= sizeof(u32); 2074b618a185SCharles Keepax 2075517ee74eSCharles Keepax alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 2076b618a185SCharles Keepax if (!alg) 2077b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 2078b618a185SCharles Keepax 2079170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, pos); 20807f7cca08SCharles Keepax 20817f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, alg, len); 2082b618a185SCharles Keepax if (ret != 0) { 20837d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 2084b618a185SCharles Keepax kfree(alg); 2085b618a185SCharles Keepax return ERR_PTR(ret); 2086b618a185SCharles Keepax } 2087b618a185SCharles Keepax 2088b618a185SCharles Keepax return alg; 2089b618a185SCharles Keepax } 2090b618a185SCharles Keepax 209114197095SCharles Keepax static struct wm_adsp_alg_region * 209214197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 209314197095SCharles Keepax { 209414197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 209514197095SCharles Keepax 209614197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 209714197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 209814197095SCharles Keepax return alg_region; 209914197095SCharles Keepax } 210014197095SCharles Keepax 210114197095SCharles Keepax return NULL; 210214197095SCharles Keepax } 210314197095SCharles Keepax 2104d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 2105d9d20e17SCharles Keepax int type, __be32 id, 2106d9d20e17SCharles Keepax __be32 base) 2107d9d20e17SCharles Keepax { 2108d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 2109d9d20e17SCharles Keepax 2110d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 2111d9d20e17SCharles Keepax if (!alg_region) 2112d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 2113d9d20e17SCharles Keepax 2114d9d20e17SCharles Keepax alg_region->type = type; 2115d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 2116d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 2117d9d20e17SCharles Keepax 2118d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 2119d9d20e17SCharles Keepax 21202323736dSCharles Keepax if (dsp->fw_ver > 0) 21212323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 21222323736dSCharles Keepax 2123d9d20e17SCharles Keepax return alg_region; 2124d9d20e17SCharles Keepax } 2125d9d20e17SCharles Keepax 212656574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 212756574d54SRichard Fitzgerald { 212856574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 212956574d54SRichard Fitzgerald 213056574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 213156574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 213256574d54SRichard Fitzgerald struct wm_adsp_alg_region, 213356574d54SRichard Fitzgerald list); 213456574d54SRichard Fitzgerald list_del(&alg_region->list); 213556574d54SRichard Fitzgerald kfree(alg_region); 213656574d54SRichard Fitzgerald } 213756574d54SRichard Fitzgerald } 213856574d54SRichard Fitzgerald 2139a5dcb24dSCharles Keepax static void wmfw_parse_id_header(struct wm_adsp *dsp, 2140a5dcb24dSCharles Keepax struct wmfw_id_hdr *fw, int nalgs) 2141a5dcb24dSCharles Keepax { 2142a5dcb24dSCharles Keepax dsp->fw_id = be32_to_cpu(fw->id); 2143a5dcb24dSCharles Keepax dsp->fw_id_version = be32_to_cpu(fw->ver); 2144a5dcb24dSCharles Keepax 2145cd537873SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 2146a5dcb24dSCharles Keepax dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 2147a5dcb24dSCharles Keepax (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2148a5dcb24dSCharles Keepax nalgs); 2149a5dcb24dSCharles Keepax } 2150a5dcb24dSCharles Keepax 2151170b1e12SWen Shi static void wmfw_v3_parse_id_header(struct wm_adsp *dsp, 2152170b1e12SWen Shi struct wmfw_v3_id_hdr *fw, int nalgs) 2153170b1e12SWen Shi { 2154170b1e12SWen Shi dsp->fw_id = be32_to_cpu(fw->id); 2155170b1e12SWen Shi dsp->fw_id_version = be32_to_cpu(fw->ver); 2156170b1e12SWen Shi dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 2157170b1e12SWen Shi 2158cd537873SCharles Keepax adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 2159170b1e12SWen Shi dsp->fw_id, dsp->fw_vendor_id, 2160170b1e12SWen Shi (dsp->fw_id_version & 0xff0000) >> 16, 2161170b1e12SWen Shi (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 2162170b1e12SWen Shi nalgs); 2163170b1e12SWen Shi } 2164170b1e12SWen Shi 2165170b1e12SWen Shi static int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions, 2166170b1e12SWen Shi int *type, __be32 *base) 2167170b1e12SWen Shi { 2168170b1e12SWen Shi struct wm_adsp_alg_region *alg_region; 2169170b1e12SWen Shi int i; 2170170b1e12SWen Shi 2171170b1e12SWen Shi for (i = 0; i < nregions; i++) { 2172170b1e12SWen Shi alg_region = wm_adsp_create_region(dsp, type[i], id, base[i]); 2173170b1e12SWen Shi if (IS_ERR(alg_region)) 2174170b1e12SWen Shi return PTR_ERR(alg_region); 2175170b1e12SWen Shi } 2176170b1e12SWen Shi 2177170b1e12SWen Shi return 0; 2178170b1e12SWen Shi } 2179170b1e12SWen Shi 2180b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 2181b618a185SCharles Keepax { 2182b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 2183b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 21843809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2185b618a185SCharles Keepax const struct wm_adsp_region *mem; 2186b618a185SCharles Keepax unsigned int pos, len; 21873809f001SCharles Keepax size_t n_algs; 2188b618a185SCharles Keepax int i, ret; 2189b618a185SCharles Keepax 2190b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 21916c452bdaSTakashi Iwai if (WARN_ON(!mem)) 2192db40517cSMark Brown return -EINVAL; 2193db40517cSMark Brown 2194b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 2195db40517cSMark Brown sizeof(adsp1_id)); 2196db40517cSMark Brown if (ret != 0) { 2197db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 2198db40517cSMark Brown ret); 2199db40517cSMark Brown return ret; 2200db40517cSMark Brown } 2201db40517cSMark Brown 22023809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 2203a5dcb24dSCharles Keepax 2204a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs); 2205db40517cSMark Brown 2206d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2207d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 2208d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2209d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2210ac50009fSMark Brown 2211d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2212d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 2213d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2214d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2215ac50009fSMark Brown 22167f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 22177f7cca08SCharles Keepax pos = sizeof(adsp1_id) / sizeof(u32); 22187f7cca08SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 2219db40517cSMark Brown 22207f7cca08SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2221b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 2222b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 2223db40517cSMark Brown 22243809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2225471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 2226db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 2227db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 2228db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 2229471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 2230471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 2231471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 2232471f4885SMark Brown 2233d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2234d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2235d9d20e17SCharles Keepax adsp1_alg[i].dm); 2236d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2237d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2238d6d52179SJS Park goto out; 2239d6d52179SJS Park } 22402323736dSCharles Keepax if (dsp->fw_ver == 0) { 22413809f001SCharles Keepax if (i + 1 < n_algs) { 22426958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 22436958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 22446958eb2aSCharles Keepax len *= 4; 22452323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 22468eb084d0SStuart Henderson len, NULL, 0, 0, 22478eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 22486ab2b7b4SDimitris Papastamos } else { 22496ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 22506ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 22516ab2b7b4SDimitris Papastamos } 22522323736dSCharles Keepax } 2253471f4885SMark Brown 2254d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2255d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2256d9d20e17SCharles Keepax adsp1_alg[i].zm); 2257d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2258d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2259d6d52179SJS Park goto out; 2260d6d52179SJS Park } 22612323736dSCharles Keepax if (dsp->fw_ver == 0) { 22623809f001SCharles Keepax if (i + 1 < n_algs) { 22636958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 22646958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 22656958eb2aSCharles Keepax len *= 4; 22662323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 22678eb084d0SStuart Henderson len, NULL, 0, 0, 22688eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 22696ab2b7b4SDimitris Papastamos } else { 22706ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 22716ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 22726ab2b7b4SDimitris Papastamos } 2273b618a185SCharles Keepax } 22742323736dSCharles Keepax } 2275db40517cSMark Brown 2276b618a185SCharles Keepax out: 2277b618a185SCharles Keepax kfree(adsp1_alg); 2278b618a185SCharles Keepax return ret; 2279b618a185SCharles Keepax } 2280b618a185SCharles Keepax 2281b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 2282b618a185SCharles Keepax { 2283b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 2284b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 22853809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2286b618a185SCharles Keepax const struct wm_adsp_region *mem; 2287b618a185SCharles Keepax unsigned int pos, len; 22883809f001SCharles Keepax size_t n_algs; 2289b618a185SCharles Keepax int i, ret; 2290b618a185SCharles Keepax 2291b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2292b618a185SCharles Keepax if (WARN_ON(!mem)) 2293b618a185SCharles Keepax return -EINVAL; 2294b618a185SCharles Keepax 2295b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 2296b618a185SCharles Keepax sizeof(adsp2_id)); 2297b618a185SCharles Keepax if (ret != 0) { 2298b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 2299b618a185SCharles Keepax ret); 2300b618a185SCharles Keepax return ret; 2301b618a185SCharles Keepax } 2302b618a185SCharles Keepax 23033809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 2304a5dcb24dSCharles Keepax 2305a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs); 2306b618a185SCharles Keepax 2307d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2308d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2309d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2310d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2311b618a185SCharles Keepax 2312d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2313d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2314d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2315d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2316b618a185SCharles Keepax 2317d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2318d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2319d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2320d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2321b618a185SCharles Keepax 23227f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 23237f7cca08SCharles Keepax pos = sizeof(adsp2_id) / sizeof(u32); 23247f7cca08SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 2325b618a185SCharles Keepax 23267f7cca08SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2327b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2328b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2329b618a185SCharles Keepax 23303809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2331471f4885SMark Brown adsp_info(dsp, 2332471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2333db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2334db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2335db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2336471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2337471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2338471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2339471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2340471f4885SMark Brown 2341d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2342d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2343d9d20e17SCharles Keepax adsp2_alg[i].xm); 2344d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2345d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2346d6d52179SJS Park goto out; 2347d6d52179SJS Park } 23482323736dSCharles Keepax if (dsp->fw_ver == 0) { 23493809f001SCharles Keepax if (i + 1 < n_algs) { 23506958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 23516958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 23526958eb2aSCharles Keepax len *= 4; 23532323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23548eb084d0SStuart Henderson len, NULL, 0, 0, 23558eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23566ab2b7b4SDimitris Papastamos } else { 23576ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 23586ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 23596ab2b7b4SDimitris Papastamos } 23602323736dSCharles Keepax } 2361471f4885SMark Brown 2362d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2363d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2364d9d20e17SCharles Keepax adsp2_alg[i].ym); 2365d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2366d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2367d6d52179SJS Park goto out; 2368d6d52179SJS Park } 23692323736dSCharles Keepax if (dsp->fw_ver == 0) { 23703809f001SCharles Keepax if (i + 1 < n_algs) { 23716958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 23726958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 23736958eb2aSCharles Keepax len *= 4; 23742323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23758eb084d0SStuart Henderson len, NULL, 0, 0, 23768eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23776ab2b7b4SDimitris Papastamos } else { 23786ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 23796ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 23806ab2b7b4SDimitris Papastamos } 23812323736dSCharles Keepax } 2382471f4885SMark Brown 2383d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2384d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2385d9d20e17SCharles Keepax adsp2_alg[i].zm); 2386d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2387d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2388d6d52179SJS Park goto out; 2389d6d52179SJS Park } 23902323736dSCharles Keepax if (dsp->fw_ver == 0) { 23913809f001SCharles Keepax if (i + 1 < n_algs) { 23926958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 23936958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 23946958eb2aSCharles Keepax len *= 4; 23952323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 23968eb084d0SStuart Henderson len, NULL, 0, 0, 23978eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 23986ab2b7b4SDimitris Papastamos } else { 23996ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 24006ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 24016ab2b7b4SDimitris Papastamos } 2402db40517cSMark Brown } 24032323736dSCharles Keepax } 2404db40517cSMark Brown 2405db40517cSMark Brown out: 2406b618a185SCharles Keepax kfree(adsp2_alg); 2407db40517cSMark Brown return ret; 2408db40517cSMark Brown } 2409db40517cSMark Brown 2410170b1e12SWen Shi static int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id, 2411170b1e12SWen Shi __be32 xm_base, __be32 ym_base) 2412170b1e12SWen Shi { 2413170b1e12SWen Shi int types[] = { 2414170b1e12SWen Shi WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 2415170b1e12SWen Shi WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 2416170b1e12SWen Shi }; 2417170b1e12SWen Shi __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 2418170b1e12SWen Shi 2419170b1e12SWen Shi return wm_adsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); 2420170b1e12SWen Shi } 2421170b1e12SWen Shi 2422170b1e12SWen Shi static int wm_halo_setup_algs(struct wm_adsp *dsp) 2423170b1e12SWen Shi { 2424170b1e12SWen Shi struct wmfw_halo_id_hdr halo_id; 2425170b1e12SWen Shi struct wmfw_halo_alg_hdr *halo_alg; 2426170b1e12SWen Shi const struct wm_adsp_region *mem; 2427170b1e12SWen Shi unsigned int pos, len; 2428170b1e12SWen Shi size_t n_algs; 2429170b1e12SWen Shi int i, ret; 2430170b1e12SWen Shi 2431170b1e12SWen Shi mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2432170b1e12SWen Shi if (WARN_ON(!mem)) 2433170b1e12SWen Shi return -EINVAL; 2434170b1e12SWen Shi 2435170b1e12SWen Shi ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 2436170b1e12SWen Shi sizeof(halo_id)); 2437170b1e12SWen Shi if (ret != 0) { 2438170b1e12SWen Shi adsp_err(dsp, "Failed to read algorithm info: %d\n", 2439170b1e12SWen Shi ret); 2440170b1e12SWen Shi return ret; 2441170b1e12SWen Shi } 2442170b1e12SWen Shi 2443170b1e12SWen Shi n_algs = be32_to_cpu(halo_id.n_algs); 2444170b1e12SWen Shi 2445170b1e12SWen Shi wmfw_v3_parse_id_header(dsp, &halo_id.fw, n_algs); 2446170b1e12SWen Shi 2447170b1e12SWen Shi ret = wm_halo_create_regions(dsp, halo_id.fw.id, 2448170b1e12SWen Shi halo_id.ym_base, halo_id.ym_base); 2449170b1e12SWen Shi if (ret) 2450170b1e12SWen Shi return ret; 2451170b1e12SWen Shi 2452170b1e12SWen Shi /* Calculate offset and length in DSP words */ 2453170b1e12SWen Shi pos = sizeof(halo_id) / sizeof(u32); 2454170b1e12SWen Shi len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 2455170b1e12SWen Shi 2456170b1e12SWen Shi halo_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2457170b1e12SWen Shi if (IS_ERR(halo_alg)) 2458170b1e12SWen Shi return PTR_ERR(halo_alg); 2459170b1e12SWen Shi 2460170b1e12SWen Shi for (i = 0; i < n_algs; i++) { 2461170b1e12SWen Shi adsp_info(dsp, 2462170b1e12SWen Shi "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 2463170b1e12SWen Shi i, be32_to_cpu(halo_alg[i].alg.id), 2464170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 2465170b1e12SWen Shi (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 2466170b1e12SWen Shi be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 2467170b1e12SWen Shi be32_to_cpu(halo_alg[i].xm_base), 2468170b1e12SWen Shi be32_to_cpu(halo_alg[i].ym_base)); 2469170b1e12SWen Shi 2470170b1e12SWen Shi ret = wm_halo_create_regions(dsp, halo_alg[i].alg.id, 2471170b1e12SWen Shi halo_alg[i].xm_base, 2472170b1e12SWen Shi halo_alg[i].ym_base); 2473170b1e12SWen Shi if (ret) 2474170b1e12SWen Shi goto out; 2475170b1e12SWen Shi } 2476170b1e12SWen Shi 2477170b1e12SWen Shi out: 2478170b1e12SWen Shi kfree(halo_alg); 2479170b1e12SWen Shi return ret; 2480170b1e12SWen Shi } 2481170b1e12SWen Shi 24822159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 24832159ad93SMark Brown { 2484cf17c83cSMark Brown LIST_HEAD(buf_list); 24852159ad93SMark Brown struct regmap *regmap = dsp->regmap; 24862159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 24872159ad93SMark Brown struct wmfw_coeff_item *blk; 24882159ad93SMark Brown const struct firmware *firmware; 2489471f4885SMark Brown const struct wm_adsp_region *mem; 2490471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 24912159ad93SMark Brown const char *region_name; 24922159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 24932159ad93SMark Brown char *file; 2494cf17c83cSMark Brown struct wm_adsp_buf *buf; 24952159ad93SMark Brown 24962159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 24972159ad93SMark Brown if (file == NULL) 24982159ad93SMark Brown return -ENOMEM; 24992159ad93SMark Brown 2500605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, 25011023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 25022159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 25032159ad93SMark Brown 25042159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 25052159ad93SMark Brown if (ret != 0) { 25062159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 25072159ad93SMark Brown ret = 0; 25082159ad93SMark Brown goto out; 25092159ad93SMark Brown } 25102159ad93SMark Brown ret = -EINVAL; 25112159ad93SMark Brown 25122159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 25132159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 25142159ad93SMark Brown file, firmware->size); 25152159ad93SMark Brown goto out_fw; 25162159ad93SMark Brown } 25172159ad93SMark Brown 25182159ad93SMark Brown hdr = (void *)&firmware->data[0]; 25192159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 25202159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2521a4cdbec7SCharles Keepax goto out_fw; 25222159ad93SMark Brown } 25232159ad93SMark Brown 2524c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2525c712326dSMark Brown case 1: 2526c712326dSMark Brown break; 2527c712326dSMark Brown default: 2528c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2529c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2530c712326dSMark Brown ret = -EINVAL; 2531c712326dSMark Brown goto out_fw; 2532c712326dSMark Brown } 2533c712326dSMark Brown 25342159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 25352159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 25362159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 25372159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 25382159ad93SMark Brown 25392159ad93SMark Brown pos = le32_to_cpu(hdr->len); 25402159ad93SMark Brown 25412159ad93SMark Brown blocks = 0; 25422159ad93SMark Brown while (pos < firmware->size && 254350dd2ea8SBen Hutchings sizeof(*blk) < firmware->size - pos) { 25442159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 25452159ad93SMark Brown 2546c712326dSMark Brown type = le16_to_cpu(blk->type); 2547c712326dSMark Brown offset = le16_to_cpu(blk->offset); 25482159ad93SMark Brown 25492159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 25502159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 25512159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 25522159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 25532159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 25542159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 25552159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 25562159ad93SMark Brown 25572159ad93SMark Brown reg = 0; 25582159ad93SMark Brown region_name = "Unknown"; 25592159ad93SMark Brown switch (type) { 2560c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2561c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 25622159ad93SMark Brown break; 2563c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2564f395a218SMark Brown /* 2565f395a218SMark Brown * Old files may use this for global 2566f395a218SMark Brown * coefficients. 2567f395a218SMark Brown */ 2568f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2569f395a218SMark Brown offset == 0) { 2570f395a218SMark Brown region_name = "global coefficients"; 2571f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2572f395a218SMark Brown if (!mem) { 2573f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2574f395a218SMark Brown break; 2575f395a218SMark Brown } 2576170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, 0); 2577f395a218SMark Brown 2578f395a218SMark Brown } else { 25792159ad93SMark Brown region_name = "register"; 25802159ad93SMark Brown reg = offset; 2581f395a218SMark Brown } 25822159ad93SMark Brown break; 2583471f4885SMark Brown 2584471f4885SMark Brown case WMFW_ADSP1_DM: 2585471f4885SMark Brown case WMFW_ADSP1_ZM: 2586471f4885SMark Brown case WMFW_ADSP2_XM: 2587471f4885SMark Brown case WMFW_ADSP2_YM: 2588170b1e12SWen Shi case WMFW_HALO_XM_PACKED: 2589170b1e12SWen Shi case WMFW_HALO_YM_PACKED: 2590170b1e12SWen Shi case WMFW_HALO_PM_PACKED: 2591471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2592471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2593471f4885SMark Brown type, le32_to_cpu(blk->id)); 2594471f4885SMark Brown 2595471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2596471f4885SMark Brown if (!mem) { 2597471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2598471f4885SMark Brown break; 2599471f4885SMark Brown } 2600471f4885SMark Brown 260114197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 260214197095SCharles Keepax le32_to_cpu(blk->id)); 260314197095SCharles Keepax if (alg_region) { 2604338c5188SMark Brown reg = alg_region->base; 2605170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, reg); 2606338c5188SMark Brown reg += offset; 260714197095SCharles Keepax } else { 2608471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2609471f4885SMark Brown type, le32_to_cpu(blk->id)); 261014197095SCharles Keepax } 2611471f4885SMark Brown break; 2612471f4885SMark Brown 26132159ad93SMark Brown default: 261425c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 261525c62f7eSMark Brown file, blocks, type, pos); 26162159ad93SMark Brown break; 26172159ad93SMark Brown } 26182159ad93SMark Brown 26192159ad93SMark Brown if (reg) { 262050dd2ea8SBen Hutchings if (le32_to_cpu(blk->len) > 262150dd2ea8SBen Hutchings firmware->size - pos - sizeof(*blk)) { 26221cab2a84SRichard Fitzgerald adsp_err(dsp, 26231cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 26241cab2a84SRichard Fitzgerald file, blocks, region_name, 26251cab2a84SRichard Fitzgerald le32_to_cpu(blk->len), 26261cab2a84SRichard Fitzgerald firmware->size); 26271cab2a84SRichard Fitzgerald ret = -EINVAL; 26281cab2a84SRichard Fitzgerald goto out_fw; 26291cab2a84SRichard Fitzgerald } 26301cab2a84SRichard Fitzgerald 2631cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2632cf17c83cSMark Brown le32_to_cpu(blk->len), 2633cf17c83cSMark Brown &buf_list); 2634a76fefabSMark Brown if (!buf) { 2635a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2636f4b82812SWei Yongjun ret = -ENOMEM; 2637f4b82812SWei Yongjun goto out_fw; 2638a76fefabSMark Brown } 2639a76fefabSMark Brown 264020da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 264120da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 264220da6d5aSMark Brown reg); 2643cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 26442159ad93SMark Brown le32_to_cpu(blk->len)); 26452159ad93SMark Brown if (ret != 0) { 26462159ad93SMark Brown adsp_err(dsp, 264743bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 264843bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 26492159ad93SMark Brown } 26502159ad93SMark Brown } 26512159ad93SMark Brown 2652be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 26532159ad93SMark Brown blocks++; 26542159ad93SMark Brown } 26552159ad93SMark Brown 2656cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2657cf17c83cSMark Brown if (ret != 0) 2658cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2659cf17c83cSMark Brown 26602159ad93SMark Brown if (pos > firmware->size) 26612159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 26622159ad93SMark Brown file, blocks, pos - firmware->size); 26632159ad93SMark Brown 2664f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2665f9f55e31SRichard Fitzgerald 26662159ad93SMark Brown out_fw: 26679da7a5a9SCharles Keepax regmap_async_complete(regmap); 26682159ad93SMark Brown release_firmware(firmware); 2669cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 26702159ad93SMark Brown out: 26712159ad93SMark Brown kfree(file); 2672f4b82812SWei Yongjun return ret; 26732159ad93SMark Brown } 26742159ad93SMark Brown 2675605391d0SRichard Fitzgerald static int wm_adsp_create_name(struct wm_adsp *dsp) 2676605391d0SRichard Fitzgerald { 2677605391d0SRichard Fitzgerald char *p; 2678605391d0SRichard Fitzgerald 2679605391d0SRichard Fitzgerald if (!dsp->name) { 2680605391d0SRichard Fitzgerald dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2681605391d0SRichard Fitzgerald dsp->num); 2682605391d0SRichard Fitzgerald if (!dsp->name) 2683605391d0SRichard Fitzgerald return -ENOMEM; 2684605391d0SRichard Fitzgerald } 2685605391d0SRichard Fitzgerald 2686605391d0SRichard Fitzgerald if (!dsp->fwf_name) { 2687605391d0SRichard Fitzgerald p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); 2688605391d0SRichard Fitzgerald if (!p) 2689605391d0SRichard Fitzgerald return -ENOMEM; 2690605391d0SRichard Fitzgerald 2691605391d0SRichard Fitzgerald dsp->fwf_name = p; 2692605391d0SRichard Fitzgerald for (; *p != 0; ++p) 2693605391d0SRichard Fitzgerald *p = tolower(*p); 2694605391d0SRichard Fitzgerald } 2695605391d0SRichard Fitzgerald 2696605391d0SRichard Fitzgerald return 0; 2697605391d0SRichard Fitzgerald } 2698605391d0SRichard Fitzgerald 2699dcad34f8SRichard Fitzgerald static int wm_adsp_common_init(struct wm_adsp *dsp) 27005e7a7a22SMark Brown { 2701605391d0SRichard Fitzgerald int ret; 2702605391d0SRichard Fitzgerald 2703605391d0SRichard Fitzgerald ret = wm_adsp_create_name(dsp); 2704605391d0SRichard Fitzgerald if (ret) 2705605391d0SRichard Fitzgerald return ret; 2706605391d0SRichard Fitzgerald 27073809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 2708dcad34f8SRichard Fitzgerald INIT_LIST_HEAD(&dsp->ctl_list); 27094f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->compr_list); 27104f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->buffer_list); 27115e7a7a22SMark Brown 2712078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2713078e7183SCharles Keepax 27145e7a7a22SMark Brown return 0; 27155e7a7a22SMark Brown } 2716dcad34f8SRichard Fitzgerald 2717dcad34f8SRichard Fitzgerald int wm_adsp1_init(struct wm_adsp *dsp) 2718dcad34f8SRichard Fitzgerald { 27194e08d50dSCharles Keepax dsp->ops = &wm_adsp1_ops; 27204e08d50dSCharles Keepax 2721dcad34f8SRichard Fitzgerald return wm_adsp_common_init(dsp); 2722dcad34f8SRichard Fitzgerald } 27235e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 27245e7a7a22SMark Brown 27252159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 27262159ad93SMark Brown struct snd_kcontrol *kcontrol, 27272159ad93SMark Brown int event) 27282159ad93SMark Brown { 27290fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 27300fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 27312159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 27326ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 27332159ad93SMark Brown int ret; 27347585a5b0SCharles Keepax unsigned int val; 27352159ad93SMark Brown 27360fe1daa6SKuninori Morimoto dsp->component = component; 273792bb4c32SDimitris Papastamos 2738078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2739078e7183SCharles Keepax 27402159ad93SMark Brown switch (event) { 27412159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 27422159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 27432159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 27442159ad93SMark Brown 274594e205bfSChris Rattray /* 274694e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 274794e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 274894e205bfSChris Rattray */ 274994e205bfSChris Rattray if (dsp->sysclk_reg) { 275094e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 275194e205bfSChris Rattray if (ret != 0) { 275294e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 275394e205bfSChris Rattray ret); 2754078e7183SCharles Keepax goto err_mutex; 275594e205bfSChris Rattray } 275694e205bfSChris Rattray 27577d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 275894e205bfSChris Rattray 275994e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 276094e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 276194e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 276294e205bfSChris Rattray if (ret != 0) { 276394e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 276494e205bfSChris Rattray ret); 2765078e7183SCharles Keepax goto err_mutex; 276694e205bfSChris Rattray } 276794e205bfSChris Rattray } 276894e205bfSChris Rattray 27692159ad93SMark Brown ret = wm_adsp_load(dsp); 27702159ad93SMark Brown if (ret != 0) 2771078e7183SCharles Keepax goto err_ena; 27722159ad93SMark Brown 2773b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2774db40517cSMark Brown if (ret != 0) 2775078e7183SCharles Keepax goto err_ena; 2776db40517cSMark Brown 27772159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 27782159ad93SMark Brown if (ret != 0) 2779078e7183SCharles Keepax goto err_ena; 27802159ad93SMark Brown 27810c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 278281ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 27836ab2b7b4SDimitris Papastamos if (ret != 0) 2784078e7183SCharles Keepax goto err_ena; 27856ab2b7b4SDimitris Papastamos 27860c2e3f34SDimitris Papastamos /* Sync set controls */ 278781ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 27886ab2b7b4SDimitris Papastamos if (ret != 0) 2789078e7183SCharles Keepax goto err_ena; 27906ab2b7b4SDimitris Papastamos 279128823ebaSCharles Keepax dsp->booted = true; 279228823ebaSCharles Keepax 27932159ad93SMark Brown /* Start the core running */ 27942159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 27952159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 27962159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 279728823ebaSCharles Keepax 279828823ebaSCharles Keepax dsp->running = true; 27992159ad93SMark Brown break; 28002159ad93SMark Brown 28012159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 280228823ebaSCharles Keepax dsp->running = false; 280328823ebaSCharles Keepax dsp->booted = false; 280428823ebaSCharles Keepax 28052159ad93SMark Brown /* Halt the core */ 28062159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28072159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 28082159ad93SMark Brown 28092159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 28102159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 28112159ad93SMark Brown 28122159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28132159ad93SMark Brown ADSP1_SYS_ENA, 0); 28146ab2b7b4SDimitris Papastamos 281581ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 28166ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2817b0101b4fSDimitris Papastamos 281856574d54SRichard Fitzgerald 281956574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 28202159ad93SMark Brown break; 28212159ad93SMark Brown 28222159ad93SMark Brown default: 28232159ad93SMark Brown break; 28242159ad93SMark Brown } 28252159ad93SMark Brown 2826078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2827078e7183SCharles Keepax 28282159ad93SMark Brown return 0; 28292159ad93SMark Brown 2830078e7183SCharles Keepax err_ena: 28312159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28322159ad93SMark Brown ADSP1_SYS_ENA, 0); 2833078e7183SCharles Keepax err_mutex: 2834078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2835078e7183SCharles Keepax 28362159ad93SMark Brown return ret; 28372159ad93SMark Brown } 28382159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 28392159ad93SMark Brown 28404e08d50dSCharles Keepax static int wm_adsp2v2_enable_core(struct wm_adsp *dsp) 28412159ad93SMark Brown { 28422159ad93SMark Brown unsigned int val; 28432159ad93SMark Brown int ret, count; 28442159ad93SMark Brown 28452159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2846939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 28477d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 28482159ad93SMark Brown if (ret != 0) 28492159ad93SMark Brown return ret; 2850939fd1e8SCharles Keepax 2851939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2852939fd1e8SCharles Keepax break; 2853939fd1e8SCharles Keepax 28541fa96f3fSCharles Keepax usleep_range(250, 500); 2855939fd1e8SCharles Keepax } 28562159ad93SMark Brown 28572159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 28582159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 28592159ad93SMark Brown return -EBUSY; 28602159ad93SMark Brown } 28612159ad93SMark Brown 28622159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 28632159ad93SMark Brown 28642159ad93SMark Brown return 0; 28652159ad93SMark Brown } 28662159ad93SMark Brown 28674e08d50dSCharles Keepax static int wm_adsp2_enable_core(struct wm_adsp *dsp) 28684e08d50dSCharles Keepax { 28694e08d50dSCharles Keepax int ret; 28704e08d50dSCharles Keepax 28714e08d50dSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 28724e08d50dSCharles Keepax ADSP2_SYS_ENA, ADSP2_SYS_ENA); 28734e08d50dSCharles Keepax if (ret != 0) 28744e08d50dSCharles Keepax return ret; 28754e08d50dSCharles Keepax 28764e08d50dSCharles Keepax return wm_adsp2v2_enable_core(dsp); 28774e08d50dSCharles Keepax } 28784e08d50dSCharles Keepax 28792b0ee49fSCharles Keepax static int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 28802b0ee49fSCharles Keepax { 28812b0ee49fSCharles Keepax struct regmap *regmap = dsp->regmap; 28822b0ee49fSCharles Keepax unsigned int code0, code1, lock_reg; 28832b0ee49fSCharles Keepax 28842b0ee49fSCharles Keepax if (!(lock_regions & WM_ADSP2_REGION_ALL)) 28852b0ee49fSCharles Keepax return 0; 28862b0ee49fSCharles Keepax 28872b0ee49fSCharles Keepax lock_regions &= WM_ADSP2_REGION_ALL; 28882b0ee49fSCharles Keepax lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 28892b0ee49fSCharles Keepax 28902b0ee49fSCharles Keepax while (lock_regions) { 28912b0ee49fSCharles Keepax code0 = code1 = 0; 28922b0ee49fSCharles Keepax if (lock_regions & BIT(0)) { 28932b0ee49fSCharles Keepax code0 = ADSP2_LOCK_CODE_0; 28942b0ee49fSCharles Keepax code1 = ADSP2_LOCK_CODE_1; 28952b0ee49fSCharles Keepax } 28962b0ee49fSCharles Keepax if (lock_regions & BIT(1)) { 28972b0ee49fSCharles Keepax code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 28982b0ee49fSCharles Keepax code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 28992b0ee49fSCharles Keepax } 29002b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code0); 29012b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code1); 29022b0ee49fSCharles Keepax lock_regions >>= 2; 29032b0ee49fSCharles Keepax lock_reg += 2; 29042b0ee49fSCharles Keepax } 29052b0ee49fSCharles Keepax 29062b0ee49fSCharles Keepax return 0; 29072b0ee49fSCharles Keepax } 29082b0ee49fSCharles Keepax 29094e08d50dSCharles Keepax static int wm_adsp2_enable_memory(struct wm_adsp *dsp) 29104e08d50dSCharles Keepax { 29114e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 29124e08d50dSCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 29134e08d50dSCharles Keepax } 29144e08d50dSCharles Keepax 29154e08d50dSCharles Keepax static void wm_adsp2_disable_memory(struct wm_adsp *dsp) 29164e08d50dSCharles Keepax { 29174e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 29184e08d50dSCharles Keepax ADSP2_MEM_ENA, 0); 29194e08d50dSCharles Keepax } 29204e08d50dSCharles Keepax 29214e08d50dSCharles Keepax static void wm_adsp2_disable_core(struct wm_adsp *dsp) 29224e08d50dSCharles Keepax { 29234e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 29244e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 29254e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 29264e08d50dSCharles Keepax 29274e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 29284e08d50dSCharles Keepax ADSP2_SYS_ENA, 0); 29294e08d50dSCharles Keepax } 29304e08d50dSCharles Keepax 29314e08d50dSCharles Keepax static void wm_adsp2v2_disable_core(struct wm_adsp *dsp) 29324e08d50dSCharles Keepax { 29334e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 29344e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 29354e08d50dSCharles Keepax regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 29364e08d50dSCharles Keepax } 29374e08d50dSCharles Keepax 29384e08d50dSCharles Keepax static void wm_adsp_boot_work(struct work_struct *work) 29392159ad93SMark Brown { 2940d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2941d8a64d6aSCharles Keepax struct wm_adsp, 2942d8a64d6aSCharles Keepax boot_work); 29432159ad93SMark Brown int ret; 29442159ad93SMark Brown 2945078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2946078e7183SCharles Keepax 29474e08d50dSCharles Keepax if (dsp->ops->enable_memory) { 29484e08d50dSCharles Keepax ret = dsp->ops->enable_memory(dsp); 294990d19ba5SCharles Keepax if (ret != 0) 295090d19ba5SCharles Keepax goto err_mutex; 29514e08d50dSCharles Keepax } 295290d19ba5SCharles Keepax 29534e08d50dSCharles Keepax if (dsp->ops->enable_core) { 29544e08d50dSCharles Keepax ret = dsp->ops->enable_core(dsp); 29552159ad93SMark Brown if (ret != 0) 2956d589d8b8SCharles Keepax goto err_mem; 29574e08d50dSCharles Keepax } 29582159ad93SMark Brown 29592159ad93SMark Brown ret = wm_adsp_load(dsp); 29602159ad93SMark Brown if (ret != 0) 2961078e7183SCharles Keepax goto err_ena; 29622159ad93SMark Brown 29634e08d50dSCharles Keepax ret = dsp->ops->setup_algs(dsp); 2964db40517cSMark Brown if (ret != 0) 2965078e7183SCharles Keepax goto err_ena; 2966db40517cSMark Brown 29672159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 29682159ad93SMark Brown if (ret != 0) 2969078e7183SCharles Keepax goto err_ena; 29702159ad93SMark Brown 29710c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 297281ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 29736ab2b7b4SDimitris Papastamos if (ret != 0) 2974078e7183SCharles Keepax goto err_ena; 29756ab2b7b4SDimitris Papastamos 29764e08d50dSCharles Keepax if (dsp->ops->disable_core) 29774e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 297890d19ba5SCharles Keepax 2979e779974bSCharles Keepax dsp->booted = true; 2980e779974bSCharles Keepax 2981078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2982078e7183SCharles Keepax 2983d8a64d6aSCharles Keepax return; 2984d8a64d6aSCharles Keepax 2985078e7183SCharles Keepax err_ena: 29864e08d50dSCharles Keepax if (dsp->ops->disable_core) 29874e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 2988d589d8b8SCharles Keepax err_mem: 29894e08d50dSCharles Keepax if (dsp->ops->disable_memory) 29904e08d50dSCharles Keepax dsp->ops->disable_memory(dsp); 2991078e7183SCharles Keepax err_mutex: 2992078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2993d8a64d6aSCharles Keepax } 2994d8a64d6aSCharles Keepax 2995170b1e12SWen Shi static int wm_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions) 2996170b1e12SWen Shi { 2997170b1e12SWen Shi struct reg_sequence config[] = { 2998170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 2999170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 3000170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 3001170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 3002170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 3003170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 3004170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 3005170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 3006170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 3007170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 3008170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 3009170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 3010170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 3011170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 3012170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 3013170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 3014170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 3015170b1e12SWen Shi { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 3016170b1e12SWen Shi { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 3017170b1e12SWen Shi { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 3018170b1e12SWen Shi { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 3019170b1e12SWen Shi { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 3020170b1e12SWen Shi { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 3021170b1e12SWen Shi }; 3022170b1e12SWen Shi 3023170b1e12SWen Shi return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 3024170b1e12SWen Shi } 3025170b1e12SWen Shi 3026b9070df4SRichard Fitzgerald int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) 3027d82d767fSCharles Keepax { 3028b9070df4SRichard Fitzgerald struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 3029b9070df4SRichard Fitzgerald struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3030b9070df4SRichard Fitzgerald struct wm_adsp *dsp = &dsps[w->shift]; 3031d82d767fSCharles Keepax int ret; 3032d82d767fSCharles Keepax 3033b9070df4SRichard Fitzgerald ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 3034d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 3035d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 3036b9070df4SRichard Fitzgerald if (ret) 3037d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 3038b9070df4SRichard Fitzgerald 3039b9070df4SRichard Fitzgerald return ret; 3040e1ea1879SRichard Fitzgerald } 3041b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); 3042d82d767fSCharles Keepax 3043af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 3044af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3045af813a6fSCharles Keepax { 30460fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3047b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3048b1470d4cSAjit Pandey struct soc_mixer_control *mc = 3049b1470d4cSAjit Pandey (struct soc_mixer_control *)kcontrol->private_value; 3050b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3051af813a6fSCharles Keepax 3052af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 3053af813a6fSCharles Keepax 3054af813a6fSCharles Keepax return 0; 3055af813a6fSCharles Keepax } 3056af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 3057af813a6fSCharles Keepax 3058af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 3059af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 3060af813a6fSCharles Keepax { 30610fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3062b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 30630fe1daa6SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 3064af813a6fSCharles Keepax struct soc_mixer_control *mc = 3065af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 3066b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 3067af813a6fSCharles Keepax char preload[32]; 3068af813a6fSCharles Keepax 3069605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 3070af813a6fSCharles Keepax 3071af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 3072af813a6fSCharles Keepax 3073af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 307495a594d0SCharles Keepax snd_soc_component_force_enable_pin(component, preload); 3075af813a6fSCharles Keepax else 307695a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3077af813a6fSCharles Keepax 3078af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 3079af813a6fSCharles Keepax 3080868e49a4SStuart Henderson flush_work(&dsp->boot_work); 3081868e49a4SStuart Henderson 3082af813a6fSCharles Keepax return 0; 3083af813a6fSCharles Keepax } 3084af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 3085af813a6fSCharles Keepax 308651a2c944SMayuresh Kulkarni static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) 308751a2c944SMayuresh Kulkarni { 308851a2c944SMayuresh Kulkarni regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 308951a2c944SMayuresh Kulkarni ADSP2_WDT_ENA_MASK, 0); 309051a2c944SMayuresh Kulkarni } 309151a2c944SMayuresh Kulkarni 30928bc144f9SStuart Henderson static void wm_halo_stop_watchdog(struct wm_adsp *dsp) 30938bc144f9SStuart Henderson { 30948bc144f9SStuart Henderson regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 30958bc144f9SStuart Henderson HALO_WDT_EN_MASK, 0); 30968bc144f9SStuart Henderson } 30978bc144f9SStuart Henderson 30984e08d50dSCharles Keepax int wm_adsp_early_event(struct snd_soc_dapm_widget *w, 3099b9070df4SRichard Fitzgerald struct snd_kcontrol *kcontrol, int event) 310012db5eddSCharles Keepax { 31010fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 31020fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 310312db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 310457a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 310512db5eddSCharles Keepax 310612db5eddSCharles Keepax switch (event) { 310712db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 310812db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 310912db5eddSCharles Keepax break; 311057a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 3111bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3112bb24ee41SCharles Keepax 311357a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 311457a60cc3SCharles Keepax 311557a60cc3SCharles Keepax dsp->fw_id = 0; 311657a60cc3SCharles Keepax dsp->fw_id_version = 0; 311757a60cc3SCharles Keepax 311857a60cc3SCharles Keepax dsp->booted = false; 311957a60cc3SCharles Keepax 31204e08d50dSCharles Keepax if (dsp->ops->disable_memory) 31214e08d50dSCharles Keepax dsp->ops->disable_memory(dsp); 312257a60cc3SCharles Keepax 312357a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 312457a60cc3SCharles Keepax ctl->enabled = 0; 312557a60cc3SCharles Keepax 312657a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 312757a60cc3SCharles Keepax 3128bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3129bb24ee41SCharles Keepax 313057a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 313157a60cc3SCharles Keepax break; 313212db5eddSCharles Keepax default: 313312db5eddSCharles Keepax break; 3134cab27258SCharles Keepax } 313512db5eddSCharles Keepax 313612db5eddSCharles Keepax return 0; 313712db5eddSCharles Keepax } 31384e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event); 313912db5eddSCharles Keepax 31404e08d50dSCharles Keepax static int wm_adsp2_start_core(struct wm_adsp *dsp) 31414e08d50dSCharles Keepax { 31424e08d50dSCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 31434e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 31444e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 31454e08d50dSCharles Keepax } 31464e08d50dSCharles Keepax 31474e08d50dSCharles Keepax static void wm_adsp2_stop_core(struct wm_adsp *dsp) 31484e08d50dSCharles Keepax { 31494e08d50dSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 31504e08d50dSCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 31514e08d50dSCharles Keepax } 31524e08d50dSCharles Keepax 31534e08d50dSCharles Keepax int wm_adsp_event(struct snd_soc_dapm_widget *w, 3154d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 3155d8a64d6aSCharles Keepax { 31560fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 31570fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 3158d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 3159d8a64d6aSCharles Keepax int ret; 3160d8a64d6aSCharles Keepax 3161d8a64d6aSCharles Keepax switch (event) { 3162d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 3163d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 3164d8a64d6aSCharles Keepax 3165bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 3166bb24ee41SCharles Keepax 3167bb24ee41SCharles Keepax if (!dsp->booted) { 3168bb24ee41SCharles Keepax ret = -EIO; 3169bb24ee41SCharles Keepax goto err; 3170bb24ee41SCharles Keepax } 3171d8a64d6aSCharles Keepax 31724e08d50dSCharles Keepax if (dsp->ops->enable_core) { 31734e08d50dSCharles Keepax ret = dsp->ops->enable_core(dsp); 317490d19ba5SCharles Keepax if (ret != 0) 317590d19ba5SCharles Keepax goto err; 31764e08d50dSCharles Keepax } 317790d19ba5SCharles Keepax 3178cef45771SCharles Keepax /* Sync set controls */ 3179cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 3180cef45771SCharles Keepax if (ret != 0) 3181cef45771SCharles Keepax goto err; 3182cef45771SCharles Keepax 31834e08d50dSCharles Keepax if (dsp->ops->lock_memory) { 31844e08d50dSCharles Keepax ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 31854e08d50dSCharles Keepax if (ret != 0) { 31864e08d50dSCharles Keepax adsp_err(dsp, "Error configuring MPU: %d\n", 31874e08d50dSCharles Keepax ret); 31884e08d50dSCharles Keepax goto err; 31894e08d50dSCharles Keepax } 31904e08d50dSCharles Keepax } 319151a2c944SMayuresh Kulkarni 31924e08d50dSCharles Keepax if (dsp->ops->start_core) { 31934e08d50dSCharles Keepax ret = dsp->ops->start_core(dsp); 3194d8a64d6aSCharles Keepax if (ret != 0) 3195d8a64d6aSCharles Keepax goto err; 31964e08d50dSCharles Keepax } 31972cd19bdbSCharles Keepax 319848c2c993SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) { 31992cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 3200bb24ee41SCharles Keepax if (ret < 0) 320148c2c993SCharles Keepax goto err; 320248c2c993SCharles Keepax } 32032cd19bdbSCharles Keepax 3204e779974bSCharles Keepax dsp->running = true; 32051023dbd9SMark Brown 3206612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 32072159ad93SMark Brown break; 32082159ad93SMark Brown 32092159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 3210f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 3211f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 3212f4f0c4c6SRichard Fitzgerald 32134e08d50dSCharles Keepax if (dsp->ops->stop_watchdog) 32144e08d50dSCharles Keepax dsp->ops->stop_watchdog(dsp); 321551a2c944SMayuresh Kulkarni 321610337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 32174e08d50dSCharles Keepax if (dsp->ops->show_fw_status) 32184e08d50dSCharles Keepax dsp->ops->show_fw_status(dsp); 321910337b07SRichard Fitzgerald 3220078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 3221078e7183SCharles Keepax 32221023dbd9SMark Brown dsp->running = false; 32231023dbd9SMark Brown 32244e08d50dSCharles Keepax if (dsp->ops->stop_core) 32254e08d50dSCharles Keepax dsp->ops->stop_core(dsp); 32264e08d50dSCharles Keepax if (dsp->ops->disable_core) 32274e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 32282d30b575SMark Brown 32292cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 32302cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 32312cd19bdbSCharles Keepax 3232a2bcbc1bSCharles Keepax dsp->fatal_error = false; 3233a2bcbc1bSCharles Keepax 3234078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3235078e7183SCharles Keepax 323657a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 32372159ad93SMark Brown break; 32382159ad93SMark Brown 32392159ad93SMark Brown default: 32402159ad93SMark Brown break; 32412159ad93SMark Brown } 32422159ad93SMark Brown 32432159ad93SMark Brown return 0; 32442159ad93SMark Brown err: 32454e08d50dSCharles Keepax if (dsp->ops->stop_core) 32464e08d50dSCharles Keepax dsp->ops->stop_core(dsp); 32474e08d50dSCharles Keepax if (dsp->ops->disable_core) 32484e08d50dSCharles Keepax dsp->ops->disable_core(dsp); 3249bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 32502159ad93SMark Brown return ret; 32512159ad93SMark Brown } 32524e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event); 3253973838a0SMark Brown 3254170b1e12SWen Shi static int wm_halo_start_core(struct wm_adsp *dsp) 3255170b1e12SWen Shi { 3256170b1e12SWen Shi return regmap_update_bits(dsp->regmap, 3257170b1e12SWen Shi dsp->base + HALO_CCM_CORE_CONTROL, 3258170b1e12SWen Shi HALO_CORE_EN, HALO_CORE_EN); 3259170b1e12SWen Shi } 3260170b1e12SWen Shi 3261170b1e12SWen Shi static void wm_halo_stop_core(struct wm_adsp *dsp) 3262170b1e12SWen Shi { 3263170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 3264170b1e12SWen Shi HALO_CORE_EN, 0); 3265170b1e12SWen Shi 3266809589a9SCharles Keepax /* reset halo core with CORE_SOFT_RESET */ 3267170b1e12SWen Shi regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 3268170b1e12SWen Shi HALO_CORE_SOFT_RESET_MASK, 1); 3269170b1e12SWen Shi } 3270170b1e12SWen Shi 32710fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 3272f5e2ce92SRichard Fitzgerald { 3273af813a6fSCharles Keepax char preload[32]; 3274af813a6fSCharles Keepax 3275605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 327695a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 3277685f51a5SRichard Fitzgerald 32780fe1daa6SKuninori Morimoto wm_adsp2_init_debugfs(dsp, component); 3279f9f55e31SRichard Fitzgerald 32800fe1daa6SKuninori Morimoto dsp->component = component; 3281af813a6fSCharles Keepax 32820a047f07SRichard Fitzgerald return 0; 3283f5e2ce92SRichard Fitzgerald } 32840fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 3285f5e2ce92SRichard Fitzgerald 32860fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 3287f5e2ce92SRichard Fitzgerald { 3288f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 3289f9f55e31SRichard Fitzgerald 3290f5e2ce92SRichard Fitzgerald return 0; 3291f5e2ce92SRichard Fitzgerald } 32920fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 3293f5e2ce92SRichard Fitzgerald 329481ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 3295973838a0SMark Brown { 3296973838a0SMark Brown int ret; 3297973838a0SMark Brown 3298dcad34f8SRichard Fitzgerald ret = wm_adsp_common_init(dsp); 3299605391d0SRichard Fitzgerald if (ret) 3300605391d0SRichard Fitzgerald return ret; 3301605391d0SRichard Fitzgerald 3302e1ea1879SRichard Fitzgerald switch (dsp->rev) { 3303e1ea1879SRichard Fitzgerald case 0: 330410a2b662SMark Brown /* 330510a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 330610a2b662SMark Brown * power saving. 330710a2b662SMark Brown */ 33083809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 330910a2b662SMark Brown ADSP2_MEM_ENA, 0); 3310e1ea1879SRichard Fitzgerald if (ret) { 3311e1ea1879SRichard Fitzgerald adsp_err(dsp, 3312e1ea1879SRichard Fitzgerald "Failed to clear memory retention: %d\n", ret); 331310a2b662SMark Brown return ret; 331410a2b662SMark Brown } 33154e08d50dSCharles Keepax 33164e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[0]; 33174e08d50dSCharles Keepax break; 33184e08d50dSCharles Keepax case 1: 33194e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[1]; 3320e1ea1879SRichard Fitzgerald break; 3321e1ea1879SRichard Fitzgerald default: 33224e08d50dSCharles Keepax dsp->ops = &wm_adsp2_ops[2]; 3323e1ea1879SRichard Fitzgerald break; 3324e1ea1879SRichard Fitzgerald } 332510a2b662SMark Brown 33264e08d50dSCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 33276ab2b7b4SDimitris Papastamos 3328973838a0SMark Brown return 0; 3329973838a0SMark Brown } 3330973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 33310a37c6efSPraveen Diwakar 3332170b1e12SWen Shi int wm_halo_init(struct wm_adsp *dsp) 3333170b1e12SWen Shi { 3334170b1e12SWen Shi int ret; 3335170b1e12SWen Shi 3336170b1e12SWen Shi ret = wm_adsp_common_init(dsp); 3337170b1e12SWen Shi if (ret) 3338170b1e12SWen Shi return ret; 3339170b1e12SWen Shi 3340170b1e12SWen Shi dsp->ops = &wm_halo_ops; 3341170b1e12SWen Shi 3342170b1e12SWen Shi INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 3343170b1e12SWen Shi 3344170b1e12SWen Shi return 0; 3345170b1e12SWen Shi } 3346170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init); 3347170b1e12SWen Shi 334866225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 334966225e98SRichard Fitzgerald { 335066225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 335166225e98SRichard Fitzgerald 335266225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 335366225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 335466225e98SRichard Fitzgerald list); 335566225e98SRichard Fitzgerald list_del(&ctl->list); 335666225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 335766225e98SRichard Fitzgerald } 335866225e98SRichard Fitzgerald } 335966225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 336066225e98SRichard Fitzgerald 3361edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 3362edd71350SCharles Keepax { 3363edd71350SCharles Keepax return compr->buf != NULL; 3364edd71350SCharles Keepax } 3365edd71350SCharles Keepax 3366edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 3367edd71350SCharles Keepax { 33684f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf = NULL, *tmp; 33694f2d4eabSStuart Henderson 3370a2bcbc1bSCharles Keepax if (compr->dsp->fatal_error) 3371a2bcbc1bSCharles Keepax return -EINVAL; 3372a2bcbc1bSCharles Keepax 33734f2d4eabSStuart Henderson list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { 33744f2d4eabSStuart Henderson if (!tmp->name || !strcmp(compr->name, tmp->name)) { 33754f2d4eabSStuart Henderson buf = tmp; 33764f2d4eabSStuart Henderson break; 33774f2d4eabSStuart Henderson } 33784f2d4eabSStuart Henderson } 33794f2d4eabSStuart Henderson 33804f2d4eabSStuart Henderson if (!buf) 3381edd71350SCharles Keepax return -EINVAL; 3382edd71350SCharles Keepax 33834f2d4eabSStuart Henderson compr->buf = buf; 3384721be3beSCharles Keepax compr->buf->compr = compr; 3385edd71350SCharles Keepax 3386edd71350SCharles Keepax return 0; 3387edd71350SCharles Keepax } 3388edd71350SCharles Keepax 3389721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 3390721be3beSCharles Keepax { 3391721be3beSCharles Keepax if (!compr) 3392721be3beSCharles Keepax return; 3393721be3beSCharles Keepax 3394721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 3395721be3beSCharles Keepax if (compr->stream) 3396721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 3397721be3beSCharles Keepax 3398721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 3399721be3beSCharles Keepax compr->buf->compr = NULL; 3400721be3beSCharles Keepax compr->buf = NULL; 3401721be3beSCharles Keepax } 3402721be3beSCharles Keepax } 3403721be3beSCharles Keepax 3404406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 3405406abc95SCharles Keepax { 34064f2d4eabSStuart Henderson struct wm_adsp_compr *compr, *tmp; 34074f2d4eabSStuart Henderson struct snd_soc_pcm_runtime *rtd = stream->private_data; 3408406abc95SCharles Keepax int ret = 0; 3409406abc95SCharles Keepax 3410406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3411406abc95SCharles Keepax 3412406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 34130d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support compressed API\n", 34140d3fba3eSCharles Keepax rtd->codec_dai->name); 3415406abc95SCharles Keepax ret = -ENXIO; 3416406abc95SCharles Keepax goto out; 3417406abc95SCharles Keepax } 3418406abc95SCharles Keepax 3419406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 34200d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support stream direction\n", 34210d3fba3eSCharles Keepax rtd->codec_dai->name); 3422406abc95SCharles Keepax ret = -EINVAL; 3423406abc95SCharles Keepax goto out; 3424406abc95SCharles Keepax } 3425406abc95SCharles Keepax 34264f2d4eabSStuart Henderson list_for_each_entry(tmp, &dsp->compr_list, list) { 34274f2d4eabSStuart Henderson if (!strcmp(tmp->name, rtd->codec_dai->name)) { 34280d3fba3eSCharles Keepax adsp_err(dsp, "%s: Only a single stream supported per dai\n", 34290d3fba3eSCharles Keepax rtd->codec_dai->name); 343095fe9597SCharles Keepax ret = -EBUSY; 343195fe9597SCharles Keepax goto out; 343295fe9597SCharles Keepax } 34334f2d4eabSStuart Henderson } 343495fe9597SCharles Keepax 3435406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 3436406abc95SCharles Keepax if (!compr) { 3437406abc95SCharles Keepax ret = -ENOMEM; 3438406abc95SCharles Keepax goto out; 3439406abc95SCharles Keepax } 3440406abc95SCharles Keepax 3441406abc95SCharles Keepax compr->dsp = dsp; 3442406abc95SCharles Keepax compr->stream = stream; 34434f2d4eabSStuart Henderson compr->name = rtd->codec_dai->name; 3444406abc95SCharles Keepax 34454f2d4eabSStuart Henderson list_add_tail(&compr->list, &dsp->compr_list); 3446406abc95SCharles Keepax 3447406abc95SCharles Keepax stream->runtime->private_data = compr; 3448406abc95SCharles Keepax 3449406abc95SCharles Keepax out: 3450406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3451406abc95SCharles Keepax 3452406abc95SCharles Keepax return ret; 3453406abc95SCharles Keepax } 3454406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 3455406abc95SCharles Keepax 3456406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 3457406abc95SCharles Keepax { 3458406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3459406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3460406abc95SCharles Keepax 3461406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3462406abc95SCharles Keepax 3463721be3beSCharles Keepax wm_adsp_compr_detach(compr); 34644f2d4eabSStuart Henderson list_del(&compr->list); 3465406abc95SCharles Keepax 346683a40ce9SCharles Keepax kfree(compr->raw_buf); 3467406abc95SCharles Keepax kfree(compr); 3468406abc95SCharles Keepax 3469406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3470406abc95SCharles Keepax 3471406abc95SCharles Keepax return 0; 3472406abc95SCharles Keepax } 3473406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 3474406abc95SCharles Keepax 3475406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 3476406abc95SCharles Keepax struct snd_compr_params *params) 3477406abc95SCharles Keepax { 3478406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3479406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3480406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3481406abc95SCharles Keepax const struct snd_codec_desc *desc; 3482406abc95SCharles Keepax int i, j; 3483406abc95SCharles Keepax 3484406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 3485406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 3486406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 3487406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 3488406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 34890d3fba3eSCharles Keepax compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", 3490406abc95SCharles Keepax params->buffer.fragment_size, 3491406abc95SCharles Keepax params->buffer.fragments); 3492406abc95SCharles Keepax 3493406abc95SCharles Keepax return -EINVAL; 3494406abc95SCharles Keepax } 3495406abc95SCharles Keepax 3496406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 3497406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 3498406abc95SCharles Keepax desc = &caps->desc; 3499406abc95SCharles Keepax 3500406abc95SCharles Keepax if (caps->id != params->codec.id) 3501406abc95SCharles Keepax continue; 3502406abc95SCharles Keepax 3503406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 3504406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 3505406abc95SCharles Keepax continue; 3506406abc95SCharles Keepax } else { 3507406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 3508406abc95SCharles Keepax continue; 3509406abc95SCharles Keepax } 3510406abc95SCharles Keepax 3511406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 3512406abc95SCharles Keepax continue; 3513406abc95SCharles Keepax 3514406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 3515406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 3516406abc95SCharles Keepax return 0; 3517406abc95SCharles Keepax } 3518406abc95SCharles Keepax 35190d3fba3eSCharles Keepax compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 3520406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 3521406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 3522406abc95SCharles Keepax return -EINVAL; 3523406abc95SCharles Keepax } 3524406abc95SCharles Keepax 3525565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 3526565ace46SCharles Keepax { 3527565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 3528565ace46SCharles Keepax } 3529565ace46SCharles Keepax 3530406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 3531406abc95SCharles Keepax struct snd_compr_params *params) 3532406abc95SCharles Keepax { 3533406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 353483a40ce9SCharles Keepax unsigned int size; 3535406abc95SCharles Keepax int ret; 3536406abc95SCharles Keepax 3537406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 3538406abc95SCharles Keepax if (ret) 3539406abc95SCharles Keepax return ret; 3540406abc95SCharles Keepax 3541406abc95SCharles Keepax compr->size = params->buffer; 3542406abc95SCharles Keepax 35430d3fba3eSCharles Keepax compr_dbg(compr, "fragment_size=%d fragments=%d\n", 3544406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 3545406abc95SCharles Keepax 354683a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 354783a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 354883a40ce9SCharles Keepax if (!compr->raw_buf) 354983a40ce9SCharles Keepax return -ENOMEM; 355083a40ce9SCharles Keepax 3551da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 3552da2b3358SCharles Keepax 3553406abc95SCharles Keepax return 0; 3554406abc95SCharles Keepax } 3555406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 3556406abc95SCharles Keepax 3557406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 3558406abc95SCharles Keepax struct snd_compr_caps *caps) 3559406abc95SCharles Keepax { 3560406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3561406abc95SCharles Keepax int fw = compr->dsp->fw; 3562406abc95SCharles Keepax int i; 3563406abc95SCharles Keepax 3564406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 3565406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 3566406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 3567406abc95SCharles Keepax 3568406abc95SCharles Keepax caps->num_codecs = i; 3569406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 3570406abc95SCharles Keepax 3571406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 3572406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 3573406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 3574406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 3575406abc95SCharles Keepax } 3576406abc95SCharles Keepax 3577406abc95SCharles Keepax return 0; 3578406abc95SCharles Keepax } 3579406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 3580406abc95SCharles Keepax 35812cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 35822cd19bdbSCharles Keepax unsigned int mem_addr, 35832cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 35842cd19bdbSCharles Keepax { 35852cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 35862cd19bdbSCharles Keepax unsigned int i, reg; 35872cd19bdbSCharles Keepax int ret; 35882cd19bdbSCharles Keepax 35892cd19bdbSCharles Keepax if (!mem) 35902cd19bdbSCharles Keepax return -EINVAL; 35912cd19bdbSCharles Keepax 3592170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 35932cd19bdbSCharles Keepax 35942cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 35952cd19bdbSCharles Keepax sizeof(*data) * num_words); 35962cd19bdbSCharles Keepax if (ret < 0) 35972cd19bdbSCharles Keepax return ret; 35982cd19bdbSCharles Keepax 35992cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 36002cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 36012cd19bdbSCharles Keepax 36022cd19bdbSCharles Keepax return 0; 36032cd19bdbSCharles Keepax } 36042cd19bdbSCharles Keepax 36052cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 36062cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 36072cd19bdbSCharles Keepax { 36082cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 36092cd19bdbSCharles Keepax } 36102cd19bdbSCharles Keepax 36112cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 36122cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 36132cd19bdbSCharles Keepax { 36142cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 36152cd19bdbSCharles Keepax unsigned int reg; 36162cd19bdbSCharles Keepax 36172cd19bdbSCharles Keepax if (!mem) 36182cd19bdbSCharles Keepax return -EINVAL; 36192cd19bdbSCharles Keepax 3620170b1e12SWen Shi reg = dsp->ops->region_to_reg(mem, mem_addr); 36212cd19bdbSCharles Keepax 36222cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 36232cd19bdbSCharles Keepax 36242cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 36252cd19bdbSCharles Keepax } 36262cd19bdbSCharles Keepax 36272cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 36282cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 36292cd19bdbSCharles Keepax { 3630fb13f19dSAndrew Ford return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, 36312cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 36322cd19bdbSCharles Keepax } 36332cd19bdbSCharles Keepax 36342cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 36352cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 36362cd19bdbSCharles Keepax { 3637fb13f19dSAndrew Ford return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, 36382cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 36392cd19bdbSCharles Keepax } 36402cd19bdbSCharles Keepax 3641cc7d6ce9SCharles Keepax static void wm_adsp_remove_padding(u32 *buf, int nwords, int data_word_size) 3642cc7d6ce9SCharles Keepax { 3643cc7d6ce9SCharles Keepax u8 *pack_in = (u8 *)buf; 3644cc7d6ce9SCharles Keepax u8 *pack_out = (u8 *)buf; 3645cc7d6ce9SCharles Keepax int i, j; 3646cc7d6ce9SCharles Keepax 3647cc7d6ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 3648cc7d6ce9SCharles Keepax for (i = 0; i < nwords; i++) { 3649cc7d6ce9SCharles Keepax for (j = 0; j < data_word_size; j++) 3650cc7d6ce9SCharles Keepax *pack_out++ = *pack_in++; 3651cc7d6ce9SCharles Keepax 3652cc7d6ce9SCharles Keepax pack_in += sizeof(*buf) - data_word_size; 3653cc7d6ce9SCharles Keepax } 3654cc7d6ce9SCharles Keepax } 3655cc7d6ce9SCharles Keepax 36561e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 36571e38f069SCharles Keepax { 36581e38f069SCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 36591e38f069SCharles Keepax struct wm_adsp_buffer_region *region; 36601e38f069SCharles Keepax u32 offset = 0; 36611e38f069SCharles Keepax int i, ret; 36621e38f069SCharles Keepax 3663a792af69SCharles Keepax buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), 3664a792af69SCharles Keepax GFP_KERNEL); 3665a792af69SCharles Keepax if (!buf->regions) 3666a792af69SCharles Keepax return -ENOMEM; 3667a792af69SCharles Keepax 36681e38f069SCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 36691e38f069SCharles Keepax region = &buf->regions[i]; 36701e38f069SCharles Keepax 36711e38f069SCharles Keepax region->offset = offset; 36721e38f069SCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 36731e38f069SCharles Keepax 36741e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 36751e38f069SCharles Keepax ®ion->base_addr); 36761e38f069SCharles Keepax if (ret < 0) 36771e38f069SCharles Keepax return ret; 36781e38f069SCharles Keepax 36791e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 36801e38f069SCharles Keepax &offset); 36811e38f069SCharles Keepax if (ret < 0) 36821e38f069SCharles Keepax return ret; 36831e38f069SCharles Keepax 36841e38f069SCharles Keepax region->cumulative_size = offset; 36851e38f069SCharles Keepax 36860d3fba3eSCharles Keepax compr_dbg(buf, 36871e38f069SCharles Keepax "region=%d type=%d base=%08x off=%08x size=%08x\n", 36881e38f069SCharles Keepax i, region->mem_type, region->base_addr, 36891e38f069SCharles Keepax region->offset, region->cumulative_size); 36901e38f069SCharles Keepax } 36911e38f069SCharles Keepax 36921e38f069SCharles Keepax return 0; 36931e38f069SCharles Keepax } 36941e38f069SCharles Keepax 36951e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 36961e38f069SCharles Keepax { 36971e38f069SCharles Keepax buf->irq_count = 0xFFFFFFFF; 36981e38f069SCharles Keepax buf->read_index = -1; 36991e38f069SCharles Keepax buf->avail = 0; 37001e38f069SCharles Keepax } 37011e38f069SCharles Keepax 3702a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) 3703a792af69SCharles Keepax { 3704a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3705a792af69SCharles Keepax 3706a792af69SCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3707a792af69SCharles Keepax if (!buf) 3708a792af69SCharles Keepax return NULL; 3709a792af69SCharles Keepax 3710a792af69SCharles Keepax buf->dsp = dsp; 3711a792af69SCharles Keepax 3712a792af69SCharles Keepax wm_adsp_buffer_clear(buf); 3713a792af69SCharles Keepax 37144f2d4eabSStuart Henderson list_add_tail(&buf->list, &dsp->buffer_list); 3715a792af69SCharles Keepax 3716a792af69SCharles Keepax return buf; 3717a792af69SCharles Keepax } 3718a792af69SCharles Keepax 3719a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) 37202cd19bdbSCharles Keepax { 37212cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 3722a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 37232cd19bdbSCharles Keepax u32 xmalg, addr, magic; 37242cd19bdbSCharles Keepax int i, ret; 37252cd19bdbSCharles Keepax 3726a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(dsp); 3727a792af69SCharles Keepax if (!buf) 3728a792af69SCharles Keepax return -ENOMEM; 3729a792af69SCharles Keepax 37302cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 3731170b1e12SWen Shi xmalg = dsp->ops->sys_config_size / sizeof(__be32); 37322cd19bdbSCharles Keepax 37332cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 37342cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 37352cd19bdbSCharles Keepax if (ret < 0) 37362cd19bdbSCharles Keepax return ret; 37372cd19bdbSCharles Keepax 37382cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 3739a792af69SCharles Keepax return -ENODEV; 37402cd19bdbSCharles Keepax 37412cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 37422cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 37432cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 37442cd19bdbSCharles Keepax &buf->host_buf_ptr); 37452cd19bdbSCharles Keepax if (ret < 0) 37462cd19bdbSCharles Keepax return ret; 37472cd19bdbSCharles Keepax 37482cd19bdbSCharles Keepax if (buf->host_buf_ptr) 37492cd19bdbSCharles Keepax break; 37502cd19bdbSCharles Keepax 37512cd19bdbSCharles Keepax usleep_range(1000, 2000); 37522cd19bdbSCharles Keepax } 37532cd19bdbSCharles Keepax 37542cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 37552cd19bdbSCharles Keepax return -EIO; 37562cd19bdbSCharles Keepax 3757fb13f19dSAndrew Ford buf->host_buf_mem_type = WMFW_ADSP2_XM; 3758fb13f19dSAndrew Ford 3759a792af69SCharles Keepax ret = wm_adsp_buffer_populate(buf); 3760a792af69SCharles Keepax if (ret < 0) 3761a792af69SCharles Keepax return ret; 3762a792af69SCharles Keepax 37630d3fba3eSCharles Keepax compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); 37642cd19bdbSCharles Keepax 37652cd19bdbSCharles Keepax return 0; 37662cd19bdbSCharles Keepax } 37672cd19bdbSCharles Keepax 3768a792af69SCharles Keepax static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) 3769d52ed4b0SRichard Fitzgerald { 37704f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 coeff_v1; 3771a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3772a792af69SCharles Keepax unsigned int val, reg; 3773a792af69SCharles Keepax int ret, i; 3774d52ed4b0SRichard Fitzgerald 3775d52ed4b0SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 3776d52ed4b0SRichard Fitzgerald if (ret) 3777d52ed4b0SRichard Fitzgerald return ret; 3778d52ed4b0SRichard Fitzgerald 3779d52ed4b0SRichard Fitzgerald for (i = 0; i < 5; ++i) { 3780a792af69SCharles Keepax ret = regmap_raw_read(ctl->dsp->regmap, reg, &val, sizeof(val)); 3781d52ed4b0SRichard Fitzgerald if (ret < 0) 3782d52ed4b0SRichard Fitzgerald return ret; 3783d52ed4b0SRichard Fitzgerald 3784d52ed4b0SRichard Fitzgerald if (val) 3785d52ed4b0SRichard Fitzgerald break; 3786d52ed4b0SRichard Fitzgerald 3787d52ed4b0SRichard Fitzgerald usleep_range(1000, 2000); 3788d52ed4b0SRichard Fitzgerald } 3789d52ed4b0SRichard Fitzgerald 3790a792af69SCharles Keepax if (!val) { 3791a792af69SCharles Keepax adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); 3792d52ed4b0SRichard Fitzgerald return -EIO; 3793d52ed4b0SRichard Fitzgerald } 3794d52ed4b0SRichard Fitzgerald 3795a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(ctl->dsp); 37962cd19bdbSCharles Keepax if (!buf) 37972cd19bdbSCharles Keepax return -ENOMEM; 37982cd19bdbSCharles Keepax 3799a792af69SCharles Keepax buf->host_buf_mem_type = ctl->alg_region.type; 3800a792af69SCharles Keepax buf->host_buf_ptr = be32_to_cpu(val); 38012cd19bdbSCharles Keepax 38022cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 3803a792af69SCharles Keepax if (ret < 0) 3804a792af69SCharles Keepax return ret; 3805a792af69SCharles Keepax 38064f2d4eabSStuart Henderson /* 38074f2d4eabSStuart Henderson * v0 host_buffer coefficients didn't have versioning, so if the 38084f2d4eabSStuart Henderson * control is one word, assume version 0. 38094f2d4eabSStuart Henderson */ 38104f2d4eabSStuart Henderson if (ctl->len == 4) { 38110d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); 3812a792af69SCharles Keepax return 0; 38132cd19bdbSCharles Keepax } 38142cd19bdbSCharles Keepax 38154f2d4eabSStuart Henderson ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, 38164f2d4eabSStuart Henderson sizeof(coeff_v1)); 38174f2d4eabSStuart Henderson if (ret < 0) 38184f2d4eabSStuart Henderson return ret; 38194f2d4eabSStuart Henderson 38204f2d4eabSStuart Henderson coeff_v1.versions = be32_to_cpu(coeff_v1.versions); 38214f2d4eabSStuart Henderson val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK; 38224f2d4eabSStuart Henderson val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; 38234f2d4eabSStuart Henderson 38244f2d4eabSStuart Henderson if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { 38254f2d4eabSStuart Henderson adsp_err(ctl->dsp, 38264f2d4eabSStuart Henderson "Host buffer coeff ver %u > supported version %u\n", 38274f2d4eabSStuart Henderson val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); 38284f2d4eabSStuart Henderson return -EINVAL; 38294f2d4eabSStuart Henderson } 38304f2d4eabSStuart Henderson 38314f2d4eabSStuart Henderson for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++) 38324f2d4eabSStuart Henderson coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]); 38334f2d4eabSStuart Henderson 38344f2d4eabSStuart Henderson wm_adsp_remove_padding((u32 *)&coeff_v1.name, 38354f2d4eabSStuart Henderson ARRAY_SIZE(coeff_v1.name), 38364f2d4eabSStuart Henderson WM_ADSP_DATA_WORD_SIZE); 38374f2d4eabSStuart Henderson 38384f2d4eabSStuart Henderson buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, 38394f2d4eabSStuart Henderson (char *)&coeff_v1.name); 38404f2d4eabSStuart Henderson 38410d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", 38424f2d4eabSStuart Henderson buf->host_buf_ptr, val); 38434f2d4eabSStuart Henderson 38444f2d4eabSStuart Henderson return val; 38454f2d4eabSStuart Henderson } 38464f2d4eabSStuart Henderson 3847a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 3848a792af69SCharles Keepax { 3849a792af69SCharles Keepax struct wm_coeff_ctl *ctl; 3850a792af69SCharles Keepax int ret; 3851a792af69SCharles Keepax 3852a792af69SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 3853a792af69SCharles Keepax if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 3854a792af69SCharles Keepax continue; 3855a792af69SCharles Keepax 3856a792af69SCharles Keepax if (!ctl->enabled) 3857a792af69SCharles Keepax continue; 3858a792af69SCharles Keepax 3859a792af69SCharles Keepax ret = wm_adsp_buffer_parse_coeff(ctl); 3860a792af69SCharles Keepax if (ret < 0) { 3861a792af69SCharles Keepax adsp_err(dsp, "Failed to parse coeff: %d\n", ret); 3862a792af69SCharles Keepax goto error; 38634f2d4eabSStuart Henderson } else if (ret == 0) { 38644f2d4eabSStuart Henderson /* Only one buffer supported for version 0 */ 3865a792af69SCharles Keepax return 0; 3866a792af69SCharles Keepax } 38674f2d4eabSStuart Henderson } 3868a792af69SCharles Keepax 38694f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 3870a792af69SCharles Keepax /* Fall back to legacy support */ 3871a792af69SCharles Keepax ret = wm_adsp_buffer_parse_legacy(dsp); 3872a792af69SCharles Keepax if (ret) { 3873a792af69SCharles Keepax adsp_err(dsp, "Failed to parse legacy: %d\n", ret); 3874a792af69SCharles Keepax goto error; 3875a792af69SCharles Keepax } 3876a792af69SCharles Keepax } 38772cd19bdbSCharles Keepax 38782cd19bdbSCharles Keepax return 0; 38792cd19bdbSCharles Keepax 3880a792af69SCharles Keepax error: 3881a792af69SCharles Keepax wm_adsp_buffer_free(dsp); 38822cd19bdbSCharles Keepax return ret; 38832cd19bdbSCharles Keepax } 38842cd19bdbSCharles Keepax 38852cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 38862cd19bdbSCharles Keepax { 38874f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf, *tmp; 3888721be3beSCharles Keepax 38894f2d4eabSStuart Henderson list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { 38904f2d4eabSStuart Henderson if (buf->compr) 38914f2d4eabSStuart Henderson wm_adsp_compr_detach(buf->compr); 38922cd19bdbSCharles Keepax 38934f2d4eabSStuart Henderson kfree(buf->name); 38944f2d4eabSStuart Henderson kfree(buf->regions); 38954f2d4eabSStuart Henderson list_del(&buf->list); 38964f2d4eabSStuart Henderson kfree(buf); 38972cd19bdbSCharles Keepax } 38982cd19bdbSCharles Keepax 38992cd19bdbSCharles Keepax return 0; 39002cd19bdbSCharles Keepax } 39012cd19bdbSCharles Keepax 3902f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 3903f938f348SStuart Henderson { 3904f938f348SStuart Henderson int ret; 3905f938f348SStuart Henderson 3906f938f348SStuart Henderson ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 3907f938f348SStuart Henderson if (ret < 0) { 390848ead31cSCharles Keepax compr_err(buf, "Failed to check buffer error: %d\n", ret); 3909f938f348SStuart Henderson return ret; 3910f938f348SStuart Henderson } 3911f938f348SStuart Henderson if (buf->error != 0) { 391248ead31cSCharles Keepax compr_err(buf, "Buffer error occurred: %d\n", buf->error); 3913f938f348SStuart Henderson return -EIO; 3914f938f348SStuart Henderson } 3915f938f348SStuart Henderson 3916f938f348SStuart Henderson return 0; 3917f938f348SStuart Henderson } 3918f938f348SStuart Henderson 391995fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 392095fe9597SCharles Keepax { 392195fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 392295fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 392395fe9597SCharles Keepax int ret = 0; 392495fe9597SCharles Keepax 39250d3fba3eSCharles Keepax compr_dbg(compr, "Trigger: %d\n", cmd); 392695fe9597SCharles Keepax 392795fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 392895fe9597SCharles Keepax 392995fe9597SCharles Keepax switch (cmd) { 393095fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 393161fc060cSCharles Keepax if (!wm_adsp_compr_attached(compr)) { 393295fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 393395fe9597SCharles Keepax if (ret < 0) { 39340d3fba3eSCharles Keepax compr_err(compr, "Failed to link buffer and stream: %d\n", 393595fe9597SCharles Keepax ret); 393695fe9597SCharles Keepax break; 393795fe9597SCharles Keepax } 393861fc060cSCharles Keepax } 393961fc060cSCharles Keepax 3940f938f348SStuart Henderson ret = wm_adsp_buffer_get_error(compr->buf); 3941f938f348SStuart Henderson if (ret < 0) 3942f938f348SStuart Henderson break; 3943f938f348SStuart Henderson 3944565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3945565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3946565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3947565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3948565ace46SCharles Keepax if (ret < 0) { 39490d3fba3eSCharles Keepax compr_err(compr, "Failed to set high water mark: %d\n", 3950565ace46SCharles Keepax ret); 3951565ace46SCharles Keepax break; 3952565ace46SCharles Keepax } 395395fe9597SCharles Keepax break; 395495fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 3955639e5eb3SCharles Keepax wm_adsp_buffer_clear(compr->buf); 395695fe9597SCharles Keepax break; 395795fe9597SCharles Keepax default: 395895fe9597SCharles Keepax ret = -EINVAL; 395995fe9597SCharles Keepax break; 396095fe9597SCharles Keepax } 396195fe9597SCharles Keepax 396295fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 396395fe9597SCharles Keepax 396495fe9597SCharles Keepax return ret; 396595fe9597SCharles Keepax } 396695fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 396795fe9597SCharles Keepax 3968565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3969565ace46SCharles Keepax { 3970565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3971565ace46SCharles Keepax 3972565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3973565ace46SCharles Keepax } 3974565ace46SCharles Keepax 3975565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3976565ace46SCharles Keepax { 3977565ace46SCharles Keepax u32 next_read_index, next_write_index; 3978565ace46SCharles Keepax int write_index, read_index, avail; 3979565ace46SCharles Keepax int ret; 3980565ace46SCharles Keepax 3981565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3982565ace46SCharles Keepax if (buf->read_index < 0) { 3983565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3984565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3985565ace46SCharles Keepax &next_read_index); 3986565ace46SCharles Keepax if (ret < 0) 3987565ace46SCharles Keepax return ret; 3988565ace46SCharles Keepax 3989565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3990565ace46SCharles Keepax 3991565ace46SCharles Keepax if (read_index < 0) { 39920d3fba3eSCharles Keepax compr_dbg(buf, "Avail check on unstarted stream\n"); 3993565ace46SCharles Keepax return 0; 3994565ace46SCharles Keepax } 3995565ace46SCharles Keepax 3996565ace46SCharles Keepax buf->read_index = read_index; 3997565ace46SCharles Keepax } 3998565ace46SCharles Keepax 3999565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 4000565ace46SCharles Keepax &next_write_index); 4001565ace46SCharles Keepax if (ret < 0) 4002565ace46SCharles Keepax return ret; 4003565ace46SCharles Keepax 4004565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 4005565ace46SCharles Keepax 4006565ace46SCharles Keepax avail = write_index - buf->read_index; 4007565ace46SCharles Keepax if (avail < 0) 4008565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 4009565ace46SCharles Keepax 40100d3fba3eSCharles Keepax compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 401133d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 4012565ace46SCharles Keepax 4013565ace46SCharles Keepax buf->avail = avail; 4014565ace46SCharles Keepax 4015565ace46SCharles Keepax return 0; 4016565ace46SCharles Keepax } 4017565ace46SCharles Keepax 4018565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 4019565ace46SCharles Keepax { 4020612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 4021612047f0SCharles Keepax struct wm_adsp_compr *compr; 4022565ace46SCharles Keepax int ret = 0; 4023565ace46SCharles Keepax 4024565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 4025565ace46SCharles Keepax 40264f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 4027565ace46SCharles Keepax ret = -ENODEV; 4028565ace46SCharles Keepax goto out; 4029565ace46SCharles Keepax } 40300d3fba3eSCharles Keepax 4031565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 4032565ace46SCharles Keepax 40334f2d4eabSStuart Henderson list_for_each_entry(buf, &dsp->buffer_list, list) { 40344f2d4eabSStuart Henderson compr = buf->compr; 40354f2d4eabSStuart Henderson 40369771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 40379771b18aSCharles Keepax if (ret < 0) 40385847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 4039565ace46SCharles Keepax 4040565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 4041565ace46SCharles Keepax &buf->irq_count); 4042565ace46SCharles Keepax if (ret < 0) { 40430d3fba3eSCharles Keepax compr_err(buf, "Failed to get irq_count: %d\n", ret); 4044565ace46SCharles Keepax goto out; 4045565ace46SCharles Keepax } 4046565ace46SCharles Keepax 4047565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4048565ace46SCharles Keepax if (ret < 0) { 40490d3fba3eSCharles Keepax compr_err(buf, "Error reading avail: %d\n", ret); 4050565ace46SCharles Keepax goto out; 4051565ace46SCharles Keepax } 4052565ace46SCharles Keepax 405320b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 405420b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 405520b7f7c5SCharles Keepax 40565847609eSCharles Keepax out_notify: 4057c7dae7c4SCharles Keepax if (compr && compr->stream) 405883a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 40594f2d4eabSStuart Henderson } 406083a40ce9SCharles Keepax 4061565ace46SCharles Keepax out: 4062565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4063565ace46SCharles Keepax 4064565ace46SCharles Keepax return ret; 4065565ace46SCharles Keepax } 4066565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 4067565ace46SCharles Keepax 4068565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 4069565ace46SCharles Keepax { 4070565ace46SCharles Keepax if (buf->irq_count & 0x01) 4071565ace46SCharles Keepax return 0; 4072565ace46SCharles Keepax 40730d3fba3eSCharles Keepax compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); 4074565ace46SCharles Keepax 4075565ace46SCharles Keepax buf->irq_count |= 0x01; 4076565ace46SCharles Keepax 4077565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 4078565ace46SCharles Keepax buf->irq_count); 4079565ace46SCharles Keepax } 4080565ace46SCharles Keepax 4081565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 4082565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 4083565ace46SCharles Keepax { 4084565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 4085565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 4086612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 4087565ace46SCharles Keepax int ret = 0; 4088565ace46SCharles Keepax 40890d3fba3eSCharles Keepax compr_dbg(compr, "Pointer request\n"); 4090565ace46SCharles Keepax 4091565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 4092565ace46SCharles Keepax 4093612047f0SCharles Keepax buf = compr->buf; 4094612047f0SCharles Keepax 409528ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 40968d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 4097565ace46SCharles Keepax ret = -EIO; 4098565ace46SCharles Keepax goto out; 4099565ace46SCharles Keepax } 4100565ace46SCharles Keepax 4101565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 4102565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 4103565ace46SCharles Keepax if (ret < 0) { 41040d3fba3eSCharles Keepax compr_err(compr, "Error reading avail: %d\n", ret); 4105565ace46SCharles Keepax goto out; 4106565ace46SCharles Keepax } 4107565ace46SCharles Keepax 4108565ace46SCharles Keepax /* 4109565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 4110565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 4111565ace46SCharles Keepax */ 4112565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 41135847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 41148d280664SCharles Keepax if (ret < 0) { 41158d280664SCharles Keepax if (compr->buf->error) 41168d280664SCharles Keepax snd_compr_stop_error(stream, 41178d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 41185847609eSCharles Keepax goto out; 41198d280664SCharles Keepax } 41205847609eSCharles Keepax 4121565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 4122565ace46SCharles Keepax if (ret < 0) { 41230d3fba3eSCharles Keepax compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", 4124565ace46SCharles Keepax ret); 4125565ace46SCharles Keepax goto out; 4126565ace46SCharles Keepax } 4127565ace46SCharles Keepax } 4128565ace46SCharles Keepax } 4129565ace46SCharles Keepax 4130565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 4131565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 4132da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 4133565ace46SCharles Keepax 4134565ace46SCharles Keepax out: 4135565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 4136565ace46SCharles Keepax 4137565ace46SCharles Keepax return ret; 4138565ace46SCharles Keepax } 4139565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 4140565ace46SCharles Keepax 414183a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 414283a40ce9SCharles Keepax { 414383a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 414483a40ce9SCharles Keepax unsigned int adsp_addr; 414583a40ce9SCharles Keepax int mem_type, nwords, max_read; 4146cc7d6ce9SCharles Keepax int i, ret; 414783a40ce9SCharles Keepax 414883a40ce9SCharles Keepax /* Calculate read parameters */ 414983a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 415083a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 415183a40ce9SCharles Keepax break; 415283a40ce9SCharles Keepax 415383a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 415483a40ce9SCharles Keepax return -EINVAL; 415583a40ce9SCharles Keepax 415683a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 415783a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 415883a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 415983a40ce9SCharles Keepax 416083a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 416183a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 416283a40ce9SCharles Keepax 416383a40ce9SCharles Keepax if (nwords > target) 416483a40ce9SCharles Keepax nwords = target; 416583a40ce9SCharles Keepax if (nwords > buf->avail) 416683a40ce9SCharles Keepax nwords = buf->avail; 416783a40ce9SCharles Keepax if (nwords > max_read) 416883a40ce9SCharles Keepax nwords = max_read; 416983a40ce9SCharles Keepax if (!nwords) 417083a40ce9SCharles Keepax return 0; 417183a40ce9SCharles Keepax 417283a40ce9SCharles Keepax /* Read data from DSP */ 417383a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 417483a40ce9SCharles Keepax nwords, compr->raw_buf); 417583a40ce9SCharles Keepax if (ret < 0) 417683a40ce9SCharles Keepax return ret; 417783a40ce9SCharles Keepax 4178cc7d6ce9SCharles Keepax wm_adsp_remove_padding(compr->raw_buf, nwords, WM_ADSP_DATA_WORD_SIZE); 417983a40ce9SCharles Keepax 418083a40ce9SCharles Keepax /* update read index to account for words read */ 418183a40ce9SCharles Keepax buf->read_index += nwords; 418283a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 418383a40ce9SCharles Keepax buf->read_index = 0; 418483a40ce9SCharles Keepax 418583a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 418683a40ce9SCharles Keepax buf->read_index); 418783a40ce9SCharles Keepax if (ret < 0) 418883a40ce9SCharles Keepax return ret; 418983a40ce9SCharles Keepax 419083a40ce9SCharles Keepax /* update avail to account for words read */ 419183a40ce9SCharles Keepax buf->avail -= nwords; 419283a40ce9SCharles Keepax 419383a40ce9SCharles Keepax return nwords; 419483a40ce9SCharles Keepax } 419583a40ce9SCharles Keepax 419683a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 419783a40ce9SCharles Keepax char __user *buf, size_t count) 419883a40ce9SCharles Keepax { 419983a40ce9SCharles Keepax int ntotal = 0; 420083a40ce9SCharles Keepax int nwords, nbytes; 420183a40ce9SCharles Keepax 42020d3fba3eSCharles Keepax compr_dbg(compr, "Requested read of %zu bytes\n", count); 420383a40ce9SCharles Keepax 420428ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 42058d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 420683a40ce9SCharles Keepax return -EIO; 42078d280664SCharles Keepax } 420883a40ce9SCharles Keepax 420983a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 421083a40ce9SCharles Keepax 421183a40ce9SCharles Keepax do { 421283a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 421383a40ce9SCharles Keepax if (nwords < 0) { 42140d3fba3eSCharles Keepax compr_err(compr, "Failed to capture block: %d\n", 42150d3fba3eSCharles Keepax nwords); 421683a40ce9SCharles Keepax return nwords; 421783a40ce9SCharles Keepax } 421883a40ce9SCharles Keepax 421983a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 422083a40ce9SCharles Keepax 42210d3fba3eSCharles Keepax compr_dbg(compr, "Read %d bytes\n", nbytes); 422283a40ce9SCharles Keepax 422383a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 42240d3fba3eSCharles Keepax compr_err(compr, "Failed to copy data to user: %d, %d\n", 422583a40ce9SCharles Keepax ntotal, nbytes); 422683a40ce9SCharles Keepax return -EFAULT; 422783a40ce9SCharles Keepax } 422883a40ce9SCharles Keepax 422983a40ce9SCharles Keepax count -= nwords; 423083a40ce9SCharles Keepax ntotal += nbytes; 423183a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 423283a40ce9SCharles Keepax 423383a40ce9SCharles Keepax compr->copied_total += ntotal; 423483a40ce9SCharles Keepax 423583a40ce9SCharles Keepax return ntotal; 423683a40ce9SCharles Keepax } 423783a40ce9SCharles Keepax 423883a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 423983a40ce9SCharles Keepax size_t count) 424083a40ce9SCharles Keepax { 424183a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 424283a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 424383a40ce9SCharles Keepax int ret; 424483a40ce9SCharles Keepax 424583a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 424683a40ce9SCharles Keepax 424783a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 424883a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 424983a40ce9SCharles Keepax else 425083a40ce9SCharles Keepax ret = -ENOTSUPP; 425183a40ce9SCharles Keepax 425283a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 425383a40ce9SCharles Keepax 425483a40ce9SCharles Keepax return ret; 425583a40ce9SCharles Keepax } 425683a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 425783a40ce9SCharles Keepax 4258a2bcbc1bSCharles Keepax static void wm_adsp_fatal_error(struct wm_adsp *dsp) 4259a2bcbc1bSCharles Keepax { 4260a2bcbc1bSCharles Keepax struct wm_adsp_compr *compr; 4261a2bcbc1bSCharles Keepax 4262a2bcbc1bSCharles Keepax dsp->fatal_error = true; 4263a2bcbc1bSCharles Keepax 4264a2bcbc1bSCharles Keepax list_for_each_entry(compr, &dsp->compr_list, list) { 4265a2bcbc1bSCharles Keepax if (compr->stream) { 4266a2bcbc1bSCharles Keepax snd_compr_stop_error(compr->stream, 4267a2bcbc1bSCharles Keepax SNDRV_PCM_STATE_XRUN); 4268a2bcbc1bSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 4269a2bcbc1bSCharles Keepax } 4270a2bcbc1bSCharles Keepax } 4271a2bcbc1bSCharles Keepax } 4272a2bcbc1bSCharles Keepax 427351a2c944SMayuresh Kulkarni irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp) 427451a2c944SMayuresh Kulkarni { 427551a2c944SMayuresh Kulkarni unsigned int val; 427651a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 427751a2c944SMayuresh Kulkarni int ret = 0; 427851a2c944SMayuresh Kulkarni 4279a2225a6dSCharles Keepax mutex_lock(&dsp->pwr_lock); 4280a2225a6dSCharles Keepax 428151a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 428251a2c944SMayuresh Kulkarni if (ret) { 428351a2c944SMayuresh Kulkarni adsp_err(dsp, 428451a2c944SMayuresh Kulkarni "Failed to read Region Lock Ctrl register: %d\n", ret); 4285a2225a6dSCharles Keepax goto error; 428651a2c944SMayuresh Kulkarni } 428751a2c944SMayuresh Kulkarni 428851a2c944SMayuresh Kulkarni if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 428951a2c944SMayuresh Kulkarni adsp_err(dsp, "watchdog timeout error\n"); 429051a2c944SMayuresh Kulkarni wm_adsp_stop_watchdog(dsp); 4291a2bcbc1bSCharles Keepax wm_adsp_fatal_error(dsp); 429251a2c944SMayuresh Kulkarni } 429351a2c944SMayuresh Kulkarni 429451a2c944SMayuresh Kulkarni if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 429551a2c944SMayuresh Kulkarni if (val & ADSP2_SLAVE_ERR_MASK) 429651a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: slave error\n"); 429751a2c944SMayuresh Kulkarni else 429851a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: region lock error\n"); 429951a2c944SMayuresh Kulkarni 430051a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 430151a2c944SMayuresh Kulkarni if (ret) { 430251a2c944SMayuresh Kulkarni adsp_err(dsp, 430351a2c944SMayuresh Kulkarni "Failed to read Bus Err Addr register: %d\n", 430451a2c944SMayuresh Kulkarni ret); 4305a2225a6dSCharles Keepax goto error; 430651a2c944SMayuresh Kulkarni } 430751a2c944SMayuresh Kulkarni 430851a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error address = 0x%x\n", 430951a2c944SMayuresh Kulkarni val & ADSP2_BUS_ERR_ADDR_MASK); 431051a2c944SMayuresh Kulkarni 431151a2c944SMayuresh Kulkarni ret = regmap_read(regmap, 431251a2c944SMayuresh Kulkarni dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 431351a2c944SMayuresh Kulkarni &val); 431451a2c944SMayuresh Kulkarni if (ret) { 431551a2c944SMayuresh Kulkarni adsp_err(dsp, 431651a2c944SMayuresh Kulkarni "Failed to read Pmem Xmem Err Addr register: %d\n", 431751a2c944SMayuresh Kulkarni ret); 4318a2225a6dSCharles Keepax goto error; 431951a2c944SMayuresh Kulkarni } 432051a2c944SMayuresh Kulkarni 432151a2c944SMayuresh Kulkarni adsp_err(dsp, "xmem error address = 0x%x\n", 432251a2c944SMayuresh Kulkarni val & ADSP2_XMEM_ERR_ADDR_MASK); 432351a2c944SMayuresh Kulkarni adsp_err(dsp, "pmem error address = 0x%x\n", 432451a2c944SMayuresh Kulkarni (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 432551a2c944SMayuresh Kulkarni ADSP2_PMEM_ERR_ADDR_SHIFT); 432651a2c944SMayuresh Kulkarni } 432751a2c944SMayuresh Kulkarni 432851a2c944SMayuresh Kulkarni regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 432951a2c944SMayuresh Kulkarni ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 433051a2c944SMayuresh Kulkarni 4331a2225a6dSCharles Keepax error: 4332a2225a6dSCharles Keepax mutex_unlock(&dsp->pwr_lock); 4333a2225a6dSCharles Keepax 433451a2c944SMayuresh Kulkarni return IRQ_HANDLED; 433551a2c944SMayuresh Kulkarni } 433651a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 433751a2c944SMayuresh Kulkarni 43382ae58138SRichard Fitzgerald irqreturn_t wm_halo_bus_error(struct wm_adsp *dsp) 43392ae58138SRichard Fitzgerald { 43402ae58138SRichard Fitzgerald struct regmap *regmap = dsp->regmap; 43412ae58138SRichard Fitzgerald unsigned int fault[6]; 43422ae58138SRichard Fitzgerald struct reg_sequence clear[] = { 43432ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 43442ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 43452ae58138SRichard Fitzgerald { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 43462ae58138SRichard Fitzgerald }; 43472ae58138SRichard Fitzgerald int ret; 43482ae58138SRichard Fitzgerald 43492ae58138SRichard Fitzgerald mutex_lock(&dsp->pwr_lock); 43502ae58138SRichard Fitzgerald 43512ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 43522ae58138SRichard Fitzgerald fault); 43532ae58138SRichard Fitzgerald if (ret) { 43542ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 43552ae58138SRichard Fitzgerald goto exit_unlock; 43562ae58138SRichard Fitzgerald } 43572ae58138SRichard Fitzgerald 43582ae58138SRichard Fitzgerald adsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 43592ae58138SRichard Fitzgerald *fault & HALO_AHBM_FLAGS_ERR_MASK, 43602ae58138SRichard Fitzgerald (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 43612ae58138SRichard Fitzgerald HALO_AHBM_CORE_ERR_ADDR_SHIFT); 43622ae58138SRichard Fitzgerald 43632ae58138SRichard Fitzgerald ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 43642ae58138SRichard Fitzgerald fault); 43652ae58138SRichard Fitzgerald if (ret) { 43662ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 43672ae58138SRichard Fitzgerald goto exit_unlock; 43682ae58138SRichard Fitzgerald } 43692ae58138SRichard Fitzgerald 43702ae58138SRichard Fitzgerald adsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 43712ae58138SRichard Fitzgerald 43722ae58138SRichard Fitzgerald ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 43732ae58138SRichard Fitzgerald fault, ARRAY_SIZE(fault)); 43742ae58138SRichard Fitzgerald if (ret) { 43752ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 43762ae58138SRichard Fitzgerald goto exit_unlock; 43772ae58138SRichard Fitzgerald } 43782ae58138SRichard Fitzgerald 43792ae58138SRichard Fitzgerald adsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 43802ae58138SRichard Fitzgerald adsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 43812ae58138SRichard Fitzgerald adsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 43822ae58138SRichard Fitzgerald 43832ae58138SRichard Fitzgerald ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 43842ae58138SRichard Fitzgerald if (ret) 43852ae58138SRichard Fitzgerald adsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 43862ae58138SRichard Fitzgerald 43872ae58138SRichard Fitzgerald exit_unlock: 43882ae58138SRichard Fitzgerald mutex_unlock(&dsp->pwr_lock); 43892ae58138SRichard Fitzgerald 43902ae58138SRichard Fitzgerald return IRQ_HANDLED; 43912ae58138SRichard Fitzgerald } 43922ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error); 43932ae58138SRichard Fitzgerald 43948bc144f9SStuart Henderson irqreturn_t wm_halo_wdt_expire(int irq, void *data) 43958bc144f9SStuart Henderson { 43968bc144f9SStuart Henderson struct wm_adsp *dsp = data; 43978bc144f9SStuart Henderson 43988bc144f9SStuart Henderson mutex_lock(&dsp->pwr_lock); 43998bc144f9SStuart Henderson 44008bc144f9SStuart Henderson adsp_warn(dsp, "WDT Expiry Fault\n"); 44018bc144f9SStuart Henderson wm_halo_stop_watchdog(dsp); 44028bc144f9SStuart Henderson wm_adsp_fatal_error(dsp); 44038bc144f9SStuart Henderson 44048bc144f9SStuart Henderson mutex_unlock(&dsp->pwr_lock); 44058bc144f9SStuart Henderson 44068bc144f9SStuart Henderson return IRQ_HANDLED; 44078bc144f9SStuart Henderson } 44088bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); 44098bc144f9SStuart Henderson 4410cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp1_ops = { 44114e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 44124e08d50dSCharles Keepax .parse_sizes = wm_adsp1_parse_sizes, 4413170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 44144e08d50dSCharles Keepax }; 44154e08d50dSCharles Keepax 4416cd537873SCharles Keepax static struct wm_adsp_ops wm_adsp2_ops[] = { 44174e08d50dSCharles Keepax { 4418170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 44194e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 44204e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 44214e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4422170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 44234e08d50dSCharles Keepax 44244e08d50dSCharles Keepax .show_fw_status = wm_adsp2_show_fw_status, 44254e08d50dSCharles Keepax 44264e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 44274e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 44284e08d50dSCharles Keepax 44294e08d50dSCharles Keepax .enable_core = wm_adsp2_enable_core, 44304e08d50dSCharles Keepax .disable_core = wm_adsp2_disable_core, 44314e08d50dSCharles Keepax 44324e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 44334e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 44344e08d50dSCharles Keepax 44354e08d50dSCharles Keepax }, 44364e08d50dSCharles Keepax { 4437170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 44384e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 44394e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 44404e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4441170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 44424e08d50dSCharles Keepax 44434e08d50dSCharles Keepax .show_fw_status = wm_adsp2v2_show_fw_status, 44444e08d50dSCharles Keepax 44454e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 44464e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 44474e08d50dSCharles Keepax .lock_memory = wm_adsp2_lock, 44484e08d50dSCharles Keepax 44494e08d50dSCharles Keepax .enable_core = wm_adsp2v2_enable_core, 44504e08d50dSCharles Keepax .disable_core = wm_adsp2v2_disable_core, 44514e08d50dSCharles Keepax 44524e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 44534e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 44544e08d50dSCharles Keepax }, 44554e08d50dSCharles Keepax { 4456170b1e12SWen Shi .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 44574e08d50dSCharles Keepax .parse_sizes = wm_adsp2_parse_sizes, 44584e08d50dSCharles Keepax .validate_version = wm_adsp_validate_version, 44594e08d50dSCharles Keepax .setup_algs = wm_adsp2_setup_algs, 4460170b1e12SWen Shi .region_to_reg = wm_adsp_region_to_reg, 44614e08d50dSCharles Keepax 44624e08d50dSCharles Keepax .show_fw_status = wm_adsp2v2_show_fw_status, 44634e08d50dSCharles Keepax .stop_watchdog = wm_adsp_stop_watchdog, 44644e08d50dSCharles Keepax 44654e08d50dSCharles Keepax .enable_memory = wm_adsp2_enable_memory, 44664e08d50dSCharles Keepax .disable_memory = wm_adsp2_disable_memory, 44674e08d50dSCharles Keepax .lock_memory = wm_adsp2_lock, 44684e08d50dSCharles Keepax 44694e08d50dSCharles Keepax .enable_core = wm_adsp2v2_enable_core, 44704e08d50dSCharles Keepax .disable_core = wm_adsp2v2_disable_core, 44714e08d50dSCharles Keepax 44724e08d50dSCharles Keepax .start_core = wm_adsp2_start_core, 44734e08d50dSCharles Keepax .stop_core = wm_adsp2_stop_core, 44744e08d50dSCharles Keepax }, 44754e08d50dSCharles Keepax }; 44764e08d50dSCharles Keepax 4477cd537873SCharles Keepax static struct wm_adsp_ops wm_halo_ops = { 4478170b1e12SWen Shi .sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr), 4479170b1e12SWen Shi .parse_sizes = wm_adsp2_parse_sizes, 4480170b1e12SWen Shi .validate_version = wm_halo_validate_version, 4481170b1e12SWen Shi .setup_algs = wm_halo_setup_algs, 4482170b1e12SWen Shi .region_to_reg = wm_halo_region_to_reg, 4483170b1e12SWen Shi 4484170b1e12SWen Shi .show_fw_status = wm_halo_show_fw_status, 44858bc144f9SStuart Henderson .stop_watchdog = wm_halo_stop_watchdog, 4486170b1e12SWen Shi 4487170b1e12SWen Shi .lock_memory = wm_halo_configure_mpu, 4488170b1e12SWen Shi 4489170b1e12SWen Shi .start_core = wm_halo_start_core, 4490170b1e12SWen Shi .stop_core = wm_halo_stop_core, 4491170b1e12SWen Shi }; 4492170b1e12SWen Shi 44930a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 4494