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, &reg);
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, &reg);
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, &reg);
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					  &region->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, &reg);
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