18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * wm_adsp.c -- Wolfson ADSP support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2012 Wolfson Microelectronics plc 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/ctype.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/firmware.h> 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/pm.h> 188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 238c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 248c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 258c2ecf20Sopenharmony_ci#include <sound/core.h> 268c2ecf20Sopenharmony_ci#include <sound/pcm.h> 278c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 288c2ecf20Sopenharmony_ci#include <sound/soc.h> 298c2ecf20Sopenharmony_ci#include <sound/jack.h> 308c2ecf20Sopenharmony_ci#include <sound/initval.h> 318c2ecf20Sopenharmony_ci#include <sound/tlv.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "wm_adsp.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define adsp_crit(_dsp, fmt, ...) \ 368c2ecf20Sopenharmony_ci dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 378c2ecf20Sopenharmony_ci#define adsp_err(_dsp, fmt, ...) \ 388c2ecf20Sopenharmony_ci dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 398c2ecf20Sopenharmony_ci#define adsp_warn(_dsp, fmt, ...) \ 408c2ecf20Sopenharmony_ci dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 418c2ecf20Sopenharmony_ci#define adsp_info(_dsp, fmt, ...) \ 428c2ecf20Sopenharmony_ci dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 438c2ecf20Sopenharmony_ci#define adsp_dbg(_dsp, fmt, ...) \ 448c2ecf20Sopenharmony_ci dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define compr_err(_obj, fmt, ...) \ 478c2ecf20Sopenharmony_ci adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 488c2ecf20Sopenharmony_ci ##__VA_ARGS__) 498c2ecf20Sopenharmony_ci#define compr_dbg(_obj, fmt, ...) \ 508c2ecf20Sopenharmony_ci adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 518c2ecf20Sopenharmony_ci ##__VA_ARGS__) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_1 0x00 548c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_2 0x02 558c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_3 0x03 568c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_4 0x04 578c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_5 0x06 588c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_6 0x07 598c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_7 0x08 608c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_8 0x09 618c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_9 0x0A 628c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_10 0x0B 638c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_11 0x0C 648c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_12 0x0D 658c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_13 0x0F 668c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_14 0x10 678c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_15 0x11 688c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_16 0x12 698c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_17 0x13 708c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_18 0x14 718c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_19 0x16 728c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_20 0x17 738c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_21 0x18 748c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_22 0x1A 758c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_23 0x1B 768c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_24 0x1C 778c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_25 0x1E 788c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_26 0x20 798c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_27 0x21 808c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_28 0x22 818c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_29 0x23 828c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_30 0x24 838c2ecf20Sopenharmony_ci#define ADSP1_CONTROL_31 0x26 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * ADSP1 Control 19 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 898c2ecf20Sopenharmony_ci#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 908c2ecf20Sopenharmony_ci#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * ADSP1 Control 30 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 978c2ecf20Sopenharmony_ci#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 988c2ecf20Sopenharmony_ci#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 998c2ecf20Sopenharmony_ci#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 1008c2ecf20Sopenharmony_ci#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1018c2ecf20Sopenharmony_ci#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1028c2ecf20Sopenharmony_ci#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1038c2ecf20Sopenharmony_ci#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1048c2ecf20Sopenharmony_ci#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1058c2ecf20Sopenharmony_ci#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1068c2ecf20Sopenharmony_ci#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1078c2ecf20Sopenharmony_ci#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1088c2ecf20Sopenharmony_ci#define ADSP1_START 0x0001 /* DSP1_START */ 1098c2ecf20Sopenharmony_ci#define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1108c2ecf20Sopenharmony_ci#define ADSP1_START_SHIFT 0 /* DSP1_START */ 1118c2ecf20Sopenharmony_ci#define ADSP1_START_WIDTH 1 /* DSP1_START */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * ADSP1 Control 31 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 1178c2ecf20Sopenharmony_ci#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 1188c2ecf20Sopenharmony_ci#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define ADSP2_CONTROL 0x0 1218c2ecf20Sopenharmony_ci#define ADSP2_CLOCKING 0x1 1228c2ecf20Sopenharmony_ci#define ADSP2V2_CLOCKING 0x2 1238c2ecf20Sopenharmony_ci#define ADSP2_STATUS1 0x4 1248c2ecf20Sopenharmony_ci#define ADSP2_WDMA_CONFIG_1 0x30 1258c2ecf20Sopenharmony_ci#define ADSP2_WDMA_CONFIG_2 0x31 1268c2ecf20Sopenharmony_ci#define ADSP2V2_WDMA_CONFIG_2 0x32 1278c2ecf20Sopenharmony_ci#define ADSP2_RDMA_CONFIG_1 0x34 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define ADSP2_SCRATCH0 0x40 1308c2ecf20Sopenharmony_ci#define ADSP2_SCRATCH1 0x41 1318c2ecf20Sopenharmony_ci#define ADSP2_SCRATCH2 0x42 1328c2ecf20Sopenharmony_ci#define ADSP2_SCRATCH3 0x43 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define ADSP2V2_SCRATCH0_1 0x40 1358c2ecf20Sopenharmony_ci#define ADSP2V2_SCRATCH2_3 0x42 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * ADSP2 Control 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1428c2ecf20Sopenharmony_ci#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1438c2ecf20Sopenharmony_ci#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1448c2ecf20Sopenharmony_ci#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1458c2ecf20Sopenharmony_ci#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1468c2ecf20Sopenharmony_ci#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1478c2ecf20Sopenharmony_ci#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1488c2ecf20Sopenharmony_ci#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1498c2ecf20Sopenharmony_ci#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1508c2ecf20Sopenharmony_ci#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1518c2ecf20Sopenharmony_ci#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1528c2ecf20Sopenharmony_ci#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1538c2ecf20Sopenharmony_ci#define ADSP2_START 0x0001 /* DSP1_START */ 1548c2ecf20Sopenharmony_ci#define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1558c2ecf20Sopenharmony_ci#define ADSP2_START_SHIFT 0 /* DSP1_START */ 1568c2ecf20Sopenharmony_ci#define ADSP2_START_WIDTH 1 /* DSP1_START */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * ADSP2 clocking 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 1628c2ecf20Sopenharmony_ci#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 1638c2ecf20Sopenharmony_ci#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * ADSP2V2 clocking 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 1698c2ecf20Sopenharmony_ci#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 1708c2ecf20Sopenharmony_ci#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 1738c2ecf20Sopenharmony_ci#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 1748c2ecf20Sopenharmony_ci#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* 1778c2ecf20Sopenharmony_ci * ADSP2 Status 1 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci#define ADSP2_RAM_RDY 0x0001 1808c2ecf20Sopenharmony_ci#define ADSP2_RAM_RDY_MASK 0x0001 1818c2ecf20Sopenharmony_ci#define ADSP2_RAM_RDY_SHIFT 0 1828c2ecf20Sopenharmony_ci#define ADSP2_RAM_RDY_WIDTH 1 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * ADSP2 Lock support 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci#define ADSP2_LOCK_CODE_0 0x5555 1888c2ecf20Sopenharmony_ci#define ADSP2_LOCK_CODE_1 0xAAAA 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci#define ADSP2_WATCHDOG 0x0A 1918c2ecf20Sopenharmony_ci#define ADSP2_BUS_ERR_ADDR 0x52 1928c2ecf20Sopenharmony_ci#define ADSP2_REGION_LOCK_STATUS 0x64 1938c2ecf20Sopenharmony_ci#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 1948c2ecf20Sopenharmony_ci#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 1958c2ecf20Sopenharmony_ci#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 1968c2ecf20Sopenharmony_ci#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 1978c2ecf20Sopenharmony_ci#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 1988c2ecf20Sopenharmony_ci#define ADSP2_LOCK_REGION_CTRL 0x7A 1998c2ecf20Sopenharmony_ci#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define ADSP2_REGION_LOCK_ERR_MASK 0x8000 2028c2ecf20Sopenharmony_ci#define ADSP2_ADDR_ERR_MASK 0x4000 2038c2ecf20Sopenharmony_ci#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 2048c2ecf20Sopenharmony_ci#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 2058c2ecf20Sopenharmony_ci#define ADSP2_CTRL_ERR_EINT 0x0001 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 2088c2ecf20Sopenharmony_ci#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 2098c2ecf20Sopenharmony_ci#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 2108c2ecf20Sopenharmony_ci#define ADSP2_PMEM_ERR_ADDR_SHIFT 16 2118c2ecf20Sopenharmony_ci#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#define ADSP2_LOCK_REGION_SHIFT 16 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci#define ADSP_MAX_STD_CTRL_SIZE 512 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 2188c2ecf20Sopenharmony_ci#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 2198c2ecf20Sopenharmony_ci#define WM_ADSP_ACKED_CTL_MIN_VALUE 0 2208c2ecf20Sopenharmony_ci#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* 2238c2ecf20Sopenharmony_ci * Event control messages 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* 2288c2ecf20Sopenharmony_ci * HALO system info 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci#define HALO_AHBM_WINDOW_DEBUG_0 0x02040 2318c2ecf20Sopenharmony_ci#define HALO_AHBM_WINDOW_DEBUG_1 0x02044 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * HALO core 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci#define HALO_SCRATCH1 0x005c0 2378c2ecf20Sopenharmony_ci#define HALO_SCRATCH2 0x005c8 2388c2ecf20Sopenharmony_ci#define HALO_SCRATCH3 0x005d0 2398c2ecf20Sopenharmony_ci#define HALO_SCRATCH4 0x005d8 2408c2ecf20Sopenharmony_ci#define HALO_CCM_CORE_CONTROL 0x41000 2418c2ecf20Sopenharmony_ci#define HALO_CORE_SOFT_RESET 0x00010 2428c2ecf20Sopenharmony_ci#define HALO_WDT_CONTROL 0x47000 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * HALO MPU banks 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci#define HALO_MPU_XMEM_ACCESS_0 0x43000 2488c2ecf20Sopenharmony_ci#define HALO_MPU_YMEM_ACCESS_0 0x43004 2498c2ecf20Sopenharmony_ci#define HALO_MPU_WINDOW_ACCESS_0 0x43008 2508c2ecf20Sopenharmony_ci#define HALO_MPU_XREG_ACCESS_0 0x4300C 2518c2ecf20Sopenharmony_ci#define HALO_MPU_YREG_ACCESS_0 0x43014 2528c2ecf20Sopenharmony_ci#define HALO_MPU_XMEM_ACCESS_1 0x43018 2538c2ecf20Sopenharmony_ci#define HALO_MPU_YMEM_ACCESS_1 0x4301C 2548c2ecf20Sopenharmony_ci#define HALO_MPU_WINDOW_ACCESS_1 0x43020 2558c2ecf20Sopenharmony_ci#define HALO_MPU_XREG_ACCESS_1 0x43024 2568c2ecf20Sopenharmony_ci#define HALO_MPU_YREG_ACCESS_1 0x4302C 2578c2ecf20Sopenharmony_ci#define HALO_MPU_XMEM_ACCESS_2 0x43030 2588c2ecf20Sopenharmony_ci#define HALO_MPU_YMEM_ACCESS_2 0x43034 2598c2ecf20Sopenharmony_ci#define HALO_MPU_WINDOW_ACCESS_2 0x43038 2608c2ecf20Sopenharmony_ci#define HALO_MPU_XREG_ACCESS_2 0x4303C 2618c2ecf20Sopenharmony_ci#define HALO_MPU_YREG_ACCESS_2 0x43044 2628c2ecf20Sopenharmony_ci#define HALO_MPU_XMEM_ACCESS_3 0x43048 2638c2ecf20Sopenharmony_ci#define HALO_MPU_YMEM_ACCESS_3 0x4304C 2648c2ecf20Sopenharmony_ci#define HALO_MPU_WINDOW_ACCESS_3 0x43050 2658c2ecf20Sopenharmony_ci#define HALO_MPU_XREG_ACCESS_3 0x43054 2668c2ecf20Sopenharmony_ci#define HALO_MPU_YREG_ACCESS_3 0x4305C 2678c2ecf20Sopenharmony_ci#define HALO_MPU_XM_VIO_ADDR 0x43100 2688c2ecf20Sopenharmony_ci#define HALO_MPU_XM_VIO_STATUS 0x43104 2698c2ecf20Sopenharmony_ci#define HALO_MPU_YM_VIO_ADDR 0x43108 2708c2ecf20Sopenharmony_ci#define HALO_MPU_YM_VIO_STATUS 0x4310C 2718c2ecf20Sopenharmony_ci#define HALO_MPU_PM_VIO_ADDR 0x43110 2728c2ecf20Sopenharmony_ci#define HALO_MPU_PM_VIO_STATUS 0x43114 2738c2ecf20Sopenharmony_ci#define HALO_MPU_LOCK_CONFIG 0x43140 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* 2768c2ecf20Sopenharmony_ci * HALO_AHBM_WINDOW_DEBUG_1 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci#define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 2798c2ecf20Sopenharmony_ci#define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 2808c2ecf20Sopenharmony_ci#define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* 2838c2ecf20Sopenharmony_ci * HALO_CCM_CORE_CONTROL 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci#define HALO_CORE_EN 0x00000001 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* 2888c2ecf20Sopenharmony_ci * HALO_CORE_SOFT_RESET 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci#define HALO_CORE_SOFT_RESET_MASK 0x00000001 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * HALO_WDT_CONTROL 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci#define HALO_WDT_EN_MASK 0x00000001 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * HALO_MPU_?M_VIO_STATUS 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci#define HALO_MPU_VIO_STS_MASK 0x007e0000 3018c2ecf20Sopenharmony_ci#define HALO_MPU_VIO_STS_SHIFT 17 3028c2ecf20Sopenharmony_ci#define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 3038c2ecf20Sopenharmony_ci#define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff 3048c2ecf20Sopenharmony_ci#define HALO_MPU_VIO_ERR_SRC_SHIFT 0 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic struct wm_adsp_ops wm_adsp1_ops; 3078c2ecf20Sopenharmony_cistatic struct wm_adsp_ops wm_adsp2_ops[]; 3088c2ecf20Sopenharmony_cistatic struct wm_adsp_ops wm_halo_ops; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistruct wm_adsp_buf { 3118c2ecf20Sopenharmony_ci struct list_head list; 3128c2ecf20Sopenharmony_ci void *buf; 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 3168c2ecf20Sopenharmony_ci struct list_head *list) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (buf == NULL) 3218c2ecf20Sopenharmony_ci return NULL; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci buf->buf = vmalloc(len); 3248c2ecf20Sopenharmony_ci if (!buf->buf) { 3258c2ecf20Sopenharmony_ci kfree(buf); 3268c2ecf20Sopenharmony_ci return NULL; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci memcpy(buf->buf, src, len); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (list) 3318c2ecf20Sopenharmony_ci list_add_tail(&buf->list, list); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return buf; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void wm_adsp_buf_free(struct list_head *list) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci while (!list_empty(list)) { 3398c2ecf20Sopenharmony_ci struct wm_adsp_buf *buf = list_first_entry(list, 3408c2ecf20Sopenharmony_ci struct wm_adsp_buf, 3418c2ecf20Sopenharmony_ci list); 3428c2ecf20Sopenharmony_ci list_del(&buf->list); 3438c2ecf20Sopenharmony_ci vfree(buf->buf); 3448c2ecf20Sopenharmony_ci kfree(buf); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci#define WM_ADSP_FW_MBC_VSS 0 3498c2ecf20Sopenharmony_ci#define WM_ADSP_FW_HIFI 1 3508c2ecf20Sopenharmony_ci#define WM_ADSP_FW_TX 2 3518c2ecf20Sopenharmony_ci#define WM_ADSP_FW_TX_SPK 3 3528c2ecf20Sopenharmony_ci#define WM_ADSP_FW_RX 4 3538c2ecf20Sopenharmony_ci#define WM_ADSP_FW_RX_ANC 5 3548c2ecf20Sopenharmony_ci#define WM_ADSP_FW_CTRL 6 3558c2ecf20Sopenharmony_ci#define WM_ADSP_FW_ASR 7 3568c2ecf20Sopenharmony_ci#define WM_ADSP_FW_TRACE 8 3578c2ecf20Sopenharmony_ci#define WM_ADSP_FW_SPK_PROT 9 3588c2ecf20Sopenharmony_ci#define WM_ADSP_FW_SPK_CALI 10 3598c2ecf20Sopenharmony_ci#define WM_ADSP_FW_SPK_DIAG 11 3608c2ecf20Sopenharmony_ci#define WM_ADSP_FW_MISC 12 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#define WM_ADSP_NUM_FW 13 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 3658c2ecf20Sopenharmony_ci [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 3668c2ecf20Sopenharmony_ci [WM_ADSP_FW_HIFI] = "MasterHiFi", 3678c2ecf20Sopenharmony_ci [WM_ADSP_FW_TX] = "Tx", 3688c2ecf20Sopenharmony_ci [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 3698c2ecf20Sopenharmony_ci [WM_ADSP_FW_RX] = "Rx", 3708c2ecf20Sopenharmony_ci [WM_ADSP_FW_RX_ANC] = "Rx ANC", 3718c2ecf20Sopenharmony_ci [WM_ADSP_FW_CTRL] = "Voice Ctrl", 3728c2ecf20Sopenharmony_ci [WM_ADSP_FW_ASR] = "ASR Assist", 3738c2ecf20Sopenharmony_ci [WM_ADSP_FW_TRACE] = "Dbg Trace", 3748c2ecf20Sopenharmony_ci [WM_ADSP_FW_SPK_PROT] = "Protection", 3758c2ecf20Sopenharmony_ci [WM_ADSP_FW_SPK_CALI] = "Calibration", 3768c2ecf20Sopenharmony_ci [WM_ADSP_FW_SPK_DIAG] = "Diagnostic", 3778c2ecf20Sopenharmony_ci [WM_ADSP_FW_MISC] = "Misc", 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistruct wm_adsp_system_config_xm_hdr { 3818c2ecf20Sopenharmony_ci __be32 sys_enable; 3828c2ecf20Sopenharmony_ci __be32 fw_id; 3838c2ecf20Sopenharmony_ci __be32 fw_rev; 3848c2ecf20Sopenharmony_ci __be32 boot_status; 3858c2ecf20Sopenharmony_ci __be32 watchdog; 3868c2ecf20Sopenharmony_ci __be32 dma_buffer_size; 3878c2ecf20Sopenharmony_ci __be32 rdma[6]; 3888c2ecf20Sopenharmony_ci __be32 wdma[8]; 3898c2ecf20Sopenharmony_ci __be32 build_job_name[3]; 3908c2ecf20Sopenharmony_ci __be32 build_job_number; 3918c2ecf20Sopenharmony_ci}; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistruct wm_halo_system_config_xm_hdr { 3948c2ecf20Sopenharmony_ci __be32 halo_heartbeat; 3958c2ecf20Sopenharmony_ci __be32 build_job_name[3]; 3968c2ecf20Sopenharmony_ci __be32 build_job_number; 3978c2ecf20Sopenharmony_ci}; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistruct wm_adsp_alg_xm_struct { 4008c2ecf20Sopenharmony_ci __be32 magic; 4018c2ecf20Sopenharmony_ci __be32 smoothing; 4028c2ecf20Sopenharmony_ci __be32 threshold; 4038c2ecf20Sopenharmony_ci __be32 host_buf_ptr; 4048c2ecf20Sopenharmony_ci __be32 start_seq; 4058c2ecf20Sopenharmony_ci __be32 high_water_mark; 4068c2ecf20Sopenharmony_ci __be32 low_water_mark; 4078c2ecf20Sopenharmony_ci __be64 smoothed_power; 4088c2ecf20Sopenharmony_ci}; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistruct wm_adsp_host_buf_coeff_v1 { 4118c2ecf20Sopenharmony_ci __be32 host_buf_ptr; /* Host buffer pointer */ 4128c2ecf20Sopenharmony_ci __be32 versions; /* Version numbers */ 4138c2ecf20Sopenharmony_ci __be32 name[4]; /* The buffer name */ 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistruct wm_adsp_buffer { 4178c2ecf20Sopenharmony_ci __be32 buf1_base; /* Base addr of first buffer area */ 4188c2ecf20Sopenharmony_ci __be32 buf1_size; /* Size of buf1 area in DSP words */ 4198c2ecf20Sopenharmony_ci __be32 buf2_base; /* Base addr of 2nd buffer area */ 4208c2ecf20Sopenharmony_ci __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 4218c2ecf20Sopenharmony_ci __be32 buf3_base; /* Base addr of buf3 area */ 4228c2ecf20Sopenharmony_ci __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 4238c2ecf20Sopenharmony_ci __be32 high_water_mark; /* Point at which IRQ is asserted */ 4248c2ecf20Sopenharmony_ci __be32 irq_count; /* bits 1-31 count IRQ assertions */ 4258c2ecf20Sopenharmony_ci __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 4268c2ecf20Sopenharmony_ci __be32 next_write_index; /* word index of next write */ 4278c2ecf20Sopenharmony_ci __be32 next_read_index; /* word index of next read */ 4288c2ecf20Sopenharmony_ci __be32 error; /* error if any */ 4298c2ecf20Sopenharmony_ci __be32 oldest_block_index; /* word index of oldest surviving */ 4308c2ecf20Sopenharmony_ci __be32 requested_rewind; /* how many blocks rewind was done */ 4318c2ecf20Sopenharmony_ci __be32 reserved_space; /* internal */ 4328c2ecf20Sopenharmony_ci __be32 min_free; /* min free space since stream start */ 4338c2ecf20Sopenharmony_ci __be32 blocks_written[2]; /* total blocks written (64 bit) */ 4348c2ecf20Sopenharmony_ci __be32 words_written[2]; /* total words written (64 bit) */ 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistruct wm_adsp_compr; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistruct wm_adsp_compr_buf { 4408c2ecf20Sopenharmony_ci struct list_head list; 4418c2ecf20Sopenharmony_ci struct wm_adsp *dsp; 4428c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci struct wm_adsp_buffer_region *regions; 4458c2ecf20Sopenharmony_ci u32 host_buf_ptr; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci u32 error; 4488c2ecf20Sopenharmony_ci u32 irq_count; 4498c2ecf20Sopenharmony_ci int read_index; 4508c2ecf20Sopenharmony_ci int avail; 4518c2ecf20Sopenharmony_ci int host_buf_mem_type; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci char *name; 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistruct wm_adsp_compr { 4578c2ecf20Sopenharmony_ci struct list_head list; 4588c2ecf20Sopenharmony_ci struct wm_adsp *dsp; 4598c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci struct snd_compr_stream *stream; 4628c2ecf20Sopenharmony_ci struct snd_compressed_buffer size; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci u32 *raw_buf; 4658c2ecf20Sopenharmony_ci unsigned int copied_total; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci unsigned int sample_rate; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci const char *name; 4708c2ecf20Sopenharmony_ci}; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci#define WM_ADSP_DATA_WORD_SIZE 3 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci#define WM_ADSP_MIN_FRAGMENTS 1 4758c2ecf20Sopenharmony_ci#define WM_ADSP_MAX_FRAGMENTS 256 4768c2ecf20Sopenharmony_ci#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 4778c2ecf20Sopenharmony_ci#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci#define HOST_BUFFER_FIELD(field) \ 4828c2ecf20Sopenharmony_ci (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci#define ALG_XM_FIELD(field) \ 4858c2ecf20Sopenharmony_ci (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci#define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 4908c2ecf20Sopenharmony_ci#define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_init(struct wm_adsp *dsp); 4938c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_free(struct wm_adsp *dsp); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistruct wm_adsp_buffer_region { 4968c2ecf20Sopenharmony_ci unsigned int offset; 4978c2ecf20Sopenharmony_ci unsigned int cumulative_size; 4988c2ecf20Sopenharmony_ci unsigned int mem_type; 4998c2ecf20Sopenharmony_ci unsigned int base_addr; 5008c2ecf20Sopenharmony_ci}; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistruct wm_adsp_buffer_region_def { 5038c2ecf20Sopenharmony_ci unsigned int mem_type; 5048c2ecf20Sopenharmony_ci unsigned int base_offset; 5058c2ecf20Sopenharmony_ci unsigned int size_offset; 5068c2ecf20Sopenharmony_ci}; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic const struct wm_adsp_buffer_region_def default_regions[] = { 5098c2ecf20Sopenharmony_ci { 5108c2ecf20Sopenharmony_ci .mem_type = WMFW_ADSP2_XM, 5118c2ecf20Sopenharmony_ci .base_offset = HOST_BUFFER_FIELD(buf1_base), 5128c2ecf20Sopenharmony_ci .size_offset = HOST_BUFFER_FIELD(buf1_size), 5138c2ecf20Sopenharmony_ci }, 5148c2ecf20Sopenharmony_ci { 5158c2ecf20Sopenharmony_ci .mem_type = WMFW_ADSP2_XM, 5168c2ecf20Sopenharmony_ci .base_offset = HOST_BUFFER_FIELD(buf2_base), 5178c2ecf20Sopenharmony_ci .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 5188c2ecf20Sopenharmony_ci }, 5198c2ecf20Sopenharmony_ci { 5208c2ecf20Sopenharmony_ci .mem_type = WMFW_ADSP2_YM, 5218c2ecf20Sopenharmony_ci .base_offset = HOST_BUFFER_FIELD(buf3_base), 5228c2ecf20Sopenharmony_ci .size_offset = HOST_BUFFER_FIELD(buf_total_size), 5238c2ecf20Sopenharmony_ci }, 5248c2ecf20Sopenharmony_ci}; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistruct wm_adsp_fw_caps { 5278c2ecf20Sopenharmony_ci u32 id; 5288c2ecf20Sopenharmony_ci struct snd_codec_desc desc; 5298c2ecf20Sopenharmony_ci int num_regions; 5308c2ecf20Sopenharmony_ci const struct wm_adsp_buffer_region_def *region_defs; 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic const struct wm_adsp_fw_caps ctrl_caps[] = { 5348c2ecf20Sopenharmony_ci { 5358c2ecf20Sopenharmony_ci .id = SND_AUDIOCODEC_BESPOKE, 5368c2ecf20Sopenharmony_ci .desc = { 5378c2ecf20Sopenharmony_ci .max_ch = 8, 5388c2ecf20Sopenharmony_ci .sample_rates = { 16000 }, 5398c2ecf20Sopenharmony_ci .num_sample_rates = 1, 5408c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 5418c2ecf20Sopenharmony_ci }, 5428c2ecf20Sopenharmony_ci .num_regions = ARRAY_SIZE(default_regions), 5438c2ecf20Sopenharmony_ci .region_defs = default_regions, 5448c2ecf20Sopenharmony_ci }, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic const struct wm_adsp_fw_caps trace_caps[] = { 5488c2ecf20Sopenharmony_ci { 5498c2ecf20Sopenharmony_ci .id = SND_AUDIOCODEC_BESPOKE, 5508c2ecf20Sopenharmony_ci .desc = { 5518c2ecf20Sopenharmony_ci .max_ch = 8, 5528c2ecf20Sopenharmony_ci .sample_rates = { 5538c2ecf20Sopenharmony_ci 4000, 8000, 11025, 12000, 16000, 22050, 5548c2ecf20Sopenharmony_ci 24000, 32000, 44100, 48000, 64000, 88200, 5558c2ecf20Sopenharmony_ci 96000, 176400, 192000 5568c2ecf20Sopenharmony_ci }, 5578c2ecf20Sopenharmony_ci .num_sample_rates = 15, 5588c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 5598c2ecf20Sopenharmony_ci }, 5608c2ecf20Sopenharmony_ci .num_regions = ARRAY_SIZE(default_regions), 5618c2ecf20Sopenharmony_ci .region_defs = default_regions, 5628c2ecf20Sopenharmony_ci }, 5638c2ecf20Sopenharmony_ci}; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic const struct { 5668c2ecf20Sopenharmony_ci const char *file; 5678c2ecf20Sopenharmony_ci int compr_direction; 5688c2ecf20Sopenharmony_ci int num_caps; 5698c2ecf20Sopenharmony_ci const struct wm_adsp_fw_caps *caps; 5708c2ecf20Sopenharmony_ci bool voice_trigger; 5718c2ecf20Sopenharmony_ci} wm_adsp_fw[WM_ADSP_NUM_FW] = { 5728c2ecf20Sopenharmony_ci [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 5738c2ecf20Sopenharmony_ci [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 5748c2ecf20Sopenharmony_ci [WM_ADSP_FW_TX] = { .file = "tx" }, 5758c2ecf20Sopenharmony_ci [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 5768c2ecf20Sopenharmony_ci [WM_ADSP_FW_RX] = { .file = "rx" }, 5778c2ecf20Sopenharmony_ci [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 5788c2ecf20Sopenharmony_ci [WM_ADSP_FW_CTRL] = { 5798c2ecf20Sopenharmony_ci .file = "ctrl", 5808c2ecf20Sopenharmony_ci .compr_direction = SND_COMPRESS_CAPTURE, 5818c2ecf20Sopenharmony_ci .num_caps = ARRAY_SIZE(ctrl_caps), 5828c2ecf20Sopenharmony_ci .caps = ctrl_caps, 5838c2ecf20Sopenharmony_ci .voice_trigger = true, 5848c2ecf20Sopenharmony_ci }, 5858c2ecf20Sopenharmony_ci [WM_ADSP_FW_ASR] = { .file = "asr" }, 5868c2ecf20Sopenharmony_ci [WM_ADSP_FW_TRACE] = { 5878c2ecf20Sopenharmony_ci .file = "trace", 5888c2ecf20Sopenharmony_ci .compr_direction = SND_COMPRESS_CAPTURE, 5898c2ecf20Sopenharmony_ci .num_caps = ARRAY_SIZE(trace_caps), 5908c2ecf20Sopenharmony_ci .caps = trace_caps, 5918c2ecf20Sopenharmony_ci }, 5928c2ecf20Sopenharmony_ci [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 5938c2ecf20Sopenharmony_ci [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" }, 5948c2ecf20Sopenharmony_ci [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" }, 5958c2ecf20Sopenharmony_ci [WM_ADSP_FW_MISC] = { .file = "misc" }, 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistruct wm_coeff_ctl_ops { 5998c2ecf20Sopenharmony_ci int (*xget)(struct snd_kcontrol *kcontrol, 6008c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 6018c2ecf20Sopenharmony_ci int (*xput)(struct snd_kcontrol *kcontrol, 6028c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol); 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistruct wm_coeff_ctl { 6068c2ecf20Sopenharmony_ci const char *name; 6078c2ecf20Sopenharmony_ci const char *fw_name; 6088c2ecf20Sopenharmony_ci /* Subname is needed to match with firmware */ 6098c2ecf20Sopenharmony_ci const char *subname; 6108c2ecf20Sopenharmony_ci unsigned int subname_len; 6118c2ecf20Sopenharmony_ci struct wm_adsp_alg_region alg_region; 6128c2ecf20Sopenharmony_ci struct wm_coeff_ctl_ops ops; 6138c2ecf20Sopenharmony_ci struct wm_adsp *dsp; 6148c2ecf20Sopenharmony_ci unsigned int enabled:1; 6158c2ecf20Sopenharmony_ci struct list_head list; 6168c2ecf20Sopenharmony_ci void *cache; 6178c2ecf20Sopenharmony_ci unsigned int offset; 6188c2ecf20Sopenharmony_ci size_t len; 6198c2ecf20Sopenharmony_ci unsigned int set:1; 6208c2ecf20Sopenharmony_ci struct soc_bytes_ext bytes_ext; 6218c2ecf20Sopenharmony_ci unsigned int flags; 6228c2ecf20Sopenharmony_ci unsigned int type; 6238c2ecf20Sopenharmony_ci}; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic const char *wm_adsp_mem_region_name(unsigned int type) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci switch (type) { 6288c2ecf20Sopenharmony_ci case WMFW_ADSP1_PM: 6298c2ecf20Sopenharmony_ci return "PM"; 6308c2ecf20Sopenharmony_ci case WMFW_HALO_PM_PACKED: 6318c2ecf20Sopenharmony_ci return "PM_PACKED"; 6328c2ecf20Sopenharmony_ci case WMFW_ADSP1_DM: 6338c2ecf20Sopenharmony_ci return "DM"; 6348c2ecf20Sopenharmony_ci case WMFW_ADSP2_XM: 6358c2ecf20Sopenharmony_ci return "XM"; 6368c2ecf20Sopenharmony_ci case WMFW_HALO_XM_PACKED: 6378c2ecf20Sopenharmony_ci return "XM_PACKED"; 6388c2ecf20Sopenharmony_ci case WMFW_ADSP2_YM: 6398c2ecf20Sopenharmony_ci return "YM"; 6408c2ecf20Sopenharmony_ci case WMFW_HALO_YM_PACKED: 6418c2ecf20Sopenharmony_ci return "YM_PACKED"; 6428c2ecf20Sopenharmony_ci case WMFW_ADSP1_ZM: 6438c2ecf20Sopenharmony_ci return "ZM"; 6448c2ecf20Sopenharmony_ci default: 6458c2ecf20Sopenharmony_ci return NULL; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 6508c2ecf20Sopenharmony_cistatic void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci kfree(dsp->wmfw_file_name); 6558c2ecf20Sopenharmony_ci dsp->wmfw_file_name = tmp; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci kfree(dsp->bin_file_name); 6638c2ecf20Sopenharmony_ci dsp->bin_file_name = tmp; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci kfree(dsp->wmfw_file_name); 6698c2ecf20Sopenharmony_ci kfree(dsp->bin_file_name); 6708c2ecf20Sopenharmony_ci dsp->wmfw_file_name = NULL; 6718c2ecf20Sopenharmony_ci dsp->bin_file_name = NULL; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 6758c2ecf20Sopenharmony_ci char __user *user_buf, 6768c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct wm_adsp *dsp = file->private_data; 6798c2ecf20Sopenharmony_ci ssize_t ret; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (!dsp->wmfw_file_name || !dsp->booted) 6848c2ecf20Sopenharmony_ci ret = 0; 6858c2ecf20Sopenharmony_ci else 6868c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, 6878c2ecf20Sopenharmony_ci dsp->wmfw_file_name, 6888c2ecf20Sopenharmony_ci strlen(dsp->wmfw_file_name)); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic ssize_t wm_adsp_debugfs_bin_read(struct file *file, 6958c2ecf20Sopenharmony_ci char __user *user_buf, 6968c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct wm_adsp *dsp = file->private_data; 6998c2ecf20Sopenharmony_ci ssize_t ret; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (!dsp->bin_file_name || !dsp->booted) 7048c2ecf20Sopenharmony_ci ret = 0; 7058c2ecf20Sopenharmony_ci else 7068c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, 7078c2ecf20Sopenharmony_ci dsp->bin_file_name, 7088c2ecf20Sopenharmony_ci strlen(dsp->bin_file_name)); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic const struct { 7158c2ecf20Sopenharmony_ci const char *name; 7168c2ecf20Sopenharmony_ci const struct file_operations fops; 7178c2ecf20Sopenharmony_ci} wm_adsp_debugfs_fops[] = { 7188c2ecf20Sopenharmony_ci { 7198c2ecf20Sopenharmony_ci .name = "wmfw_file_name", 7208c2ecf20Sopenharmony_ci .fops = { 7218c2ecf20Sopenharmony_ci .open = simple_open, 7228c2ecf20Sopenharmony_ci .read = wm_adsp_debugfs_wmfw_read, 7238c2ecf20Sopenharmony_ci }, 7248c2ecf20Sopenharmony_ci }, 7258c2ecf20Sopenharmony_ci { 7268c2ecf20Sopenharmony_ci .name = "bin_file_name", 7278c2ecf20Sopenharmony_ci .fops = { 7288c2ecf20Sopenharmony_ci .open = simple_open, 7298c2ecf20Sopenharmony_ci .read = wm_adsp_debugfs_bin_read, 7308c2ecf20Sopenharmony_ci }, 7318c2ecf20Sopenharmony_ci }, 7328c2ecf20Sopenharmony_ci}; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7358c2ecf20Sopenharmony_ci struct snd_soc_component *component) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct dentry *root = NULL; 7388c2ecf20Sopenharmony_ci int i; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci root = debugfs_create_dir(dsp->name, component->debugfs_root); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci debugfs_create_bool("booted", 0444, root, &dsp->booted); 7438c2ecf20Sopenharmony_ci debugfs_create_bool("running", 0444, root, &dsp->running); 7448c2ecf20Sopenharmony_ci debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); 7458c2ecf20Sopenharmony_ci debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) 7488c2ecf20Sopenharmony_ci debugfs_create_file(wm_adsp_debugfs_fops[i].name, 0444, root, 7498c2ecf20Sopenharmony_ci dsp, &wm_adsp_debugfs_fops[i].fops); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci dsp->debugfs_root = root; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci wm_adsp_debugfs_clear(dsp); 7578c2ecf20Sopenharmony_ci debugfs_remove_recursive(dsp->debugfs_root); 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci#else 7608c2ecf20Sopenharmony_cistatic inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 7618c2ecf20Sopenharmony_ci struct snd_soc_component *component) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 7708c2ecf20Sopenharmony_ci const char *s) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 7758c2ecf20Sopenharmony_ci const char *s) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci#endif 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ciint wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 7858c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7888c2ecf20Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7898c2ecf20Sopenharmony_ci struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci return 0; 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_fw_get); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciint wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 7988c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 8018c2ecf20Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 8028c2ecf20Sopenharmony_ci struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 8038c2ecf20Sopenharmony_ci int ret = 1; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 8068c2ecf20Sopenharmony_ci return 0; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 8098c2ecf20Sopenharmony_ci return -EINVAL; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci mutex_lock(&dsp[e->shift_l].pwr_lock); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) 8148c2ecf20Sopenharmony_ci ret = -EBUSY; 8158c2ecf20Sopenharmony_ci else 8168c2ecf20Sopenharmony_ci dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci mutex_unlock(&dsp[e->shift_l].pwr_lock); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return ret; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_fw_put); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ciconst struct soc_enum wm_adsp_fw_enum[] = { 8258c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8268c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8278c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8288c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8298c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8308c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8318c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 8328c2ecf20Sopenharmony_ci}; 8338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 8368c2ecf20Sopenharmony_ci int type) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci int i; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci for (i = 0; i < dsp->num_mems; i++) 8418c2ecf20Sopenharmony_ci if (dsp->mem[i].type == type) 8428c2ecf20Sopenharmony_ci return &dsp->mem[i]; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return NULL; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 8488c2ecf20Sopenharmony_ci unsigned int offset) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci switch (mem->type) { 8518c2ecf20Sopenharmony_ci case WMFW_ADSP1_PM: 8528c2ecf20Sopenharmony_ci return mem->base + (offset * 3); 8538c2ecf20Sopenharmony_ci case WMFW_ADSP1_DM: 8548c2ecf20Sopenharmony_ci case WMFW_ADSP2_XM: 8558c2ecf20Sopenharmony_ci case WMFW_ADSP2_YM: 8568c2ecf20Sopenharmony_ci case WMFW_ADSP1_ZM: 8578c2ecf20Sopenharmony_ci return mem->base + (offset * 2); 8588c2ecf20Sopenharmony_ci default: 8598c2ecf20Sopenharmony_ci WARN(1, "Unknown memory region type"); 8608c2ecf20Sopenharmony_ci return offset; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic unsigned int wm_halo_region_to_reg(struct wm_adsp_region const *mem, 8658c2ecf20Sopenharmony_ci unsigned int offset) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci switch (mem->type) { 8688c2ecf20Sopenharmony_ci case WMFW_ADSP2_XM: 8698c2ecf20Sopenharmony_ci case WMFW_ADSP2_YM: 8708c2ecf20Sopenharmony_ci return mem->base + (offset * 4); 8718c2ecf20Sopenharmony_ci case WMFW_HALO_XM_PACKED: 8728c2ecf20Sopenharmony_ci case WMFW_HALO_YM_PACKED: 8738c2ecf20Sopenharmony_ci return (mem->base + (offset * 3)) & ~0x3; 8748c2ecf20Sopenharmony_ci case WMFW_HALO_PM_PACKED: 8758c2ecf20Sopenharmony_ci return mem->base + (offset * 5); 8768c2ecf20Sopenharmony_ci default: 8778c2ecf20Sopenharmony_ci WARN(1, "Unknown memory region type"); 8788c2ecf20Sopenharmony_ci return offset; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic void wm_adsp_read_fw_status(struct wm_adsp *dsp, 8838c2ecf20Sopenharmony_ci int noffs, unsigned int *offs) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci unsigned int i; 8868c2ecf20Sopenharmony_ci int ret; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci for (i = 0; i < noffs; ++i) { 8898c2ecf20Sopenharmony_ci ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 8908c2ecf20Sopenharmony_ci if (ret) { 8918c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 8928c2ecf20Sopenharmony_ci return; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci unsigned int offs[] = { 9008c2ecf20Sopenharmony_ci ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 9018c2ecf20Sopenharmony_ci }; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9068c2ecf20Sopenharmony_ci offs[0], offs[1], offs[2], offs[3]); 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9168c2ecf20Sopenharmony_ci offs[0] & 0xFFFF, offs[0] >> 16, 9178c2ecf20Sopenharmony_ci offs[1] & 0xFFFF, offs[1] >> 16); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic void wm_halo_show_fw_status(struct wm_adsp *dsp) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci unsigned int offs[] = { 9238c2ecf20Sopenharmony_ci HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, 9248c2ecf20Sopenharmony_ci }; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 9298c2ecf20Sopenharmony_ci offs[0], offs[1], offs[2], offs[3]); 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci return container_of(ext, struct wm_coeff_ctl, bytes_ext); 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 9408c2ecf20Sopenharmony_ci struct wm_adsp *dsp = ctl->dsp; 9418c2ecf20Sopenharmony_ci const struct wm_adsp_region *mem; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci mem = wm_adsp_find_region(dsp, alg_region->type); 9448c2ecf20Sopenharmony_ci if (!mem) { 9458c2ecf20Sopenharmony_ci adsp_err(dsp, "No base for region %x\n", 9468c2ecf20Sopenharmony_ci alg_region->type); 9478c2ecf20Sopenharmony_ci return -EINVAL; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return 0; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic int wm_coeff_info(struct snd_kcontrol *kctl, 9568c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct soc_bytes_ext *bytes_ext = 9598c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kctl->private_value; 9608c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci switch (ctl->type) { 9638c2ecf20Sopenharmony_ci case WMFW_CTL_TYPE_ACKED: 9648c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 9658c2ecf20Sopenharmony_ci uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 9668c2ecf20Sopenharmony_ci uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 9678c2ecf20Sopenharmony_ci uinfo->value.integer.step = 1; 9688c2ecf20Sopenharmony_ci uinfo->count = 1; 9698c2ecf20Sopenharmony_ci break; 9708c2ecf20Sopenharmony_ci default: 9718c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 9728c2ecf20Sopenharmony_ci uinfo->count = ctl->len; 9738c2ecf20Sopenharmony_ci break; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 9808c2ecf20Sopenharmony_ci unsigned int event_id) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct wm_adsp *dsp = ctl->dsp; 9838c2ecf20Sopenharmony_ci u32 val = cpu_to_be32(event_id); 9848c2ecf20Sopenharmony_ci unsigned int reg; 9858c2ecf20Sopenharmony_ci int i, ret; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci ret = wm_coeff_base_reg(ctl, ®); 9888c2ecf20Sopenharmony_ci if (ret) 9898c2ecf20Sopenharmony_ci return ret; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 9928c2ecf20Sopenharmony_ci event_id, ctl->alg_region.alg, 9938c2ecf20Sopenharmony_ci wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 9968c2ecf20Sopenharmony_ci if (ret) { 9978c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 9988c2ecf20Sopenharmony_ci return ret; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* 10028c2ecf20Sopenharmony_ci * Poll for ack, we initially poll at ~1ms intervals for firmwares 10038c2ecf20Sopenharmony_ci * that respond quickly, then go to ~10ms polls. A firmware is unlikely 10048c2ecf20Sopenharmony_ci * to ack instantly so we do the first 1ms delay before reading the 10058c2ecf20Sopenharmony_ci * control to avoid a pointless bus transaction 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 10088c2ecf20Sopenharmony_ci switch (i) { 10098c2ecf20Sopenharmony_ci case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 10108c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 10118c2ecf20Sopenharmony_ci i++; 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci default: 10148c2ecf20Sopenharmony_ci usleep_range(10000, 20000); 10158c2ecf20Sopenharmony_ci i += 10; 10168c2ecf20Sopenharmony_ci break; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 10208c2ecf20Sopenharmony_ci if (ret) { 10218c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (val == 0) { 10268c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 10328c2ecf20Sopenharmony_ci reg, ctl->alg_region.alg, 10338c2ecf20Sopenharmony_ci wm_adsp_mem_region_name(ctl->alg_region.type), 10348c2ecf20Sopenharmony_ci ctl->offset); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return -ETIMEDOUT; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic int wm_coeff_write_ctrl_raw(struct wm_coeff_ctl *ctl, 10408c2ecf20Sopenharmony_ci const void *buf, size_t len) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct wm_adsp *dsp = ctl->dsp; 10438c2ecf20Sopenharmony_ci void *scratch; 10448c2ecf20Sopenharmony_ci int ret; 10458c2ecf20Sopenharmony_ci unsigned int reg; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci ret = wm_coeff_base_reg(ctl, ®); 10488c2ecf20Sopenharmony_ci if (ret) 10498c2ecf20Sopenharmony_ci return ret; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 10528c2ecf20Sopenharmony_ci if (!scratch) 10538c2ecf20Sopenharmony_ci return -ENOMEM; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ret = regmap_raw_write(dsp->regmap, reg, scratch, 10568c2ecf20Sopenharmony_ci len); 10578c2ecf20Sopenharmony_ci if (ret) { 10588c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 10598c2ecf20Sopenharmony_ci len, reg, ret); 10608c2ecf20Sopenharmony_ci kfree(scratch); 10618c2ecf20Sopenharmony_ci return ret; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci kfree(scratch); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return 0; 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistatic int wm_coeff_write_ctrl(struct wm_coeff_ctl *ctl, 10718c2ecf20Sopenharmony_ci const void *buf, size_t len) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci int ret = 0; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 10768c2ecf20Sopenharmony_ci ret = -EPERM; 10778c2ecf20Sopenharmony_ci else if (buf != ctl->cache) 10788c2ecf20Sopenharmony_ci memcpy(ctl->cache, buf, len); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci ctl->set = 1; 10818c2ecf20Sopenharmony_ci if (ctl->enabled && ctl->dsp->running) 10828c2ecf20Sopenharmony_ci ret = wm_coeff_write_ctrl_raw(ctl, buf, len); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci return ret; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int wm_coeff_put(struct snd_kcontrol *kctl, 10888c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct soc_bytes_ext *bytes_ext = 10918c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kctl->private_value; 10928c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10938c2ecf20Sopenharmony_ci char *p = ucontrol->value.bytes.data; 10948c2ecf20Sopenharmony_ci int ret = 0; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci mutex_lock(&ctl->dsp->pwr_lock); 10978c2ecf20Sopenharmony_ci ret = wm_coeff_write_ctrl(ctl, p, ctl->len); 10988c2ecf20Sopenharmony_ci mutex_unlock(&ctl->dsp->pwr_lock); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return ret; 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 11048c2ecf20Sopenharmony_ci const unsigned int __user *bytes, unsigned int size) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct soc_bytes_ext *bytes_ext = 11078c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kctl->private_value; 11088c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11098c2ecf20Sopenharmony_ci int ret = 0; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci mutex_lock(&ctl->dsp->pwr_lock); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci if (copy_from_user(ctl->cache, bytes, size)) 11148c2ecf20Sopenharmony_ci ret = -EFAULT; 11158c2ecf20Sopenharmony_ci else 11168c2ecf20Sopenharmony_ci ret = wm_coeff_write_ctrl(ctl, ctl->cache, size); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci mutex_unlock(&ctl->dsp->pwr_lock); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci return ret; 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic int wm_coeff_put_acked(struct snd_kcontrol *kctl, 11248c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct soc_bytes_ext *bytes_ext = 11278c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kctl->private_value; 11288c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11298c2ecf20Sopenharmony_ci unsigned int val = ucontrol->value.integer.value[0]; 11308c2ecf20Sopenharmony_ci int ret; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (val == 0) 11338c2ecf20Sopenharmony_ci return 0; /* 0 means no event */ 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci mutex_lock(&ctl->dsp->pwr_lock); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (ctl->enabled && ctl->dsp->running) 11388c2ecf20Sopenharmony_ci ret = wm_coeff_write_acked_control(ctl, val); 11398c2ecf20Sopenharmony_ci else 11408c2ecf20Sopenharmony_ci ret = -EPERM; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci mutex_unlock(&ctl->dsp->pwr_lock); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return ret; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int wm_coeff_read_ctrl_raw(struct wm_coeff_ctl *ctl, 11488c2ecf20Sopenharmony_ci void *buf, size_t len) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct wm_adsp *dsp = ctl->dsp; 11518c2ecf20Sopenharmony_ci void *scratch; 11528c2ecf20Sopenharmony_ci int ret; 11538c2ecf20Sopenharmony_ci unsigned int reg; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci ret = wm_coeff_base_reg(ctl, ®); 11568c2ecf20Sopenharmony_ci if (ret) 11578c2ecf20Sopenharmony_ci return ret; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 11608c2ecf20Sopenharmony_ci if (!scratch) 11618c2ecf20Sopenharmony_ci return -ENOMEM; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 11648c2ecf20Sopenharmony_ci if (ret) { 11658c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 11668c2ecf20Sopenharmony_ci len, reg, ret); 11678c2ecf20Sopenharmony_ci kfree(scratch); 11688c2ecf20Sopenharmony_ci return ret; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci memcpy(buf, scratch, len); 11738c2ecf20Sopenharmony_ci kfree(scratch); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci return 0; 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic int wm_coeff_read_ctrl(struct wm_coeff_ctl *ctl, void *buf, size_t len) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci int ret = 0; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 11838c2ecf20Sopenharmony_ci if (ctl->enabled && ctl->dsp->running) 11848c2ecf20Sopenharmony_ci return wm_coeff_read_ctrl_raw(ctl, buf, len); 11858c2ecf20Sopenharmony_ci else 11868c2ecf20Sopenharmony_ci return -EPERM; 11878c2ecf20Sopenharmony_ci } else { 11888c2ecf20Sopenharmony_ci if (!ctl->flags && ctl->enabled && ctl->dsp->running) 11898c2ecf20Sopenharmony_ci ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (buf != ctl->cache) 11928c2ecf20Sopenharmony_ci memcpy(buf, ctl->cache, len); 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return ret; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic int wm_coeff_get(struct snd_kcontrol *kctl, 11998c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct soc_bytes_ext *bytes_ext = 12028c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kctl->private_value; 12038c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12048c2ecf20Sopenharmony_ci char *p = ucontrol->value.bytes.data; 12058c2ecf20Sopenharmony_ci int ret; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci mutex_lock(&ctl->dsp->pwr_lock); 12088c2ecf20Sopenharmony_ci ret = wm_coeff_read_ctrl(ctl, p, ctl->len); 12098c2ecf20Sopenharmony_ci mutex_unlock(&ctl->dsp->pwr_lock); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci return ret; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 12158c2ecf20Sopenharmony_ci unsigned int __user *bytes, unsigned int size) 12168c2ecf20Sopenharmony_ci{ 12178c2ecf20Sopenharmony_ci struct soc_bytes_ext *bytes_ext = 12188c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kctl->private_value; 12198c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 12208c2ecf20Sopenharmony_ci int ret = 0; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci mutex_lock(&ctl->dsp->pwr_lock); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci ret = wm_coeff_read_ctrl(ctl, ctl->cache, size); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (!ret && copy_to_user(bytes, ctl->cache, size)) 12278c2ecf20Sopenharmony_ci ret = -EFAULT; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci mutex_unlock(&ctl->dsp->pwr_lock); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return ret; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 12358c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci /* 12388c2ecf20Sopenharmony_ci * Although it's not useful to read an acked control, we must satisfy 12398c2ecf20Sopenharmony_ci * user-side assumptions that all controls are readable and that a 12408c2ecf20Sopenharmony_ci * write of the same value should be filtered out (it's valid to send 12418c2ecf20Sopenharmony_ci * the same event number again to the firmware). We therefore return 0, 12428c2ecf20Sopenharmony_ci * meaning "no event" so valid event numbers will always be a change 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci return 0; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistruct wmfw_ctl_work { 12508c2ecf20Sopenharmony_ci struct wm_adsp *dsp; 12518c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 12528c2ecf20Sopenharmony_ci struct work_struct work; 12538c2ecf20Sopenharmony_ci}; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_cistatic unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci unsigned int out, rd, wr, vol; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (len > ADSP_MAX_STD_CTRL_SIZE) { 12608c2ecf20Sopenharmony_ci rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 12618c2ecf20Sopenharmony_ci wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 12628c2ecf20Sopenharmony_ci vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 12658c2ecf20Sopenharmony_ci } else { 12668c2ecf20Sopenharmony_ci rd = SNDRV_CTL_ELEM_ACCESS_READ; 12678c2ecf20Sopenharmony_ci wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 12688c2ecf20Sopenharmony_ci vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci out = 0; 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (in) { 12748c2ecf20Sopenharmony_ci out |= rd; 12758c2ecf20Sopenharmony_ci if (in & WMFW_CTL_FLAG_WRITEABLE) 12768c2ecf20Sopenharmony_ci out |= wr; 12778c2ecf20Sopenharmony_ci if (in & WMFW_CTL_FLAG_VOLATILE) 12788c2ecf20Sopenharmony_ci out |= vol; 12798c2ecf20Sopenharmony_ci } else { 12808c2ecf20Sopenharmony_ci out |= rd | wr | vol; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci return out; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kcontrol; 12898c2ecf20Sopenharmony_ci int ret; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (!ctl || !ctl->name) 12928c2ecf20Sopenharmony_ci return -EINVAL; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 12958c2ecf20Sopenharmony_ci if (!kcontrol) 12968c2ecf20Sopenharmony_ci return -ENOMEM; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci kcontrol->name = ctl->name; 12998c2ecf20Sopenharmony_ci kcontrol->info = wm_coeff_info; 13008c2ecf20Sopenharmony_ci kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 13018c2ecf20Sopenharmony_ci kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 13028c2ecf20Sopenharmony_ci kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 13038c2ecf20Sopenharmony_ci kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci switch (ctl->type) { 13068c2ecf20Sopenharmony_ci case WMFW_CTL_TYPE_ACKED: 13078c2ecf20Sopenharmony_ci kcontrol->get = wm_coeff_get_acked; 13088c2ecf20Sopenharmony_ci kcontrol->put = wm_coeff_put_acked; 13098c2ecf20Sopenharmony_ci break; 13108c2ecf20Sopenharmony_ci default: 13118c2ecf20Sopenharmony_ci if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 13128c2ecf20Sopenharmony_ci ctl->bytes_ext.max = ctl->len; 13138c2ecf20Sopenharmony_ci ctl->bytes_ext.get = wm_coeff_tlv_get; 13148c2ecf20Sopenharmony_ci ctl->bytes_ext.put = wm_coeff_tlv_put; 13158c2ecf20Sopenharmony_ci } else { 13168c2ecf20Sopenharmony_ci kcontrol->get = wm_coeff_get; 13178c2ecf20Sopenharmony_ci kcontrol->put = wm_coeff_put; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci break; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); 13238c2ecf20Sopenharmony_ci if (ret < 0) 13248c2ecf20Sopenharmony_ci goto err_kcontrol; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci kfree(kcontrol); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci return 0; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cierr_kcontrol: 13318c2ecf20Sopenharmony_ci kfree(kcontrol); 13328c2ecf20Sopenharmony_ci return ret; 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic int wm_coeff_init_control_caches(struct wm_adsp *dsp) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 13388c2ecf20Sopenharmony_ci int ret; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) { 13418c2ecf20Sopenharmony_ci if (!ctl->enabled || ctl->set) 13428c2ecf20Sopenharmony_ci continue; 13438c2ecf20Sopenharmony_ci if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 13448c2ecf20Sopenharmony_ci continue; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci /* 13478c2ecf20Sopenharmony_ci * For readable controls populate the cache from the DSP memory. 13488c2ecf20Sopenharmony_ci * For non-readable controls the cache was zero-filled when 13498c2ecf20Sopenharmony_ci * created so we don't need to do anything. 13508c2ecf20Sopenharmony_ci */ 13518c2ecf20Sopenharmony_ci if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 13528c2ecf20Sopenharmony_ci ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 13538c2ecf20Sopenharmony_ci if (ret < 0) 13548c2ecf20Sopenharmony_ci return ret; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci return 0; 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic int wm_coeff_sync_controls(struct wm_adsp *dsp) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 13648c2ecf20Sopenharmony_ci int ret; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) { 13678c2ecf20Sopenharmony_ci if (!ctl->enabled) 13688c2ecf20Sopenharmony_ci continue; 13698c2ecf20Sopenharmony_ci if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 13708c2ecf20Sopenharmony_ci ret = wm_coeff_write_ctrl_raw(ctl, ctl->cache, 13718c2ecf20Sopenharmony_ci ctl->len); 13728c2ecf20Sopenharmony_ci if (ret < 0) 13738c2ecf20Sopenharmony_ci return ret; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci return 0; 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 13818c2ecf20Sopenharmony_ci unsigned int event) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 13848c2ecf20Sopenharmony_ci int ret; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) { 13878c2ecf20Sopenharmony_ci if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 13888c2ecf20Sopenharmony_ci continue; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if (!ctl->enabled) 13918c2ecf20Sopenharmony_ci continue; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci ret = wm_coeff_write_acked_control(ctl, event); 13948c2ecf20Sopenharmony_ci if (ret) 13958c2ecf20Sopenharmony_ci adsp_warn(dsp, 13968c2ecf20Sopenharmony_ci "Failed to send 0x%x event to alg 0x%x (%d)\n", 13978c2ecf20Sopenharmony_ci event, ctl->alg_region.alg, ret); 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic void wm_adsp_ctl_work(struct work_struct *work) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci struct wmfw_ctl_work *ctl_work = container_of(work, 14048c2ecf20Sopenharmony_ci struct wmfw_ctl_work, 14058c2ecf20Sopenharmony_ci work); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 14088c2ecf20Sopenharmony_ci kfree(ctl_work); 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci kfree(ctl->cache); 14148c2ecf20Sopenharmony_ci kfree(ctl->name); 14158c2ecf20Sopenharmony_ci kfree(ctl->subname); 14168c2ecf20Sopenharmony_ci kfree(ctl); 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_cistatic int wm_adsp_create_control(struct wm_adsp *dsp, 14208c2ecf20Sopenharmony_ci const struct wm_adsp_alg_region *alg_region, 14218c2ecf20Sopenharmony_ci unsigned int offset, unsigned int len, 14228c2ecf20Sopenharmony_ci const char *subname, unsigned int subname_len, 14238c2ecf20Sopenharmony_ci unsigned int flags, unsigned int type) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 14268c2ecf20Sopenharmony_ci struct wmfw_ctl_work *ctl_work; 14278c2ecf20Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 14288c2ecf20Sopenharmony_ci const char *region_name; 14298c2ecf20Sopenharmony_ci int ret; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci region_name = wm_adsp_mem_region_name(alg_region->type); 14328c2ecf20Sopenharmony_ci if (!region_name) { 14338c2ecf20Sopenharmony_ci adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 14348c2ecf20Sopenharmony_ci return -EINVAL; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci switch (dsp->fw_ver) { 14388c2ecf20Sopenharmony_ci case 0: 14398c2ecf20Sopenharmony_ci case 1: 14408c2ecf20Sopenharmony_ci snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", 14418c2ecf20Sopenharmony_ci dsp->name, region_name, alg_region->alg); 14428c2ecf20Sopenharmony_ci subname = NULL; /* don't append subname */ 14438c2ecf20Sopenharmony_ci break; 14448c2ecf20Sopenharmony_ci case 2: 14458c2ecf20Sopenharmony_ci ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 14468c2ecf20Sopenharmony_ci "%s%c %.12s %x", dsp->name, *region_name, 14478c2ecf20Sopenharmony_ci wm_adsp_fw_text[dsp->fw], alg_region->alg); 14488c2ecf20Sopenharmony_ci break; 14498c2ecf20Sopenharmony_ci default: 14508c2ecf20Sopenharmony_ci ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 14518c2ecf20Sopenharmony_ci "%s %.12s %x", dsp->name, 14528c2ecf20Sopenharmony_ci wm_adsp_fw_text[dsp->fw], alg_region->alg); 14538c2ecf20Sopenharmony_ci break; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (subname) { 14578c2ecf20Sopenharmony_ci int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 14588c2ecf20Sopenharmony_ci int skip = 0; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (dsp->component->name_prefix) 14618c2ecf20Sopenharmony_ci avail -= strlen(dsp->component->name_prefix) + 1; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* Truncate the subname from the start if it is too long */ 14648c2ecf20Sopenharmony_ci if (subname_len > avail) 14658c2ecf20Sopenharmony_ci skip = subname_len - avail; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, 14688c2ecf20Sopenharmony_ci " %.*s", subname_len - skip, subname + skip); 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) { 14728c2ecf20Sopenharmony_ci if (!strcmp(ctl->name, name)) { 14738c2ecf20Sopenharmony_ci if (!ctl->enabled) 14748c2ecf20Sopenharmony_ci ctl->enabled = 1; 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 14808c2ecf20Sopenharmony_ci if (!ctl) 14818c2ecf20Sopenharmony_ci return -ENOMEM; 14828c2ecf20Sopenharmony_ci ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 14838c2ecf20Sopenharmony_ci ctl->alg_region = *alg_region; 14848c2ecf20Sopenharmony_ci ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 14858c2ecf20Sopenharmony_ci if (!ctl->name) { 14868c2ecf20Sopenharmony_ci ret = -ENOMEM; 14878c2ecf20Sopenharmony_ci goto err_ctl; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci if (subname) { 14908c2ecf20Sopenharmony_ci ctl->subname_len = subname_len; 14918c2ecf20Sopenharmony_ci ctl->subname = kmemdup(subname, 14928c2ecf20Sopenharmony_ci strlen(subname) + 1, GFP_KERNEL); 14938c2ecf20Sopenharmony_ci if (!ctl->subname) { 14948c2ecf20Sopenharmony_ci ret = -ENOMEM; 14958c2ecf20Sopenharmony_ci goto err_ctl_name; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci ctl->enabled = 1; 14998c2ecf20Sopenharmony_ci ctl->set = 0; 15008c2ecf20Sopenharmony_ci ctl->ops.xget = wm_coeff_get; 15018c2ecf20Sopenharmony_ci ctl->ops.xput = wm_coeff_put; 15028c2ecf20Sopenharmony_ci ctl->dsp = dsp; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci ctl->flags = flags; 15058c2ecf20Sopenharmony_ci ctl->type = type; 15068c2ecf20Sopenharmony_ci ctl->offset = offset; 15078c2ecf20Sopenharmony_ci ctl->len = len; 15088c2ecf20Sopenharmony_ci ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 15098c2ecf20Sopenharmony_ci if (!ctl->cache) { 15108c2ecf20Sopenharmony_ci ret = -ENOMEM; 15118c2ecf20Sopenharmony_ci goto err_ctl_subname; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci list_add(&ctl->list, &dsp->ctl_list); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (flags & WMFW_CTL_FLAG_SYS) 15178c2ecf20Sopenharmony_ci return 0; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 15208c2ecf20Sopenharmony_ci if (!ctl_work) { 15218c2ecf20Sopenharmony_ci ret = -ENOMEM; 15228c2ecf20Sopenharmony_ci goto err_list_del; 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci ctl_work->dsp = dsp; 15268c2ecf20Sopenharmony_ci ctl_work->ctl = ctl; 15278c2ecf20Sopenharmony_ci INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 15288c2ecf20Sopenharmony_ci schedule_work(&ctl_work->work); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci return 0; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cierr_list_del: 15338c2ecf20Sopenharmony_ci list_del(&ctl->list); 15348c2ecf20Sopenharmony_ci kfree(ctl->cache); 15358c2ecf20Sopenharmony_cierr_ctl_subname: 15368c2ecf20Sopenharmony_ci kfree(ctl->subname); 15378c2ecf20Sopenharmony_cierr_ctl_name: 15388c2ecf20Sopenharmony_ci kfree(ctl->name); 15398c2ecf20Sopenharmony_cierr_ctl: 15408c2ecf20Sopenharmony_ci kfree(ctl); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci return ret; 15438c2ecf20Sopenharmony_ci} 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_cistruct wm_coeff_parsed_alg { 15468c2ecf20Sopenharmony_ci int id; 15478c2ecf20Sopenharmony_ci const u8 *name; 15488c2ecf20Sopenharmony_ci int name_len; 15498c2ecf20Sopenharmony_ci int ncoeff; 15508c2ecf20Sopenharmony_ci}; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_cistruct wm_coeff_parsed_coeff { 15538c2ecf20Sopenharmony_ci int offset; 15548c2ecf20Sopenharmony_ci int mem_type; 15558c2ecf20Sopenharmony_ci const u8 *name; 15568c2ecf20Sopenharmony_ci int name_len; 15578c2ecf20Sopenharmony_ci int ctl_type; 15588c2ecf20Sopenharmony_ci int flags; 15598c2ecf20Sopenharmony_ci int len; 15608c2ecf20Sopenharmony_ci}; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci int length; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci switch (bytes) { 15678c2ecf20Sopenharmony_ci case 1: 15688c2ecf20Sopenharmony_ci length = **pos; 15698c2ecf20Sopenharmony_ci break; 15708c2ecf20Sopenharmony_ci case 2: 15718c2ecf20Sopenharmony_ci length = le16_to_cpu(*((__le16 *)*pos)); 15728c2ecf20Sopenharmony_ci break; 15738c2ecf20Sopenharmony_ci default: 15748c2ecf20Sopenharmony_ci return 0; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (str) 15788c2ecf20Sopenharmony_ci *str = *pos + bytes; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci *pos += ((length + bytes) + 3) & ~0x03; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci return length; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_cistatic int wm_coeff_parse_int(int bytes, const u8 **pos) 15868c2ecf20Sopenharmony_ci{ 15878c2ecf20Sopenharmony_ci int val = 0; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci switch (bytes) { 15908c2ecf20Sopenharmony_ci case 2: 15918c2ecf20Sopenharmony_ci val = le16_to_cpu(*((__le16 *)*pos)); 15928c2ecf20Sopenharmony_ci break; 15938c2ecf20Sopenharmony_ci case 4: 15948c2ecf20Sopenharmony_ci val = le32_to_cpu(*((__le32 *)*pos)); 15958c2ecf20Sopenharmony_ci break; 15968c2ecf20Sopenharmony_ci default: 15978c2ecf20Sopenharmony_ci break; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci *pos += bytes; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci return val; 16038c2ecf20Sopenharmony_ci} 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_cistatic inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 16068c2ecf20Sopenharmony_ci struct wm_coeff_parsed_alg *blk) 16078c2ecf20Sopenharmony_ci{ 16088c2ecf20Sopenharmony_ci const struct wmfw_adsp_alg_data *raw; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci switch (dsp->fw_ver) { 16118c2ecf20Sopenharmony_ci case 0: 16128c2ecf20Sopenharmony_ci case 1: 16138c2ecf20Sopenharmony_ci raw = (const struct wmfw_adsp_alg_data *)*data; 16148c2ecf20Sopenharmony_ci *data = raw->data; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci blk->id = le32_to_cpu(raw->id); 16178c2ecf20Sopenharmony_ci blk->name = raw->name; 16188c2ecf20Sopenharmony_ci blk->name_len = strlen(raw->name); 16198c2ecf20Sopenharmony_ci blk->ncoeff = le32_to_cpu(raw->ncoeff); 16208c2ecf20Sopenharmony_ci break; 16218c2ecf20Sopenharmony_ci default: 16228c2ecf20Sopenharmony_ci blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 16238c2ecf20Sopenharmony_ci blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 16248c2ecf20Sopenharmony_ci &blk->name); 16258c2ecf20Sopenharmony_ci wm_coeff_parse_string(sizeof(u16), data, NULL); 16268c2ecf20Sopenharmony_ci blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 16278c2ecf20Sopenharmony_ci break; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 16318c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 16328c2ecf20Sopenharmony_ci adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 16338c2ecf20Sopenharmony_ci} 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_cistatic inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 16368c2ecf20Sopenharmony_ci struct wm_coeff_parsed_coeff *blk) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci const struct wmfw_adsp_coeff_data *raw; 16398c2ecf20Sopenharmony_ci const u8 *tmp; 16408c2ecf20Sopenharmony_ci int length; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci switch (dsp->fw_ver) { 16438c2ecf20Sopenharmony_ci case 0: 16448c2ecf20Sopenharmony_ci case 1: 16458c2ecf20Sopenharmony_ci raw = (const struct wmfw_adsp_coeff_data *)*data; 16468c2ecf20Sopenharmony_ci *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci blk->offset = le16_to_cpu(raw->hdr.offset); 16498c2ecf20Sopenharmony_ci blk->mem_type = le16_to_cpu(raw->hdr.type); 16508c2ecf20Sopenharmony_ci blk->name = raw->name; 16518c2ecf20Sopenharmony_ci blk->name_len = strlen(raw->name); 16528c2ecf20Sopenharmony_ci blk->ctl_type = le16_to_cpu(raw->ctl_type); 16538c2ecf20Sopenharmony_ci blk->flags = le16_to_cpu(raw->flags); 16548c2ecf20Sopenharmony_ci blk->len = le32_to_cpu(raw->len); 16558c2ecf20Sopenharmony_ci break; 16568c2ecf20Sopenharmony_ci default: 16578c2ecf20Sopenharmony_ci tmp = *data; 16588c2ecf20Sopenharmony_ci blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 16598c2ecf20Sopenharmony_ci blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 16608c2ecf20Sopenharmony_ci length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 16618c2ecf20Sopenharmony_ci blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 16628c2ecf20Sopenharmony_ci &blk->name); 16638c2ecf20Sopenharmony_ci wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 16648c2ecf20Sopenharmony_ci wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 16658c2ecf20Sopenharmony_ci blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 16668c2ecf20Sopenharmony_ci blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 16678c2ecf20Sopenharmony_ci blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci *data = *data + sizeof(raw->hdr) + length; 16708c2ecf20Sopenharmony_ci break; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 16748c2ecf20Sopenharmony_ci adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 16758c2ecf20Sopenharmony_ci adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 16768c2ecf20Sopenharmony_ci adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 16778c2ecf20Sopenharmony_ci adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 16788c2ecf20Sopenharmony_ci adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_cistatic int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 16828c2ecf20Sopenharmony_ci const struct wm_coeff_parsed_coeff *coeff_blk, 16838c2ecf20Sopenharmony_ci unsigned int f_required, 16848c2ecf20Sopenharmony_ci unsigned int f_illegal) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci if ((coeff_blk->flags & f_illegal) || 16878c2ecf20Sopenharmony_ci ((coeff_blk->flags & f_required) != f_required)) { 16888c2ecf20Sopenharmony_ci adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 16898c2ecf20Sopenharmony_ci coeff_blk->flags, coeff_blk->ctl_type); 16908c2ecf20Sopenharmony_ci return -EINVAL; 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci return 0; 16948c2ecf20Sopenharmony_ci} 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_cistatic int wm_adsp_parse_coeff(struct wm_adsp *dsp, 16978c2ecf20Sopenharmony_ci const struct wmfw_region *region) 16988c2ecf20Sopenharmony_ci{ 16998c2ecf20Sopenharmony_ci struct wm_adsp_alg_region alg_region = {}; 17008c2ecf20Sopenharmony_ci struct wm_coeff_parsed_alg alg_blk; 17018c2ecf20Sopenharmony_ci struct wm_coeff_parsed_coeff coeff_blk; 17028c2ecf20Sopenharmony_ci const u8 *data = region->data; 17038c2ecf20Sopenharmony_ci int i, ret; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci wm_coeff_parse_alg(dsp, &data, &alg_blk); 17068c2ecf20Sopenharmony_ci for (i = 0; i < alg_blk.ncoeff; i++) { 17078c2ecf20Sopenharmony_ci wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci switch (coeff_blk.ctl_type) { 17108c2ecf20Sopenharmony_ci case SNDRV_CTL_ELEM_TYPE_BYTES: 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci case WMFW_CTL_TYPE_ACKED: 17138c2ecf20Sopenharmony_ci if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 17148c2ecf20Sopenharmony_ci continue; /* ignore */ 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 17178c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_VOLATILE | 17188c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_WRITEABLE | 17198c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_READABLE, 17208c2ecf20Sopenharmony_ci 0); 17218c2ecf20Sopenharmony_ci if (ret) 17228c2ecf20Sopenharmony_ci return -EINVAL; 17238c2ecf20Sopenharmony_ci break; 17248c2ecf20Sopenharmony_ci case WMFW_CTL_TYPE_HOSTEVENT: 17258c2ecf20Sopenharmony_ci ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 17268c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_SYS | 17278c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_VOLATILE | 17288c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_WRITEABLE | 17298c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_READABLE, 17308c2ecf20Sopenharmony_ci 0); 17318c2ecf20Sopenharmony_ci if (ret) 17328c2ecf20Sopenharmony_ci return -EINVAL; 17338c2ecf20Sopenharmony_ci break; 17348c2ecf20Sopenharmony_ci case WMFW_CTL_TYPE_HOST_BUFFER: 17358c2ecf20Sopenharmony_ci ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 17368c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_SYS | 17378c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_VOLATILE | 17388c2ecf20Sopenharmony_ci WMFW_CTL_FLAG_READABLE, 17398c2ecf20Sopenharmony_ci 0); 17408c2ecf20Sopenharmony_ci if (ret) 17418c2ecf20Sopenharmony_ci return -EINVAL; 17428c2ecf20Sopenharmony_ci break; 17438c2ecf20Sopenharmony_ci default: 17448c2ecf20Sopenharmony_ci adsp_err(dsp, "Unknown control type: %d\n", 17458c2ecf20Sopenharmony_ci coeff_blk.ctl_type); 17468c2ecf20Sopenharmony_ci return -EINVAL; 17478c2ecf20Sopenharmony_ci } 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci alg_region.type = coeff_blk.mem_type; 17508c2ecf20Sopenharmony_ci alg_region.alg = alg_blk.id; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci ret = wm_adsp_create_control(dsp, &alg_region, 17538c2ecf20Sopenharmony_ci coeff_blk.offset, 17548c2ecf20Sopenharmony_ci coeff_blk.len, 17558c2ecf20Sopenharmony_ci coeff_blk.name, 17568c2ecf20Sopenharmony_ci coeff_blk.name_len, 17578c2ecf20Sopenharmony_ci coeff_blk.flags, 17588c2ecf20Sopenharmony_ci coeff_blk.ctl_type); 17598c2ecf20Sopenharmony_ci if (ret < 0) 17608c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to create control: %.*s, %d\n", 17618c2ecf20Sopenharmony_ci coeff_blk.name_len, coeff_blk.name, ret); 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci return 0; 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_cistatic unsigned int wm_adsp1_parse_sizes(struct wm_adsp *dsp, 17688c2ecf20Sopenharmony_ci const char * const file, 17698c2ecf20Sopenharmony_ci unsigned int pos, 17708c2ecf20Sopenharmony_ci const struct firmware *firmware) 17718c2ecf20Sopenharmony_ci{ 17728c2ecf20Sopenharmony_ci const struct wmfw_adsp1_sizes *adsp1_sizes; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci adsp1_sizes = (void *)&firmware->data[pos]; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 17778c2ecf20Sopenharmony_ci le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 17788c2ecf20Sopenharmony_ci le32_to_cpu(adsp1_sizes->zm)); 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci return pos + sizeof(*adsp1_sizes); 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_cistatic unsigned int wm_adsp2_parse_sizes(struct wm_adsp *dsp, 17848c2ecf20Sopenharmony_ci const char * const file, 17858c2ecf20Sopenharmony_ci unsigned int pos, 17868c2ecf20Sopenharmony_ci const struct firmware *firmware) 17878c2ecf20Sopenharmony_ci{ 17888c2ecf20Sopenharmony_ci const struct wmfw_adsp2_sizes *adsp2_sizes; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci adsp2_sizes = (void *)&firmware->data[pos]; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 17938c2ecf20Sopenharmony_ci le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 17948c2ecf20Sopenharmony_ci le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci return pos + sizeof(*adsp2_sizes); 17978c2ecf20Sopenharmony_ci} 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_cistatic bool wm_adsp_validate_version(struct wm_adsp *dsp, unsigned int version) 18008c2ecf20Sopenharmony_ci{ 18018c2ecf20Sopenharmony_ci switch (version) { 18028c2ecf20Sopenharmony_ci case 0: 18038c2ecf20Sopenharmony_ci adsp_warn(dsp, "Deprecated file format %d\n", version); 18048c2ecf20Sopenharmony_ci return true; 18058c2ecf20Sopenharmony_ci case 1: 18068c2ecf20Sopenharmony_ci case 2: 18078c2ecf20Sopenharmony_ci return true; 18088c2ecf20Sopenharmony_ci default: 18098c2ecf20Sopenharmony_ci return false; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci} 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_cistatic bool wm_halo_validate_version(struct wm_adsp *dsp, unsigned int version) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci switch (version) { 18168c2ecf20Sopenharmony_ci case 3: 18178c2ecf20Sopenharmony_ci return true; 18188c2ecf20Sopenharmony_ci default: 18198c2ecf20Sopenharmony_ci return false; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci} 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_cistatic int wm_adsp_load(struct wm_adsp *dsp) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci LIST_HEAD(buf_list); 18268c2ecf20Sopenharmony_ci const struct firmware *firmware; 18278c2ecf20Sopenharmony_ci struct regmap *regmap = dsp->regmap; 18288c2ecf20Sopenharmony_ci unsigned int pos = 0; 18298c2ecf20Sopenharmony_ci const struct wmfw_header *header; 18308c2ecf20Sopenharmony_ci const struct wmfw_adsp1_sizes *adsp1_sizes; 18318c2ecf20Sopenharmony_ci const struct wmfw_footer *footer; 18328c2ecf20Sopenharmony_ci const struct wmfw_region *region; 18338c2ecf20Sopenharmony_ci const struct wm_adsp_region *mem; 18348c2ecf20Sopenharmony_ci const char *region_name; 18358c2ecf20Sopenharmony_ci char *file, *text = NULL; 18368c2ecf20Sopenharmony_ci struct wm_adsp_buf *buf; 18378c2ecf20Sopenharmony_ci unsigned int reg; 18388c2ecf20Sopenharmony_ci int regions = 0; 18398c2ecf20Sopenharmony_ci int ret, offset, type; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci file = kzalloc(PAGE_SIZE, GFP_KERNEL); 18428c2ecf20Sopenharmony_ci if (file == NULL) 18438c2ecf20Sopenharmony_ci return -ENOMEM; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, 18468c2ecf20Sopenharmony_ci wm_adsp_fw[dsp->fw].file); 18478c2ecf20Sopenharmony_ci file[PAGE_SIZE - 1] = '\0'; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci ret = request_firmware(&firmware, file, dsp->dev); 18508c2ecf20Sopenharmony_ci if (ret != 0) { 18518c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to request '%s'\n", file); 18528c2ecf20Sopenharmony_ci goto out; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci ret = -EINVAL; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 18578c2ecf20Sopenharmony_ci if (pos >= firmware->size) { 18588c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: file too short, %zu bytes\n", 18598c2ecf20Sopenharmony_ci file, firmware->size); 18608c2ecf20Sopenharmony_ci goto out_fw; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci header = (void *)&firmware->data[0]; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 18668c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: invalid magic\n", file); 18678c2ecf20Sopenharmony_ci goto out_fw; 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci if (!dsp->ops->validate_version(dsp, header->ver)) { 18718c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: unknown file format %d\n", 18728c2ecf20Sopenharmony_ci file, header->ver); 18738c2ecf20Sopenharmony_ci goto out_fw; 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci adsp_info(dsp, "Firmware version: %d\n", header->ver); 18778c2ecf20Sopenharmony_ci dsp->fw_ver = header->ver; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci if (header->core != dsp->type) { 18808c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: invalid core %d != %d\n", 18818c2ecf20Sopenharmony_ci file, header->core, dsp->type); 18828c2ecf20Sopenharmony_ci goto out_fw; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci pos = sizeof(*header); 18868c2ecf20Sopenharmony_ci pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci footer = (void *)&firmware->data[pos]; 18898c2ecf20Sopenharmony_ci pos += sizeof(*footer); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if (le32_to_cpu(header->len) != pos) { 18928c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: unexpected header length %d\n", 18938c2ecf20Sopenharmony_ci file, le32_to_cpu(header->len)); 18948c2ecf20Sopenharmony_ci goto out_fw; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s: timestamp %llu\n", file, 18988c2ecf20Sopenharmony_ci le64_to_cpu(footer->timestamp)); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci while (pos < firmware->size && 19018c2ecf20Sopenharmony_ci sizeof(*region) < firmware->size - pos) { 19028c2ecf20Sopenharmony_ci region = (void *)&(firmware->data[pos]); 19038c2ecf20Sopenharmony_ci region_name = "Unknown"; 19048c2ecf20Sopenharmony_ci reg = 0; 19058c2ecf20Sopenharmony_ci text = NULL; 19068c2ecf20Sopenharmony_ci offset = le32_to_cpu(region->offset) & 0xffffff; 19078c2ecf20Sopenharmony_ci type = be32_to_cpu(region->type) & 0xff; 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci switch (type) { 19108c2ecf20Sopenharmony_ci case WMFW_NAME_TEXT: 19118c2ecf20Sopenharmony_ci region_name = "Firmware name"; 19128c2ecf20Sopenharmony_ci text = kzalloc(le32_to_cpu(region->len) + 1, 19138c2ecf20Sopenharmony_ci GFP_KERNEL); 19148c2ecf20Sopenharmony_ci break; 19158c2ecf20Sopenharmony_ci case WMFW_ALGORITHM_DATA: 19168c2ecf20Sopenharmony_ci region_name = "Algorithm"; 19178c2ecf20Sopenharmony_ci ret = wm_adsp_parse_coeff(dsp, region); 19188c2ecf20Sopenharmony_ci if (ret != 0) 19198c2ecf20Sopenharmony_ci goto out_fw; 19208c2ecf20Sopenharmony_ci break; 19218c2ecf20Sopenharmony_ci case WMFW_INFO_TEXT: 19228c2ecf20Sopenharmony_ci region_name = "Information"; 19238c2ecf20Sopenharmony_ci text = kzalloc(le32_to_cpu(region->len) + 1, 19248c2ecf20Sopenharmony_ci GFP_KERNEL); 19258c2ecf20Sopenharmony_ci break; 19268c2ecf20Sopenharmony_ci case WMFW_ABSOLUTE: 19278c2ecf20Sopenharmony_ci region_name = "Absolute"; 19288c2ecf20Sopenharmony_ci reg = offset; 19298c2ecf20Sopenharmony_ci break; 19308c2ecf20Sopenharmony_ci case WMFW_ADSP1_PM: 19318c2ecf20Sopenharmony_ci case WMFW_ADSP1_DM: 19328c2ecf20Sopenharmony_ci case WMFW_ADSP2_XM: 19338c2ecf20Sopenharmony_ci case WMFW_ADSP2_YM: 19348c2ecf20Sopenharmony_ci case WMFW_ADSP1_ZM: 19358c2ecf20Sopenharmony_ci case WMFW_HALO_PM_PACKED: 19368c2ecf20Sopenharmony_ci case WMFW_HALO_XM_PACKED: 19378c2ecf20Sopenharmony_ci case WMFW_HALO_YM_PACKED: 19388c2ecf20Sopenharmony_ci mem = wm_adsp_find_region(dsp, type); 19398c2ecf20Sopenharmony_ci if (!mem) { 19408c2ecf20Sopenharmony_ci adsp_err(dsp, "No region of type: %x\n", type); 19418c2ecf20Sopenharmony_ci ret = -EINVAL; 19428c2ecf20Sopenharmony_ci goto out_fw; 19438c2ecf20Sopenharmony_ci } 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci region_name = wm_adsp_mem_region_name(type); 19468c2ecf20Sopenharmony_ci reg = dsp->ops->region_to_reg(mem, offset); 19478c2ecf20Sopenharmony_ci break; 19488c2ecf20Sopenharmony_ci default: 19498c2ecf20Sopenharmony_ci adsp_warn(dsp, 19508c2ecf20Sopenharmony_ci "%s.%d: Unknown region type %x at %d(%x)\n", 19518c2ecf20Sopenharmony_ci file, regions, type, pos, pos); 19528c2ecf20Sopenharmony_ci break; 19538c2ecf20Sopenharmony_ci } 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 19568c2ecf20Sopenharmony_ci regions, le32_to_cpu(region->len), offset, 19578c2ecf20Sopenharmony_ci region_name); 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci if (le32_to_cpu(region->len) > 19608c2ecf20Sopenharmony_ci firmware->size - pos - sizeof(*region)) { 19618c2ecf20Sopenharmony_ci adsp_err(dsp, 19628c2ecf20Sopenharmony_ci "%s.%d: %s region len %d bytes exceeds file length %zu\n", 19638c2ecf20Sopenharmony_ci file, regions, region_name, 19648c2ecf20Sopenharmony_ci le32_to_cpu(region->len), firmware->size); 19658c2ecf20Sopenharmony_ci ret = -EINVAL; 19668c2ecf20Sopenharmony_ci goto out_fw; 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci if (text) { 19708c2ecf20Sopenharmony_ci memcpy(text, region->data, le32_to_cpu(region->len)); 19718c2ecf20Sopenharmony_ci adsp_info(dsp, "%s: %s\n", file, text); 19728c2ecf20Sopenharmony_ci kfree(text); 19738c2ecf20Sopenharmony_ci text = NULL; 19748c2ecf20Sopenharmony_ci } 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci if (reg) { 19778c2ecf20Sopenharmony_ci buf = wm_adsp_buf_alloc(region->data, 19788c2ecf20Sopenharmony_ci le32_to_cpu(region->len), 19798c2ecf20Sopenharmony_ci &buf_list); 19808c2ecf20Sopenharmony_ci if (!buf) { 19818c2ecf20Sopenharmony_ci adsp_err(dsp, "Out of memory\n"); 19828c2ecf20Sopenharmony_ci ret = -ENOMEM; 19838c2ecf20Sopenharmony_ci goto out_fw; 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci ret = regmap_raw_write_async(regmap, reg, buf->buf, 19878c2ecf20Sopenharmony_ci le32_to_cpu(region->len)); 19888c2ecf20Sopenharmony_ci if (ret != 0) { 19898c2ecf20Sopenharmony_ci adsp_err(dsp, 19908c2ecf20Sopenharmony_ci "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 19918c2ecf20Sopenharmony_ci file, regions, 19928c2ecf20Sopenharmony_ci le32_to_cpu(region->len), offset, 19938c2ecf20Sopenharmony_ci region_name, ret); 19948c2ecf20Sopenharmony_ci goto out_fw; 19958c2ecf20Sopenharmony_ci } 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci pos += le32_to_cpu(region->len) + sizeof(*region); 19998c2ecf20Sopenharmony_ci regions++; 20008c2ecf20Sopenharmony_ci } 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci ret = regmap_async_complete(regmap); 20038c2ecf20Sopenharmony_ci if (ret != 0) { 20048c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to complete async write: %d\n", ret); 20058c2ecf20Sopenharmony_ci goto out_fw; 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci if (pos > firmware->size) 20098c2ecf20Sopenharmony_ci adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 20108c2ecf20Sopenharmony_ci file, regions, pos - firmware->size); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci wm_adsp_debugfs_save_wmfwname(dsp, file); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ciout_fw: 20158c2ecf20Sopenharmony_ci regmap_async_complete(regmap); 20168c2ecf20Sopenharmony_ci wm_adsp_buf_free(&buf_list); 20178c2ecf20Sopenharmony_ci release_firmware(firmware); 20188c2ecf20Sopenharmony_ci kfree(text); 20198c2ecf20Sopenharmony_ciout: 20208c2ecf20Sopenharmony_ci kfree(file); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci return ret; 20238c2ecf20Sopenharmony_ci} 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci/* 20268c2ecf20Sopenharmony_ci * Find wm_coeff_ctl with input name as its subname 20278c2ecf20Sopenharmony_ci * If not found, return NULL 20288c2ecf20Sopenharmony_ci */ 20298c2ecf20Sopenharmony_cistatic struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp, 20308c2ecf20Sopenharmony_ci const char *name, int type, 20318c2ecf20Sopenharmony_ci unsigned int alg) 20328c2ecf20Sopenharmony_ci{ 20338c2ecf20Sopenharmony_ci struct wm_coeff_ctl *pos, *rslt = NULL; 20348c2ecf20Sopenharmony_ci const char *fw_txt = wm_adsp_fw_text[dsp->fw]; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci list_for_each_entry(pos, &dsp->ctl_list, list) { 20378c2ecf20Sopenharmony_ci if (!pos->subname) 20388c2ecf20Sopenharmony_ci continue; 20398c2ecf20Sopenharmony_ci if (strncmp(pos->subname, name, pos->subname_len) == 0 && 20408c2ecf20Sopenharmony_ci strncmp(pos->fw_name, fw_txt, 20418c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0 && 20428c2ecf20Sopenharmony_ci pos->alg_region.alg == alg && 20438c2ecf20Sopenharmony_ci pos->alg_region.type == type) { 20448c2ecf20Sopenharmony_ci rslt = pos; 20458c2ecf20Sopenharmony_ci break; 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci return rslt; 20508c2ecf20Sopenharmony_ci} 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ciint wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, 20538c2ecf20Sopenharmony_ci unsigned int alg, void *buf, size_t len) 20548c2ecf20Sopenharmony_ci{ 20558c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 20568c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol; 20578c2ecf20Sopenharmony_ci char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 20588c2ecf20Sopenharmony_ci int ret; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci ctl = wm_adsp_get_ctl(dsp, name, type, alg); 20618c2ecf20Sopenharmony_ci if (!ctl) 20628c2ecf20Sopenharmony_ci return -EINVAL; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci if (len > ctl->len) 20658c2ecf20Sopenharmony_ci return -EINVAL; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci ret = wm_coeff_write_ctrl(ctl, buf, len); 20688c2ecf20Sopenharmony_ci if (ret) 20698c2ecf20Sopenharmony_ci return ret; 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci if (ctl->flags & WMFW_CTL_FLAG_SYS) 20728c2ecf20Sopenharmony_ci return 0; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci if (dsp->component->name_prefix) 20758c2ecf20Sopenharmony_ci snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", 20768c2ecf20Sopenharmony_ci dsp->component->name_prefix, ctl->name); 20778c2ecf20Sopenharmony_ci else 20788c2ecf20Sopenharmony_ci snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", 20798c2ecf20Sopenharmony_ci ctl->name); 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); 20828c2ecf20Sopenharmony_ci if (!kcontrol) { 20838c2ecf20Sopenharmony_ci adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); 20848c2ecf20Sopenharmony_ci return -EINVAL; 20858c2ecf20Sopenharmony_ci } 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci snd_ctl_notify(dsp->component->card->snd_card, 20888c2ecf20Sopenharmony_ci SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci return ret; 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_write_ctl); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ciint wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, 20958c2ecf20Sopenharmony_ci unsigned int alg, void *buf, size_t len) 20968c2ecf20Sopenharmony_ci{ 20978c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci ctl = wm_adsp_get_ctl(dsp, name, type, alg); 21008c2ecf20Sopenharmony_ci if (!ctl) 21018c2ecf20Sopenharmony_ci return -EINVAL; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci if (len > ctl->len) 21048c2ecf20Sopenharmony_ci return -EINVAL; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci return wm_coeff_read_ctrl(ctl, buf, len); 21078c2ecf20Sopenharmony_ci} 21088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_read_ctl); 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_cistatic void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 21118c2ecf20Sopenharmony_ci const struct wm_adsp_alg_region *alg_region) 21128c2ecf20Sopenharmony_ci{ 21138c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) { 21168c2ecf20Sopenharmony_ci if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 21178c2ecf20Sopenharmony_ci alg_region->alg == ctl->alg_region.alg && 21188c2ecf20Sopenharmony_ci alg_region->type == ctl->alg_region.type) { 21198c2ecf20Sopenharmony_ci ctl->alg_region.base = alg_region->base; 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci} 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cistatic void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 21258c2ecf20Sopenharmony_ci const struct wm_adsp_region *mem, 21268c2ecf20Sopenharmony_ci unsigned int pos, unsigned int len) 21278c2ecf20Sopenharmony_ci{ 21288c2ecf20Sopenharmony_ci void *alg; 21298c2ecf20Sopenharmony_ci unsigned int reg; 21308c2ecf20Sopenharmony_ci int ret; 21318c2ecf20Sopenharmony_ci __be32 val; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci if (n_algs == 0) { 21348c2ecf20Sopenharmony_ci adsp_err(dsp, "No algorithms\n"); 21358c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 21368c2ecf20Sopenharmony_ci } 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci if (n_algs > 1024) { 21398c2ecf20Sopenharmony_ci adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 21408c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 21418c2ecf20Sopenharmony_ci } 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci /* Read the terminator first to validate the length */ 21448c2ecf20Sopenharmony_ci reg = dsp->ops->region_to_reg(mem, pos + len); 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 21478c2ecf20Sopenharmony_ci if (ret != 0) { 21488c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read algorithm list end: %d\n", 21498c2ecf20Sopenharmony_ci ret); 21508c2ecf20Sopenharmony_ci return ERR_PTR(ret); 21518c2ecf20Sopenharmony_ci } 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci if (be32_to_cpu(val) != 0xbedead) 21548c2ecf20Sopenharmony_ci adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 21558c2ecf20Sopenharmony_ci reg, be32_to_cpu(val)); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci /* Convert length from DSP words to bytes */ 21588c2ecf20Sopenharmony_ci len *= sizeof(u32); 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 21618c2ecf20Sopenharmony_ci if (!alg) 21628c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci reg = dsp->ops->region_to_reg(mem, pos); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, reg, alg, len); 21678c2ecf20Sopenharmony_ci if (ret != 0) { 21688c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 21698c2ecf20Sopenharmony_ci kfree(alg); 21708c2ecf20Sopenharmony_ci return ERR_PTR(ret); 21718c2ecf20Sopenharmony_ci } 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci return alg; 21748c2ecf20Sopenharmony_ci} 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_cistatic struct wm_adsp_alg_region * 21778c2ecf20Sopenharmony_ci wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 21788c2ecf20Sopenharmony_ci{ 21798c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci list_for_each_entry(alg_region, &dsp->alg_regions, list) { 21828c2ecf20Sopenharmony_ci if (id == alg_region->alg && type == alg_region->type) 21838c2ecf20Sopenharmony_ci return alg_region; 21848c2ecf20Sopenharmony_ci } 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci return NULL; 21878c2ecf20Sopenharmony_ci} 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_cistatic struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 21908c2ecf20Sopenharmony_ci int type, __be32 id, 21918c2ecf20Sopenharmony_ci __be32 base) 21928c2ecf20Sopenharmony_ci{ 21938c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 21968c2ecf20Sopenharmony_ci if (!alg_region) 21978c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci alg_region->type = type; 22008c2ecf20Sopenharmony_ci alg_region->alg = be32_to_cpu(id); 22018c2ecf20Sopenharmony_ci alg_region->base = be32_to_cpu(base); 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci list_add_tail(&alg_region->list, &dsp->alg_regions); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci if (dsp->fw_ver > 0) 22068c2ecf20Sopenharmony_ci wm_adsp_ctl_fixup_base(dsp, alg_region); 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci return alg_region; 22098c2ecf20Sopenharmony_ci} 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_cistatic void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 22128c2ecf20Sopenharmony_ci{ 22138c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci while (!list_empty(&dsp->alg_regions)) { 22168c2ecf20Sopenharmony_ci alg_region = list_first_entry(&dsp->alg_regions, 22178c2ecf20Sopenharmony_ci struct wm_adsp_alg_region, 22188c2ecf20Sopenharmony_ci list); 22198c2ecf20Sopenharmony_ci list_del(&alg_region->list); 22208c2ecf20Sopenharmony_ci kfree(alg_region); 22218c2ecf20Sopenharmony_ci } 22228c2ecf20Sopenharmony_ci} 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_cistatic void wmfw_parse_id_header(struct wm_adsp *dsp, 22258c2ecf20Sopenharmony_ci struct wmfw_id_hdr *fw, int nalgs) 22268c2ecf20Sopenharmony_ci{ 22278c2ecf20Sopenharmony_ci dsp->fw_id = be32_to_cpu(fw->id); 22288c2ecf20Sopenharmony_ci dsp->fw_id_version = be32_to_cpu(fw->ver); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci adsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 22318c2ecf20Sopenharmony_ci dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 22328c2ecf20Sopenharmony_ci (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 22338c2ecf20Sopenharmony_ci nalgs); 22348c2ecf20Sopenharmony_ci} 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_cistatic void wmfw_v3_parse_id_header(struct wm_adsp *dsp, 22378c2ecf20Sopenharmony_ci struct wmfw_v3_id_hdr *fw, int nalgs) 22388c2ecf20Sopenharmony_ci{ 22398c2ecf20Sopenharmony_ci dsp->fw_id = be32_to_cpu(fw->id); 22408c2ecf20Sopenharmony_ci dsp->fw_id_version = be32_to_cpu(fw->ver); 22418c2ecf20Sopenharmony_ci dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 22448c2ecf20Sopenharmony_ci dsp->fw_id, dsp->fw_vendor_id, 22458c2ecf20Sopenharmony_ci (dsp->fw_id_version & 0xff0000) >> 16, 22468c2ecf20Sopenharmony_ci (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 22478c2ecf20Sopenharmony_ci nalgs); 22488c2ecf20Sopenharmony_ci} 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_cistatic int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions, 22518c2ecf20Sopenharmony_ci int *type, __be32 *base) 22528c2ecf20Sopenharmony_ci{ 22538c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 22548c2ecf20Sopenharmony_ci int i; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci for (i = 0; i < nregions; i++) { 22578c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, type[i], id, base[i]); 22588c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) 22598c2ecf20Sopenharmony_ci return PTR_ERR(alg_region); 22608c2ecf20Sopenharmony_ci } 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci return 0; 22638c2ecf20Sopenharmony_ci} 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_cistatic int wm_adsp1_setup_algs(struct wm_adsp *dsp) 22668c2ecf20Sopenharmony_ci{ 22678c2ecf20Sopenharmony_ci struct wmfw_adsp1_id_hdr adsp1_id; 22688c2ecf20Sopenharmony_ci struct wmfw_adsp1_alg_hdr *adsp1_alg; 22698c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 22708c2ecf20Sopenharmony_ci const struct wm_adsp_region *mem; 22718c2ecf20Sopenharmony_ci unsigned int pos, len; 22728c2ecf20Sopenharmony_ci size_t n_algs; 22738c2ecf20Sopenharmony_ci int i, ret; 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 22768c2ecf20Sopenharmony_ci if (WARN_ON(!mem)) 22778c2ecf20Sopenharmony_ci return -EINVAL; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 22808c2ecf20Sopenharmony_ci sizeof(adsp1_id)); 22818c2ecf20Sopenharmony_ci if (ret != 0) { 22828c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read algorithm info: %d\n", 22838c2ecf20Sopenharmony_ci ret); 22848c2ecf20Sopenharmony_ci return ret; 22858c2ecf20Sopenharmony_ci } 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci n_algs = be32_to_cpu(adsp1_id.n_algs); 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 22928c2ecf20Sopenharmony_ci adsp1_id.fw.id, adsp1_id.zm); 22938c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) 22948c2ecf20Sopenharmony_ci return PTR_ERR(alg_region); 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 22978c2ecf20Sopenharmony_ci adsp1_id.fw.id, adsp1_id.dm); 22988c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) 22998c2ecf20Sopenharmony_ci return PTR_ERR(alg_region); 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci /* Calculate offset and length in DSP words */ 23028c2ecf20Sopenharmony_ci pos = sizeof(adsp1_id) / sizeof(u32); 23038c2ecf20Sopenharmony_ci len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 23068c2ecf20Sopenharmony_ci if (IS_ERR(adsp1_alg)) 23078c2ecf20Sopenharmony_ci return PTR_ERR(adsp1_alg); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci for (i = 0; i < n_algs; i++) { 23108c2ecf20Sopenharmony_ci adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 23118c2ecf20Sopenharmony_ci i, be32_to_cpu(adsp1_alg[i].alg.id), 23128c2ecf20Sopenharmony_ci (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 23138c2ecf20Sopenharmony_ci (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 23148c2ecf20Sopenharmony_ci be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 23158c2ecf20Sopenharmony_ci be32_to_cpu(adsp1_alg[i].dm), 23168c2ecf20Sopenharmony_ci be32_to_cpu(adsp1_alg[i].zm)); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 23198c2ecf20Sopenharmony_ci adsp1_alg[i].alg.id, 23208c2ecf20Sopenharmony_ci adsp1_alg[i].dm); 23218c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) { 23228c2ecf20Sopenharmony_ci ret = PTR_ERR(alg_region); 23238c2ecf20Sopenharmony_ci goto out; 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci if (dsp->fw_ver == 0) { 23268c2ecf20Sopenharmony_ci if (i + 1 < n_algs) { 23278c2ecf20Sopenharmony_ci len = be32_to_cpu(adsp1_alg[i + 1].dm); 23288c2ecf20Sopenharmony_ci len -= be32_to_cpu(adsp1_alg[i].dm); 23298c2ecf20Sopenharmony_ci len *= 4; 23308c2ecf20Sopenharmony_ci wm_adsp_create_control(dsp, alg_region, 0, 23318c2ecf20Sopenharmony_ci len, NULL, 0, 0, 23328c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_TYPE_BYTES); 23338c2ecf20Sopenharmony_ci } else { 23348c2ecf20Sopenharmony_ci adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 23358c2ecf20Sopenharmony_ci be32_to_cpu(adsp1_alg[i].alg.id)); 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci } 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 23408c2ecf20Sopenharmony_ci adsp1_alg[i].alg.id, 23418c2ecf20Sopenharmony_ci adsp1_alg[i].zm); 23428c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) { 23438c2ecf20Sopenharmony_ci ret = PTR_ERR(alg_region); 23448c2ecf20Sopenharmony_ci goto out; 23458c2ecf20Sopenharmony_ci } 23468c2ecf20Sopenharmony_ci if (dsp->fw_ver == 0) { 23478c2ecf20Sopenharmony_ci if (i + 1 < n_algs) { 23488c2ecf20Sopenharmony_ci len = be32_to_cpu(adsp1_alg[i + 1].zm); 23498c2ecf20Sopenharmony_ci len -= be32_to_cpu(adsp1_alg[i].zm); 23508c2ecf20Sopenharmony_ci len *= 4; 23518c2ecf20Sopenharmony_ci wm_adsp_create_control(dsp, alg_region, 0, 23528c2ecf20Sopenharmony_ci len, NULL, 0, 0, 23538c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_TYPE_BYTES); 23548c2ecf20Sopenharmony_ci } else { 23558c2ecf20Sopenharmony_ci adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 23568c2ecf20Sopenharmony_ci be32_to_cpu(adsp1_alg[i].alg.id)); 23578c2ecf20Sopenharmony_ci } 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci } 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ciout: 23628c2ecf20Sopenharmony_ci kfree(adsp1_alg); 23638c2ecf20Sopenharmony_ci return ret; 23648c2ecf20Sopenharmony_ci} 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_cistatic int wm_adsp2_setup_algs(struct wm_adsp *dsp) 23678c2ecf20Sopenharmony_ci{ 23688c2ecf20Sopenharmony_ci struct wmfw_adsp2_id_hdr adsp2_id; 23698c2ecf20Sopenharmony_ci struct wmfw_adsp2_alg_hdr *adsp2_alg; 23708c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 23718c2ecf20Sopenharmony_ci const struct wm_adsp_region *mem; 23728c2ecf20Sopenharmony_ci unsigned int pos, len; 23738c2ecf20Sopenharmony_ci size_t n_algs; 23748c2ecf20Sopenharmony_ci int i, ret; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 23778c2ecf20Sopenharmony_ci if (WARN_ON(!mem)) 23788c2ecf20Sopenharmony_ci return -EINVAL; 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 23818c2ecf20Sopenharmony_ci sizeof(adsp2_id)); 23828c2ecf20Sopenharmony_ci if (ret != 0) { 23838c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read algorithm info: %d\n", 23848c2ecf20Sopenharmony_ci ret); 23858c2ecf20Sopenharmony_ci return ret; 23868c2ecf20Sopenharmony_ci } 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci n_algs = be32_to_cpu(adsp2_id.n_algs); 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs); 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 23938c2ecf20Sopenharmony_ci adsp2_id.fw.id, adsp2_id.xm); 23948c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) 23958c2ecf20Sopenharmony_ci return PTR_ERR(alg_region); 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 23988c2ecf20Sopenharmony_ci adsp2_id.fw.id, adsp2_id.ym); 23998c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) 24008c2ecf20Sopenharmony_ci return PTR_ERR(alg_region); 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 24038c2ecf20Sopenharmony_ci adsp2_id.fw.id, adsp2_id.zm); 24048c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) 24058c2ecf20Sopenharmony_ci return PTR_ERR(alg_region); 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* Calculate offset and length in DSP words */ 24088c2ecf20Sopenharmony_ci pos = sizeof(adsp2_id) / sizeof(u32); 24098c2ecf20Sopenharmony_ci len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 24128c2ecf20Sopenharmony_ci if (IS_ERR(adsp2_alg)) 24138c2ecf20Sopenharmony_ci return PTR_ERR(adsp2_alg); 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci for (i = 0; i < n_algs; i++) { 24168c2ecf20Sopenharmony_ci adsp_info(dsp, 24178c2ecf20Sopenharmony_ci "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 24188c2ecf20Sopenharmony_ci i, be32_to_cpu(adsp2_alg[i].alg.id), 24198c2ecf20Sopenharmony_ci (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 24208c2ecf20Sopenharmony_ci (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 24218c2ecf20Sopenharmony_ci be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 24228c2ecf20Sopenharmony_ci be32_to_cpu(adsp2_alg[i].xm), 24238c2ecf20Sopenharmony_ci be32_to_cpu(adsp2_alg[i].ym), 24248c2ecf20Sopenharmony_ci be32_to_cpu(adsp2_alg[i].zm)); 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 24278c2ecf20Sopenharmony_ci adsp2_alg[i].alg.id, 24288c2ecf20Sopenharmony_ci adsp2_alg[i].xm); 24298c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) { 24308c2ecf20Sopenharmony_ci ret = PTR_ERR(alg_region); 24318c2ecf20Sopenharmony_ci goto out; 24328c2ecf20Sopenharmony_ci } 24338c2ecf20Sopenharmony_ci if (dsp->fw_ver == 0) { 24348c2ecf20Sopenharmony_ci if (i + 1 < n_algs) { 24358c2ecf20Sopenharmony_ci len = be32_to_cpu(adsp2_alg[i + 1].xm); 24368c2ecf20Sopenharmony_ci len -= be32_to_cpu(adsp2_alg[i].xm); 24378c2ecf20Sopenharmony_ci len *= 4; 24388c2ecf20Sopenharmony_ci wm_adsp_create_control(dsp, alg_region, 0, 24398c2ecf20Sopenharmony_ci len, NULL, 0, 0, 24408c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_TYPE_BYTES); 24418c2ecf20Sopenharmony_ci } else { 24428c2ecf20Sopenharmony_ci adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 24438c2ecf20Sopenharmony_ci be32_to_cpu(adsp2_alg[i].alg.id)); 24448c2ecf20Sopenharmony_ci } 24458c2ecf20Sopenharmony_ci } 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 24488c2ecf20Sopenharmony_ci adsp2_alg[i].alg.id, 24498c2ecf20Sopenharmony_ci adsp2_alg[i].ym); 24508c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) { 24518c2ecf20Sopenharmony_ci ret = PTR_ERR(alg_region); 24528c2ecf20Sopenharmony_ci goto out; 24538c2ecf20Sopenharmony_ci } 24548c2ecf20Sopenharmony_ci if (dsp->fw_ver == 0) { 24558c2ecf20Sopenharmony_ci if (i + 1 < n_algs) { 24568c2ecf20Sopenharmony_ci len = be32_to_cpu(adsp2_alg[i + 1].ym); 24578c2ecf20Sopenharmony_ci len -= be32_to_cpu(adsp2_alg[i].ym); 24588c2ecf20Sopenharmony_ci len *= 4; 24598c2ecf20Sopenharmony_ci wm_adsp_create_control(dsp, alg_region, 0, 24608c2ecf20Sopenharmony_ci len, NULL, 0, 0, 24618c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_TYPE_BYTES); 24628c2ecf20Sopenharmony_ci } else { 24638c2ecf20Sopenharmony_ci adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 24648c2ecf20Sopenharmony_ci be32_to_cpu(adsp2_alg[i].alg.id)); 24658c2ecf20Sopenharmony_ci } 24668c2ecf20Sopenharmony_ci } 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 24698c2ecf20Sopenharmony_ci adsp2_alg[i].alg.id, 24708c2ecf20Sopenharmony_ci adsp2_alg[i].zm); 24718c2ecf20Sopenharmony_ci if (IS_ERR(alg_region)) { 24728c2ecf20Sopenharmony_ci ret = PTR_ERR(alg_region); 24738c2ecf20Sopenharmony_ci goto out; 24748c2ecf20Sopenharmony_ci } 24758c2ecf20Sopenharmony_ci if (dsp->fw_ver == 0) { 24768c2ecf20Sopenharmony_ci if (i + 1 < n_algs) { 24778c2ecf20Sopenharmony_ci len = be32_to_cpu(adsp2_alg[i + 1].zm); 24788c2ecf20Sopenharmony_ci len -= be32_to_cpu(adsp2_alg[i].zm); 24798c2ecf20Sopenharmony_ci len *= 4; 24808c2ecf20Sopenharmony_ci wm_adsp_create_control(dsp, alg_region, 0, 24818c2ecf20Sopenharmony_ci len, NULL, 0, 0, 24828c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_TYPE_BYTES); 24838c2ecf20Sopenharmony_ci } else { 24848c2ecf20Sopenharmony_ci adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 24858c2ecf20Sopenharmony_ci be32_to_cpu(adsp2_alg[i].alg.id)); 24868c2ecf20Sopenharmony_ci } 24878c2ecf20Sopenharmony_ci } 24888c2ecf20Sopenharmony_ci } 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ciout: 24918c2ecf20Sopenharmony_ci kfree(adsp2_alg); 24928c2ecf20Sopenharmony_ci return ret; 24938c2ecf20Sopenharmony_ci} 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_cistatic int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id, 24968c2ecf20Sopenharmony_ci __be32 xm_base, __be32 ym_base) 24978c2ecf20Sopenharmony_ci{ 24988c2ecf20Sopenharmony_ci int types[] = { 24998c2ecf20Sopenharmony_ci WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 25008c2ecf20Sopenharmony_ci WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 25018c2ecf20Sopenharmony_ci }; 25028c2ecf20Sopenharmony_ci __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci return wm_adsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); 25058c2ecf20Sopenharmony_ci} 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_cistatic int wm_halo_setup_algs(struct wm_adsp *dsp) 25088c2ecf20Sopenharmony_ci{ 25098c2ecf20Sopenharmony_ci struct wmfw_halo_id_hdr halo_id; 25108c2ecf20Sopenharmony_ci struct wmfw_halo_alg_hdr *halo_alg; 25118c2ecf20Sopenharmony_ci const struct wm_adsp_region *mem; 25128c2ecf20Sopenharmony_ci unsigned int pos, len; 25138c2ecf20Sopenharmony_ci size_t n_algs; 25148c2ecf20Sopenharmony_ci int i, ret; 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 25178c2ecf20Sopenharmony_ci if (WARN_ON(!mem)) 25188c2ecf20Sopenharmony_ci return -EINVAL; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 25218c2ecf20Sopenharmony_ci sizeof(halo_id)); 25228c2ecf20Sopenharmony_ci if (ret != 0) { 25238c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read algorithm info: %d\n", 25248c2ecf20Sopenharmony_ci ret); 25258c2ecf20Sopenharmony_ci return ret; 25268c2ecf20Sopenharmony_ci } 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci n_algs = be32_to_cpu(halo_id.n_algs); 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci wmfw_v3_parse_id_header(dsp, &halo_id.fw, n_algs); 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci ret = wm_halo_create_regions(dsp, halo_id.fw.id, 25338c2ecf20Sopenharmony_ci halo_id.xm_base, halo_id.ym_base); 25348c2ecf20Sopenharmony_ci if (ret) 25358c2ecf20Sopenharmony_ci return ret; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci /* Calculate offset and length in DSP words */ 25388c2ecf20Sopenharmony_ci pos = sizeof(halo_id) / sizeof(u32); 25398c2ecf20Sopenharmony_ci len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci halo_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 25428c2ecf20Sopenharmony_ci if (IS_ERR(halo_alg)) 25438c2ecf20Sopenharmony_ci return PTR_ERR(halo_alg); 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci for (i = 0; i < n_algs; i++) { 25468c2ecf20Sopenharmony_ci adsp_info(dsp, 25478c2ecf20Sopenharmony_ci "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 25488c2ecf20Sopenharmony_ci i, be32_to_cpu(halo_alg[i].alg.id), 25498c2ecf20Sopenharmony_ci (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 25508c2ecf20Sopenharmony_ci (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 25518c2ecf20Sopenharmony_ci be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 25528c2ecf20Sopenharmony_ci be32_to_cpu(halo_alg[i].xm_base), 25538c2ecf20Sopenharmony_ci be32_to_cpu(halo_alg[i].ym_base)); 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci ret = wm_halo_create_regions(dsp, halo_alg[i].alg.id, 25568c2ecf20Sopenharmony_ci halo_alg[i].xm_base, 25578c2ecf20Sopenharmony_ci halo_alg[i].ym_base); 25588c2ecf20Sopenharmony_ci if (ret) 25598c2ecf20Sopenharmony_ci goto out; 25608c2ecf20Sopenharmony_ci } 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ciout: 25638c2ecf20Sopenharmony_ci kfree(halo_alg); 25648c2ecf20Sopenharmony_ci return ret; 25658c2ecf20Sopenharmony_ci} 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_cistatic int wm_adsp_load_coeff(struct wm_adsp *dsp) 25688c2ecf20Sopenharmony_ci{ 25698c2ecf20Sopenharmony_ci LIST_HEAD(buf_list); 25708c2ecf20Sopenharmony_ci struct regmap *regmap = dsp->regmap; 25718c2ecf20Sopenharmony_ci struct wmfw_coeff_hdr *hdr; 25728c2ecf20Sopenharmony_ci struct wmfw_coeff_item *blk; 25738c2ecf20Sopenharmony_ci const struct firmware *firmware; 25748c2ecf20Sopenharmony_ci const struct wm_adsp_region *mem; 25758c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 25768c2ecf20Sopenharmony_ci const char *region_name; 25778c2ecf20Sopenharmony_ci int ret, pos, blocks, type, offset, reg; 25788c2ecf20Sopenharmony_ci char *file; 25798c2ecf20Sopenharmony_ci struct wm_adsp_buf *buf; 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci file = kzalloc(PAGE_SIZE, GFP_KERNEL); 25828c2ecf20Sopenharmony_ci if (file == NULL) 25838c2ecf20Sopenharmony_ci return -ENOMEM; 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, 25868c2ecf20Sopenharmony_ci wm_adsp_fw[dsp->fw].file); 25878c2ecf20Sopenharmony_ci file[PAGE_SIZE - 1] = '\0'; 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci ret = request_firmware(&firmware, file, dsp->dev); 25908c2ecf20Sopenharmony_ci if (ret != 0) { 25918c2ecf20Sopenharmony_ci adsp_warn(dsp, "Failed to request '%s'\n", file); 25928c2ecf20Sopenharmony_ci ret = 0; 25938c2ecf20Sopenharmony_ci goto out; 25948c2ecf20Sopenharmony_ci } 25958c2ecf20Sopenharmony_ci ret = -EINVAL; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci if (sizeof(*hdr) >= firmware->size) { 25988c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: file too short, %zu bytes\n", 25998c2ecf20Sopenharmony_ci file, firmware->size); 26008c2ecf20Sopenharmony_ci goto out_fw; 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci hdr = (void *)&firmware->data[0]; 26048c2ecf20Sopenharmony_ci if (memcmp(hdr->magic, "WMDR", 4) != 0) { 26058c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: invalid magic\n", file); 26068c2ecf20Sopenharmony_ci goto out_fw; 26078c2ecf20Sopenharmony_ci } 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci switch (be32_to_cpu(hdr->rev) & 0xff) { 26108c2ecf20Sopenharmony_ci case 1: 26118c2ecf20Sopenharmony_ci break; 26128c2ecf20Sopenharmony_ci default: 26138c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 26148c2ecf20Sopenharmony_ci file, be32_to_cpu(hdr->rev) & 0xff); 26158c2ecf20Sopenharmony_ci ret = -EINVAL; 26168c2ecf20Sopenharmony_ci goto out_fw; 26178c2ecf20Sopenharmony_ci } 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 26208c2ecf20Sopenharmony_ci (le32_to_cpu(hdr->ver) >> 16) & 0xff, 26218c2ecf20Sopenharmony_ci (le32_to_cpu(hdr->ver) >> 8) & 0xff, 26228c2ecf20Sopenharmony_ci le32_to_cpu(hdr->ver) & 0xff); 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci pos = le32_to_cpu(hdr->len); 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci blocks = 0; 26278c2ecf20Sopenharmony_ci while (pos < firmware->size && 26288c2ecf20Sopenharmony_ci sizeof(*blk) < firmware->size - pos) { 26298c2ecf20Sopenharmony_ci blk = (void *)(&firmware->data[pos]); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci type = le16_to_cpu(blk->type); 26328c2ecf20Sopenharmony_ci offset = le16_to_cpu(blk->offset); 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 26358c2ecf20Sopenharmony_ci file, blocks, le32_to_cpu(blk->id), 26368c2ecf20Sopenharmony_ci (le32_to_cpu(blk->ver) >> 16) & 0xff, 26378c2ecf20Sopenharmony_ci (le32_to_cpu(blk->ver) >> 8) & 0xff, 26388c2ecf20Sopenharmony_ci le32_to_cpu(blk->ver) & 0xff); 26398c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 26408c2ecf20Sopenharmony_ci file, blocks, le32_to_cpu(blk->len), offset, type); 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci reg = 0; 26438c2ecf20Sopenharmony_ci region_name = "Unknown"; 26448c2ecf20Sopenharmony_ci switch (type) { 26458c2ecf20Sopenharmony_ci case (WMFW_NAME_TEXT << 8): 26468c2ecf20Sopenharmony_ci case (WMFW_INFO_TEXT << 8): 26478c2ecf20Sopenharmony_ci case (WMFW_METADATA << 8): 26488c2ecf20Sopenharmony_ci break; 26498c2ecf20Sopenharmony_ci case (WMFW_ABSOLUTE << 8): 26508c2ecf20Sopenharmony_ci /* 26518c2ecf20Sopenharmony_ci * Old files may use this for global 26528c2ecf20Sopenharmony_ci * coefficients. 26538c2ecf20Sopenharmony_ci */ 26548c2ecf20Sopenharmony_ci if (le32_to_cpu(blk->id) == dsp->fw_id && 26558c2ecf20Sopenharmony_ci offset == 0) { 26568c2ecf20Sopenharmony_ci region_name = "global coefficients"; 26578c2ecf20Sopenharmony_ci mem = wm_adsp_find_region(dsp, type); 26588c2ecf20Sopenharmony_ci if (!mem) { 26598c2ecf20Sopenharmony_ci adsp_err(dsp, "No ZM\n"); 26608c2ecf20Sopenharmony_ci break; 26618c2ecf20Sopenharmony_ci } 26628c2ecf20Sopenharmony_ci reg = dsp->ops->region_to_reg(mem, 0); 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci } else { 26658c2ecf20Sopenharmony_ci region_name = "register"; 26668c2ecf20Sopenharmony_ci reg = offset; 26678c2ecf20Sopenharmony_ci } 26688c2ecf20Sopenharmony_ci break; 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci case WMFW_ADSP1_DM: 26718c2ecf20Sopenharmony_ci case WMFW_ADSP1_ZM: 26728c2ecf20Sopenharmony_ci case WMFW_ADSP2_XM: 26738c2ecf20Sopenharmony_ci case WMFW_ADSP2_YM: 26748c2ecf20Sopenharmony_ci case WMFW_HALO_XM_PACKED: 26758c2ecf20Sopenharmony_ci case WMFW_HALO_YM_PACKED: 26768c2ecf20Sopenharmony_ci case WMFW_HALO_PM_PACKED: 26778c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 26788c2ecf20Sopenharmony_ci file, blocks, le32_to_cpu(blk->len), 26798c2ecf20Sopenharmony_ci type, le32_to_cpu(blk->id)); 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci mem = wm_adsp_find_region(dsp, type); 26828c2ecf20Sopenharmony_ci if (!mem) { 26838c2ecf20Sopenharmony_ci adsp_err(dsp, "No base for region %x\n", type); 26848c2ecf20Sopenharmony_ci break; 26858c2ecf20Sopenharmony_ci } 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci alg_region = wm_adsp_find_alg_region(dsp, type, 26888c2ecf20Sopenharmony_ci le32_to_cpu(blk->id)); 26898c2ecf20Sopenharmony_ci if (alg_region) { 26908c2ecf20Sopenharmony_ci reg = alg_region->base; 26918c2ecf20Sopenharmony_ci reg = dsp->ops->region_to_reg(mem, reg); 26928c2ecf20Sopenharmony_ci reg += offset; 26938c2ecf20Sopenharmony_ci } else { 26948c2ecf20Sopenharmony_ci adsp_err(dsp, "No %x for algorithm %x\n", 26958c2ecf20Sopenharmony_ci type, le32_to_cpu(blk->id)); 26968c2ecf20Sopenharmony_ci } 26978c2ecf20Sopenharmony_ci break; 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci default: 27008c2ecf20Sopenharmony_ci adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 27018c2ecf20Sopenharmony_ci file, blocks, type, pos); 27028c2ecf20Sopenharmony_ci break; 27038c2ecf20Sopenharmony_ci } 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci if (reg) { 27068c2ecf20Sopenharmony_ci if (le32_to_cpu(blk->len) > 27078c2ecf20Sopenharmony_ci firmware->size - pos - sizeof(*blk)) { 27088c2ecf20Sopenharmony_ci adsp_err(dsp, 27098c2ecf20Sopenharmony_ci "%s.%d: %s region len %d bytes exceeds file length %zu\n", 27108c2ecf20Sopenharmony_ci file, blocks, region_name, 27118c2ecf20Sopenharmony_ci le32_to_cpu(blk->len), 27128c2ecf20Sopenharmony_ci firmware->size); 27138c2ecf20Sopenharmony_ci ret = -EINVAL; 27148c2ecf20Sopenharmony_ci goto out_fw; 27158c2ecf20Sopenharmony_ci } 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci buf = wm_adsp_buf_alloc(blk->data, 27188c2ecf20Sopenharmony_ci le32_to_cpu(blk->len), 27198c2ecf20Sopenharmony_ci &buf_list); 27208c2ecf20Sopenharmony_ci if (!buf) { 27218c2ecf20Sopenharmony_ci adsp_err(dsp, "Out of memory\n"); 27228c2ecf20Sopenharmony_ci ret = -ENOMEM; 27238c2ecf20Sopenharmony_ci goto out_fw; 27248c2ecf20Sopenharmony_ci } 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 27278c2ecf20Sopenharmony_ci file, blocks, le32_to_cpu(blk->len), 27288c2ecf20Sopenharmony_ci reg); 27298c2ecf20Sopenharmony_ci ret = regmap_raw_write_async(regmap, reg, buf->buf, 27308c2ecf20Sopenharmony_ci le32_to_cpu(blk->len)); 27318c2ecf20Sopenharmony_ci if (ret != 0) { 27328c2ecf20Sopenharmony_ci adsp_err(dsp, 27338c2ecf20Sopenharmony_ci "%s.%d: Failed to write to %x in %s: %d\n", 27348c2ecf20Sopenharmony_ci file, blocks, reg, region_name, ret); 27358c2ecf20Sopenharmony_ci } 27368c2ecf20Sopenharmony_ci } 27378c2ecf20Sopenharmony_ci 27388c2ecf20Sopenharmony_ci pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 27398c2ecf20Sopenharmony_ci blocks++; 27408c2ecf20Sopenharmony_ci } 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci ret = regmap_async_complete(regmap); 27438c2ecf20Sopenharmony_ci if (ret != 0) 27448c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to complete async write: %d\n", ret); 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci if (pos > firmware->size) 27478c2ecf20Sopenharmony_ci adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 27488c2ecf20Sopenharmony_ci file, blocks, pos - firmware->size); 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci wm_adsp_debugfs_save_binname(dsp, file); 27518c2ecf20Sopenharmony_ci 27528c2ecf20Sopenharmony_ciout_fw: 27538c2ecf20Sopenharmony_ci regmap_async_complete(regmap); 27548c2ecf20Sopenharmony_ci release_firmware(firmware); 27558c2ecf20Sopenharmony_ci wm_adsp_buf_free(&buf_list); 27568c2ecf20Sopenharmony_ciout: 27578c2ecf20Sopenharmony_ci kfree(file); 27588c2ecf20Sopenharmony_ci return ret; 27598c2ecf20Sopenharmony_ci} 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_cistatic int wm_adsp_create_name(struct wm_adsp *dsp) 27628c2ecf20Sopenharmony_ci{ 27638c2ecf20Sopenharmony_ci char *p; 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci if (!dsp->name) { 27668c2ecf20Sopenharmony_ci dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 27678c2ecf20Sopenharmony_ci dsp->num); 27688c2ecf20Sopenharmony_ci if (!dsp->name) 27698c2ecf20Sopenharmony_ci return -ENOMEM; 27708c2ecf20Sopenharmony_ci } 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci if (!dsp->fwf_name) { 27738c2ecf20Sopenharmony_ci p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); 27748c2ecf20Sopenharmony_ci if (!p) 27758c2ecf20Sopenharmony_ci return -ENOMEM; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci dsp->fwf_name = p; 27788c2ecf20Sopenharmony_ci for (; *p != 0; ++p) 27798c2ecf20Sopenharmony_ci *p = tolower(*p); 27808c2ecf20Sopenharmony_ci } 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci return 0; 27838c2ecf20Sopenharmony_ci} 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_cistatic int wm_adsp_common_init(struct wm_adsp *dsp) 27868c2ecf20Sopenharmony_ci{ 27878c2ecf20Sopenharmony_ci int ret; 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci ret = wm_adsp_create_name(dsp); 27908c2ecf20Sopenharmony_ci if (ret) 27918c2ecf20Sopenharmony_ci return ret; 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dsp->alg_regions); 27948c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dsp->ctl_list); 27958c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dsp->compr_list); 27968c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dsp->buffer_list); 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci mutex_init(&dsp->pwr_lock); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci return 0; 28018c2ecf20Sopenharmony_ci} 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ciint wm_adsp1_init(struct wm_adsp *dsp) 28048c2ecf20Sopenharmony_ci{ 28058c2ecf20Sopenharmony_ci dsp->ops = &wm_adsp1_ops; 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci return wm_adsp_common_init(dsp); 28088c2ecf20Sopenharmony_ci} 28098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp1_init); 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ciint wm_adsp1_event(struct snd_soc_dapm_widget *w, 28128c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, 28138c2ecf20Sopenharmony_ci int event) 28148c2ecf20Sopenharmony_ci{ 28158c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 28168c2ecf20Sopenharmony_ci struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 28178c2ecf20Sopenharmony_ci struct wm_adsp *dsp = &dsps[w->shift]; 28188c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 28198c2ecf20Sopenharmony_ci int ret; 28208c2ecf20Sopenharmony_ci unsigned int val; 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci dsp->component = component; 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci switch (event) { 28278c2ecf20Sopenharmony_ci case SND_SOC_DAPM_POST_PMU: 28288c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28298c2ecf20Sopenharmony_ci ADSP1_SYS_ENA, ADSP1_SYS_ENA); 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci /* 28328c2ecf20Sopenharmony_ci * For simplicity set the DSP clock rate to be the 28338c2ecf20Sopenharmony_ci * SYSCLK rate rather than making it configurable. 28348c2ecf20Sopenharmony_ci */ 28358c2ecf20Sopenharmony_ci if (dsp->sysclk_reg) { 28368c2ecf20Sopenharmony_ci ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 28378c2ecf20Sopenharmony_ci if (ret != 0) { 28388c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 28398c2ecf20Sopenharmony_ci ret); 28408c2ecf20Sopenharmony_ci goto err_mutex; 28418c2ecf20Sopenharmony_ci } 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci ret = regmap_update_bits(dsp->regmap, 28468c2ecf20Sopenharmony_ci dsp->base + ADSP1_CONTROL_31, 28478c2ecf20Sopenharmony_ci ADSP1_CLK_SEL_MASK, val); 28488c2ecf20Sopenharmony_ci if (ret != 0) { 28498c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to set clock rate: %d\n", 28508c2ecf20Sopenharmony_ci ret); 28518c2ecf20Sopenharmony_ci goto err_mutex; 28528c2ecf20Sopenharmony_ci } 28538c2ecf20Sopenharmony_ci } 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci ret = wm_adsp_load(dsp); 28568c2ecf20Sopenharmony_ci if (ret != 0) 28578c2ecf20Sopenharmony_ci goto err_ena; 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci ret = wm_adsp1_setup_algs(dsp); 28608c2ecf20Sopenharmony_ci if (ret != 0) 28618c2ecf20Sopenharmony_ci goto err_ena; 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci ret = wm_adsp_load_coeff(dsp); 28648c2ecf20Sopenharmony_ci if (ret != 0) 28658c2ecf20Sopenharmony_ci goto err_ena; 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci /* Initialize caches for enabled and unset controls */ 28688c2ecf20Sopenharmony_ci ret = wm_coeff_init_control_caches(dsp); 28698c2ecf20Sopenharmony_ci if (ret != 0) 28708c2ecf20Sopenharmony_ci goto err_ena; 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci /* Sync set controls */ 28738c2ecf20Sopenharmony_ci ret = wm_coeff_sync_controls(dsp); 28748c2ecf20Sopenharmony_ci if (ret != 0) 28758c2ecf20Sopenharmony_ci goto err_ena; 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci dsp->booted = true; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci /* Start the core running */ 28808c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28818c2ecf20Sopenharmony_ci ADSP1_CORE_ENA | ADSP1_START, 28828c2ecf20Sopenharmony_ci ADSP1_CORE_ENA | ADSP1_START); 28838c2ecf20Sopenharmony_ci 28848c2ecf20Sopenharmony_ci dsp->running = true; 28858c2ecf20Sopenharmony_ci break; 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 28888c2ecf20Sopenharmony_ci dsp->running = false; 28898c2ecf20Sopenharmony_ci dsp->booted = false; 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci /* Halt the core */ 28928c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28938c2ecf20Sopenharmony_ci ADSP1_CORE_ENA | ADSP1_START, 0); 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 28968c2ecf20Sopenharmony_ci ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 28978c2ecf20Sopenharmony_ci 28988c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 28998c2ecf20Sopenharmony_ci ADSP1_SYS_ENA, 0); 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) 29028c2ecf20Sopenharmony_ci ctl->enabled = 0; 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci wm_adsp_free_alg_regions(dsp); 29068c2ecf20Sopenharmony_ci break; 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci default: 29098c2ecf20Sopenharmony_ci break; 29108c2ecf20Sopenharmony_ci } 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci return 0; 29158c2ecf20Sopenharmony_ci 29168c2ecf20Sopenharmony_cierr_ena: 29178c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 29188c2ecf20Sopenharmony_ci ADSP1_SYS_ENA, 0); 29198c2ecf20Sopenharmony_cierr_mutex: 29208c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci return ret; 29238c2ecf20Sopenharmony_ci} 29248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp1_event); 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_cistatic int wm_adsp2v2_enable_core(struct wm_adsp *dsp) 29278c2ecf20Sopenharmony_ci{ 29288c2ecf20Sopenharmony_ci unsigned int val; 29298c2ecf20Sopenharmony_ci int ret, count; 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci /* Wait for the RAM to start, should be near instantaneous */ 29328c2ecf20Sopenharmony_ci for (count = 0; count < 10; ++count) { 29338c2ecf20Sopenharmony_ci ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 29348c2ecf20Sopenharmony_ci if (ret != 0) 29358c2ecf20Sopenharmony_ci return ret; 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci if (val & ADSP2_RAM_RDY) 29388c2ecf20Sopenharmony_ci break; 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci usleep_range(250, 500); 29418c2ecf20Sopenharmony_ci } 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_ci if (!(val & ADSP2_RAM_RDY)) { 29448c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to start DSP RAM\n"); 29458c2ecf20Sopenharmony_ci return -EBUSY; 29468c2ecf20Sopenharmony_ci } 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci adsp_dbg(dsp, "RAM ready after %d polls\n", count); 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci return 0; 29518c2ecf20Sopenharmony_ci} 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_cistatic int wm_adsp2_enable_core(struct wm_adsp *dsp) 29548c2ecf20Sopenharmony_ci{ 29558c2ecf20Sopenharmony_ci int ret; 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 29588c2ecf20Sopenharmony_ci ADSP2_SYS_ENA, ADSP2_SYS_ENA); 29598c2ecf20Sopenharmony_ci if (ret != 0) 29608c2ecf20Sopenharmony_ci return ret; 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci return wm_adsp2v2_enable_core(dsp); 29638c2ecf20Sopenharmony_ci} 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_cistatic int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 29668c2ecf20Sopenharmony_ci{ 29678c2ecf20Sopenharmony_ci struct regmap *regmap = dsp->regmap; 29688c2ecf20Sopenharmony_ci unsigned int code0, code1, lock_reg; 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_ci if (!(lock_regions & WM_ADSP2_REGION_ALL)) 29718c2ecf20Sopenharmony_ci return 0; 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci lock_regions &= WM_ADSP2_REGION_ALL; 29748c2ecf20Sopenharmony_ci lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci while (lock_regions) { 29778c2ecf20Sopenharmony_ci code0 = code1 = 0; 29788c2ecf20Sopenharmony_ci if (lock_regions & BIT(0)) { 29798c2ecf20Sopenharmony_ci code0 = ADSP2_LOCK_CODE_0; 29808c2ecf20Sopenharmony_ci code1 = ADSP2_LOCK_CODE_1; 29818c2ecf20Sopenharmony_ci } 29828c2ecf20Sopenharmony_ci if (lock_regions & BIT(1)) { 29838c2ecf20Sopenharmony_ci code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 29848c2ecf20Sopenharmony_ci code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 29858c2ecf20Sopenharmony_ci } 29868c2ecf20Sopenharmony_ci regmap_write(regmap, lock_reg, code0); 29878c2ecf20Sopenharmony_ci regmap_write(regmap, lock_reg, code1); 29888c2ecf20Sopenharmony_ci lock_regions >>= 2; 29898c2ecf20Sopenharmony_ci lock_reg += 2; 29908c2ecf20Sopenharmony_ci } 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci return 0; 29938c2ecf20Sopenharmony_ci} 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_cistatic int wm_adsp2_enable_memory(struct wm_adsp *dsp) 29968c2ecf20Sopenharmony_ci{ 29978c2ecf20Sopenharmony_ci return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 29988c2ecf20Sopenharmony_ci ADSP2_MEM_ENA, ADSP2_MEM_ENA); 29998c2ecf20Sopenharmony_ci} 30008c2ecf20Sopenharmony_ci 30018c2ecf20Sopenharmony_cistatic void wm_adsp2_disable_memory(struct wm_adsp *dsp) 30028c2ecf20Sopenharmony_ci{ 30038c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 30048c2ecf20Sopenharmony_ci ADSP2_MEM_ENA, 0); 30058c2ecf20Sopenharmony_ci} 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_cistatic void wm_adsp2_disable_core(struct wm_adsp *dsp) 30088c2ecf20Sopenharmony_ci{ 30098c2ecf20Sopenharmony_ci regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 30108c2ecf20Sopenharmony_ci regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 30118c2ecf20Sopenharmony_ci regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 30148c2ecf20Sopenharmony_ci ADSP2_SYS_ENA, 0); 30158c2ecf20Sopenharmony_ci} 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_cistatic void wm_adsp2v2_disable_core(struct wm_adsp *dsp) 30188c2ecf20Sopenharmony_ci{ 30198c2ecf20Sopenharmony_ci regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 30208c2ecf20Sopenharmony_ci regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 30218c2ecf20Sopenharmony_ci regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 30228c2ecf20Sopenharmony_ci} 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_cistatic void wm_adsp_boot_work(struct work_struct *work) 30258c2ecf20Sopenharmony_ci{ 30268c2ecf20Sopenharmony_ci struct wm_adsp *dsp = container_of(work, 30278c2ecf20Sopenharmony_ci struct wm_adsp, 30288c2ecf20Sopenharmony_ci boot_work); 30298c2ecf20Sopenharmony_ci int ret; 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci if (dsp->ops->enable_memory) { 30348c2ecf20Sopenharmony_ci ret = dsp->ops->enable_memory(dsp); 30358c2ecf20Sopenharmony_ci if (ret != 0) 30368c2ecf20Sopenharmony_ci goto err_mutex; 30378c2ecf20Sopenharmony_ci } 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci if (dsp->ops->enable_core) { 30408c2ecf20Sopenharmony_ci ret = dsp->ops->enable_core(dsp); 30418c2ecf20Sopenharmony_ci if (ret != 0) 30428c2ecf20Sopenharmony_ci goto err_mem; 30438c2ecf20Sopenharmony_ci } 30448c2ecf20Sopenharmony_ci 30458c2ecf20Sopenharmony_ci ret = wm_adsp_load(dsp); 30468c2ecf20Sopenharmony_ci if (ret != 0) 30478c2ecf20Sopenharmony_ci goto err_ena; 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci ret = dsp->ops->setup_algs(dsp); 30508c2ecf20Sopenharmony_ci if (ret != 0) 30518c2ecf20Sopenharmony_ci goto err_ena; 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci ret = wm_adsp_load_coeff(dsp); 30548c2ecf20Sopenharmony_ci if (ret != 0) 30558c2ecf20Sopenharmony_ci goto err_ena; 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci /* Initialize caches for enabled and unset controls */ 30588c2ecf20Sopenharmony_ci ret = wm_coeff_init_control_caches(dsp); 30598c2ecf20Sopenharmony_ci if (ret != 0) 30608c2ecf20Sopenharmony_ci goto err_ena; 30618c2ecf20Sopenharmony_ci 30628c2ecf20Sopenharmony_ci if (dsp->ops->disable_core) 30638c2ecf20Sopenharmony_ci dsp->ops->disable_core(dsp); 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci dsp->booted = true; 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci return; 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_cierr_ena: 30728c2ecf20Sopenharmony_ci if (dsp->ops->disable_core) 30738c2ecf20Sopenharmony_ci dsp->ops->disable_core(dsp); 30748c2ecf20Sopenharmony_cierr_mem: 30758c2ecf20Sopenharmony_ci if (dsp->ops->disable_memory) 30768c2ecf20Sopenharmony_ci dsp->ops->disable_memory(dsp); 30778c2ecf20Sopenharmony_cierr_mutex: 30788c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 30798c2ecf20Sopenharmony_ci} 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_cistatic int wm_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions) 30828c2ecf20Sopenharmony_ci{ 30838c2ecf20Sopenharmony_ci struct reg_sequence config[] = { 30848c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 30858c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 30868c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 30878c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 30888c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 30898c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 30908c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 30918c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 30928c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 30938c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 30948c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 30958c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 30968c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 30978c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 30988c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 30998c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 31008c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 31018c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 31028c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 31038c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 31048c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 31058c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 31068c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 31078c2ecf20Sopenharmony_ci }; 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 31108c2ecf20Sopenharmony_ci} 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ciint wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) 31138c2ecf20Sopenharmony_ci{ 31148c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 31158c2ecf20Sopenharmony_ci struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 31168c2ecf20Sopenharmony_ci struct wm_adsp *dsp = &dsps[w->shift]; 31178c2ecf20Sopenharmony_ci int ret; 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_ci ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 31208c2ecf20Sopenharmony_ci ADSP2_CLK_SEL_MASK, 31218c2ecf20Sopenharmony_ci freq << ADSP2_CLK_SEL_SHIFT); 31228c2ecf20Sopenharmony_ci if (ret) 31238c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ci return ret; 31268c2ecf20Sopenharmony_ci} 31278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ciint wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 31308c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 31318c2ecf20Sopenharmony_ci{ 31328c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 31338c2ecf20Sopenharmony_ci struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 31348c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 31358c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 31368c2ecf20Sopenharmony_ci struct wm_adsp *dsp = &dsps[mc->shift - 1]; 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = dsp->preloaded; 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_ci return 0; 31418c2ecf20Sopenharmony_ci} 31428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ciint wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 31458c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 31468c2ecf20Sopenharmony_ci{ 31478c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 31488c2ecf20Sopenharmony_ci struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 31498c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 31508c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 31518c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 31528c2ecf20Sopenharmony_ci struct wm_adsp *dsp = &dsps[mc->shift - 1]; 31538c2ecf20Sopenharmony_ci char preload[32]; 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci dsp->preloaded = ucontrol->value.integer.value[0]; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0]) 31608c2ecf20Sopenharmony_ci snd_soc_component_force_enable_pin(component, preload); 31618c2ecf20Sopenharmony_ci else 31628c2ecf20Sopenharmony_ci snd_soc_component_disable_pin(component, preload); 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci snd_soc_dapm_sync(dapm); 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci flush_work(&dsp->boot_work); 31678c2ecf20Sopenharmony_ci 31688c2ecf20Sopenharmony_ci return 0; 31698c2ecf20Sopenharmony_ci} 31708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_cistatic void wm_adsp_stop_watchdog(struct wm_adsp *dsp) 31738c2ecf20Sopenharmony_ci{ 31748c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 31758c2ecf20Sopenharmony_ci ADSP2_WDT_ENA_MASK, 0); 31768c2ecf20Sopenharmony_ci} 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_cistatic void wm_halo_stop_watchdog(struct wm_adsp *dsp) 31798c2ecf20Sopenharmony_ci{ 31808c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 31818c2ecf20Sopenharmony_ci HALO_WDT_EN_MASK, 0); 31828c2ecf20Sopenharmony_ci} 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ciint wm_adsp_early_event(struct snd_soc_dapm_widget *w, 31858c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 31868c2ecf20Sopenharmony_ci{ 31878c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 31888c2ecf20Sopenharmony_ci struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 31898c2ecf20Sopenharmony_ci struct wm_adsp *dsp = &dsps[w->shift]; 31908c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_ci switch (event) { 31938c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 31948c2ecf20Sopenharmony_ci queue_work(system_unbound_wq, &dsp->boot_work); 31958c2ecf20Sopenharmony_ci break; 31968c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 31978c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci wm_adsp_debugfs_clear(dsp); 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci dsp->fw_id = 0; 32028c2ecf20Sopenharmony_ci dsp->fw_id_version = 0; 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci dsp->booted = false; 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci if (dsp->ops->disable_memory) 32078c2ecf20Sopenharmony_ci dsp->ops->disable_memory(dsp); 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) 32108c2ecf20Sopenharmony_ci ctl->enabled = 0; 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci wm_adsp_free_alg_regions(dsp); 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Shutdown complete\n"); 32178c2ecf20Sopenharmony_ci break; 32188c2ecf20Sopenharmony_ci default: 32198c2ecf20Sopenharmony_ci break; 32208c2ecf20Sopenharmony_ci } 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ci return 0; 32238c2ecf20Sopenharmony_ci} 32248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_early_event); 32258c2ecf20Sopenharmony_ci 32268c2ecf20Sopenharmony_cistatic int wm_adsp2_start_core(struct wm_adsp *dsp) 32278c2ecf20Sopenharmony_ci{ 32288c2ecf20Sopenharmony_ci return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 32298c2ecf20Sopenharmony_ci ADSP2_CORE_ENA | ADSP2_START, 32308c2ecf20Sopenharmony_ci ADSP2_CORE_ENA | ADSP2_START); 32318c2ecf20Sopenharmony_ci} 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_cistatic void wm_adsp2_stop_core(struct wm_adsp *dsp) 32348c2ecf20Sopenharmony_ci{ 32358c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 32368c2ecf20Sopenharmony_ci ADSP2_CORE_ENA | ADSP2_START, 0); 32378c2ecf20Sopenharmony_ci} 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_ciint wm_adsp_event(struct snd_soc_dapm_widget *w, 32408c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 32418c2ecf20Sopenharmony_ci{ 32428c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 32438c2ecf20Sopenharmony_ci struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 32448c2ecf20Sopenharmony_ci struct wm_adsp *dsp = &dsps[w->shift]; 32458c2ecf20Sopenharmony_ci int ret; 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci switch (event) { 32488c2ecf20Sopenharmony_ci case SND_SOC_DAPM_POST_PMU: 32498c2ecf20Sopenharmony_ci flush_work(&dsp->boot_work); 32508c2ecf20Sopenharmony_ci 32518c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 32528c2ecf20Sopenharmony_ci 32538c2ecf20Sopenharmony_ci if (!dsp->booted) { 32548c2ecf20Sopenharmony_ci ret = -EIO; 32558c2ecf20Sopenharmony_ci goto err; 32568c2ecf20Sopenharmony_ci } 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci if (dsp->ops->enable_core) { 32598c2ecf20Sopenharmony_ci ret = dsp->ops->enable_core(dsp); 32608c2ecf20Sopenharmony_ci if (ret != 0) 32618c2ecf20Sopenharmony_ci goto err; 32628c2ecf20Sopenharmony_ci } 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci /* Sync set controls */ 32658c2ecf20Sopenharmony_ci ret = wm_coeff_sync_controls(dsp); 32668c2ecf20Sopenharmony_ci if (ret != 0) 32678c2ecf20Sopenharmony_ci goto err; 32688c2ecf20Sopenharmony_ci 32698c2ecf20Sopenharmony_ci if (dsp->ops->lock_memory) { 32708c2ecf20Sopenharmony_ci ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 32718c2ecf20Sopenharmony_ci if (ret != 0) { 32728c2ecf20Sopenharmony_ci adsp_err(dsp, "Error configuring MPU: %d\n", 32738c2ecf20Sopenharmony_ci ret); 32748c2ecf20Sopenharmony_ci goto err; 32758c2ecf20Sopenharmony_ci } 32768c2ecf20Sopenharmony_ci } 32778c2ecf20Sopenharmony_ci 32788c2ecf20Sopenharmony_ci if (dsp->ops->start_core) { 32798c2ecf20Sopenharmony_ci ret = dsp->ops->start_core(dsp); 32808c2ecf20Sopenharmony_ci if (ret != 0) 32818c2ecf20Sopenharmony_ci goto err; 32828c2ecf20Sopenharmony_ci } 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci if (wm_adsp_fw[dsp->fw].num_caps != 0) { 32858c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_init(dsp); 32868c2ecf20Sopenharmony_ci if (ret < 0) 32878c2ecf20Sopenharmony_ci goto err; 32888c2ecf20Sopenharmony_ci } 32898c2ecf20Sopenharmony_ci 32908c2ecf20Sopenharmony_ci dsp->running = true; 32918c2ecf20Sopenharmony_ci 32928c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 32938c2ecf20Sopenharmony_ci break; 32948c2ecf20Sopenharmony_ci 32958c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 32968c2ecf20Sopenharmony_ci /* Tell the firmware to cleanup */ 32978c2ecf20Sopenharmony_ci wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci if (dsp->ops->stop_watchdog) 33008c2ecf20Sopenharmony_ci dsp->ops->stop_watchdog(dsp); 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci /* Log firmware state, it can be useful for analysis */ 33038c2ecf20Sopenharmony_ci if (dsp->ops->show_fw_status) 33048c2ecf20Sopenharmony_ci dsp->ops->show_fw_status(dsp); 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 33078c2ecf20Sopenharmony_ci 33088c2ecf20Sopenharmony_ci dsp->running = false; 33098c2ecf20Sopenharmony_ci 33108c2ecf20Sopenharmony_ci if (dsp->ops->stop_core) 33118c2ecf20Sopenharmony_ci dsp->ops->stop_core(dsp); 33128c2ecf20Sopenharmony_ci if (dsp->ops->disable_core) 33138c2ecf20Sopenharmony_ci dsp->ops->disable_core(dsp); 33148c2ecf20Sopenharmony_ci 33158c2ecf20Sopenharmony_ci if (wm_adsp_fw[dsp->fw].num_caps != 0) 33168c2ecf20Sopenharmony_ci wm_adsp_buffer_free(dsp); 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci dsp->fatal_error = false; 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 33218c2ecf20Sopenharmony_ci 33228c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Execution stopped\n"); 33238c2ecf20Sopenharmony_ci break; 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci default: 33268c2ecf20Sopenharmony_ci break; 33278c2ecf20Sopenharmony_ci } 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci return 0; 33308c2ecf20Sopenharmony_cierr: 33318c2ecf20Sopenharmony_ci if (dsp->ops->stop_core) 33328c2ecf20Sopenharmony_ci dsp->ops->stop_core(dsp); 33338c2ecf20Sopenharmony_ci if (dsp->ops->disable_core) 33348c2ecf20Sopenharmony_ci dsp->ops->disable_core(dsp); 33358c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 33368c2ecf20Sopenharmony_ci return ret; 33378c2ecf20Sopenharmony_ci} 33388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_event); 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_cistatic int wm_halo_start_core(struct wm_adsp *dsp) 33418c2ecf20Sopenharmony_ci{ 33428c2ecf20Sopenharmony_ci return regmap_update_bits(dsp->regmap, 33438c2ecf20Sopenharmony_ci dsp->base + HALO_CCM_CORE_CONTROL, 33448c2ecf20Sopenharmony_ci HALO_CORE_EN, HALO_CORE_EN); 33458c2ecf20Sopenharmony_ci} 33468c2ecf20Sopenharmony_ci 33478c2ecf20Sopenharmony_cistatic void wm_halo_stop_core(struct wm_adsp *dsp) 33488c2ecf20Sopenharmony_ci{ 33498c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 33508c2ecf20Sopenharmony_ci HALO_CORE_EN, 0); 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci /* reset halo core with CORE_SOFT_RESET */ 33538c2ecf20Sopenharmony_ci regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 33548c2ecf20Sopenharmony_ci HALO_CORE_SOFT_RESET_MASK, 1); 33558c2ecf20Sopenharmony_ci} 33568c2ecf20Sopenharmony_ci 33578c2ecf20Sopenharmony_ciint wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 33588c2ecf20Sopenharmony_ci{ 33598c2ecf20Sopenharmony_ci char preload[32]; 33608c2ecf20Sopenharmony_ci 33618c2ecf20Sopenharmony_ci snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 33628c2ecf20Sopenharmony_ci snd_soc_component_disable_pin(component, preload); 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci wm_adsp2_init_debugfs(dsp, component); 33658c2ecf20Sopenharmony_ci 33668c2ecf20Sopenharmony_ci dsp->component = component; 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_ci return 0; 33698c2ecf20Sopenharmony_ci} 33708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 33718c2ecf20Sopenharmony_ci 33728c2ecf20Sopenharmony_ciint wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 33738c2ecf20Sopenharmony_ci{ 33748c2ecf20Sopenharmony_ci wm_adsp2_cleanup_debugfs(dsp); 33758c2ecf20Sopenharmony_ci 33768c2ecf20Sopenharmony_ci return 0; 33778c2ecf20Sopenharmony_ci} 33788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 33798c2ecf20Sopenharmony_ci 33808c2ecf20Sopenharmony_ciint wm_adsp2_init(struct wm_adsp *dsp) 33818c2ecf20Sopenharmony_ci{ 33828c2ecf20Sopenharmony_ci int ret; 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci ret = wm_adsp_common_init(dsp); 33858c2ecf20Sopenharmony_ci if (ret) 33868c2ecf20Sopenharmony_ci return ret; 33878c2ecf20Sopenharmony_ci 33888c2ecf20Sopenharmony_ci switch (dsp->rev) { 33898c2ecf20Sopenharmony_ci case 0: 33908c2ecf20Sopenharmony_ci /* 33918c2ecf20Sopenharmony_ci * Disable the DSP memory by default when in reset for a small 33928c2ecf20Sopenharmony_ci * power saving. 33938c2ecf20Sopenharmony_ci */ 33948c2ecf20Sopenharmony_ci ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 33958c2ecf20Sopenharmony_ci ADSP2_MEM_ENA, 0); 33968c2ecf20Sopenharmony_ci if (ret) { 33978c2ecf20Sopenharmony_ci adsp_err(dsp, 33988c2ecf20Sopenharmony_ci "Failed to clear memory retention: %d\n", ret); 33998c2ecf20Sopenharmony_ci return ret; 34008c2ecf20Sopenharmony_ci } 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ci dsp->ops = &wm_adsp2_ops[0]; 34038c2ecf20Sopenharmony_ci break; 34048c2ecf20Sopenharmony_ci case 1: 34058c2ecf20Sopenharmony_ci dsp->ops = &wm_adsp2_ops[1]; 34068c2ecf20Sopenharmony_ci break; 34078c2ecf20Sopenharmony_ci default: 34088c2ecf20Sopenharmony_ci dsp->ops = &wm_adsp2_ops[2]; 34098c2ecf20Sopenharmony_ci break; 34108c2ecf20Sopenharmony_ci } 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 34138c2ecf20Sopenharmony_ci 34148c2ecf20Sopenharmony_ci return 0; 34158c2ecf20Sopenharmony_ci} 34168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_init); 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ciint wm_halo_init(struct wm_adsp *dsp) 34198c2ecf20Sopenharmony_ci{ 34208c2ecf20Sopenharmony_ci int ret; 34218c2ecf20Sopenharmony_ci 34228c2ecf20Sopenharmony_ci ret = wm_adsp_common_init(dsp); 34238c2ecf20Sopenharmony_ci if (ret) 34248c2ecf20Sopenharmony_ci return ret; 34258c2ecf20Sopenharmony_ci 34268c2ecf20Sopenharmony_ci dsp->ops = &wm_halo_ops; 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci return 0; 34318c2ecf20Sopenharmony_ci} 34328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_halo_init); 34338c2ecf20Sopenharmony_ci 34348c2ecf20Sopenharmony_civoid wm_adsp2_remove(struct wm_adsp *dsp) 34358c2ecf20Sopenharmony_ci{ 34368c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci while (!list_empty(&dsp->ctl_list)) { 34398c2ecf20Sopenharmony_ci ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 34408c2ecf20Sopenharmony_ci list); 34418c2ecf20Sopenharmony_ci list_del(&ctl->list); 34428c2ecf20Sopenharmony_ci wm_adsp_free_ctl_blk(ctl); 34438c2ecf20Sopenharmony_ci } 34448c2ecf20Sopenharmony_ci} 34458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_remove); 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_cistatic inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 34488c2ecf20Sopenharmony_ci{ 34498c2ecf20Sopenharmony_ci return compr->buf != NULL; 34508c2ecf20Sopenharmony_ci} 34518c2ecf20Sopenharmony_ci 34528c2ecf20Sopenharmony_cistatic int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 34538c2ecf20Sopenharmony_ci{ 34548c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf = NULL, *tmp; 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci if (compr->dsp->fatal_error) 34578c2ecf20Sopenharmony_ci return -EINVAL; 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { 34608c2ecf20Sopenharmony_ci if (!tmp->name || !strcmp(compr->name, tmp->name)) { 34618c2ecf20Sopenharmony_ci buf = tmp; 34628c2ecf20Sopenharmony_ci break; 34638c2ecf20Sopenharmony_ci } 34648c2ecf20Sopenharmony_ci } 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci if (!buf) 34678c2ecf20Sopenharmony_ci return -EINVAL; 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_ci compr->buf = buf; 34708c2ecf20Sopenharmony_ci buf->compr = compr; 34718c2ecf20Sopenharmony_ci 34728c2ecf20Sopenharmony_ci return 0; 34738c2ecf20Sopenharmony_ci} 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_cistatic void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 34768c2ecf20Sopenharmony_ci{ 34778c2ecf20Sopenharmony_ci if (!compr) 34788c2ecf20Sopenharmony_ci return; 34798c2ecf20Sopenharmony_ci 34808c2ecf20Sopenharmony_ci /* Wake the poll so it can see buffer is no longer attached */ 34818c2ecf20Sopenharmony_ci if (compr->stream) 34828c2ecf20Sopenharmony_ci snd_compr_fragment_elapsed(compr->stream); 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_ci if (wm_adsp_compr_attached(compr)) { 34858c2ecf20Sopenharmony_ci compr->buf->compr = NULL; 34868c2ecf20Sopenharmony_ci compr->buf = NULL; 34878c2ecf20Sopenharmony_ci } 34888c2ecf20Sopenharmony_ci} 34898c2ecf20Sopenharmony_ci 34908c2ecf20Sopenharmony_ciint wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 34918c2ecf20Sopenharmony_ci{ 34928c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr, *tmp; 34938c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = stream->private_data; 34948c2ecf20Sopenharmony_ci int ret = 0; 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 34978c2ecf20Sopenharmony_ci 34988c2ecf20Sopenharmony_ci if (wm_adsp_fw[dsp->fw].num_caps == 0) { 34998c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: Firmware does not support compressed API\n", 35008c2ecf20Sopenharmony_ci asoc_rtd_to_codec(rtd, 0)->name); 35018c2ecf20Sopenharmony_ci ret = -ENXIO; 35028c2ecf20Sopenharmony_ci goto out; 35038c2ecf20Sopenharmony_ci } 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ci if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 35068c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: Firmware does not support stream direction\n", 35078c2ecf20Sopenharmony_ci asoc_rtd_to_codec(rtd, 0)->name); 35088c2ecf20Sopenharmony_ci ret = -EINVAL; 35098c2ecf20Sopenharmony_ci goto out; 35108c2ecf20Sopenharmony_ci } 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &dsp->compr_list, list) { 35138c2ecf20Sopenharmony_ci if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) { 35148c2ecf20Sopenharmony_ci adsp_err(dsp, "%s: Only a single stream supported per dai\n", 35158c2ecf20Sopenharmony_ci asoc_rtd_to_codec(rtd, 0)->name); 35168c2ecf20Sopenharmony_ci ret = -EBUSY; 35178c2ecf20Sopenharmony_ci goto out; 35188c2ecf20Sopenharmony_ci } 35198c2ecf20Sopenharmony_ci } 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci compr = kzalloc(sizeof(*compr), GFP_KERNEL); 35228c2ecf20Sopenharmony_ci if (!compr) { 35238c2ecf20Sopenharmony_ci ret = -ENOMEM; 35248c2ecf20Sopenharmony_ci goto out; 35258c2ecf20Sopenharmony_ci } 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci compr->dsp = dsp; 35288c2ecf20Sopenharmony_ci compr->stream = stream; 35298c2ecf20Sopenharmony_ci compr->name = asoc_rtd_to_codec(rtd, 0)->name; 35308c2ecf20Sopenharmony_ci 35318c2ecf20Sopenharmony_ci list_add_tail(&compr->list, &dsp->compr_list); 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci stream->runtime->private_data = compr; 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ciout: 35368c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_ci return ret; 35398c2ecf20Sopenharmony_ci} 35408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_open); 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ciint wm_adsp_compr_free(struct snd_soc_component *component, 35438c2ecf20Sopenharmony_ci struct snd_compr_stream *stream) 35448c2ecf20Sopenharmony_ci{ 35458c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr = stream->runtime->private_data; 35468c2ecf20Sopenharmony_ci struct wm_adsp *dsp = compr->dsp; 35478c2ecf20Sopenharmony_ci 35488c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci wm_adsp_compr_detach(compr); 35518c2ecf20Sopenharmony_ci list_del(&compr->list); 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci kfree(compr->raw_buf); 35548c2ecf20Sopenharmony_ci kfree(compr); 35558c2ecf20Sopenharmony_ci 35568c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci return 0; 35598c2ecf20Sopenharmony_ci} 35608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_free); 35618c2ecf20Sopenharmony_ci 35628c2ecf20Sopenharmony_cistatic int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 35638c2ecf20Sopenharmony_ci struct snd_compr_params *params) 35648c2ecf20Sopenharmony_ci{ 35658c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr = stream->runtime->private_data; 35668c2ecf20Sopenharmony_ci struct wm_adsp *dsp = compr->dsp; 35678c2ecf20Sopenharmony_ci const struct wm_adsp_fw_caps *caps; 35688c2ecf20Sopenharmony_ci const struct snd_codec_desc *desc; 35698c2ecf20Sopenharmony_ci int i, j; 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_ci if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 35728c2ecf20Sopenharmony_ci params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 35738c2ecf20Sopenharmony_ci params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 35748c2ecf20Sopenharmony_ci params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 35758c2ecf20Sopenharmony_ci params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 35768c2ecf20Sopenharmony_ci compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", 35778c2ecf20Sopenharmony_ci params->buffer.fragment_size, 35788c2ecf20Sopenharmony_ci params->buffer.fragments); 35798c2ecf20Sopenharmony_ci 35808c2ecf20Sopenharmony_ci return -EINVAL; 35818c2ecf20Sopenharmony_ci } 35828c2ecf20Sopenharmony_ci 35838c2ecf20Sopenharmony_ci for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 35848c2ecf20Sopenharmony_ci caps = &wm_adsp_fw[dsp->fw].caps[i]; 35858c2ecf20Sopenharmony_ci desc = &caps->desc; 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_ci if (caps->id != params->codec.id) 35888c2ecf20Sopenharmony_ci continue; 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_PLAYBACK) { 35918c2ecf20Sopenharmony_ci if (desc->max_ch < params->codec.ch_out) 35928c2ecf20Sopenharmony_ci continue; 35938c2ecf20Sopenharmony_ci } else { 35948c2ecf20Sopenharmony_ci if (desc->max_ch < params->codec.ch_in) 35958c2ecf20Sopenharmony_ci continue; 35968c2ecf20Sopenharmony_ci } 35978c2ecf20Sopenharmony_ci 35988c2ecf20Sopenharmony_ci if (!(desc->formats & (1 << params->codec.format))) 35998c2ecf20Sopenharmony_ci continue; 36008c2ecf20Sopenharmony_ci 36018c2ecf20Sopenharmony_ci for (j = 0; j < desc->num_sample_rates; ++j) 36028c2ecf20Sopenharmony_ci if (desc->sample_rates[j] == params->codec.sample_rate) 36038c2ecf20Sopenharmony_ci return 0; 36048c2ecf20Sopenharmony_ci } 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 36078c2ecf20Sopenharmony_ci params->codec.id, params->codec.ch_in, params->codec.ch_out, 36088c2ecf20Sopenharmony_ci params->codec.sample_rate, params->codec.format); 36098c2ecf20Sopenharmony_ci return -EINVAL; 36108c2ecf20Sopenharmony_ci} 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_cistatic inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 36138c2ecf20Sopenharmony_ci{ 36148c2ecf20Sopenharmony_ci return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 36158c2ecf20Sopenharmony_ci} 36168c2ecf20Sopenharmony_ci 36178c2ecf20Sopenharmony_ciint wm_adsp_compr_set_params(struct snd_soc_component *component, 36188c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 36198c2ecf20Sopenharmony_ci struct snd_compr_params *params) 36208c2ecf20Sopenharmony_ci{ 36218c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr = stream->runtime->private_data; 36228c2ecf20Sopenharmony_ci unsigned int size; 36238c2ecf20Sopenharmony_ci int ret; 36248c2ecf20Sopenharmony_ci 36258c2ecf20Sopenharmony_ci ret = wm_adsp_compr_check_params(stream, params); 36268c2ecf20Sopenharmony_ci if (ret) 36278c2ecf20Sopenharmony_ci return ret; 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci compr->size = params->buffer; 36308c2ecf20Sopenharmony_ci 36318c2ecf20Sopenharmony_ci compr_dbg(compr, "fragment_size=%d fragments=%d\n", 36328c2ecf20Sopenharmony_ci compr->size.fragment_size, compr->size.fragments); 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 36358c2ecf20Sopenharmony_ci compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 36368c2ecf20Sopenharmony_ci if (!compr->raw_buf) 36378c2ecf20Sopenharmony_ci return -ENOMEM; 36388c2ecf20Sopenharmony_ci 36398c2ecf20Sopenharmony_ci compr->sample_rate = params->codec.sample_rate; 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_ci return 0; 36428c2ecf20Sopenharmony_ci} 36438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ciint wm_adsp_compr_get_caps(struct snd_soc_component *component, 36468c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 36478c2ecf20Sopenharmony_ci struct snd_compr_caps *caps) 36488c2ecf20Sopenharmony_ci{ 36498c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr = stream->runtime->private_data; 36508c2ecf20Sopenharmony_ci int fw = compr->dsp->fw; 36518c2ecf20Sopenharmony_ci int i; 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_ci if (wm_adsp_fw[fw].caps) { 36548c2ecf20Sopenharmony_ci for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 36558c2ecf20Sopenharmony_ci caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci caps->num_codecs = i; 36588c2ecf20Sopenharmony_ci caps->direction = wm_adsp_fw[fw].compr_direction; 36598c2ecf20Sopenharmony_ci 36608c2ecf20Sopenharmony_ci caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 36618c2ecf20Sopenharmony_ci caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 36628c2ecf20Sopenharmony_ci caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 36638c2ecf20Sopenharmony_ci caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 36648c2ecf20Sopenharmony_ci } 36658c2ecf20Sopenharmony_ci 36668c2ecf20Sopenharmony_ci return 0; 36678c2ecf20Sopenharmony_ci} 36688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 36698c2ecf20Sopenharmony_ci 36708c2ecf20Sopenharmony_cistatic int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 36718c2ecf20Sopenharmony_ci unsigned int mem_addr, 36728c2ecf20Sopenharmony_ci unsigned int num_words, u32 *data) 36738c2ecf20Sopenharmony_ci{ 36748c2ecf20Sopenharmony_ci struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 36758c2ecf20Sopenharmony_ci unsigned int i, reg; 36768c2ecf20Sopenharmony_ci int ret; 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_ci if (!mem) 36798c2ecf20Sopenharmony_ci return -EINVAL; 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci reg = dsp->ops->region_to_reg(mem, mem_addr); 36828c2ecf20Sopenharmony_ci 36838c2ecf20Sopenharmony_ci ret = regmap_raw_read(dsp->regmap, reg, data, 36848c2ecf20Sopenharmony_ci sizeof(*data) * num_words); 36858c2ecf20Sopenharmony_ci if (ret < 0) 36868c2ecf20Sopenharmony_ci return ret; 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_ci for (i = 0; i < num_words; ++i) 36898c2ecf20Sopenharmony_ci data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 36908c2ecf20Sopenharmony_ci 36918c2ecf20Sopenharmony_ci return 0; 36928c2ecf20Sopenharmony_ci} 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_cistatic inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 36958c2ecf20Sopenharmony_ci unsigned int mem_addr, u32 *data) 36968c2ecf20Sopenharmony_ci{ 36978c2ecf20Sopenharmony_ci return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 36988c2ecf20Sopenharmony_ci} 36998c2ecf20Sopenharmony_ci 37008c2ecf20Sopenharmony_cistatic int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 37018c2ecf20Sopenharmony_ci unsigned int mem_addr, u32 data) 37028c2ecf20Sopenharmony_ci{ 37038c2ecf20Sopenharmony_ci struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 37048c2ecf20Sopenharmony_ci unsigned int reg; 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_ci if (!mem) 37078c2ecf20Sopenharmony_ci return -EINVAL; 37088c2ecf20Sopenharmony_ci 37098c2ecf20Sopenharmony_ci reg = dsp->ops->region_to_reg(mem, mem_addr); 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci data = cpu_to_be32(data & 0x00ffffffu); 37128c2ecf20Sopenharmony_ci 37138c2ecf20Sopenharmony_ci return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 37148c2ecf20Sopenharmony_ci} 37158c2ecf20Sopenharmony_ci 37168c2ecf20Sopenharmony_cistatic inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 37178c2ecf20Sopenharmony_ci unsigned int field_offset, u32 *data) 37188c2ecf20Sopenharmony_ci{ 37198c2ecf20Sopenharmony_ci return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, 37208c2ecf20Sopenharmony_ci buf->host_buf_ptr + field_offset, data); 37218c2ecf20Sopenharmony_ci} 37228c2ecf20Sopenharmony_ci 37238c2ecf20Sopenharmony_cistatic inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 37248c2ecf20Sopenharmony_ci unsigned int field_offset, u32 data) 37258c2ecf20Sopenharmony_ci{ 37268c2ecf20Sopenharmony_ci return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, 37278c2ecf20Sopenharmony_ci buf->host_buf_ptr + field_offset, data); 37288c2ecf20Sopenharmony_ci} 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_cistatic void wm_adsp_remove_padding(u32 *buf, int nwords, int data_word_size) 37318c2ecf20Sopenharmony_ci{ 37328c2ecf20Sopenharmony_ci u8 *pack_in = (u8 *)buf; 37338c2ecf20Sopenharmony_ci u8 *pack_out = (u8 *)buf; 37348c2ecf20Sopenharmony_ci int i, j; 37358c2ecf20Sopenharmony_ci 37368c2ecf20Sopenharmony_ci /* Remove the padding bytes from the data read from the DSP */ 37378c2ecf20Sopenharmony_ci for (i = 0; i < nwords; i++) { 37388c2ecf20Sopenharmony_ci for (j = 0; j < data_word_size; j++) 37398c2ecf20Sopenharmony_ci *pack_out++ = *pack_in++; 37408c2ecf20Sopenharmony_ci 37418c2ecf20Sopenharmony_ci pack_in += sizeof(*buf) - data_word_size; 37428c2ecf20Sopenharmony_ci } 37438c2ecf20Sopenharmony_ci} 37448c2ecf20Sopenharmony_ci 37458c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 37468c2ecf20Sopenharmony_ci{ 37478c2ecf20Sopenharmony_ci const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 37488c2ecf20Sopenharmony_ci struct wm_adsp_buffer_region *region; 37498c2ecf20Sopenharmony_ci u32 offset = 0; 37508c2ecf20Sopenharmony_ci int i, ret; 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), 37538c2ecf20Sopenharmony_ci GFP_KERNEL); 37548c2ecf20Sopenharmony_ci if (!buf->regions) 37558c2ecf20Sopenharmony_ci return -ENOMEM; 37568c2ecf20Sopenharmony_ci 37578c2ecf20Sopenharmony_ci for (i = 0; i < caps->num_regions; ++i) { 37588c2ecf20Sopenharmony_ci region = &buf->regions[i]; 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci region->offset = offset; 37618c2ecf20Sopenharmony_ci region->mem_type = caps->region_defs[i].mem_type; 37628c2ecf20Sopenharmony_ci 37638c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 37648c2ecf20Sopenharmony_ci ®ion->base_addr); 37658c2ecf20Sopenharmony_ci if (ret < 0) 37668c2ecf20Sopenharmony_ci goto err; 37678c2ecf20Sopenharmony_ci 37688c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 37698c2ecf20Sopenharmony_ci &offset); 37708c2ecf20Sopenharmony_ci if (ret < 0) 37718c2ecf20Sopenharmony_ci goto err; 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci region->cumulative_size = offset; 37748c2ecf20Sopenharmony_ci 37758c2ecf20Sopenharmony_ci compr_dbg(buf, 37768c2ecf20Sopenharmony_ci "region=%d type=%d base=%08x off=%08x size=%08x\n", 37778c2ecf20Sopenharmony_ci i, region->mem_type, region->base_addr, 37788c2ecf20Sopenharmony_ci region->offset, region->cumulative_size); 37798c2ecf20Sopenharmony_ci } 37808c2ecf20Sopenharmony_ci 37818c2ecf20Sopenharmony_ci return 0; 37828c2ecf20Sopenharmony_ci 37838c2ecf20Sopenharmony_cierr: 37848c2ecf20Sopenharmony_ci kfree(buf->regions); 37858c2ecf20Sopenharmony_ci return ret; 37868c2ecf20Sopenharmony_ci} 37878c2ecf20Sopenharmony_ci 37888c2ecf20Sopenharmony_cistatic void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 37898c2ecf20Sopenharmony_ci{ 37908c2ecf20Sopenharmony_ci buf->irq_count = 0xFFFFFFFF; 37918c2ecf20Sopenharmony_ci buf->read_index = -1; 37928c2ecf20Sopenharmony_ci buf->avail = 0; 37938c2ecf20Sopenharmony_ci} 37948c2ecf20Sopenharmony_ci 37958c2ecf20Sopenharmony_cistatic struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) 37968c2ecf20Sopenharmony_ci{ 37978c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf; 37988c2ecf20Sopenharmony_ci 37998c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 38008c2ecf20Sopenharmony_ci if (!buf) 38018c2ecf20Sopenharmony_ci return NULL; 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_ci buf->dsp = dsp; 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_ci wm_adsp_buffer_clear(buf); 38068c2ecf20Sopenharmony_ci 38078c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &dsp->buffer_list); 38088c2ecf20Sopenharmony_ci 38098c2ecf20Sopenharmony_ci return buf; 38108c2ecf20Sopenharmony_ci} 38118c2ecf20Sopenharmony_ci 38128c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) 38138c2ecf20Sopenharmony_ci{ 38148c2ecf20Sopenharmony_ci struct wm_adsp_alg_region *alg_region; 38158c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf; 38168c2ecf20Sopenharmony_ci u32 xmalg, addr, magic; 38178c2ecf20Sopenharmony_ci int i, ret; 38188c2ecf20Sopenharmony_ci 38198c2ecf20Sopenharmony_ci alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 38208c2ecf20Sopenharmony_ci if (!alg_region) { 38218c2ecf20Sopenharmony_ci adsp_err(dsp, "No algorithm region found\n"); 38228c2ecf20Sopenharmony_ci return -EINVAL; 38238c2ecf20Sopenharmony_ci } 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_ci buf = wm_adsp_buffer_alloc(dsp); 38268c2ecf20Sopenharmony_ci if (!buf) 38278c2ecf20Sopenharmony_ci return -ENOMEM; 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci xmalg = dsp->ops->sys_config_size / sizeof(__be32); 38308c2ecf20Sopenharmony_ci 38318c2ecf20Sopenharmony_ci addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 38328c2ecf20Sopenharmony_ci ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 38338c2ecf20Sopenharmony_ci if (ret < 0) 38348c2ecf20Sopenharmony_ci return ret; 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 38378c2ecf20Sopenharmony_ci return -ENODEV; 38388c2ecf20Sopenharmony_ci 38398c2ecf20Sopenharmony_ci addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 38408c2ecf20Sopenharmony_ci for (i = 0; i < 5; ++i) { 38418c2ecf20Sopenharmony_ci ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 38428c2ecf20Sopenharmony_ci &buf->host_buf_ptr); 38438c2ecf20Sopenharmony_ci if (ret < 0) 38448c2ecf20Sopenharmony_ci return ret; 38458c2ecf20Sopenharmony_ci 38468c2ecf20Sopenharmony_ci if (buf->host_buf_ptr) 38478c2ecf20Sopenharmony_ci break; 38488c2ecf20Sopenharmony_ci 38498c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 38508c2ecf20Sopenharmony_ci } 38518c2ecf20Sopenharmony_ci 38528c2ecf20Sopenharmony_ci if (!buf->host_buf_ptr) 38538c2ecf20Sopenharmony_ci return -EIO; 38548c2ecf20Sopenharmony_ci 38558c2ecf20Sopenharmony_ci buf->host_buf_mem_type = WMFW_ADSP2_XM; 38568c2ecf20Sopenharmony_ci 38578c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_populate(buf); 38588c2ecf20Sopenharmony_ci if (ret < 0) 38598c2ecf20Sopenharmony_ci return ret; 38608c2ecf20Sopenharmony_ci 38618c2ecf20Sopenharmony_ci compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_ci return 0; 38648c2ecf20Sopenharmony_ci} 38658c2ecf20Sopenharmony_ci 38668c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) 38678c2ecf20Sopenharmony_ci{ 38688c2ecf20Sopenharmony_ci struct wm_adsp_host_buf_coeff_v1 coeff_v1; 38698c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf; 38708c2ecf20Sopenharmony_ci unsigned int val, reg; 38718c2ecf20Sopenharmony_ci int ret, i; 38728c2ecf20Sopenharmony_ci 38738c2ecf20Sopenharmony_ci ret = wm_coeff_base_reg(ctl, ®); 38748c2ecf20Sopenharmony_ci if (ret) 38758c2ecf20Sopenharmony_ci return ret; 38768c2ecf20Sopenharmony_ci 38778c2ecf20Sopenharmony_ci for (i = 0; i < 5; ++i) { 38788c2ecf20Sopenharmony_ci ret = regmap_raw_read(ctl->dsp->regmap, reg, &val, sizeof(val)); 38798c2ecf20Sopenharmony_ci if (ret < 0) 38808c2ecf20Sopenharmony_ci return ret; 38818c2ecf20Sopenharmony_ci 38828c2ecf20Sopenharmony_ci if (val) 38838c2ecf20Sopenharmony_ci break; 38848c2ecf20Sopenharmony_ci 38858c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 38868c2ecf20Sopenharmony_ci } 38878c2ecf20Sopenharmony_ci 38888c2ecf20Sopenharmony_ci if (!val) { 38898c2ecf20Sopenharmony_ci adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); 38908c2ecf20Sopenharmony_ci return -EIO; 38918c2ecf20Sopenharmony_ci } 38928c2ecf20Sopenharmony_ci 38938c2ecf20Sopenharmony_ci buf = wm_adsp_buffer_alloc(ctl->dsp); 38948c2ecf20Sopenharmony_ci if (!buf) 38958c2ecf20Sopenharmony_ci return -ENOMEM; 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci buf->host_buf_mem_type = ctl->alg_region.type; 38988c2ecf20Sopenharmony_ci buf->host_buf_ptr = be32_to_cpu(val); 38998c2ecf20Sopenharmony_ci 39008c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_populate(buf); 39018c2ecf20Sopenharmony_ci if (ret < 0) 39028c2ecf20Sopenharmony_ci return ret; 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci /* 39058c2ecf20Sopenharmony_ci * v0 host_buffer coefficients didn't have versioning, so if the 39068c2ecf20Sopenharmony_ci * control is one word, assume version 0. 39078c2ecf20Sopenharmony_ci */ 39088c2ecf20Sopenharmony_ci if (ctl->len == 4) { 39098c2ecf20Sopenharmony_ci compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); 39108c2ecf20Sopenharmony_ci return 0; 39118c2ecf20Sopenharmony_ci } 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, 39148c2ecf20Sopenharmony_ci sizeof(coeff_v1)); 39158c2ecf20Sopenharmony_ci if (ret < 0) 39168c2ecf20Sopenharmony_ci return ret; 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci coeff_v1.versions = be32_to_cpu(coeff_v1.versions); 39198c2ecf20Sopenharmony_ci val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK; 39208c2ecf20Sopenharmony_ci val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { 39238c2ecf20Sopenharmony_ci adsp_err(ctl->dsp, 39248c2ecf20Sopenharmony_ci "Host buffer coeff ver %u > supported version %u\n", 39258c2ecf20Sopenharmony_ci val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); 39268c2ecf20Sopenharmony_ci return -EINVAL; 39278c2ecf20Sopenharmony_ci } 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++) 39308c2ecf20Sopenharmony_ci coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]); 39318c2ecf20Sopenharmony_ci 39328c2ecf20Sopenharmony_ci wm_adsp_remove_padding((u32 *)&coeff_v1.name, 39338c2ecf20Sopenharmony_ci ARRAY_SIZE(coeff_v1.name), 39348c2ecf20Sopenharmony_ci WM_ADSP_DATA_WORD_SIZE); 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, 39378c2ecf20Sopenharmony_ci (char *)&coeff_v1.name); 39388c2ecf20Sopenharmony_ci 39398c2ecf20Sopenharmony_ci compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", 39408c2ecf20Sopenharmony_ci buf->host_buf_ptr, val); 39418c2ecf20Sopenharmony_ci 39428c2ecf20Sopenharmony_ci return val; 39438c2ecf20Sopenharmony_ci} 39448c2ecf20Sopenharmony_ci 39458c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_init(struct wm_adsp *dsp) 39468c2ecf20Sopenharmony_ci{ 39478c2ecf20Sopenharmony_ci struct wm_coeff_ctl *ctl; 39488c2ecf20Sopenharmony_ci int ret; 39498c2ecf20Sopenharmony_ci 39508c2ecf20Sopenharmony_ci list_for_each_entry(ctl, &dsp->ctl_list, list) { 39518c2ecf20Sopenharmony_ci if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 39528c2ecf20Sopenharmony_ci continue; 39538c2ecf20Sopenharmony_ci 39548c2ecf20Sopenharmony_ci if (!ctl->enabled) 39558c2ecf20Sopenharmony_ci continue; 39568c2ecf20Sopenharmony_ci 39578c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_parse_coeff(ctl); 39588c2ecf20Sopenharmony_ci if (ret < 0) { 39598c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to parse coeff: %d\n", ret); 39608c2ecf20Sopenharmony_ci goto error; 39618c2ecf20Sopenharmony_ci } else if (ret == 0) { 39628c2ecf20Sopenharmony_ci /* Only one buffer supported for version 0 */ 39638c2ecf20Sopenharmony_ci return 0; 39648c2ecf20Sopenharmony_ci } 39658c2ecf20Sopenharmony_ci } 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci if (list_empty(&dsp->buffer_list)) { 39688c2ecf20Sopenharmony_ci /* Fall back to legacy support */ 39698c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_parse_legacy(dsp); 39708c2ecf20Sopenharmony_ci if (ret) { 39718c2ecf20Sopenharmony_ci adsp_err(dsp, "Failed to parse legacy: %d\n", ret); 39728c2ecf20Sopenharmony_ci goto error; 39738c2ecf20Sopenharmony_ci } 39748c2ecf20Sopenharmony_ci } 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci return 0; 39778c2ecf20Sopenharmony_ci 39788c2ecf20Sopenharmony_cierror: 39798c2ecf20Sopenharmony_ci wm_adsp_buffer_free(dsp); 39808c2ecf20Sopenharmony_ci return ret; 39818c2ecf20Sopenharmony_ci} 39828c2ecf20Sopenharmony_ci 39838c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_free(struct wm_adsp *dsp) 39848c2ecf20Sopenharmony_ci{ 39858c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf, *tmp; 39868c2ecf20Sopenharmony_ci 39878c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { 39888c2ecf20Sopenharmony_ci wm_adsp_compr_detach(buf->compr); 39898c2ecf20Sopenharmony_ci 39908c2ecf20Sopenharmony_ci kfree(buf->name); 39918c2ecf20Sopenharmony_ci kfree(buf->regions); 39928c2ecf20Sopenharmony_ci list_del(&buf->list); 39938c2ecf20Sopenharmony_ci kfree(buf); 39948c2ecf20Sopenharmony_ci } 39958c2ecf20Sopenharmony_ci 39968c2ecf20Sopenharmony_ci return 0; 39978c2ecf20Sopenharmony_ci} 39988c2ecf20Sopenharmony_ci 39998c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 40008c2ecf20Sopenharmony_ci{ 40018c2ecf20Sopenharmony_ci int ret; 40028c2ecf20Sopenharmony_ci 40038c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 40048c2ecf20Sopenharmony_ci if (ret < 0) { 40058c2ecf20Sopenharmony_ci compr_err(buf, "Failed to check buffer error: %d\n", ret); 40068c2ecf20Sopenharmony_ci return ret; 40078c2ecf20Sopenharmony_ci } 40088c2ecf20Sopenharmony_ci if (buf->error != 0) { 40098c2ecf20Sopenharmony_ci compr_err(buf, "Buffer error occurred: %d\n", buf->error); 40108c2ecf20Sopenharmony_ci return -EIO; 40118c2ecf20Sopenharmony_ci } 40128c2ecf20Sopenharmony_ci 40138c2ecf20Sopenharmony_ci return 0; 40148c2ecf20Sopenharmony_ci} 40158c2ecf20Sopenharmony_ci 40168c2ecf20Sopenharmony_ciint wm_adsp_compr_trigger(struct snd_soc_component *component, 40178c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, int cmd) 40188c2ecf20Sopenharmony_ci{ 40198c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr = stream->runtime->private_data; 40208c2ecf20Sopenharmony_ci struct wm_adsp *dsp = compr->dsp; 40218c2ecf20Sopenharmony_ci int ret = 0; 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_ci compr_dbg(compr, "Trigger: %d\n", cmd); 40248c2ecf20Sopenharmony_ci 40258c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci switch (cmd) { 40288c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 40298c2ecf20Sopenharmony_ci if (!wm_adsp_compr_attached(compr)) { 40308c2ecf20Sopenharmony_ci ret = wm_adsp_compr_attach(compr); 40318c2ecf20Sopenharmony_ci if (ret < 0) { 40328c2ecf20Sopenharmony_ci compr_err(compr, "Failed to link buffer and stream: %d\n", 40338c2ecf20Sopenharmony_ci ret); 40348c2ecf20Sopenharmony_ci break; 40358c2ecf20Sopenharmony_ci } 40368c2ecf20Sopenharmony_ci } 40378c2ecf20Sopenharmony_ci 40388c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_get_error(compr->buf); 40398c2ecf20Sopenharmony_ci if (ret < 0) 40408c2ecf20Sopenharmony_ci break; 40418c2ecf20Sopenharmony_ci 40428c2ecf20Sopenharmony_ci /* Trigger the IRQ at one fragment of data */ 40438c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_write(compr->buf, 40448c2ecf20Sopenharmony_ci HOST_BUFFER_FIELD(high_water_mark), 40458c2ecf20Sopenharmony_ci wm_adsp_compr_frag_words(compr)); 40468c2ecf20Sopenharmony_ci if (ret < 0) { 40478c2ecf20Sopenharmony_ci compr_err(compr, "Failed to set high water mark: %d\n", 40488c2ecf20Sopenharmony_ci ret); 40498c2ecf20Sopenharmony_ci break; 40508c2ecf20Sopenharmony_ci } 40518c2ecf20Sopenharmony_ci break; 40528c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 40538c2ecf20Sopenharmony_ci if (wm_adsp_compr_attached(compr)) 40548c2ecf20Sopenharmony_ci wm_adsp_buffer_clear(compr->buf); 40558c2ecf20Sopenharmony_ci break; 40568c2ecf20Sopenharmony_ci default: 40578c2ecf20Sopenharmony_ci ret = -EINVAL; 40588c2ecf20Sopenharmony_ci break; 40598c2ecf20Sopenharmony_ci } 40608c2ecf20Sopenharmony_ci 40618c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 40628c2ecf20Sopenharmony_ci 40638c2ecf20Sopenharmony_ci return ret; 40648c2ecf20Sopenharmony_ci} 40658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 40668c2ecf20Sopenharmony_ci 40678c2ecf20Sopenharmony_cistatic inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 40688c2ecf20Sopenharmony_ci{ 40698c2ecf20Sopenharmony_ci int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci return buf->regions[last_region].cumulative_size; 40728c2ecf20Sopenharmony_ci} 40738c2ecf20Sopenharmony_ci 40748c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 40758c2ecf20Sopenharmony_ci{ 40768c2ecf20Sopenharmony_ci u32 next_read_index, next_write_index; 40778c2ecf20Sopenharmony_ci int write_index, read_index, avail; 40788c2ecf20Sopenharmony_ci int ret; 40798c2ecf20Sopenharmony_ci 40808c2ecf20Sopenharmony_ci /* Only sync read index if we haven't already read a valid index */ 40818c2ecf20Sopenharmony_ci if (buf->read_index < 0) { 40828c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_read(buf, 40838c2ecf20Sopenharmony_ci HOST_BUFFER_FIELD(next_read_index), 40848c2ecf20Sopenharmony_ci &next_read_index); 40858c2ecf20Sopenharmony_ci if (ret < 0) 40868c2ecf20Sopenharmony_ci return ret; 40878c2ecf20Sopenharmony_ci 40888c2ecf20Sopenharmony_ci read_index = sign_extend32(next_read_index, 23); 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci if (read_index < 0) { 40918c2ecf20Sopenharmony_ci compr_dbg(buf, "Avail check on unstarted stream\n"); 40928c2ecf20Sopenharmony_ci return 0; 40938c2ecf20Sopenharmony_ci } 40948c2ecf20Sopenharmony_ci 40958c2ecf20Sopenharmony_ci buf->read_index = read_index; 40968c2ecf20Sopenharmony_ci } 40978c2ecf20Sopenharmony_ci 40988c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 40998c2ecf20Sopenharmony_ci &next_write_index); 41008c2ecf20Sopenharmony_ci if (ret < 0) 41018c2ecf20Sopenharmony_ci return ret; 41028c2ecf20Sopenharmony_ci 41038c2ecf20Sopenharmony_ci write_index = sign_extend32(next_write_index, 23); 41048c2ecf20Sopenharmony_ci 41058c2ecf20Sopenharmony_ci avail = write_index - buf->read_index; 41068c2ecf20Sopenharmony_ci if (avail < 0) 41078c2ecf20Sopenharmony_ci avail += wm_adsp_buffer_size(buf); 41088c2ecf20Sopenharmony_ci 41098c2ecf20Sopenharmony_ci compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 41108c2ecf20Sopenharmony_ci buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 41118c2ecf20Sopenharmony_ci 41128c2ecf20Sopenharmony_ci buf->avail = avail; 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci return 0; 41158c2ecf20Sopenharmony_ci} 41168c2ecf20Sopenharmony_ci 41178c2ecf20Sopenharmony_ciint wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 41188c2ecf20Sopenharmony_ci{ 41198c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf; 41208c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr; 41218c2ecf20Sopenharmony_ci int ret = 0; 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 41248c2ecf20Sopenharmony_ci 41258c2ecf20Sopenharmony_ci if (list_empty(&dsp->buffer_list)) { 41268c2ecf20Sopenharmony_ci ret = -ENODEV; 41278c2ecf20Sopenharmony_ci goto out; 41288c2ecf20Sopenharmony_ci } 41298c2ecf20Sopenharmony_ci 41308c2ecf20Sopenharmony_ci adsp_dbg(dsp, "Handling buffer IRQ\n"); 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ci list_for_each_entry(buf, &dsp->buffer_list, list) { 41338c2ecf20Sopenharmony_ci compr = buf->compr; 41348c2ecf20Sopenharmony_ci 41358c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_get_error(buf); 41368c2ecf20Sopenharmony_ci if (ret < 0) 41378c2ecf20Sopenharmony_ci goto out_notify; /* Wake poll to report error */ 41388c2ecf20Sopenharmony_ci 41398c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 41408c2ecf20Sopenharmony_ci &buf->irq_count); 41418c2ecf20Sopenharmony_ci if (ret < 0) { 41428c2ecf20Sopenharmony_ci compr_err(buf, "Failed to get irq_count: %d\n", ret); 41438c2ecf20Sopenharmony_ci goto out; 41448c2ecf20Sopenharmony_ci } 41458c2ecf20Sopenharmony_ci 41468c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_update_avail(buf); 41478c2ecf20Sopenharmony_ci if (ret < 0) { 41488c2ecf20Sopenharmony_ci compr_err(buf, "Error reading avail: %d\n", ret); 41498c2ecf20Sopenharmony_ci goto out; 41508c2ecf20Sopenharmony_ci } 41518c2ecf20Sopenharmony_ci 41528c2ecf20Sopenharmony_ci if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 41538c2ecf20Sopenharmony_ci ret = WM_ADSP_COMPR_VOICE_TRIGGER; 41548c2ecf20Sopenharmony_ci 41558c2ecf20Sopenharmony_ciout_notify: 41568c2ecf20Sopenharmony_ci if (compr && compr->stream) 41578c2ecf20Sopenharmony_ci snd_compr_fragment_elapsed(compr->stream); 41588c2ecf20Sopenharmony_ci } 41598c2ecf20Sopenharmony_ci 41608c2ecf20Sopenharmony_ciout: 41618c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 41628c2ecf20Sopenharmony_ci 41638c2ecf20Sopenharmony_ci return ret; 41648c2ecf20Sopenharmony_ci} 41658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 41688c2ecf20Sopenharmony_ci{ 41698c2ecf20Sopenharmony_ci if (buf->irq_count & 0x01) 41708c2ecf20Sopenharmony_ci return 0; 41718c2ecf20Sopenharmony_ci 41728c2ecf20Sopenharmony_ci compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); 41738c2ecf20Sopenharmony_ci 41748c2ecf20Sopenharmony_ci buf->irq_count |= 0x01; 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_ci return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 41778c2ecf20Sopenharmony_ci buf->irq_count); 41788c2ecf20Sopenharmony_ci} 41798c2ecf20Sopenharmony_ci 41808c2ecf20Sopenharmony_ciint wm_adsp_compr_pointer(struct snd_soc_component *component, 41818c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, 41828c2ecf20Sopenharmony_ci struct snd_compr_tstamp *tstamp) 41838c2ecf20Sopenharmony_ci{ 41848c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr = stream->runtime->private_data; 41858c2ecf20Sopenharmony_ci struct wm_adsp *dsp = compr->dsp; 41868c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf; 41878c2ecf20Sopenharmony_ci int ret = 0; 41888c2ecf20Sopenharmony_ci 41898c2ecf20Sopenharmony_ci compr_dbg(compr, "Pointer request\n"); 41908c2ecf20Sopenharmony_ci 41918c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 41928c2ecf20Sopenharmony_ci 41938c2ecf20Sopenharmony_ci buf = compr->buf; 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci if (dsp->fatal_error || !buf || buf->error) { 41968c2ecf20Sopenharmony_ci snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 41978c2ecf20Sopenharmony_ci ret = -EIO; 41988c2ecf20Sopenharmony_ci goto out; 41998c2ecf20Sopenharmony_ci } 42008c2ecf20Sopenharmony_ci 42018c2ecf20Sopenharmony_ci if (buf->avail < wm_adsp_compr_frag_words(compr)) { 42028c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_update_avail(buf); 42038c2ecf20Sopenharmony_ci if (ret < 0) { 42048c2ecf20Sopenharmony_ci compr_err(compr, "Error reading avail: %d\n", ret); 42058c2ecf20Sopenharmony_ci goto out; 42068c2ecf20Sopenharmony_ci } 42078c2ecf20Sopenharmony_ci 42088c2ecf20Sopenharmony_ci /* 42098c2ecf20Sopenharmony_ci * If we really have less than 1 fragment available tell the 42108c2ecf20Sopenharmony_ci * DSP to inform us once a whole fragment is available. 42118c2ecf20Sopenharmony_ci */ 42128c2ecf20Sopenharmony_ci if (buf->avail < wm_adsp_compr_frag_words(compr)) { 42138c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_get_error(buf); 42148c2ecf20Sopenharmony_ci if (ret < 0) { 42158c2ecf20Sopenharmony_ci if (buf->error) 42168c2ecf20Sopenharmony_ci snd_compr_stop_error(stream, 42178c2ecf20Sopenharmony_ci SNDRV_PCM_STATE_XRUN); 42188c2ecf20Sopenharmony_ci goto out; 42198c2ecf20Sopenharmony_ci } 42208c2ecf20Sopenharmony_ci 42218c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_reenable_irq(buf); 42228c2ecf20Sopenharmony_ci if (ret < 0) { 42238c2ecf20Sopenharmony_ci compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", 42248c2ecf20Sopenharmony_ci ret); 42258c2ecf20Sopenharmony_ci goto out; 42268c2ecf20Sopenharmony_ci } 42278c2ecf20Sopenharmony_ci } 42288c2ecf20Sopenharmony_ci } 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci tstamp->copied_total = compr->copied_total; 42318c2ecf20Sopenharmony_ci tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 42328c2ecf20Sopenharmony_ci tstamp->sampling_rate = compr->sample_rate; 42338c2ecf20Sopenharmony_ci 42348c2ecf20Sopenharmony_ciout: 42358c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 42368c2ecf20Sopenharmony_ci 42378c2ecf20Sopenharmony_ci return ret; 42388c2ecf20Sopenharmony_ci} 42398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 42408c2ecf20Sopenharmony_ci 42418c2ecf20Sopenharmony_cistatic int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 42428c2ecf20Sopenharmony_ci{ 42438c2ecf20Sopenharmony_ci struct wm_adsp_compr_buf *buf = compr->buf; 42448c2ecf20Sopenharmony_ci unsigned int adsp_addr; 42458c2ecf20Sopenharmony_ci int mem_type, nwords, max_read; 42468c2ecf20Sopenharmony_ci int i, ret; 42478c2ecf20Sopenharmony_ci 42488c2ecf20Sopenharmony_ci /* Calculate read parameters */ 42498c2ecf20Sopenharmony_ci for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 42508c2ecf20Sopenharmony_ci if (buf->read_index < buf->regions[i].cumulative_size) 42518c2ecf20Sopenharmony_ci break; 42528c2ecf20Sopenharmony_ci 42538c2ecf20Sopenharmony_ci if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 42548c2ecf20Sopenharmony_ci return -EINVAL; 42558c2ecf20Sopenharmony_ci 42568c2ecf20Sopenharmony_ci mem_type = buf->regions[i].mem_type; 42578c2ecf20Sopenharmony_ci adsp_addr = buf->regions[i].base_addr + 42588c2ecf20Sopenharmony_ci (buf->read_index - buf->regions[i].offset); 42598c2ecf20Sopenharmony_ci 42608c2ecf20Sopenharmony_ci max_read = wm_adsp_compr_frag_words(compr); 42618c2ecf20Sopenharmony_ci nwords = buf->regions[i].cumulative_size - buf->read_index; 42628c2ecf20Sopenharmony_ci 42638c2ecf20Sopenharmony_ci if (nwords > target) 42648c2ecf20Sopenharmony_ci nwords = target; 42658c2ecf20Sopenharmony_ci if (nwords > buf->avail) 42668c2ecf20Sopenharmony_ci nwords = buf->avail; 42678c2ecf20Sopenharmony_ci if (nwords > max_read) 42688c2ecf20Sopenharmony_ci nwords = max_read; 42698c2ecf20Sopenharmony_ci if (!nwords) 42708c2ecf20Sopenharmony_ci return 0; 42718c2ecf20Sopenharmony_ci 42728c2ecf20Sopenharmony_ci /* Read data from DSP */ 42738c2ecf20Sopenharmony_ci ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 42748c2ecf20Sopenharmony_ci nwords, compr->raw_buf); 42758c2ecf20Sopenharmony_ci if (ret < 0) 42768c2ecf20Sopenharmony_ci return ret; 42778c2ecf20Sopenharmony_ci 42788c2ecf20Sopenharmony_ci wm_adsp_remove_padding(compr->raw_buf, nwords, WM_ADSP_DATA_WORD_SIZE); 42798c2ecf20Sopenharmony_ci 42808c2ecf20Sopenharmony_ci /* update read index to account for words read */ 42818c2ecf20Sopenharmony_ci buf->read_index += nwords; 42828c2ecf20Sopenharmony_ci if (buf->read_index == wm_adsp_buffer_size(buf)) 42838c2ecf20Sopenharmony_ci buf->read_index = 0; 42848c2ecf20Sopenharmony_ci 42858c2ecf20Sopenharmony_ci ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 42868c2ecf20Sopenharmony_ci buf->read_index); 42878c2ecf20Sopenharmony_ci if (ret < 0) 42888c2ecf20Sopenharmony_ci return ret; 42898c2ecf20Sopenharmony_ci 42908c2ecf20Sopenharmony_ci /* update avail to account for words read */ 42918c2ecf20Sopenharmony_ci buf->avail -= nwords; 42928c2ecf20Sopenharmony_ci 42938c2ecf20Sopenharmony_ci return nwords; 42948c2ecf20Sopenharmony_ci} 42958c2ecf20Sopenharmony_ci 42968c2ecf20Sopenharmony_cistatic int wm_adsp_compr_read(struct wm_adsp_compr *compr, 42978c2ecf20Sopenharmony_ci char __user *buf, size_t count) 42988c2ecf20Sopenharmony_ci{ 42998c2ecf20Sopenharmony_ci struct wm_adsp *dsp = compr->dsp; 43008c2ecf20Sopenharmony_ci int ntotal = 0; 43018c2ecf20Sopenharmony_ci int nwords, nbytes; 43028c2ecf20Sopenharmony_ci 43038c2ecf20Sopenharmony_ci compr_dbg(compr, "Requested read of %zu bytes\n", count); 43048c2ecf20Sopenharmony_ci 43058c2ecf20Sopenharmony_ci if (dsp->fatal_error || !compr->buf || compr->buf->error) { 43068c2ecf20Sopenharmony_ci snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 43078c2ecf20Sopenharmony_ci return -EIO; 43088c2ecf20Sopenharmony_ci } 43098c2ecf20Sopenharmony_ci 43108c2ecf20Sopenharmony_ci count /= WM_ADSP_DATA_WORD_SIZE; 43118c2ecf20Sopenharmony_ci 43128c2ecf20Sopenharmony_ci do { 43138c2ecf20Sopenharmony_ci nwords = wm_adsp_buffer_capture_block(compr, count); 43148c2ecf20Sopenharmony_ci if (nwords < 0) { 43158c2ecf20Sopenharmony_ci compr_err(compr, "Failed to capture block: %d\n", 43168c2ecf20Sopenharmony_ci nwords); 43178c2ecf20Sopenharmony_ci return nwords; 43188c2ecf20Sopenharmony_ci } 43198c2ecf20Sopenharmony_ci 43208c2ecf20Sopenharmony_ci nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 43218c2ecf20Sopenharmony_ci 43228c2ecf20Sopenharmony_ci compr_dbg(compr, "Read %d bytes\n", nbytes); 43238c2ecf20Sopenharmony_ci 43248c2ecf20Sopenharmony_ci if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 43258c2ecf20Sopenharmony_ci compr_err(compr, "Failed to copy data to user: %d, %d\n", 43268c2ecf20Sopenharmony_ci ntotal, nbytes); 43278c2ecf20Sopenharmony_ci return -EFAULT; 43288c2ecf20Sopenharmony_ci } 43298c2ecf20Sopenharmony_ci 43308c2ecf20Sopenharmony_ci count -= nwords; 43318c2ecf20Sopenharmony_ci ntotal += nbytes; 43328c2ecf20Sopenharmony_ci } while (nwords > 0 && count > 0); 43338c2ecf20Sopenharmony_ci 43348c2ecf20Sopenharmony_ci compr->copied_total += ntotal; 43358c2ecf20Sopenharmony_ci 43368c2ecf20Sopenharmony_ci return ntotal; 43378c2ecf20Sopenharmony_ci} 43388c2ecf20Sopenharmony_ci 43398c2ecf20Sopenharmony_ciint wm_adsp_compr_copy(struct snd_soc_component *component, 43408c2ecf20Sopenharmony_ci struct snd_compr_stream *stream, char __user *buf, 43418c2ecf20Sopenharmony_ci size_t count) 43428c2ecf20Sopenharmony_ci{ 43438c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr = stream->runtime->private_data; 43448c2ecf20Sopenharmony_ci struct wm_adsp *dsp = compr->dsp; 43458c2ecf20Sopenharmony_ci int ret; 43468c2ecf20Sopenharmony_ci 43478c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 43488c2ecf20Sopenharmony_ci 43498c2ecf20Sopenharmony_ci if (stream->direction == SND_COMPRESS_CAPTURE) 43508c2ecf20Sopenharmony_ci ret = wm_adsp_compr_read(compr, buf, count); 43518c2ecf20Sopenharmony_ci else 43528c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 43538c2ecf20Sopenharmony_ci 43548c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 43558c2ecf20Sopenharmony_ci 43568c2ecf20Sopenharmony_ci return ret; 43578c2ecf20Sopenharmony_ci} 43588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 43598c2ecf20Sopenharmony_ci 43608c2ecf20Sopenharmony_cistatic void wm_adsp_fatal_error(struct wm_adsp *dsp) 43618c2ecf20Sopenharmony_ci{ 43628c2ecf20Sopenharmony_ci struct wm_adsp_compr *compr; 43638c2ecf20Sopenharmony_ci 43648c2ecf20Sopenharmony_ci dsp->fatal_error = true; 43658c2ecf20Sopenharmony_ci 43668c2ecf20Sopenharmony_ci list_for_each_entry(compr, &dsp->compr_list, list) { 43678c2ecf20Sopenharmony_ci if (compr->stream) 43688c2ecf20Sopenharmony_ci snd_compr_fragment_elapsed(compr->stream); 43698c2ecf20Sopenharmony_ci } 43708c2ecf20Sopenharmony_ci} 43718c2ecf20Sopenharmony_ci 43728c2ecf20Sopenharmony_ciirqreturn_t wm_adsp2_bus_error(int irq, void *data) 43738c2ecf20Sopenharmony_ci{ 43748c2ecf20Sopenharmony_ci struct wm_adsp *dsp = (struct wm_adsp *)data; 43758c2ecf20Sopenharmony_ci unsigned int val; 43768c2ecf20Sopenharmony_ci struct regmap *regmap = dsp->regmap; 43778c2ecf20Sopenharmony_ci int ret = 0; 43788c2ecf20Sopenharmony_ci 43798c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 43808c2ecf20Sopenharmony_ci 43818c2ecf20Sopenharmony_ci ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 43828c2ecf20Sopenharmony_ci if (ret) { 43838c2ecf20Sopenharmony_ci adsp_err(dsp, 43848c2ecf20Sopenharmony_ci "Failed to read Region Lock Ctrl register: %d\n", ret); 43858c2ecf20Sopenharmony_ci goto error; 43868c2ecf20Sopenharmony_ci } 43878c2ecf20Sopenharmony_ci 43888c2ecf20Sopenharmony_ci if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 43898c2ecf20Sopenharmony_ci adsp_err(dsp, "watchdog timeout error\n"); 43908c2ecf20Sopenharmony_ci dsp->ops->stop_watchdog(dsp); 43918c2ecf20Sopenharmony_ci wm_adsp_fatal_error(dsp); 43928c2ecf20Sopenharmony_ci } 43938c2ecf20Sopenharmony_ci 43948c2ecf20Sopenharmony_ci if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 43958c2ecf20Sopenharmony_ci if (val & ADSP2_ADDR_ERR_MASK) 43968c2ecf20Sopenharmony_ci adsp_err(dsp, "bus error: address error\n"); 43978c2ecf20Sopenharmony_ci else 43988c2ecf20Sopenharmony_ci adsp_err(dsp, "bus error: region lock error\n"); 43998c2ecf20Sopenharmony_ci 44008c2ecf20Sopenharmony_ci ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 44018c2ecf20Sopenharmony_ci if (ret) { 44028c2ecf20Sopenharmony_ci adsp_err(dsp, 44038c2ecf20Sopenharmony_ci "Failed to read Bus Err Addr register: %d\n", 44048c2ecf20Sopenharmony_ci ret); 44058c2ecf20Sopenharmony_ci goto error; 44068c2ecf20Sopenharmony_ci } 44078c2ecf20Sopenharmony_ci 44088c2ecf20Sopenharmony_ci adsp_err(dsp, "bus error address = 0x%x\n", 44098c2ecf20Sopenharmony_ci val & ADSP2_BUS_ERR_ADDR_MASK); 44108c2ecf20Sopenharmony_ci 44118c2ecf20Sopenharmony_ci ret = regmap_read(regmap, 44128c2ecf20Sopenharmony_ci dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 44138c2ecf20Sopenharmony_ci &val); 44148c2ecf20Sopenharmony_ci if (ret) { 44158c2ecf20Sopenharmony_ci adsp_err(dsp, 44168c2ecf20Sopenharmony_ci "Failed to read Pmem Xmem Err Addr register: %d\n", 44178c2ecf20Sopenharmony_ci ret); 44188c2ecf20Sopenharmony_ci goto error; 44198c2ecf20Sopenharmony_ci } 44208c2ecf20Sopenharmony_ci 44218c2ecf20Sopenharmony_ci adsp_err(dsp, "xmem error address = 0x%x\n", 44228c2ecf20Sopenharmony_ci val & ADSP2_XMEM_ERR_ADDR_MASK); 44238c2ecf20Sopenharmony_ci adsp_err(dsp, "pmem error address = 0x%x\n", 44248c2ecf20Sopenharmony_ci (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 44258c2ecf20Sopenharmony_ci ADSP2_PMEM_ERR_ADDR_SHIFT); 44268c2ecf20Sopenharmony_ci } 44278c2ecf20Sopenharmony_ci 44288c2ecf20Sopenharmony_ci regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 44298c2ecf20Sopenharmony_ci ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 44308c2ecf20Sopenharmony_ci 44318c2ecf20Sopenharmony_cierror: 44328c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 44338c2ecf20Sopenharmony_ci 44348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 44358c2ecf20Sopenharmony_ci} 44368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 44378c2ecf20Sopenharmony_ci 44388c2ecf20Sopenharmony_ciirqreturn_t wm_halo_bus_error(int irq, void *data) 44398c2ecf20Sopenharmony_ci{ 44408c2ecf20Sopenharmony_ci struct wm_adsp *dsp = (struct wm_adsp *)data; 44418c2ecf20Sopenharmony_ci struct regmap *regmap = dsp->regmap; 44428c2ecf20Sopenharmony_ci unsigned int fault[6]; 44438c2ecf20Sopenharmony_ci struct reg_sequence clear[] = { 44448c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 44458c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 44468c2ecf20Sopenharmony_ci { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 44478c2ecf20Sopenharmony_ci }; 44488c2ecf20Sopenharmony_ci int ret; 44498c2ecf20Sopenharmony_ci 44508c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 44518c2ecf20Sopenharmony_ci 44528c2ecf20Sopenharmony_ci ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 44538c2ecf20Sopenharmony_ci fault); 44548c2ecf20Sopenharmony_ci if (ret) { 44558c2ecf20Sopenharmony_ci adsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 44568c2ecf20Sopenharmony_ci goto exit_unlock; 44578c2ecf20Sopenharmony_ci } 44588c2ecf20Sopenharmony_ci 44598c2ecf20Sopenharmony_ci adsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 44608c2ecf20Sopenharmony_ci *fault & HALO_AHBM_FLAGS_ERR_MASK, 44618c2ecf20Sopenharmony_ci (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 44628c2ecf20Sopenharmony_ci HALO_AHBM_CORE_ERR_ADDR_SHIFT); 44638c2ecf20Sopenharmony_ci 44648c2ecf20Sopenharmony_ci ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 44658c2ecf20Sopenharmony_ci fault); 44668c2ecf20Sopenharmony_ci if (ret) { 44678c2ecf20Sopenharmony_ci adsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 44688c2ecf20Sopenharmony_ci goto exit_unlock; 44698c2ecf20Sopenharmony_ci } 44708c2ecf20Sopenharmony_ci 44718c2ecf20Sopenharmony_ci adsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 44728c2ecf20Sopenharmony_ci 44738c2ecf20Sopenharmony_ci ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 44748c2ecf20Sopenharmony_ci fault, ARRAY_SIZE(fault)); 44758c2ecf20Sopenharmony_ci if (ret) { 44768c2ecf20Sopenharmony_ci adsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 44778c2ecf20Sopenharmony_ci goto exit_unlock; 44788c2ecf20Sopenharmony_ci } 44798c2ecf20Sopenharmony_ci 44808c2ecf20Sopenharmony_ci adsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 44818c2ecf20Sopenharmony_ci adsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 44828c2ecf20Sopenharmony_ci adsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 44838c2ecf20Sopenharmony_ci 44848c2ecf20Sopenharmony_ci ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 44858c2ecf20Sopenharmony_ci if (ret) 44868c2ecf20Sopenharmony_ci adsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 44878c2ecf20Sopenharmony_ci 44888c2ecf20Sopenharmony_ciexit_unlock: 44898c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 44908c2ecf20Sopenharmony_ci 44918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 44928c2ecf20Sopenharmony_ci} 44938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_halo_bus_error); 44948c2ecf20Sopenharmony_ci 44958c2ecf20Sopenharmony_ciirqreturn_t wm_halo_wdt_expire(int irq, void *data) 44968c2ecf20Sopenharmony_ci{ 44978c2ecf20Sopenharmony_ci struct wm_adsp *dsp = data; 44988c2ecf20Sopenharmony_ci 44998c2ecf20Sopenharmony_ci mutex_lock(&dsp->pwr_lock); 45008c2ecf20Sopenharmony_ci 45018c2ecf20Sopenharmony_ci adsp_warn(dsp, "WDT Expiry Fault\n"); 45028c2ecf20Sopenharmony_ci dsp->ops->stop_watchdog(dsp); 45038c2ecf20Sopenharmony_ci wm_adsp_fatal_error(dsp); 45048c2ecf20Sopenharmony_ci 45058c2ecf20Sopenharmony_ci mutex_unlock(&dsp->pwr_lock); 45068c2ecf20Sopenharmony_ci 45078c2ecf20Sopenharmony_ci return IRQ_HANDLED; 45088c2ecf20Sopenharmony_ci} 45098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wm_halo_wdt_expire); 45108c2ecf20Sopenharmony_ci 45118c2ecf20Sopenharmony_cistatic struct wm_adsp_ops wm_adsp1_ops = { 45128c2ecf20Sopenharmony_ci .validate_version = wm_adsp_validate_version, 45138c2ecf20Sopenharmony_ci .parse_sizes = wm_adsp1_parse_sizes, 45148c2ecf20Sopenharmony_ci .region_to_reg = wm_adsp_region_to_reg, 45158c2ecf20Sopenharmony_ci}; 45168c2ecf20Sopenharmony_ci 45178c2ecf20Sopenharmony_cistatic struct wm_adsp_ops wm_adsp2_ops[] = { 45188c2ecf20Sopenharmony_ci { 45198c2ecf20Sopenharmony_ci .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 45208c2ecf20Sopenharmony_ci .parse_sizes = wm_adsp2_parse_sizes, 45218c2ecf20Sopenharmony_ci .validate_version = wm_adsp_validate_version, 45228c2ecf20Sopenharmony_ci .setup_algs = wm_adsp2_setup_algs, 45238c2ecf20Sopenharmony_ci .region_to_reg = wm_adsp_region_to_reg, 45248c2ecf20Sopenharmony_ci 45258c2ecf20Sopenharmony_ci .show_fw_status = wm_adsp2_show_fw_status, 45268c2ecf20Sopenharmony_ci 45278c2ecf20Sopenharmony_ci .enable_memory = wm_adsp2_enable_memory, 45288c2ecf20Sopenharmony_ci .disable_memory = wm_adsp2_disable_memory, 45298c2ecf20Sopenharmony_ci 45308c2ecf20Sopenharmony_ci .enable_core = wm_adsp2_enable_core, 45318c2ecf20Sopenharmony_ci .disable_core = wm_adsp2_disable_core, 45328c2ecf20Sopenharmony_ci 45338c2ecf20Sopenharmony_ci .start_core = wm_adsp2_start_core, 45348c2ecf20Sopenharmony_ci .stop_core = wm_adsp2_stop_core, 45358c2ecf20Sopenharmony_ci 45368c2ecf20Sopenharmony_ci }, 45378c2ecf20Sopenharmony_ci { 45388c2ecf20Sopenharmony_ci .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 45398c2ecf20Sopenharmony_ci .parse_sizes = wm_adsp2_parse_sizes, 45408c2ecf20Sopenharmony_ci .validate_version = wm_adsp_validate_version, 45418c2ecf20Sopenharmony_ci .setup_algs = wm_adsp2_setup_algs, 45428c2ecf20Sopenharmony_ci .region_to_reg = wm_adsp_region_to_reg, 45438c2ecf20Sopenharmony_ci 45448c2ecf20Sopenharmony_ci .show_fw_status = wm_adsp2v2_show_fw_status, 45458c2ecf20Sopenharmony_ci 45468c2ecf20Sopenharmony_ci .enable_memory = wm_adsp2_enable_memory, 45478c2ecf20Sopenharmony_ci .disable_memory = wm_adsp2_disable_memory, 45488c2ecf20Sopenharmony_ci .lock_memory = wm_adsp2_lock, 45498c2ecf20Sopenharmony_ci 45508c2ecf20Sopenharmony_ci .enable_core = wm_adsp2v2_enable_core, 45518c2ecf20Sopenharmony_ci .disable_core = wm_adsp2v2_disable_core, 45528c2ecf20Sopenharmony_ci 45538c2ecf20Sopenharmony_ci .start_core = wm_adsp2_start_core, 45548c2ecf20Sopenharmony_ci .stop_core = wm_adsp2_stop_core, 45558c2ecf20Sopenharmony_ci }, 45568c2ecf20Sopenharmony_ci { 45578c2ecf20Sopenharmony_ci .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), 45588c2ecf20Sopenharmony_ci .parse_sizes = wm_adsp2_parse_sizes, 45598c2ecf20Sopenharmony_ci .validate_version = wm_adsp_validate_version, 45608c2ecf20Sopenharmony_ci .setup_algs = wm_adsp2_setup_algs, 45618c2ecf20Sopenharmony_ci .region_to_reg = wm_adsp_region_to_reg, 45628c2ecf20Sopenharmony_ci 45638c2ecf20Sopenharmony_ci .show_fw_status = wm_adsp2v2_show_fw_status, 45648c2ecf20Sopenharmony_ci .stop_watchdog = wm_adsp_stop_watchdog, 45658c2ecf20Sopenharmony_ci 45668c2ecf20Sopenharmony_ci .enable_memory = wm_adsp2_enable_memory, 45678c2ecf20Sopenharmony_ci .disable_memory = wm_adsp2_disable_memory, 45688c2ecf20Sopenharmony_ci .lock_memory = wm_adsp2_lock, 45698c2ecf20Sopenharmony_ci 45708c2ecf20Sopenharmony_ci .enable_core = wm_adsp2v2_enable_core, 45718c2ecf20Sopenharmony_ci .disable_core = wm_adsp2v2_disable_core, 45728c2ecf20Sopenharmony_ci 45738c2ecf20Sopenharmony_ci .start_core = wm_adsp2_start_core, 45748c2ecf20Sopenharmony_ci .stop_core = wm_adsp2_stop_core, 45758c2ecf20Sopenharmony_ci }, 45768c2ecf20Sopenharmony_ci}; 45778c2ecf20Sopenharmony_ci 45788c2ecf20Sopenharmony_cistatic struct wm_adsp_ops wm_halo_ops = { 45798c2ecf20Sopenharmony_ci .sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr), 45808c2ecf20Sopenharmony_ci .parse_sizes = wm_adsp2_parse_sizes, 45818c2ecf20Sopenharmony_ci .validate_version = wm_halo_validate_version, 45828c2ecf20Sopenharmony_ci .setup_algs = wm_halo_setup_algs, 45838c2ecf20Sopenharmony_ci .region_to_reg = wm_halo_region_to_reg, 45848c2ecf20Sopenharmony_ci 45858c2ecf20Sopenharmony_ci .show_fw_status = wm_halo_show_fw_status, 45868c2ecf20Sopenharmony_ci .stop_watchdog = wm_halo_stop_watchdog, 45878c2ecf20Sopenharmony_ci 45888c2ecf20Sopenharmony_ci .lock_memory = wm_halo_configure_mpu, 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ci .start_core = wm_halo_start_core, 45918c2ecf20Sopenharmony_ci .stop_core = wm_halo_stop_core, 45928c2ecf20Sopenharmony_ci}; 45938c2ecf20Sopenharmony_ci 45948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4595