1f6bc909eSSimon Trimmer // SPDX-License-Identifier: GPL-2.0-only 2f6bc909eSSimon Trimmer /* 3f6bc909eSSimon Trimmer * cs_dsp.c -- Cirrus Logic DSP firmware support 4f6bc909eSSimon Trimmer * 5f6bc909eSSimon Trimmer * Based on sound/soc/codecs/wm_adsp.c 6f6bc909eSSimon Trimmer * 7f6bc909eSSimon Trimmer * Copyright 2012 Wolfson Microelectronics plc 8f6bc909eSSimon Trimmer * Copyright (C) 2015-2021 Cirrus Logic, Inc. and 9f6bc909eSSimon Trimmer * Cirrus Logic International Semiconductor Ltd. 10f6bc909eSSimon Trimmer */ 11f6bc909eSSimon Trimmer 12f6bc909eSSimon Trimmer #include <linux/ctype.h> 13f6bc909eSSimon Trimmer #include <linux/debugfs.h> 14f6bc909eSSimon Trimmer #include <linux/delay.h> 15f6bc909eSSimon Trimmer #include <linux/module.h> 16f6bc909eSSimon Trimmer #include <linux/moduleparam.h> 177a3f924cSSimon Trimmer #include <linux/seq_file.h> 18f6bc909eSSimon Trimmer #include <linux/slab.h> 19f6bc909eSSimon Trimmer #include <linux/vmalloc.h> 20f6bc909eSSimon Trimmer 21f6bc909eSSimon Trimmer #include <linux/firmware/cirrus/cs_dsp.h> 22f6bc909eSSimon Trimmer #include <linux/firmware/cirrus/wmfw.h> 23f6bc909eSSimon Trimmer 24f6bc909eSSimon Trimmer #define cs_dsp_err(_dsp, fmt, ...) \ 25f6bc909eSSimon Trimmer dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 26f6bc909eSSimon Trimmer #define cs_dsp_warn(_dsp, fmt, ...) \ 27f6bc909eSSimon Trimmer dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 28f6bc909eSSimon Trimmer #define cs_dsp_info(_dsp, fmt, ...) \ 29f6bc909eSSimon Trimmer dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 30f6bc909eSSimon Trimmer #define cs_dsp_dbg(_dsp, fmt, ...) \ 31f6bc909eSSimon Trimmer dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 32f6bc909eSSimon Trimmer 33f6bc909eSSimon Trimmer #define ADSP1_CONTROL_1 0x00 34f6bc909eSSimon Trimmer #define ADSP1_CONTROL_2 0x02 35f6bc909eSSimon Trimmer #define ADSP1_CONTROL_3 0x03 36f6bc909eSSimon Trimmer #define ADSP1_CONTROL_4 0x04 37f6bc909eSSimon Trimmer #define ADSP1_CONTROL_5 0x06 38f6bc909eSSimon Trimmer #define ADSP1_CONTROL_6 0x07 39f6bc909eSSimon Trimmer #define ADSP1_CONTROL_7 0x08 40f6bc909eSSimon Trimmer #define ADSP1_CONTROL_8 0x09 41f6bc909eSSimon Trimmer #define ADSP1_CONTROL_9 0x0A 42f6bc909eSSimon Trimmer #define ADSP1_CONTROL_10 0x0B 43f6bc909eSSimon Trimmer #define ADSP1_CONTROL_11 0x0C 44f6bc909eSSimon Trimmer #define ADSP1_CONTROL_12 0x0D 45f6bc909eSSimon Trimmer #define ADSP1_CONTROL_13 0x0F 46f6bc909eSSimon Trimmer #define ADSP1_CONTROL_14 0x10 47f6bc909eSSimon Trimmer #define ADSP1_CONTROL_15 0x11 48f6bc909eSSimon Trimmer #define ADSP1_CONTROL_16 0x12 49f6bc909eSSimon Trimmer #define ADSP1_CONTROL_17 0x13 50f6bc909eSSimon Trimmer #define ADSP1_CONTROL_18 0x14 51f6bc909eSSimon Trimmer #define ADSP1_CONTROL_19 0x16 52f6bc909eSSimon Trimmer #define ADSP1_CONTROL_20 0x17 53f6bc909eSSimon Trimmer #define ADSP1_CONTROL_21 0x18 54f6bc909eSSimon Trimmer #define ADSP1_CONTROL_22 0x1A 55f6bc909eSSimon Trimmer #define ADSP1_CONTROL_23 0x1B 56f6bc909eSSimon Trimmer #define ADSP1_CONTROL_24 0x1C 57f6bc909eSSimon Trimmer #define ADSP1_CONTROL_25 0x1E 58f6bc909eSSimon Trimmer #define ADSP1_CONTROL_26 0x20 59f6bc909eSSimon Trimmer #define ADSP1_CONTROL_27 0x21 60f6bc909eSSimon Trimmer #define ADSP1_CONTROL_28 0x22 61f6bc909eSSimon Trimmer #define ADSP1_CONTROL_29 0x23 62f6bc909eSSimon Trimmer #define ADSP1_CONTROL_30 0x24 63f6bc909eSSimon Trimmer #define ADSP1_CONTROL_31 0x26 64f6bc909eSSimon Trimmer 65f6bc909eSSimon Trimmer /* 66f6bc909eSSimon Trimmer * ADSP1 Control 19 67f6bc909eSSimon Trimmer */ 68f6bc909eSSimon Trimmer #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 69f6bc909eSSimon Trimmer #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 70f6bc909eSSimon Trimmer #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 71f6bc909eSSimon Trimmer 72f6bc909eSSimon Trimmer /* 73f6bc909eSSimon Trimmer * ADSP1 Control 30 74f6bc909eSSimon Trimmer */ 75f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 76f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 77f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 78f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 79f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 80f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 81f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 82f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 83f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 84f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 85f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 86f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 87f6bc909eSSimon Trimmer #define ADSP1_START 0x0001 /* DSP1_START */ 88f6bc909eSSimon Trimmer #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 89f6bc909eSSimon Trimmer #define ADSP1_START_SHIFT 0 /* DSP1_START */ 90f6bc909eSSimon Trimmer #define ADSP1_START_WIDTH 1 /* DSP1_START */ 91f6bc909eSSimon Trimmer 92f6bc909eSSimon Trimmer /* 93f6bc909eSSimon Trimmer * ADSP1 Control 31 94f6bc909eSSimon Trimmer */ 95f6bc909eSSimon Trimmer #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 96f6bc909eSSimon Trimmer #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 97f6bc909eSSimon Trimmer #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 98f6bc909eSSimon Trimmer 99f6bc909eSSimon Trimmer #define ADSP2_CONTROL 0x0 100f6bc909eSSimon Trimmer #define ADSP2_CLOCKING 0x1 101f6bc909eSSimon Trimmer #define ADSP2V2_CLOCKING 0x2 102f6bc909eSSimon Trimmer #define ADSP2_STATUS1 0x4 103f6bc909eSSimon Trimmer #define ADSP2_WDMA_CONFIG_1 0x30 104f6bc909eSSimon Trimmer #define ADSP2_WDMA_CONFIG_2 0x31 105f6bc909eSSimon Trimmer #define ADSP2V2_WDMA_CONFIG_2 0x32 106f6bc909eSSimon Trimmer #define ADSP2_RDMA_CONFIG_1 0x34 107f6bc909eSSimon Trimmer 108f6bc909eSSimon Trimmer #define ADSP2_SCRATCH0 0x40 109f6bc909eSSimon Trimmer #define ADSP2_SCRATCH1 0x41 110f6bc909eSSimon Trimmer #define ADSP2_SCRATCH2 0x42 111f6bc909eSSimon Trimmer #define ADSP2_SCRATCH3 0x43 112f6bc909eSSimon Trimmer 113f6bc909eSSimon Trimmer #define ADSP2V2_SCRATCH0_1 0x40 114f6bc909eSSimon Trimmer #define ADSP2V2_SCRATCH2_3 0x42 115f6bc909eSSimon Trimmer 116f6bc909eSSimon Trimmer /* 117f6bc909eSSimon Trimmer * ADSP2 Control 118f6bc909eSSimon Trimmer */ 119f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 120f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 121f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 122f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 123f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 124f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 125f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 126f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 127f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 128f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 129f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 130f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 131f6bc909eSSimon Trimmer #define ADSP2_START 0x0001 /* DSP1_START */ 132f6bc909eSSimon Trimmer #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 133f6bc909eSSimon Trimmer #define ADSP2_START_SHIFT 0 /* DSP1_START */ 134f6bc909eSSimon Trimmer #define ADSP2_START_WIDTH 1 /* DSP1_START */ 135f6bc909eSSimon Trimmer 136f6bc909eSSimon Trimmer /* 137f6bc909eSSimon Trimmer * ADSP2 clocking 138f6bc909eSSimon Trimmer */ 139f6bc909eSSimon Trimmer #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 140f6bc909eSSimon Trimmer #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 141f6bc909eSSimon Trimmer #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 142f6bc909eSSimon Trimmer 143f6bc909eSSimon Trimmer /* 144f6bc909eSSimon Trimmer * ADSP2V2 clocking 145f6bc909eSSimon Trimmer */ 146f6bc909eSSimon Trimmer #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 147f6bc909eSSimon Trimmer #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 148f6bc909eSSimon Trimmer #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 149f6bc909eSSimon Trimmer 150f6bc909eSSimon Trimmer #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 151f6bc909eSSimon Trimmer #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 152f6bc909eSSimon Trimmer #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 153f6bc909eSSimon Trimmer 154f6bc909eSSimon Trimmer /* 155f6bc909eSSimon Trimmer * ADSP2 Status 1 156f6bc909eSSimon Trimmer */ 157f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY 0x0001 158f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY_MASK 0x0001 159f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY_SHIFT 0 160f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY_WIDTH 1 161f6bc909eSSimon Trimmer 162f6bc909eSSimon Trimmer /* 163f6bc909eSSimon Trimmer * ADSP2 Lock support 164f6bc909eSSimon Trimmer */ 165f6bc909eSSimon Trimmer #define ADSP2_LOCK_CODE_0 0x5555 166f6bc909eSSimon Trimmer #define ADSP2_LOCK_CODE_1 0xAAAA 167f6bc909eSSimon Trimmer 168f6bc909eSSimon Trimmer #define ADSP2_WATCHDOG 0x0A 169f6bc909eSSimon Trimmer #define ADSP2_BUS_ERR_ADDR 0x52 170f6bc909eSSimon Trimmer #define ADSP2_REGION_LOCK_STATUS 0x64 171f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 172f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 173f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 174f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 175f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 176f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_CTRL 0x7A 177f6bc909eSSimon Trimmer #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 178f6bc909eSSimon Trimmer 179f6bc909eSSimon Trimmer #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 180f6bc909eSSimon Trimmer #define ADSP2_ADDR_ERR_MASK 0x4000 181f6bc909eSSimon Trimmer #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 182f6bc909eSSimon Trimmer #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 183f6bc909eSSimon Trimmer #define ADSP2_CTRL_ERR_EINT 0x0001 184f6bc909eSSimon Trimmer 185f6bc909eSSimon Trimmer #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 186f6bc909eSSimon Trimmer #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 187f6bc909eSSimon Trimmer #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 188f6bc909eSSimon Trimmer #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 189f6bc909eSSimon Trimmer #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 190f6bc909eSSimon Trimmer 191f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_SHIFT 16 192f6bc909eSSimon Trimmer 193f6bc909eSSimon Trimmer /* 194f6bc909eSSimon Trimmer * Event control messages 195f6bc909eSSimon Trimmer */ 196f6bc909eSSimon Trimmer #define CS_DSP_FW_EVENT_SHUTDOWN 0x000001 197f6bc909eSSimon Trimmer 198f6bc909eSSimon Trimmer /* 199f6bc909eSSimon Trimmer * HALO system info 200f6bc909eSSimon Trimmer */ 201f6bc909eSSimon Trimmer #define HALO_AHBM_WINDOW_DEBUG_0 0x02040 202f6bc909eSSimon Trimmer #define HALO_AHBM_WINDOW_DEBUG_1 0x02044 203f6bc909eSSimon Trimmer 204f6bc909eSSimon Trimmer /* 205f6bc909eSSimon Trimmer * HALO core 206f6bc909eSSimon Trimmer */ 207f6bc909eSSimon Trimmer #define HALO_SCRATCH1 0x005c0 208f6bc909eSSimon Trimmer #define HALO_SCRATCH2 0x005c8 209f6bc909eSSimon Trimmer #define HALO_SCRATCH3 0x005d0 210f6bc909eSSimon Trimmer #define HALO_SCRATCH4 0x005d8 211f6bc909eSSimon Trimmer #define HALO_CCM_CORE_CONTROL 0x41000 212f6bc909eSSimon Trimmer #define HALO_CORE_SOFT_RESET 0x00010 213f6bc909eSSimon Trimmer #define HALO_WDT_CONTROL 0x47000 214f6bc909eSSimon Trimmer 215f6bc909eSSimon Trimmer /* 216f6bc909eSSimon Trimmer * HALO MPU banks 217f6bc909eSSimon Trimmer */ 218f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_0 0x43000 219f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_0 0x43004 220f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_0 0x43008 221f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_0 0x4300C 222f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_0 0x43014 223f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_1 0x43018 224f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_1 0x4301C 225f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_1 0x43020 226f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_1 0x43024 227f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_1 0x4302C 228f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_2 0x43030 229f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_2 0x43034 230f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_2 0x43038 231f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_2 0x4303C 232f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_2 0x43044 233f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_3 0x43048 234f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_3 0x4304C 235f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_3 0x43050 236f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_3 0x43054 237f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_3 0x4305C 238f6bc909eSSimon Trimmer #define HALO_MPU_XM_VIO_ADDR 0x43100 239f6bc909eSSimon Trimmer #define HALO_MPU_XM_VIO_STATUS 0x43104 240f6bc909eSSimon Trimmer #define HALO_MPU_YM_VIO_ADDR 0x43108 241f6bc909eSSimon Trimmer #define HALO_MPU_YM_VIO_STATUS 0x4310C 242f6bc909eSSimon Trimmer #define HALO_MPU_PM_VIO_ADDR 0x43110 243f6bc909eSSimon Trimmer #define HALO_MPU_PM_VIO_STATUS 0x43114 244f6bc909eSSimon Trimmer #define HALO_MPU_LOCK_CONFIG 0x43140 245f6bc909eSSimon Trimmer 246f6bc909eSSimon Trimmer /* 247f6bc909eSSimon Trimmer * HALO_AHBM_WINDOW_DEBUG_1 248f6bc909eSSimon Trimmer */ 249f6bc909eSSimon Trimmer #define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 250f6bc909eSSimon Trimmer #define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 251f6bc909eSSimon Trimmer #define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff 252f6bc909eSSimon Trimmer 253f6bc909eSSimon Trimmer /* 254f6bc909eSSimon Trimmer * HALO_CCM_CORE_CONTROL 255f6bc909eSSimon Trimmer */ 256f6bc909eSSimon Trimmer #define HALO_CORE_RESET 0x00000200 257f6bc909eSSimon Trimmer #define HALO_CORE_EN 0x00000001 258f6bc909eSSimon Trimmer 259f6bc909eSSimon Trimmer /* 260f6bc909eSSimon Trimmer * HALO_CORE_SOFT_RESET 261f6bc909eSSimon Trimmer */ 262f6bc909eSSimon Trimmer #define HALO_CORE_SOFT_RESET_MASK 0x00000001 263f6bc909eSSimon Trimmer 264f6bc909eSSimon Trimmer /* 265f6bc909eSSimon Trimmer * HALO_WDT_CONTROL 266f6bc909eSSimon Trimmer */ 267f6bc909eSSimon Trimmer #define HALO_WDT_EN_MASK 0x00000001 268f6bc909eSSimon Trimmer 269f6bc909eSSimon Trimmer /* 270f6bc909eSSimon Trimmer * HALO_MPU_?M_VIO_STATUS 271f6bc909eSSimon Trimmer */ 272f6bc909eSSimon Trimmer #define HALO_MPU_VIO_STS_MASK 0x007e0000 273f6bc909eSSimon Trimmer #define HALO_MPU_VIO_STS_SHIFT 17 274f6bc909eSSimon Trimmer #define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 275f6bc909eSSimon Trimmer #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff 276f6bc909eSSimon Trimmer #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 277f6bc909eSSimon Trimmer 278f6bc909eSSimon Trimmer struct cs_dsp_ops { 279f6bc909eSSimon Trimmer bool (*validate_version)(struct cs_dsp *dsp, unsigned int version); 280f6bc909eSSimon Trimmer unsigned int (*parse_sizes)(struct cs_dsp *dsp, 281f6bc909eSSimon Trimmer const char * const file, 282f6bc909eSSimon Trimmer unsigned int pos, 283f6bc909eSSimon Trimmer const struct firmware *firmware); 284f6bc909eSSimon Trimmer int (*setup_algs)(struct cs_dsp *dsp); 285f6bc909eSSimon Trimmer unsigned int (*region_to_reg)(struct cs_dsp_region const *mem, 286f6bc909eSSimon Trimmer unsigned int offset); 287f6bc909eSSimon Trimmer 288f6bc909eSSimon Trimmer void (*show_fw_status)(struct cs_dsp *dsp); 289f6bc909eSSimon Trimmer void (*stop_watchdog)(struct cs_dsp *dsp); 290f6bc909eSSimon Trimmer 291f6bc909eSSimon Trimmer int (*enable_memory)(struct cs_dsp *dsp); 292f6bc909eSSimon Trimmer void (*disable_memory)(struct cs_dsp *dsp); 293f6bc909eSSimon Trimmer int (*lock_memory)(struct cs_dsp *dsp, unsigned int lock_regions); 294f6bc909eSSimon Trimmer 295f6bc909eSSimon Trimmer int (*enable_core)(struct cs_dsp *dsp); 296f6bc909eSSimon Trimmer void (*disable_core)(struct cs_dsp *dsp); 297f6bc909eSSimon Trimmer 298f6bc909eSSimon Trimmer int (*start_core)(struct cs_dsp *dsp); 299f6bc909eSSimon Trimmer void (*stop_core)(struct cs_dsp *dsp); 300f6bc909eSSimon Trimmer }; 301f6bc909eSSimon Trimmer 302f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp1_ops; 303f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp2_ops[]; 304f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ops; 3057062e1c7SSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ao_ops; 306f6bc909eSSimon Trimmer 307f6bc909eSSimon Trimmer struct cs_dsp_buf { 308f6bc909eSSimon Trimmer struct list_head list; 309f6bc909eSSimon Trimmer void *buf; 310f6bc909eSSimon Trimmer }; 311f6bc909eSSimon Trimmer 312f6bc909eSSimon Trimmer static struct cs_dsp_buf *cs_dsp_buf_alloc(const void *src, size_t len, 313f6bc909eSSimon Trimmer struct list_head *list) 314f6bc909eSSimon Trimmer { 315f6bc909eSSimon Trimmer struct cs_dsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 316f6bc909eSSimon Trimmer 317f6bc909eSSimon Trimmer if (buf == NULL) 318f6bc909eSSimon Trimmer return NULL; 319f6bc909eSSimon Trimmer 320f6bc909eSSimon Trimmer buf->buf = vmalloc(len); 321f6bc909eSSimon Trimmer if (!buf->buf) { 322f6bc909eSSimon Trimmer kfree(buf); 323f6bc909eSSimon Trimmer return NULL; 324f6bc909eSSimon Trimmer } 325f6bc909eSSimon Trimmer memcpy(buf->buf, src, len); 326f6bc909eSSimon Trimmer 327f6bc909eSSimon Trimmer if (list) 328f6bc909eSSimon Trimmer list_add_tail(&buf->list, list); 329f6bc909eSSimon Trimmer 330f6bc909eSSimon Trimmer return buf; 331f6bc909eSSimon Trimmer } 332f6bc909eSSimon Trimmer 333f6bc909eSSimon Trimmer static void cs_dsp_buf_free(struct list_head *list) 334f6bc909eSSimon Trimmer { 335f6bc909eSSimon Trimmer while (!list_empty(list)) { 336f6bc909eSSimon Trimmer struct cs_dsp_buf *buf = list_first_entry(list, 337f6bc909eSSimon Trimmer struct cs_dsp_buf, 338f6bc909eSSimon Trimmer list); 339f6bc909eSSimon Trimmer list_del(&buf->list); 340f6bc909eSSimon Trimmer vfree(buf->buf); 341f6bc909eSSimon Trimmer kfree(buf); 342f6bc909eSSimon Trimmer } 343f6bc909eSSimon Trimmer } 344f6bc909eSSimon Trimmer 345f6bc909eSSimon Trimmer /** 346f6bc909eSSimon Trimmer * cs_dsp_mem_region_name() - Return a name string for a memory type 347f6bc909eSSimon Trimmer * @type: the memory type to match 348f6bc909eSSimon Trimmer * 349f6bc909eSSimon Trimmer * Return: A const string identifying the memory region. 350f6bc909eSSimon Trimmer */ 351f6bc909eSSimon Trimmer const char *cs_dsp_mem_region_name(unsigned int type) 352f6bc909eSSimon Trimmer { 353f6bc909eSSimon Trimmer switch (type) { 354f6bc909eSSimon Trimmer case WMFW_ADSP1_PM: 355f6bc909eSSimon Trimmer return "PM"; 356f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 357f6bc909eSSimon Trimmer return "PM_PACKED"; 358f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 359f6bc909eSSimon Trimmer return "DM"; 360f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 361f6bc909eSSimon Trimmer return "XM"; 362f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 363f6bc909eSSimon Trimmer return "XM_PACKED"; 364f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 365f6bc909eSSimon Trimmer return "YM"; 366f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 367f6bc909eSSimon Trimmer return "YM_PACKED"; 368f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 369f6bc909eSSimon Trimmer return "ZM"; 370f6bc909eSSimon Trimmer default: 371f6bc909eSSimon Trimmer return NULL; 372f6bc909eSSimon Trimmer } 373f6bc909eSSimon Trimmer } 374e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_mem_region_name, FW_CS_DSP); 375f6bc909eSSimon Trimmer 376f6bc909eSSimon Trimmer #ifdef CONFIG_DEBUG_FS 377f6bc909eSSimon Trimmer static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s) 378f6bc909eSSimon Trimmer { 379f6bc909eSSimon Trimmer char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 380f6bc909eSSimon Trimmer 381f6bc909eSSimon Trimmer kfree(dsp->wmfw_file_name); 382f6bc909eSSimon Trimmer dsp->wmfw_file_name = tmp; 383f6bc909eSSimon Trimmer } 384f6bc909eSSimon Trimmer 385f6bc909eSSimon Trimmer static void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, const char *s) 386f6bc909eSSimon Trimmer { 387f6bc909eSSimon Trimmer char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 388f6bc909eSSimon Trimmer 389f6bc909eSSimon Trimmer kfree(dsp->bin_file_name); 390f6bc909eSSimon Trimmer dsp->bin_file_name = tmp; 391f6bc909eSSimon Trimmer } 392f6bc909eSSimon Trimmer 393f6bc909eSSimon Trimmer static void cs_dsp_debugfs_clear(struct cs_dsp *dsp) 394f6bc909eSSimon Trimmer { 395f6bc909eSSimon Trimmer kfree(dsp->wmfw_file_name); 396f6bc909eSSimon Trimmer kfree(dsp->bin_file_name); 397f6bc909eSSimon Trimmer dsp->wmfw_file_name = NULL; 398f6bc909eSSimon Trimmer dsp->bin_file_name = NULL; 399f6bc909eSSimon Trimmer } 400f6bc909eSSimon Trimmer 401f6bc909eSSimon Trimmer static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file, 402f6bc909eSSimon Trimmer char __user *user_buf, 403f6bc909eSSimon Trimmer size_t count, loff_t *ppos) 404f6bc909eSSimon Trimmer { 405f6bc909eSSimon Trimmer struct cs_dsp *dsp = file->private_data; 406f6bc909eSSimon Trimmer ssize_t ret; 407f6bc909eSSimon Trimmer 408f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 409f6bc909eSSimon Trimmer 410f6bc909eSSimon Trimmer if (!dsp->wmfw_file_name || !dsp->booted) 411f6bc909eSSimon Trimmer ret = 0; 412f6bc909eSSimon Trimmer else 413f6bc909eSSimon Trimmer ret = simple_read_from_buffer(user_buf, count, ppos, 414f6bc909eSSimon Trimmer dsp->wmfw_file_name, 415f6bc909eSSimon Trimmer strlen(dsp->wmfw_file_name)); 416f6bc909eSSimon Trimmer 417f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 418f6bc909eSSimon Trimmer return ret; 419f6bc909eSSimon Trimmer } 420f6bc909eSSimon Trimmer 421f6bc909eSSimon Trimmer static ssize_t cs_dsp_debugfs_bin_read(struct file *file, 422f6bc909eSSimon Trimmer char __user *user_buf, 423f6bc909eSSimon Trimmer size_t count, loff_t *ppos) 424f6bc909eSSimon Trimmer { 425f6bc909eSSimon Trimmer struct cs_dsp *dsp = file->private_data; 426f6bc909eSSimon Trimmer ssize_t ret; 427f6bc909eSSimon Trimmer 428f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 429f6bc909eSSimon Trimmer 430f6bc909eSSimon Trimmer if (!dsp->bin_file_name || !dsp->booted) 431f6bc909eSSimon Trimmer ret = 0; 432f6bc909eSSimon Trimmer else 433f6bc909eSSimon Trimmer ret = simple_read_from_buffer(user_buf, count, ppos, 434f6bc909eSSimon Trimmer dsp->bin_file_name, 435f6bc909eSSimon Trimmer strlen(dsp->bin_file_name)); 436f6bc909eSSimon Trimmer 437f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 438f6bc909eSSimon Trimmer return ret; 439f6bc909eSSimon Trimmer } 440f6bc909eSSimon Trimmer 441f6bc909eSSimon Trimmer static const struct { 442f6bc909eSSimon Trimmer const char *name; 443f6bc909eSSimon Trimmer const struct file_operations fops; 444f6bc909eSSimon Trimmer } cs_dsp_debugfs_fops[] = { 445f6bc909eSSimon Trimmer { 446f6bc909eSSimon Trimmer .name = "wmfw_file_name", 447f6bc909eSSimon Trimmer .fops = { 448f6bc909eSSimon Trimmer .open = simple_open, 449f6bc909eSSimon Trimmer .read = cs_dsp_debugfs_wmfw_read, 450f6bc909eSSimon Trimmer }, 451f6bc909eSSimon Trimmer }, 452f6bc909eSSimon Trimmer { 453f6bc909eSSimon Trimmer .name = "bin_file_name", 454f6bc909eSSimon Trimmer .fops = { 455f6bc909eSSimon Trimmer .open = simple_open, 456f6bc909eSSimon Trimmer .read = cs_dsp_debugfs_bin_read, 457f6bc909eSSimon Trimmer }, 458f6bc909eSSimon Trimmer }, 459f6bc909eSSimon Trimmer }; 460f6bc909eSSimon Trimmer 4617a3f924cSSimon Trimmer static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg, 4627a3f924cSSimon Trimmer unsigned int off); 4637a3f924cSSimon Trimmer 4647a3f924cSSimon Trimmer static int cs_dsp_debugfs_read_controls_show(struct seq_file *s, void *ignored) 4657a3f924cSSimon Trimmer { 4667a3f924cSSimon Trimmer struct cs_dsp *dsp = s->private; 4677a3f924cSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 4687a3f924cSSimon Trimmer unsigned int reg; 4697a3f924cSSimon Trimmer 4707a3f924cSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 4717a3f924cSSimon Trimmer cs_dsp_coeff_base_reg(ctl, ®, 0); 4727a3f924cSSimon Trimmer seq_printf(s, "%22.*s: %#8zx %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n", 4737a3f924cSSimon Trimmer ctl->subname_len, ctl->subname, ctl->len, 4747a3f924cSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), 4757a3f924cSSimon Trimmer ctl->offset, reg, ctl->fw_name, ctl->alg_region.alg, ctl->type, 4767a3f924cSSimon Trimmer ctl->flags & WMFW_CTL_FLAG_VOLATILE ? 'V' : '-', 4777a3f924cSSimon Trimmer ctl->flags & WMFW_CTL_FLAG_SYS ? 'S' : '-', 4787a3f924cSSimon Trimmer ctl->flags & WMFW_CTL_FLAG_READABLE ? 'R' : '-', 4797a3f924cSSimon Trimmer ctl->flags & WMFW_CTL_FLAG_WRITEABLE ? 'W' : '-', 4807a3f924cSSimon Trimmer ctl->enabled ? "enabled" : "disabled", 4817a3f924cSSimon Trimmer ctl->set ? "dirty" : "clean"); 4827a3f924cSSimon Trimmer } 4837a3f924cSSimon Trimmer 4847a3f924cSSimon Trimmer return 0; 4857a3f924cSSimon Trimmer } 4867a3f924cSSimon Trimmer DEFINE_SHOW_ATTRIBUTE(cs_dsp_debugfs_read_controls); 4877a3f924cSSimon Trimmer 488f6bc909eSSimon Trimmer /** 489f6bc909eSSimon Trimmer * cs_dsp_init_debugfs() - Create and populate DSP representation in debugfs 490f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 491f6bc909eSSimon Trimmer * @debugfs_root: pointer to debugfs directory in which to create this DSP 492f6bc909eSSimon Trimmer * representation 493f6bc909eSSimon Trimmer */ 494f6bc909eSSimon Trimmer void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) 495f6bc909eSSimon Trimmer { 496f6bc909eSSimon Trimmer struct dentry *root = NULL; 497f6bc909eSSimon Trimmer int i; 498f6bc909eSSimon Trimmer 499f6bc909eSSimon Trimmer root = debugfs_create_dir(dsp->name, debugfs_root); 500f6bc909eSSimon Trimmer 501f6bc909eSSimon Trimmer debugfs_create_bool("booted", 0444, root, &dsp->booted); 502f6bc909eSSimon Trimmer debugfs_create_bool("running", 0444, root, &dsp->running); 503f6bc909eSSimon Trimmer debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); 504f6bc909eSSimon Trimmer debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); 505f6bc909eSSimon Trimmer 506f6bc909eSSimon Trimmer for (i = 0; i < ARRAY_SIZE(cs_dsp_debugfs_fops); ++i) 507f6bc909eSSimon Trimmer debugfs_create_file(cs_dsp_debugfs_fops[i].name, 0444, root, 508f6bc909eSSimon Trimmer dsp, &cs_dsp_debugfs_fops[i].fops); 509f6bc909eSSimon Trimmer 5107a3f924cSSimon Trimmer debugfs_create_file("controls", 0444, root, dsp, 5117a3f924cSSimon Trimmer &cs_dsp_debugfs_read_controls_fops); 5127a3f924cSSimon Trimmer 513f6bc909eSSimon Trimmer dsp->debugfs_root = root; 514f6bc909eSSimon Trimmer } 515e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP); 516f6bc909eSSimon Trimmer 517f6bc909eSSimon Trimmer /** 518f6bc909eSSimon Trimmer * cs_dsp_cleanup_debugfs() - Removes DSP representation from debugfs 519f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 520f6bc909eSSimon Trimmer */ 521f6bc909eSSimon Trimmer void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) 522f6bc909eSSimon Trimmer { 523f6bc909eSSimon Trimmer cs_dsp_debugfs_clear(dsp); 524f6bc909eSSimon Trimmer debugfs_remove_recursive(dsp->debugfs_root); 52566626b15SRichard Fitzgerald dsp->debugfs_root = ERR_PTR(-ENODEV); 526f6bc909eSSimon Trimmer } 527e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP); 528f6bc909eSSimon Trimmer #else 529f6bc909eSSimon Trimmer void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) 530f6bc909eSSimon Trimmer { 531f6bc909eSSimon Trimmer } 532e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP); 533f6bc909eSSimon Trimmer 534f6bc909eSSimon Trimmer void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) 535f6bc909eSSimon Trimmer { 536f6bc909eSSimon Trimmer } 537e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP); 538f6bc909eSSimon Trimmer 539f6bc909eSSimon Trimmer static inline void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, 540f6bc909eSSimon Trimmer const char *s) 541f6bc909eSSimon Trimmer { 542f6bc909eSSimon Trimmer } 543f6bc909eSSimon Trimmer 544f6bc909eSSimon Trimmer static inline void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, 545f6bc909eSSimon Trimmer const char *s) 546f6bc909eSSimon Trimmer { 547f6bc909eSSimon Trimmer } 548f6bc909eSSimon Trimmer 549f6bc909eSSimon Trimmer static inline void cs_dsp_debugfs_clear(struct cs_dsp *dsp) 550f6bc909eSSimon Trimmer { 551f6bc909eSSimon Trimmer } 552f6bc909eSSimon Trimmer #endif 553f6bc909eSSimon Trimmer 554f6bc909eSSimon Trimmer static const struct cs_dsp_region *cs_dsp_find_region(struct cs_dsp *dsp, 555f6bc909eSSimon Trimmer int type) 556f6bc909eSSimon Trimmer { 557f6bc909eSSimon Trimmer int i; 558f6bc909eSSimon Trimmer 559f6bc909eSSimon Trimmer for (i = 0; i < dsp->num_mems; i++) 560f6bc909eSSimon Trimmer if (dsp->mem[i].type == type) 561f6bc909eSSimon Trimmer return &dsp->mem[i]; 562f6bc909eSSimon Trimmer 563f6bc909eSSimon Trimmer return NULL; 564f6bc909eSSimon Trimmer } 565f6bc909eSSimon Trimmer 566f6bc909eSSimon Trimmer static unsigned int cs_dsp_region_to_reg(struct cs_dsp_region const *mem, 567f6bc909eSSimon Trimmer unsigned int offset) 568f6bc909eSSimon Trimmer { 569f6bc909eSSimon Trimmer switch (mem->type) { 570f6bc909eSSimon Trimmer case WMFW_ADSP1_PM: 571f6bc909eSSimon Trimmer return mem->base + (offset * 3); 572f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 573f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 574f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 575f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 576f6bc909eSSimon Trimmer return mem->base + (offset * 2); 577f6bc909eSSimon Trimmer default: 578f6bc909eSSimon Trimmer WARN(1, "Unknown memory region type"); 579f6bc909eSSimon Trimmer return offset; 580f6bc909eSSimon Trimmer } 581f6bc909eSSimon Trimmer } 582f6bc909eSSimon Trimmer 583f6bc909eSSimon Trimmer static unsigned int cs_dsp_halo_region_to_reg(struct cs_dsp_region const *mem, 584f6bc909eSSimon Trimmer unsigned int offset) 585f6bc909eSSimon Trimmer { 586f6bc909eSSimon Trimmer switch (mem->type) { 587f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 588f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 589f6bc909eSSimon Trimmer return mem->base + (offset * 4); 590f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 591f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 592f6bc909eSSimon Trimmer return (mem->base + (offset * 3)) & ~0x3; 593f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 594f6bc909eSSimon Trimmer return mem->base + (offset * 5); 595f6bc909eSSimon Trimmer default: 596f6bc909eSSimon Trimmer WARN(1, "Unknown memory region type"); 597f6bc909eSSimon Trimmer return offset; 598f6bc909eSSimon Trimmer } 599f6bc909eSSimon Trimmer } 600f6bc909eSSimon Trimmer 601f6bc909eSSimon Trimmer static void cs_dsp_read_fw_status(struct cs_dsp *dsp, 602f6bc909eSSimon Trimmer int noffs, unsigned int *offs) 603f6bc909eSSimon Trimmer { 604f6bc909eSSimon Trimmer unsigned int i; 605f6bc909eSSimon Trimmer int ret; 606f6bc909eSSimon Trimmer 607f6bc909eSSimon Trimmer for (i = 0; i < noffs; ++i) { 608f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 609f6bc909eSSimon Trimmer if (ret) { 610f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 611f6bc909eSSimon Trimmer return; 612f6bc909eSSimon Trimmer } 613f6bc909eSSimon Trimmer } 614f6bc909eSSimon Trimmer } 615f6bc909eSSimon Trimmer 616f6bc909eSSimon Trimmer static void cs_dsp_adsp2_show_fw_status(struct cs_dsp *dsp) 617f6bc909eSSimon Trimmer { 618f6bc909eSSimon Trimmer unsigned int offs[] = { 619f6bc909eSSimon Trimmer ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 620f6bc909eSSimon Trimmer }; 621f6bc909eSSimon Trimmer 622f6bc909eSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 623f6bc909eSSimon Trimmer 624f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 625f6bc909eSSimon Trimmer offs[0], offs[1], offs[2], offs[3]); 626f6bc909eSSimon Trimmer } 627f6bc909eSSimon Trimmer 628f6bc909eSSimon Trimmer static void cs_dsp_adsp2v2_show_fw_status(struct cs_dsp *dsp) 629f6bc909eSSimon Trimmer { 630f6bc909eSSimon Trimmer unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 631f6bc909eSSimon Trimmer 632f6bc909eSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 633f6bc909eSSimon Trimmer 634f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 635f6bc909eSSimon Trimmer offs[0] & 0xFFFF, offs[0] >> 16, 636f6bc909eSSimon Trimmer offs[1] & 0xFFFF, offs[1] >> 16); 637f6bc909eSSimon Trimmer } 638f6bc909eSSimon Trimmer 639f6bc909eSSimon Trimmer static void cs_dsp_halo_show_fw_status(struct cs_dsp *dsp) 640f6bc909eSSimon Trimmer { 641f6bc909eSSimon Trimmer unsigned int offs[] = { 642f6bc909eSSimon Trimmer HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, 643f6bc909eSSimon Trimmer }; 644f6bc909eSSimon Trimmer 645f6bc909eSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 646f6bc909eSSimon Trimmer 647f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 648f6bc909eSSimon Trimmer offs[0], offs[1], offs[2], offs[3]); 649f6bc909eSSimon Trimmer } 650f6bc909eSSimon Trimmer 651f444da38SCharles Keepax static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg, 652f444da38SCharles Keepax unsigned int off) 653f6bc909eSSimon Trimmer { 654f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region = &ctl->alg_region; 655f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 656f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 657f6bc909eSSimon Trimmer 658f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, alg_region->type); 659f6bc909eSSimon Trimmer if (!mem) { 660f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", 661f6bc909eSSimon Trimmer alg_region->type); 662f6bc909eSSimon Trimmer return -EINVAL; 663f6bc909eSSimon Trimmer } 664f6bc909eSSimon Trimmer 665f444da38SCharles Keepax *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset + off); 666f6bc909eSSimon Trimmer 667f6bc909eSSimon Trimmer return 0; 668f6bc909eSSimon Trimmer } 669f6bc909eSSimon Trimmer 670f6bc909eSSimon Trimmer /** 671f6bc909eSSimon Trimmer * cs_dsp_coeff_write_acked_control() - Sends event_id to the acked control 672f6bc909eSSimon Trimmer * @ctl: pointer to acked coefficient control 673f6bc909eSSimon Trimmer * @event_id: the value to write to the given acked control 674f6bc909eSSimon Trimmer * 675f6bc909eSSimon Trimmer * Once the value has been written to the control the function shall block 676f6bc909eSSimon Trimmer * until the running firmware acknowledges the write or timeout is exceeded. 677f6bc909eSSimon Trimmer * 678f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 679f6bc909eSSimon Trimmer * 680f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 681f6bc909eSSimon Trimmer */ 682f6bc909eSSimon Trimmer int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int event_id) 683f6bc909eSSimon Trimmer { 684f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 685f6bc909eSSimon Trimmer __be32 val = cpu_to_be32(event_id); 686f6bc909eSSimon Trimmer unsigned int reg; 687f6bc909eSSimon Trimmer int i, ret; 688f6bc909eSSimon Trimmer 6895065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 6905065cfabSCharles Keepax 691f6bc909eSSimon Trimmer if (!dsp->running) 692f6bc909eSSimon Trimmer return -EPERM; 693f6bc909eSSimon Trimmer 694f444da38SCharles Keepax ret = cs_dsp_coeff_base_reg(ctl, ®, 0); 695f6bc909eSSimon Trimmer if (ret) 696f6bc909eSSimon Trimmer return ret; 697f6bc909eSSimon Trimmer 698f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 699f6bc909eSSimon Trimmer event_id, ctl->alg_region.alg, 700f6bc909eSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), ctl->offset); 701f6bc909eSSimon Trimmer 702f6bc909eSSimon Trimmer ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 703f6bc909eSSimon Trimmer if (ret) { 704f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 705f6bc909eSSimon Trimmer return ret; 706f6bc909eSSimon Trimmer } 707f6bc909eSSimon Trimmer 708f6bc909eSSimon Trimmer /* 709f6bc909eSSimon Trimmer * Poll for ack, we initially poll at ~1ms intervals for firmwares 710f6bc909eSSimon Trimmer * that respond quickly, then go to ~10ms polls. A firmware is unlikely 711f6bc909eSSimon Trimmer * to ack instantly so we do the first 1ms delay before reading the 712f6bc909eSSimon Trimmer * control to avoid a pointless bus transaction 713f6bc909eSSimon Trimmer */ 714f6bc909eSSimon Trimmer for (i = 0; i < CS_DSP_ACKED_CTL_TIMEOUT_MS;) { 715f6bc909eSSimon Trimmer switch (i) { 716f6bc909eSSimon Trimmer case 0 ... CS_DSP_ACKED_CTL_N_QUICKPOLLS - 1: 717f6bc909eSSimon Trimmer usleep_range(1000, 2000); 718f6bc909eSSimon Trimmer i++; 719f6bc909eSSimon Trimmer break; 720f6bc909eSSimon Trimmer default: 721f6bc909eSSimon Trimmer usleep_range(10000, 20000); 722f6bc909eSSimon Trimmer i += 10; 723f6bc909eSSimon Trimmer break; 724f6bc909eSSimon Trimmer } 725f6bc909eSSimon Trimmer 726f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 727f6bc909eSSimon Trimmer if (ret) { 728f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 729f6bc909eSSimon Trimmer return ret; 730f6bc909eSSimon Trimmer } 731f6bc909eSSimon Trimmer 732f6bc909eSSimon Trimmer if (val == 0) { 733f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 734f6bc909eSSimon Trimmer return 0; 735f6bc909eSSimon Trimmer } 736f6bc909eSSimon Trimmer } 737f6bc909eSSimon Trimmer 738f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 739f6bc909eSSimon Trimmer reg, ctl->alg_region.alg, 740f6bc909eSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), 741f6bc909eSSimon Trimmer ctl->offset); 742f6bc909eSSimon Trimmer 743f6bc909eSSimon Trimmer return -ETIMEDOUT; 744f6bc909eSSimon Trimmer } 745e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_acked_control, FW_CS_DSP); 746f6bc909eSSimon Trimmer 747f6bc909eSSimon Trimmer static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, 748f444da38SCharles Keepax unsigned int off, const void *buf, size_t len) 749f6bc909eSSimon Trimmer { 750f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 751f6bc909eSSimon Trimmer void *scratch; 752f6bc909eSSimon Trimmer int ret; 753f6bc909eSSimon Trimmer unsigned int reg; 754f6bc909eSSimon Trimmer 755f444da38SCharles Keepax ret = cs_dsp_coeff_base_reg(ctl, ®, off); 756f6bc909eSSimon Trimmer if (ret) 757f6bc909eSSimon Trimmer return ret; 758f6bc909eSSimon Trimmer 759f6bc909eSSimon Trimmer scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 760f6bc909eSSimon Trimmer if (!scratch) 761f6bc909eSSimon Trimmer return -ENOMEM; 762f6bc909eSSimon Trimmer 763f6bc909eSSimon Trimmer ret = regmap_raw_write(dsp->regmap, reg, scratch, 764f6bc909eSSimon Trimmer len); 765f6bc909eSSimon Trimmer if (ret) { 766f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 767f6bc909eSSimon Trimmer len, reg, ret); 768f6bc909eSSimon Trimmer kfree(scratch); 769f6bc909eSSimon Trimmer return ret; 770f6bc909eSSimon Trimmer } 771f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 772f6bc909eSSimon Trimmer 773f6bc909eSSimon Trimmer kfree(scratch); 774f6bc909eSSimon Trimmer 775f6bc909eSSimon Trimmer return 0; 776f6bc909eSSimon Trimmer } 777f6bc909eSSimon Trimmer 778f6bc909eSSimon Trimmer /** 779f6bc909eSSimon Trimmer * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control 780f6bc909eSSimon Trimmer * @ctl: pointer to coefficient control 781f444da38SCharles Keepax * @off: word offset at which data should be written 782f6bc909eSSimon Trimmer * @buf: the buffer to write to the given control 783b329b3d3SCharles Keepax * @len: the length of the buffer in bytes 784f6bc909eSSimon Trimmer * 785f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 786f6bc909eSSimon Trimmer * 787c56f4b24SSimon Trimmer * Return: < 0 on error, 1 when the control value changed and 0 when it has not. 788f6bc909eSSimon Trimmer */ 789f444da38SCharles Keepax int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, 790f444da38SCharles Keepax unsigned int off, const void *buf, size_t len) 791f6bc909eSSimon Trimmer { 792f6bc909eSSimon Trimmer int ret = 0; 793f6bc909eSSimon Trimmer 79486c60804SCharles Keepax if (!ctl) 79586c60804SCharles Keepax return -ENOENT; 79686c60804SCharles Keepax 797043c0a62SCharles Keepax lockdep_assert_held(&ctl->dsp->pwr_lock); 798043c0a62SCharles Keepax 799f444da38SCharles Keepax if (len + off * sizeof(u32) > ctl->len) 800f444da38SCharles Keepax return -EINVAL; 801f444da38SCharles Keepax 802c56f4b24SSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 803f6bc909eSSimon Trimmer ret = -EPERM; 804c56f4b24SSimon Trimmer } else if (buf != ctl->cache) { 805c56f4b24SSimon Trimmer if (memcmp(ctl->cache + off * sizeof(u32), buf, len)) 806f444da38SCharles Keepax memcpy(ctl->cache + off * sizeof(u32), buf, len); 807c56f4b24SSimon Trimmer else 808c56f4b24SSimon Trimmer return 0; 809c56f4b24SSimon Trimmer } 810f6bc909eSSimon Trimmer 811f6bc909eSSimon Trimmer ctl->set = 1; 812f6bc909eSSimon Trimmer if (ctl->enabled && ctl->dsp->running) 813f444da38SCharles Keepax ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); 814f6bc909eSSimon Trimmer 815c56f4b24SSimon Trimmer if (ret < 0) 816f6bc909eSSimon Trimmer return ret; 817c56f4b24SSimon Trimmer 818c56f4b24SSimon Trimmer return 1; 819f6bc909eSSimon Trimmer } 820e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_ctrl, FW_CS_DSP); 821f6bc909eSSimon Trimmer 8224d033379SSimon Trimmer /** 8234d033379SSimon Trimmer * cs_dsp_coeff_lock_and_write_ctrl() - Writes the given buffer to the given coefficient control 8244d033379SSimon Trimmer * @ctl: pointer to coefficient control 8254d033379SSimon Trimmer * @off: word offset at which data should be written 8264d033379SSimon Trimmer * @buf: the buffer to write to the given control 8274d033379SSimon Trimmer * @len: the length of the buffer in bytes 8284d033379SSimon Trimmer * 8294d033379SSimon Trimmer * Same as cs_dsp_coeff_write_ctrl() but takes pwr_lock. 8304d033379SSimon Trimmer * 8314d033379SSimon Trimmer * Return: A negative number on error, 1 when the control value changed and 0 when it has not. 8324d033379SSimon Trimmer */ 8334d033379SSimon Trimmer int cs_dsp_coeff_lock_and_write_ctrl(struct cs_dsp_coeff_ctl *ctl, 8344d033379SSimon Trimmer unsigned int off, const void *buf, size_t len) 8354d033379SSimon Trimmer { 8364d033379SSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 8374d033379SSimon Trimmer int ret; 8384d033379SSimon Trimmer 8394d033379SSimon Trimmer lockdep_assert_not_held(&dsp->pwr_lock); 8404d033379SSimon Trimmer 8414d033379SSimon Trimmer mutex_lock(&dsp->pwr_lock); 8424d033379SSimon Trimmer ret = cs_dsp_coeff_write_ctrl(ctl, off, buf, len); 8434d033379SSimon Trimmer mutex_unlock(&dsp->pwr_lock); 8444d033379SSimon Trimmer 8454d033379SSimon Trimmer return ret; 8464d033379SSimon Trimmer } 8474d033379SSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_lock_and_write_ctrl); 8484d033379SSimon Trimmer 849f444da38SCharles Keepax static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, 850f444da38SCharles Keepax unsigned int off, void *buf, size_t len) 851f6bc909eSSimon Trimmer { 852f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 853f6bc909eSSimon Trimmer void *scratch; 854f6bc909eSSimon Trimmer int ret; 855f6bc909eSSimon Trimmer unsigned int reg; 856f6bc909eSSimon Trimmer 857f444da38SCharles Keepax ret = cs_dsp_coeff_base_reg(ctl, ®, off); 858f6bc909eSSimon Trimmer if (ret) 859f6bc909eSSimon Trimmer return ret; 860f6bc909eSSimon Trimmer 861f6bc909eSSimon Trimmer scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 862f6bc909eSSimon Trimmer if (!scratch) 863f6bc909eSSimon Trimmer return -ENOMEM; 864f6bc909eSSimon Trimmer 865f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 866f6bc909eSSimon Trimmer if (ret) { 867f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 868f6bc909eSSimon Trimmer len, reg, ret); 869f6bc909eSSimon Trimmer kfree(scratch); 870f6bc909eSSimon Trimmer return ret; 871f6bc909eSSimon Trimmer } 872f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 873f6bc909eSSimon Trimmer 874f6bc909eSSimon Trimmer memcpy(buf, scratch, len); 875f6bc909eSSimon Trimmer kfree(scratch); 876f6bc909eSSimon Trimmer 877f6bc909eSSimon Trimmer return 0; 878f6bc909eSSimon Trimmer } 879f6bc909eSSimon Trimmer 880f6bc909eSSimon Trimmer /** 881f6bc909eSSimon Trimmer * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer 882f6bc909eSSimon Trimmer * @ctl: pointer to coefficient control 883f444da38SCharles Keepax * @off: word offset at which data should be read 884f6bc909eSSimon Trimmer * @buf: the buffer to store to the given control 885b329b3d3SCharles Keepax * @len: the length of the buffer in bytes 886f6bc909eSSimon Trimmer * 887f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 888f6bc909eSSimon Trimmer * 889f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 890f6bc909eSSimon Trimmer */ 891f444da38SCharles Keepax int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, 892f444da38SCharles Keepax unsigned int off, void *buf, size_t len) 893f6bc909eSSimon Trimmer { 894f6bc909eSSimon Trimmer int ret = 0; 895f6bc909eSSimon Trimmer 89686c60804SCharles Keepax if (!ctl) 89786c60804SCharles Keepax return -ENOENT; 89886c60804SCharles Keepax 899043c0a62SCharles Keepax lockdep_assert_held(&ctl->dsp->pwr_lock); 900043c0a62SCharles Keepax 901f444da38SCharles Keepax if (len + off * sizeof(u32) > ctl->len) 902f444da38SCharles Keepax return -EINVAL; 903f444da38SCharles Keepax 904f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 905f6bc909eSSimon Trimmer if (ctl->enabled && ctl->dsp->running) 906f444da38SCharles Keepax return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); 907f6bc909eSSimon Trimmer else 908f6bc909eSSimon Trimmer return -EPERM; 909f6bc909eSSimon Trimmer } else { 910f6bc909eSSimon Trimmer if (!ctl->flags && ctl->enabled && ctl->dsp->running) 911f444da38SCharles Keepax ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); 912f6bc909eSSimon Trimmer 913f6bc909eSSimon Trimmer if (buf != ctl->cache) 914f444da38SCharles Keepax memcpy(buf, ctl->cache + off * sizeof(u32), len); 915f6bc909eSSimon Trimmer } 916f6bc909eSSimon Trimmer 917f6bc909eSSimon Trimmer return ret; 918f6bc909eSSimon Trimmer } 919e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_read_ctrl, FW_CS_DSP); 920f6bc909eSSimon Trimmer 9214d033379SSimon Trimmer /** 9224d033379SSimon Trimmer * cs_dsp_coeff_lock_and_read_ctrl() - Reads the given coefficient control into the given buffer 9234d033379SSimon Trimmer * @ctl: pointer to coefficient control 9244d033379SSimon Trimmer * @off: word offset at which data should be read 9254d033379SSimon Trimmer * @buf: the buffer to store to the given control 9264d033379SSimon Trimmer * @len: the length of the buffer in bytes 9274d033379SSimon Trimmer * 9284d033379SSimon Trimmer * Same as cs_dsp_coeff_read_ctrl() but takes pwr_lock. 9294d033379SSimon Trimmer * 9304d033379SSimon Trimmer * Return: Zero for success, a negative number on error. 9314d033379SSimon Trimmer */ 9324d033379SSimon Trimmer int cs_dsp_coeff_lock_and_read_ctrl(struct cs_dsp_coeff_ctl *ctl, 9334d033379SSimon Trimmer unsigned int off, void *buf, size_t len) 9344d033379SSimon Trimmer { 9354d033379SSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 9364d033379SSimon Trimmer int ret; 9374d033379SSimon Trimmer 9384d033379SSimon Trimmer lockdep_assert_not_held(&dsp->pwr_lock); 9394d033379SSimon Trimmer 9404d033379SSimon Trimmer mutex_lock(&dsp->pwr_lock); 9414d033379SSimon Trimmer ret = cs_dsp_coeff_read_ctrl(ctl, off, buf, len); 9424d033379SSimon Trimmer mutex_unlock(&dsp->pwr_lock); 9434d033379SSimon Trimmer 9444d033379SSimon Trimmer return ret; 9454d033379SSimon Trimmer } 9464d033379SSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_lock_and_read_ctrl); 9474d033379SSimon Trimmer 948f6bc909eSSimon Trimmer static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp) 949f6bc909eSSimon Trimmer { 950f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 951f6bc909eSSimon Trimmer int ret; 952f6bc909eSSimon Trimmer 953f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 954f6bc909eSSimon Trimmer if (!ctl->enabled || ctl->set) 955f6bc909eSSimon Trimmer continue; 956f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 957f6bc909eSSimon Trimmer continue; 958f6bc909eSSimon Trimmer 959f6bc909eSSimon Trimmer /* 960f6bc909eSSimon Trimmer * For readable controls populate the cache from the DSP memory. 961f6bc909eSSimon Trimmer * For non-readable controls the cache was zero-filled when 962f6bc909eSSimon Trimmer * created so we don't need to do anything. 963f6bc909eSSimon Trimmer */ 964f6bc909eSSimon Trimmer if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 965f444da38SCharles Keepax ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); 966f6bc909eSSimon Trimmer if (ret < 0) 967f6bc909eSSimon Trimmer return ret; 968f6bc909eSSimon Trimmer } 969f6bc909eSSimon Trimmer } 970f6bc909eSSimon Trimmer 971f6bc909eSSimon Trimmer return 0; 972f6bc909eSSimon Trimmer } 973f6bc909eSSimon Trimmer 974f6bc909eSSimon Trimmer static int cs_dsp_coeff_sync_controls(struct cs_dsp *dsp) 975f6bc909eSSimon Trimmer { 976f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 977f6bc909eSSimon Trimmer int ret; 978f6bc909eSSimon Trimmer 979f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 980f6bc909eSSimon Trimmer if (!ctl->enabled) 981f6bc909eSSimon Trimmer continue; 982f6bc909eSSimon Trimmer if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 983f444da38SCharles Keepax ret = cs_dsp_coeff_write_ctrl_raw(ctl, 0, ctl->cache, 984f6bc909eSSimon Trimmer ctl->len); 985f6bc909eSSimon Trimmer if (ret < 0) 986f6bc909eSSimon Trimmer return ret; 987f6bc909eSSimon Trimmer } 988f6bc909eSSimon Trimmer } 989f6bc909eSSimon Trimmer 990f6bc909eSSimon Trimmer return 0; 991f6bc909eSSimon Trimmer } 992f6bc909eSSimon Trimmer 993f6bc909eSSimon Trimmer static void cs_dsp_signal_event_controls(struct cs_dsp *dsp, 994f6bc909eSSimon Trimmer unsigned int event) 995f6bc909eSSimon Trimmer { 996f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 997f6bc909eSSimon Trimmer int ret; 998f6bc909eSSimon Trimmer 999f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 1000f6bc909eSSimon Trimmer if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1001f6bc909eSSimon Trimmer continue; 1002f6bc909eSSimon Trimmer 1003f6bc909eSSimon Trimmer if (!ctl->enabled) 1004f6bc909eSSimon Trimmer continue; 1005f6bc909eSSimon Trimmer 1006f6bc909eSSimon Trimmer ret = cs_dsp_coeff_write_acked_control(ctl, event); 1007f6bc909eSSimon Trimmer if (ret) 1008f6bc909eSSimon Trimmer cs_dsp_warn(dsp, 1009f6bc909eSSimon Trimmer "Failed to send 0x%x event to alg 0x%x (%d)\n", 1010f6bc909eSSimon Trimmer event, ctl->alg_region.alg, ret); 1011f6bc909eSSimon Trimmer } 1012f6bc909eSSimon Trimmer } 1013f6bc909eSSimon Trimmer 1014f6bc909eSSimon Trimmer static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl) 1015f6bc909eSSimon Trimmer { 1016f6bc909eSSimon Trimmer kfree(ctl->cache); 1017f6bc909eSSimon Trimmer kfree(ctl->subname); 1018f6bc909eSSimon Trimmer kfree(ctl); 1019f6bc909eSSimon Trimmer } 1020f6bc909eSSimon Trimmer 1021f6bc909eSSimon Trimmer static int cs_dsp_create_control(struct cs_dsp *dsp, 1022f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region, 1023f6bc909eSSimon Trimmer unsigned int offset, unsigned int len, 1024f6bc909eSSimon Trimmer const char *subname, unsigned int subname_len, 1025f6bc909eSSimon Trimmer unsigned int flags, unsigned int type) 1026f6bc909eSSimon Trimmer { 1027f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1028f6bc909eSSimon Trimmer int ret; 1029f6bc909eSSimon Trimmer 1030f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 1031f6bc909eSSimon Trimmer if (ctl->fw_name == dsp->fw_name && 1032f6bc909eSSimon Trimmer ctl->alg_region.alg == alg_region->alg && 1033f6bc909eSSimon Trimmer ctl->alg_region.type == alg_region->type) { 1034f6bc909eSSimon Trimmer if ((!subname && !ctl->subname) || 10357ac1102bSVlad Karpovich (subname && (ctl->subname_len == subname_len) && 10367ac1102bSVlad Karpovich !strncmp(ctl->subname, subname, ctl->subname_len))) { 1037f6bc909eSSimon Trimmer if (!ctl->enabled) 1038f6bc909eSSimon Trimmer ctl->enabled = 1; 1039f6bc909eSSimon Trimmer return 0; 1040f6bc909eSSimon Trimmer } 1041f6bc909eSSimon Trimmer } 1042f6bc909eSSimon Trimmer } 1043f6bc909eSSimon Trimmer 1044f6bc909eSSimon Trimmer ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1045f6bc909eSSimon Trimmer if (!ctl) 1046f6bc909eSSimon Trimmer return -ENOMEM; 1047f6bc909eSSimon Trimmer 1048f6bc909eSSimon Trimmer ctl->fw_name = dsp->fw_name; 1049f6bc909eSSimon Trimmer ctl->alg_region = *alg_region; 1050f6bc909eSSimon Trimmer if (subname && dsp->fw_ver >= 2) { 1051f6bc909eSSimon Trimmer ctl->subname_len = subname_len; 10525b933c72SRichard Fitzgerald ctl->subname = kasprintf(GFP_KERNEL, "%.*s", subname_len, subname); 1053f6bc909eSSimon Trimmer if (!ctl->subname) { 1054f6bc909eSSimon Trimmer ret = -ENOMEM; 1055f6bc909eSSimon Trimmer goto err_ctl; 1056f6bc909eSSimon Trimmer } 1057f6bc909eSSimon Trimmer } 1058f6bc909eSSimon Trimmer ctl->enabled = 1; 1059f6bc909eSSimon Trimmer ctl->set = 0; 1060f6bc909eSSimon Trimmer ctl->dsp = dsp; 1061f6bc909eSSimon Trimmer 1062f6bc909eSSimon Trimmer ctl->flags = flags; 1063f6bc909eSSimon Trimmer ctl->type = type; 1064f6bc909eSSimon Trimmer ctl->offset = offset; 1065f6bc909eSSimon Trimmer ctl->len = len; 1066f6bc909eSSimon Trimmer ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1067f6bc909eSSimon Trimmer if (!ctl->cache) { 1068f6bc909eSSimon Trimmer ret = -ENOMEM; 1069f6bc909eSSimon Trimmer goto err_ctl_subname; 1070f6bc909eSSimon Trimmer } 1071f6bc909eSSimon Trimmer 1072f6bc909eSSimon Trimmer list_add(&ctl->list, &dsp->ctl_list); 1073f6bc909eSSimon Trimmer 1074f6bc909eSSimon Trimmer if (dsp->client_ops->control_add) { 1075f6bc909eSSimon Trimmer ret = dsp->client_ops->control_add(ctl); 1076f6bc909eSSimon Trimmer if (ret) 1077f6bc909eSSimon Trimmer goto err_list_del; 1078f6bc909eSSimon Trimmer } 1079f6bc909eSSimon Trimmer 1080f6bc909eSSimon Trimmer return 0; 1081f6bc909eSSimon Trimmer 1082f6bc909eSSimon Trimmer err_list_del: 1083f6bc909eSSimon Trimmer list_del(&ctl->list); 1084f6bc909eSSimon Trimmer kfree(ctl->cache); 1085f6bc909eSSimon Trimmer err_ctl_subname: 1086f6bc909eSSimon Trimmer kfree(ctl->subname); 1087f6bc909eSSimon Trimmer err_ctl: 1088f6bc909eSSimon Trimmer kfree(ctl); 1089f6bc909eSSimon Trimmer 1090f6bc909eSSimon Trimmer return ret; 1091f6bc909eSSimon Trimmer } 1092f6bc909eSSimon Trimmer 1093f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg { 1094f6bc909eSSimon Trimmer int id; 1095f6bc909eSSimon Trimmer const u8 *name; 1096f6bc909eSSimon Trimmer int name_len; 1097f6bc909eSSimon Trimmer int ncoeff; 1098f6bc909eSSimon Trimmer }; 1099f6bc909eSSimon Trimmer 1100f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff { 1101f6bc909eSSimon Trimmer int offset; 1102f6bc909eSSimon Trimmer int mem_type; 1103f6bc909eSSimon Trimmer const u8 *name; 1104f6bc909eSSimon Trimmer int name_len; 1105f6bc909eSSimon Trimmer unsigned int ctl_type; 1106f6bc909eSSimon Trimmer int flags; 1107f6bc909eSSimon Trimmer int len; 1108f6bc909eSSimon Trimmer }; 1109f6bc909eSSimon Trimmer 1110*2163aff6SRichard Fitzgerald static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, unsigned int avail, 1111*2163aff6SRichard Fitzgerald const u8 **str) 1112f6bc909eSSimon Trimmer { 1113*2163aff6SRichard Fitzgerald int length, total_field_len; 1114*2163aff6SRichard Fitzgerald 1115*2163aff6SRichard Fitzgerald /* String fields are at least one __le32 */ 1116*2163aff6SRichard Fitzgerald if (sizeof(__le32) > avail) { 1117*2163aff6SRichard Fitzgerald *pos = NULL; 1118*2163aff6SRichard Fitzgerald return 0; 1119*2163aff6SRichard Fitzgerald } 1120f6bc909eSSimon Trimmer 1121f6bc909eSSimon Trimmer switch (bytes) { 1122f6bc909eSSimon Trimmer case 1: 1123f6bc909eSSimon Trimmer length = **pos; 1124f6bc909eSSimon Trimmer break; 1125f6bc909eSSimon Trimmer case 2: 1126f6bc909eSSimon Trimmer length = le16_to_cpu(*((__le16 *)*pos)); 1127f6bc909eSSimon Trimmer break; 1128f6bc909eSSimon Trimmer default: 1129f6bc909eSSimon Trimmer return 0; 1130f6bc909eSSimon Trimmer } 1131f6bc909eSSimon Trimmer 1132*2163aff6SRichard Fitzgerald total_field_len = ((length + bytes) + 3) & ~0x03; 1133*2163aff6SRichard Fitzgerald if ((unsigned int)total_field_len > avail) { 1134*2163aff6SRichard Fitzgerald *pos = NULL; 1135*2163aff6SRichard Fitzgerald return 0; 1136*2163aff6SRichard Fitzgerald } 1137*2163aff6SRichard Fitzgerald 1138f6bc909eSSimon Trimmer if (str) 1139f6bc909eSSimon Trimmer *str = *pos + bytes; 1140f6bc909eSSimon Trimmer 1141*2163aff6SRichard Fitzgerald *pos += total_field_len; 1142f6bc909eSSimon Trimmer 1143f6bc909eSSimon Trimmer return length; 1144f6bc909eSSimon Trimmer } 1145f6bc909eSSimon Trimmer 1146f6bc909eSSimon Trimmer static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos) 1147f6bc909eSSimon Trimmer { 1148f6bc909eSSimon Trimmer int val = 0; 1149f6bc909eSSimon Trimmer 1150f6bc909eSSimon Trimmer switch (bytes) { 1151f6bc909eSSimon Trimmer case 2: 1152f6bc909eSSimon Trimmer val = le16_to_cpu(*((__le16 *)*pos)); 1153f6bc909eSSimon Trimmer break; 1154f6bc909eSSimon Trimmer case 4: 1155f6bc909eSSimon Trimmer val = le32_to_cpu(*((__le32 *)*pos)); 1156f6bc909eSSimon Trimmer break; 1157f6bc909eSSimon Trimmer default: 1158f6bc909eSSimon Trimmer break; 1159f6bc909eSSimon Trimmer } 1160f6bc909eSSimon Trimmer 1161f6bc909eSSimon Trimmer *pos += bytes; 1162f6bc909eSSimon Trimmer 1163f6bc909eSSimon Trimmer return val; 1164f6bc909eSSimon Trimmer } 1165f6bc909eSSimon Trimmer 1166*2163aff6SRichard Fitzgerald static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, 1167*2163aff6SRichard Fitzgerald const struct wmfw_region *region, 1168f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg *blk) 1169f6bc909eSSimon Trimmer { 1170f6bc909eSSimon Trimmer const struct wmfw_adsp_alg_data *raw; 1171*2163aff6SRichard Fitzgerald unsigned int data_len = le32_to_cpu(region->len); 1172*2163aff6SRichard Fitzgerald unsigned int pos; 1173*2163aff6SRichard Fitzgerald const u8 *tmp; 1174*2163aff6SRichard Fitzgerald 1175*2163aff6SRichard Fitzgerald raw = (const struct wmfw_adsp_alg_data *)region->data; 1176f6bc909eSSimon Trimmer 1177f6bc909eSSimon Trimmer switch (dsp->fw_ver) { 1178f6bc909eSSimon Trimmer case 0: 1179f6bc909eSSimon Trimmer case 1: 1180*2163aff6SRichard Fitzgerald if (sizeof(*raw) > data_len) 1181*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1182f6bc909eSSimon Trimmer 1183f6bc909eSSimon Trimmer blk->id = le32_to_cpu(raw->id); 1184f6bc909eSSimon Trimmer blk->name = raw->name; 1185f6bc909eSSimon Trimmer blk->name_len = strlen(raw->name); 1186f6bc909eSSimon Trimmer blk->ncoeff = le32_to_cpu(raw->ncoeff); 1187*2163aff6SRichard Fitzgerald 1188*2163aff6SRichard Fitzgerald pos = sizeof(*raw); 1189f6bc909eSSimon Trimmer break; 1190f6bc909eSSimon Trimmer default: 1191*2163aff6SRichard Fitzgerald if (sizeof(raw->id) > data_len) 1192*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1193*2163aff6SRichard Fitzgerald 1194*2163aff6SRichard Fitzgerald tmp = region->data; 1195*2163aff6SRichard Fitzgerald blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), &tmp); 1196*2163aff6SRichard Fitzgerald pos = tmp - region->data; 1197*2163aff6SRichard Fitzgerald 1198*2163aff6SRichard Fitzgerald tmp = ®ion->data[pos]; 1199*2163aff6SRichard Fitzgerald blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, 1200f6bc909eSSimon Trimmer &blk->name); 1201*2163aff6SRichard Fitzgerald if (!tmp) 1202*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1203*2163aff6SRichard Fitzgerald 1204*2163aff6SRichard Fitzgerald pos = tmp - region->data; 1205*2163aff6SRichard Fitzgerald cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL); 1206*2163aff6SRichard Fitzgerald if (!tmp) 1207*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1208*2163aff6SRichard Fitzgerald 1209*2163aff6SRichard Fitzgerald pos = tmp - region->data; 1210*2163aff6SRichard Fitzgerald if (sizeof(raw->ncoeff) > (data_len - pos)) 1211*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1212*2163aff6SRichard Fitzgerald 1213*2163aff6SRichard Fitzgerald blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), &tmp); 1214*2163aff6SRichard Fitzgerald pos += sizeof(raw->ncoeff); 1215f6bc909eSSimon Trimmer break; 1216f6bc909eSSimon Trimmer } 1217f6bc909eSSimon Trimmer 1218*2163aff6SRichard Fitzgerald if ((int)blk->ncoeff < 0) 1219*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1220*2163aff6SRichard Fitzgerald 1221f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 1222f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 1223f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 1224*2163aff6SRichard Fitzgerald 1225*2163aff6SRichard Fitzgerald return pos; 1226f6bc909eSSimon Trimmer } 1227f6bc909eSSimon Trimmer 1228*2163aff6SRichard Fitzgerald static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, 1229*2163aff6SRichard Fitzgerald const struct wmfw_region *region, 1230*2163aff6SRichard Fitzgerald unsigned int pos, 1231f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff *blk) 1232f6bc909eSSimon Trimmer { 1233f6bc909eSSimon Trimmer const struct wmfw_adsp_coeff_data *raw; 1234*2163aff6SRichard Fitzgerald unsigned int data_len = le32_to_cpu(region->len); 1235*2163aff6SRichard Fitzgerald unsigned int blk_len, blk_end_pos; 1236f6bc909eSSimon Trimmer const u8 *tmp; 1237*2163aff6SRichard Fitzgerald 1238*2163aff6SRichard Fitzgerald raw = (const struct wmfw_adsp_coeff_data *)®ion->data[pos]; 1239*2163aff6SRichard Fitzgerald if (sizeof(raw->hdr) > (data_len - pos)) 1240*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1241*2163aff6SRichard Fitzgerald 1242*2163aff6SRichard Fitzgerald blk_len = le32_to_cpu(raw->hdr.size); 1243*2163aff6SRichard Fitzgerald if (blk_len > S32_MAX) 1244*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1245*2163aff6SRichard Fitzgerald 1246*2163aff6SRichard Fitzgerald if (blk_len > (data_len - pos - sizeof(raw->hdr))) 1247*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1248*2163aff6SRichard Fitzgerald 1249*2163aff6SRichard Fitzgerald blk_end_pos = pos + sizeof(raw->hdr) + blk_len; 1250*2163aff6SRichard Fitzgerald 1251*2163aff6SRichard Fitzgerald blk->offset = le16_to_cpu(raw->hdr.offset); 1252*2163aff6SRichard Fitzgerald blk->mem_type = le16_to_cpu(raw->hdr.type); 1253f6bc909eSSimon Trimmer 1254f6bc909eSSimon Trimmer switch (dsp->fw_ver) { 1255f6bc909eSSimon Trimmer case 0: 1256f6bc909eSSimon Trimmer case 1: 1257*2163aff6SRichard Fitzgerald if (sizeof(*raw) > (data_len - pos)) 1258*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1259f6bc909eSSimon Trimmer 1260f6bc909eSSimon Trimmer blk->name = raw->name; 1261f6bc909eSSimon Trimmer blk->name_len = strlen(raw->name); 1262f6bc909eSSimon Trimmer blk->ctl_type = le16_to_cpu(raw->ctl_type); 1263f6bc909eSSimon Trimmer blk->flags = le16_to_cpu(raw->flags); 1264f6bc909eSSimon Trimmer blk->len = le32_to_cpu(raw->len); 1265f6bc909eSSimon Trimmer break; 1266f6bc909eSSimon Trimmer default: 1267*2163aff6SRichard Fitzgerald pos += sizeof(raw->hdr); 1268*2163aff6SRichard Fitzgerald tmp = ®ion->data[pos]; 1269*2163aff6SRichard Fitzgerald blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, 1270f6bc909eSSimon Trimmer &blk->name); 1271*2163aff6SRichard Fitzgerald if (!tmp) 1272*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1273f6bc909eSSimon Trimmer 1274*2163aff6SRichard Fitzgerald pos = tmp - region->data; 1275*2163aff6SRichard Fitzgerald cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, NULL); 1276*2163aff6SRichard Fitzgerald if (!tmp) 1277*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1278*2163aff6SRichard Fitzgerald 1279*2163aff6SRichard Fitzgerald pos = tmp - region->data; 1280*2163aff6SRichard Fitzgerald cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL); 1281*2163aff6SRichard Fitzgerald if (!tmp) 1282*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1283*2163aff6SRichard Fitzgerald 1284*2163aff6SRichard Fitzgerald pos = tmp - region->data; 1285*2163aff6SRichard Fitzgerald if (sizeof(raw->ctl_type) + sizeof(raw->flags) + sizeof(raw->len) > 1286*2163aff6SRichard Fitzgerald (data_len - pos)) 1287*2163aff6SRichard Fitzgerald return -EOVERFLOW; 1288*2163aff6SRichard Fitzgerald 1289*2163aff6SRichard Fitzgerald blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1290*2163aff6SRichard Fitzgerald pos += sizeof(raw->ctl_type); 1291*2163aff6SRichard Fitzgerald blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp); 1292*2163aff6SRichard Fitzgerald pos += sizeof(raw->flags); 1293*2163aff6SRichard Fitzgerald blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp); 1294f6bc909eSSimon Trimmer break; 1295f6bc909eSSimon Trimmer } 1296f6bc909eSSimon Trimmer 1297f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 1298f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 1299f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 1300f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 1301f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 1302f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 1303*2163aff6SRichard Fitzgerald 1304*2163aff6SRichard Fitzgerald return blk_end_pos; 1305f6bc909eSSimon Trimmer } 1306f6bc909eSSimon Trimmer 1307f6bc909eSSimon Trimmer static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp, 1308f6bc909eSSimon Trimmer const struct cs_dsp_coeff_parsed_coeff *coeff_blk, 1309f6bc909eSSimon Trimmer unsigned int f_required, 1310f6bc909eSSimon Trimmer unsigned int f_illegal) 1311f6bc909eSSimon Trimmer { 1312f6bc909eSSimon Trimmer if ((coeff_blk->flags & f_illegal) || 1313f6bc909eSSimon Trimmer ((coeff_blk->flags & f_required) != f_required)) { 1314f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1315f6bc909eSSimon Trimmer coeff_blk->flags, coeff_blk->ctl_type); 1316f6bc909eSSimon Trimmer return -EINVAL; 1317f6bc909eSSimon Trimmer } 1318f6bc909eSSimon Trimmer 1319f6bc909eSSimon Trimmer return 0; 1320f6bc909eSSimon Trimmer } 1321f6bc909eSSimon Trimmer 1322f6bc909eSSimon Trimmer static int cs_dsp_parse_coeff(struct cs_dsp *dsp, 1323f6bc909eSSimon Trimmer const struct wmfw_region *region) 1324f6bc909eSSimon Trimmer { 1325f6bc909eSSimon Trimmer struct cs_dsp_alg_region alg_region = {}; 1326f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg alg_blk; 1327f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff coeff_blk; 1328*2163aff6SRichard Fitzgerald int i, pos, ret; 1329f6bc909eSSimon Trimmer 1330*2163aff6SRichard Fitzgerald pos = cs_dsp_coeff_parse_alg(dsp, region, &alg_blk); 1331*2163aff6SRichard Fitzgerald if (pos < 0) 1332*2163aff6SRichard Fitzgerald return pos; 1333*2163aff6SRichard Fitzgerald 1334f6bc909eSSimon Trimmer for (i = 0; i < alg_blk.ncoeff; i++) { 1335*2163aff6SRichard Fitzgerald pos = cs_dsp_coeff_parse_coeff(dsp, region, pos, &coeff_blk); 1336*2163aff6SRichard Fitzgerald if (pos < 0) 1337*2163aff6SRichard Fitzgerald return pos; 1338f6bc909eSSimon Trimmer 1339f6bc909eSSimon Trimmer switch (coeff_blk.ctl_type) { 1340f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_BYTES: 1341f6bc909eSSimon Trimmer break; 1342f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_ACKED: 1343f6bc909eSSimon Trimmer if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1344f6bc909eSSimon Trimmer continue; /* ignore */ 1345f6bc909eSSimon Trimmer 1346f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1347f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1348f6bc909eSSimon Trimmer WMFW_CTL_FLAG_WRITEABLE | 1349f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1350f6bc909eSSimon Trimmer 0); 1351f6bc909eSSimon Trimmer if (ret) 1352f6bc909eSSimon Trimmer return -EINVAL; 1353f6bc909eSSimon Trimmer break; 1354f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_HOSTEVENT: 13555c903f64SCharles Keepax case WMFW_CTL_TYPE_FWEVENT: 1356f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1357f6bc909eSSimon Trimmer WMFW_CTL_FLAG_SYS | 1358f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1359f6bc909eSSimon Trimmer WMFW_CTL_FLAG_WRITEABLE | 1360f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1361f6bc909eSSimon Trimmer 0); 1362f6bc909eSSimon Trimmer if (ret) 1363f6bc909eSSimon Trimmer return -EINVAL; 1364f6bc909eSSimon Trimmer break; 1365f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_HOST_BUFFER: 1366f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1367f6bc909eSSimon Trimmer WMFW_CTL_FLAG_SYS | 1368f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1369f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1370f6bc909eSSimon Trimmer 0); 1371f6bc909eSSimon Trimmer if (ret) 1372f6bc909eSSimon Trimmer return -EINVAL; 1373f6bc909eSSimon Trimmer break; 1374f6bc909eSSimon Trimmer default: 1375f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Unknown control type: %d\n", 1376f6bc909eSSimon Trimmer coeff_blk.ctl_type); 1377f6bc909eSSimon Trimmer return -EINVAL; 1378f6bc909eSSimon Trimmer } 1379f6bc909eSSimon Trimmer 1380f6bc909eSSimon Trimmer alg_region.type = coeff_blk.mem_type; 1381f6bc909eSSimon Trimmer alg_region.alg = alg_blk.id; 1382f6bc909eSSimon Trimmer 1383f6bc909eSSimon Trimmer ret = cs_dsp_create_control(dsp, &alg_region, 1384f6bc909eSSimon Trimmer coeff_blk.offset, 1385f6bc909eSSimon Trimmer coeff_blk.len, 1386f6bc909eSSimon Trimmer coeff_blk.name, 1387f6bc909eSSimon Trimmer coeff_blk.name_len, 1388f6bc909eSSimon Trimmer coeff_blk.flags, 1389f6bc909eSSimon Trimmer coeff_blk.ctl_type); 1390f6bc909eSSimon Trimmer if (ret < 0) 1391f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to create control: %.*s, %d\n", 1392f6bc909eSSimon Trimmer coeff_blk.name_len, coeff_blk.name, ret); 1393f6bc909eSSimon Trimmer } 1394f6bc909eSSimon Trimmer 1395f6bc909eSSimon Trimmer return 0; 1396f6bc909eSSimon Trimmer } 1397f6bc909eSSimon Trimmer 1398f6bc909eSSimon Trimmer static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp, 1399f6bc909eSSimon Trimmer const char * const file, 1400f6bc909eSSimon Trimmer unsigned int pos, 1401f6bc909eSSimon Trimmer const struct firmware *firmware) 1402f6bc909eSSimon Trimmer { 1403f6bc909eSSimon Trimmer const struct wmfw_adsp1_sizes *adsp1_sizes; 1404f6bc909eSSimon Trimmer 1405f6bc909eSSimon Trimmer adsp1_sizes = (void *)&firmware->data[pos]; 14063019b86bSRichard Fitzgerald if (sizeof(*adsp1_sizes) > firmware->size - pos) { 14073019b86bSRichard Fitzgerald cs_dsp_err(dsp, "%s: file truncated\n", file); 14083019b86bSRichard Fitzgerald return 0; 14093019b86bSRichard Fitzgerald } 1410f6bc909eSSimon Trimmer 1411f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 1412f6bc909eSSimon Trimmer le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 1413f6bc909eSSimon Trimmer le32_to_cpu(adsp1_sizes->zm)); 1414f6bc909eSSimon Trimmer 1415f6bc909eSSimon Trimmer return pos + sizeof(*adsp1_sizes); 1416f6bc909eSSimon Trimmer } 1417f6bc909eSSimon Trimmer 1418f6bc909eSSimon Trimmer static unsigned int cs_dsp_adsp2_parse_sizes(struct cs_dsp *dsp, 1419f6bc909eSSimon Trimmer const char * const file, 1420f6bc909eSSimon Trimmer unsigned int pos, 1421f6bc909eSSimon Trimmer const struct firmware *firmware) 1422f6bc909eSSimon Trimmer { 1423f6bc909eSSimon Trimmer const struct wmfw_adsp2_sizes *adsp2_sizes; 1424f6bc909eSSimon Trimmer 1425f6bc909eSSimon Trimmer adsp2_sizes = (void *)&firmware->data[pos]; 14263019b86bSRichard Fitzgerald if (sizeof(*adsp2_sizes) > firmware->size - pos) { 14273019b86bSRichard Fitzgerald cs_dsp_err(dsp, "%s: file truncated\n", file); 14283019b86bSRichard Fitzgerald return 0; 14293019b86bSRichard Fitzgerald } 1430f6bc909eSSimon Trimmer 1431f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 1432f6bc909eSSimon Trimmer le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 1433f6bc909eSSimon Trimmer le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 1434f6bc909eSSimon Trimmer 1435f6bc909eSSimon Trimmer return pos + sizeof(*adsp2_sizes); 1436f6bc909eSSimon Trimmer } 1437f6bc909eSSimon Trimmer 1438f6bc909eSSimon Trimmer static bool cs_dsp_validate_version(struct cs_dsp *dsp, unsigned int version) 1439f6bc909eSSimon Trimmer { 1440f6bc909eSSimon Trimmer switch (version) { 1441f6bc909eSSimon Trimmer case 0: 1442f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Deprecated file format %d\n", version); 1443f6bc909eSSimon Trimmer return true; 1444f6bc909eSSimon Trimmer case 1: 1445f6bc909eSSimon Trimmer case 2: 1446f6bc909eSSimon Trimmer return true; 1447f6bc909eSSimon Trimmer default: 1448f6bc909eSSimon Trimmer return false; 1449f6bc909eSSimon Trimmer } 1450f6bc909eSSimon Trimmer } 1451f6bc909eSSimon Trimmer 1452f6bc909eSSimon Trimmer static bool cs_dsp_halo_validate_version(struct cs_dsp *dsp, unsigned int version) 1453f6bc909eSSimon Trimmer { 1454f6bc909eSSimon Trimmer switch (version) { 1455f6bc909eSSimon Trimmer case 3: 1456f6bc909eSSimon Trimmer return true; 1457f6bc909eSSimon Trimmer default: 1458f6bc909eSSimon Trimmer return false; 1459f6bc909eSSimon Trimmer } 1460f6bc909eSSimon Trimmer } 1461f6bc909eSSimon Trimmer 1462f6bc909eSSimon Trimmer static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, 1463f6bc909eSSimon Trimmer const char *file) 1464f6bc909eSSimon Trimmer { 1465f6bc909eSSimon Trimmer LIST_HEAD(buf_list); 1466f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 1467f6bc909eSSimon Trimmer unsigned int pos = 0; 1468f6bc909eSSimon Trimmer const struct wmfw_header *header; 1469f6bc909eSSimon Trimmer const struct wmfw_footer *footer; 1470f6bc909eSSimon Trimmer const struct wmfw_region *region; 1471f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1472f6bc909eSSimon Trimmer const char *region_name; 1473f6bc909eSSimon Trimmer char *text = NULL; 1474f6bc909eSSimon Trimmer struct cs_dsp_buf *buf; 1475f6bc909eSSimon Trimmer unsigned int reg; 1476f6bc909eSSimon Trimmer int regions = 0; 1477f6bc909eSSimon Trimmer int ret, offset, type; 1478f6bc909eSSimon Trimmer 1479107c1674SSimon Trimmer if (!firmware) 1480107c1674SSimon Trimmer return 0; 1481107c1674SSimon Trimmer 1482f6bc909eSSimon Trimmer ret = -EINVAL; 1483f6bc909eSSimon Trimmer 14843019b86bSRichard Fitzgerald if (sizeof(*header) >= firmware->size) { 14853019b86bSRichard Fitzgerald ret = -EOVERFLOW; 1486f6bc909eSSimon Trimmer goto out_fw; 1487f6bc909eSSimon Trimmer } 1488f6bc909eSSimon Trimmer 1489f6bc909eSSimon Trimmer header = (void *)&firmware->data[0]; 1490f6bc909eSSimon Trimmer 1491f6bc909eSSimon Trimmer if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 1492f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid magic\n", file); 1493f6bc909eSSimon Trimmer goto out_fw; 1494f6bc909eSSimon Trimmer } 1495f6bc909eSSimon Trimmer 1496f6bc909eSSimon Trimmer if (!dsp->ops->validate_version(dsp, header->ver)) { 1497f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: unknown file format %d\n", 1498f6bc909eSSimon Trimmer file, header->ver); 1499f6bc909eSSimon Trimmer goto out_fw; 1500f6bc909eSSimon Trimmer } 1501f6bc909eSSimon Trimmer 1502f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware version: %d\n", header->ver); 1503f6bc909eSSimon Trimmer dsp->fw_ver = header->ver; 1504f6bc909eSSimon Trimmer 1505f6bc909eSSimon Trimmer if (header->core != dsp->type) { 1506f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid core %d != %d\n", 1507f6bc909eSSimon Trimmer file, header->core, dsp->type); 1508f6bc909eSSimon Trimmer goto out_fw; 1509f6bc909eSSimon Trimmer } 1510f6bc909eSSimon Trimmer 1511f6bc909eSSimon Trimmer pos = sizeof(*header); 1512f6bc909eSSimon Trimmer pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 15133019b86bSRichard Fitzgerald if ((pos == 0) || (sizeof(*footer) > firmware->size - pos)) { 15143019b86bSRichard Fitzgerald ret = -EOVERFLOW; 15153019b86bSRichard Fitzgerald goto out_fw; 15163019b86bSRichard Fitzgerald } 1517f6bc909eSSimon Trimmer 1518f6bc909eSSimon Trimmer footer = (void *)&firmware->data[pos]; 1519f6bc909eSSimon Trimmer pos += sizeof(*footer); 1520f6bc909eSSimon Trimmer 1521f6bc909eSSimon Trimmer if (le32_to_cpu(header->len) != pos) { 15223019b86bSRichard Fitzgerald ret = -EOVERFLOW; 1523f6bc909eSSimon Trimmer goto out_fw; 1524f6bc909eSSimon Trimmer } 1525f6bc909eSSimon Trimmer 1526f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, 1527f6bc909eSSimon Trimmer le64_to_cpu(footer->timestamp)); 1528f6bc909eSSimon Trimmer 1529959fe01eSRichard Fitzgerald while (pos < firmware->size) { 1530959fe01eSRichard Fitzgerald /* Is there enough data for a complete block header? */ 1531959fe01eSRichard Fitzgerald if (sizeof(*region) > firmware->size - pos) { 1532959fe01eSRichard Fitzgerald ret = -EOVERFLOW; 1533959fe01eSRichard Fitzgerald goto out_fw; 1534959fe01eSRichard Fitzgerald } 1535959fe01eSRichard Fitzgerald 1536f6bc909eSSimon Trimmer region = (void *)&(firmware->data[pos]); 15376598afa9SRichard Fitzgerald 15386598afa9SRichard Fitzgerald if (le32_to_cpu(region->len) > firmware->size - pos - sizeof(*region)) { 15396598afa9SRichard Fitzgerald ret = -EOVERFLOW; 15406598afa9SRichard Fitzgerald goto out_fw; 15416598afa9SRichard Fitzgerald } 15426598afa9SRichard Fitzgerald 1543f6bc909eSSimon Trimmer region_name = "Unknown"; 1544f6bc909eSSimon Trimmer reg = 0; 1545f6bc909eSSimon Trimmer text = NULL; 1546f6bc909eSSimon Trimmer offset = le32_to_cpu(region->offset) & 0xffffff; 1547f6bc909eSSimon Trimmer type = be32_to_cpu(region->type) & 0xff; 1548f6bc909eSSimon Trimmer 1549f6bc909eSSimon Trimmer switch (type) { 1550f6bc909eSSimon Trimmer case WMFW_NAME_TEXT: 1551f6bc909eSSimon Trimmer region_name = "Firmware name"; 1552f6bc909eSSimon Trimmer text = kzalloc(le32_to_cpu(region->len) + 1, 1553f6bc909eSSimon Trimmer GFP_KERNEL); 1554f6bc909eSSimon Trimmer break; 1555f6bc909eSSimon Trimmer case WMFW_ALGORITHM_DATA: 1556f6bc909eSSimon Trimmer region_name = "Algorithm"; 1557f6bc909eSSimon Trimmer ret = cs_dsp_parse_coeff(dsp, region); 1558f6bc909eSSimon Trimmer if (ret != 0) 1559f6bc909eSSimon Trimmer goto out_fw; 1560f6bc909eSSimon Trimmer break; 1561f6bc909eSSimon Trimmer case WMFW_INFO_TEXT: 1562f6bc909eSSimon Trimmer region_name = "Information"; 1563f6bc909eSSimon Trimmer text = kzalloc(le32_to_cpu(region->len) + 1, 1564f6bc909eSSimon Trimmer GFP_KERNEL); 1565f6bc909eSSimon Trimmer break; 1566f6bc909eSSimon Trimmer case WMFW_ABSOLUTE: 1567f6bc909eSSimon Trimmer region_name = "Absolute"; 1568f6bc909eSSimon Trimmer reg = offset; 1569f6bc909eSSimon Trimmer break; 1570f6bc909eSSimon Trimmer case WMFW_ADSP1_PM: 1571f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 1572f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 1573f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 1574f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 1575f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 1576f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 1577f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 1578f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 1579f6bc909eSSimon Trimmer if (!mem) { 1580f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No region of type: %x\n", type); 1581f6bc909eSSimon Trimmer ret = -EINVAL; 1582f6bc909eSSimon Trimmer goto out_fw; 1583f6bc909eSSimon Trimmer } 1584f6bc909eSSimon Trimmer 1585f6bc909eSSimon Trimmer region_name = cs_dsp_mem_region_name(type); 1586f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, offset); 1587f6bc909eSSimon Trimmer break; 1588f6bc909eSSimon Trimmer default: 1589f6bc909eSSimon Trimmer cs_dsp_warn(dsp, 1590f6bc909eSSimon Trimmer "%s.%d: Unknown region type %x at %d(%x)\n", 1591f6bc909eSSimon Trimmer file, regions, type, pos, pos); 1592f6bc909eSSimon Trimmer break; 1593f6bc909eSSimon Trimmer } 1594f6bc909eSSimon Trimmer 1595f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 1596f6bc909eSSimon Trimmer regions, le32_to_cpu(region->len), offset, 1597f6bc909eSSimon Trimmer region_name); 1598f6bc909eSSimon Trimmer 1599f6bc909eSSimon Trimmer if (text) { 1600f6bc909eSSimon Trimmer memcpy(text, region->data, le32_to_cpu(region->len)); 1601f6bc909eSSimon Trimmer cs_dsp_info(dsp, "%s: %s\n", file, text); 1602f6bc909eSSimon Trimmer kfree(text); 1603f6bc909eSSimon Trimmer text = NULL; 1604f6bc909eSSimon Trimmer } 1605f6bc909eSSimon Trimmer 1606f6bc909eSSimon Trimmer if (reg) { 1607f6bc909eSSimon Trimmer buf = cs_dsp_buf_alloc(region->data, 1608f6bc909eSSimon Trimmer le32_to_cpu(region->len), 1609f6bc909eSSimon Trimmer &buf_list); 1610f6bc909eSSimon Trimmer if (!buf) { 1611f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 1612f6bc909eSSimon Trimmer ret = -ENOMEM; 1613f6bc909eSSimon Trimmer goto out_fw; 1614f6bc909eSSimon Trimmer } 1615f6bc909eSSimon Trimmer 1616f6bc909eSSimon Trimmer ret = regmap_raw_write_async(regmap, reg, buf->buf, 1617f6bc909eSSimon Trimmer le32_to_cpu(region->len)); 1618f6bc909eSSimon Trimmer if (ret != 0) { 1619f6bc909eSSimon Trimmer cs_dsp_err(dsp, 1620f6bc909eSSimon Trimmer "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 1621f6bc909eSSimon Trimmer file, regions, 1622f6bc909eSSimon Trimmer le32_to_cpu(region->len), offset, 1623f6bc909eSSimon Trimmer region_name, ret); 1624f6bc909eSSimon Trimmer goto out_fw; 1625f6bc909eSSimon Trimmer } 1626f6bc909eSSimon Trimmer } 1627f6bc909eSSimon Trimmer 1628f6bc909eSSimon Trimmer pos += le32_to_cpu(region->len) + sizeof(*region); 1629f6bc909eSSimon Trimmer regions++; 1630f6bc909eSSimon Trimmer } 1631f6bc909eSSimon Trimmer 1632f6bc909eSSimon Trimmer ret = regmap_async_complete(regmap); 1633f6bc909eSSimon Trimmer if (ret != 0) { 1634f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 1635f6bc909eSSimon Trimmer goto out_fw; 1636f6bc909eSSimon Trimmer } 1637f6bc909eSSimon Trimmer 1638f6bc909eSSimon Trimmer if (pos > firmware->size) 1639f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 1640f6bc909eSSimon Trimmer file, regions, pos - firmware->size); 1641f6bc909eSSimon Trimmer 1642f6bc909eSSimon Trimmer cs_dsp_debugfs_save_wmfwname(dsp, file); 1643f6bc909eSSimon Trimmer 1644f6bc909eSSimon Trimmer out_fw: 1645f6bc909eSSimon Trimmer regmap_async_complete(regmap); 1646f6bc909eSSimon Trimmer cs_dsp_buf_free(&buf_list); 1647f6bc909eSSimon Trimmer kfree(text); 1648f6bc909eSSimon Trimmer 16493019b86bSRichard Fitzgerald if (ret == -EOVERFLOW) 16503019b86bSRichard Fitzgerald cs_dsp_err(dsp, "%s: file content overflows file data\n", file); 16513019b86bSRichard Fitzgerald 1652f6bc909eSSimon Trimmer return ret; 1653f6bc909eSSimon Trimmer } 1654f6bc909eSSimon Trimmer 1655f6bc909eSSimon Trimmer /** 1656f6bc909eSSimon Trimmer * cs_dsp_get_ctl() - Finds a matching coefficient control 1657f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 1658f6bc909eSSimon Trimmer * @name: pointer to string to match with a control's subname 1659f6bc909eSSimon Trimmer * @type: the algorithm type to match 1660f6bc909eSSimon Trimmer * @alg: the algorithm id to match 1661f6bc909eSSimon Trimmer * 1662f6bc909eSSimon Trimmer * Find cs_dsp_coeff_ctl with input name as its subname 1663f6bc909eSSimon Trimmer * 1664f6bc909eSSimon Trimmer * Return: pointer to the control on success, NULL if not found 1665f6bc909eSSimon Trimmer */ 1666f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, int type, 1667f6bc909eSSimon Trimmer unsigned int alg) 1668f6bc909eSSimon Trimmer { 1669f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *pos, *rslt = NULL; 1670f6bc909eSSimon Trimmer 16715065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 16725065cfabSCharles Keepax 1673f6bc909eSSimon Trimmer list_for_each_entry(pos, &dsp->ctl_list, list) { 1674f6bc909eSSimon Trimmer if (!pos->subname) 1675f6bc909eSSimon Trimmer continue; 1676f6bc909eSSimon Trimmer if (strncmp(pos->subname, name, pos->subname_len) == 0 && 1677f6bc909eSSimon Trimmer pos->fw_name == dsp->fw_name && 1678f6bc909eSSimon Trimmer pos->alg_region.alg == alg && 1679f6bc909eSSimon Trimmer pos->alg_region.type == type) { 1680f6bc909eSSimon Trimmer rslt = pos; 1681f6bc909eSSimon Trimmer break; 1682f6bc909eSSimon Trimmer } 1683f6bc909eSSimon Trimmer } 1684f6bc909eSSimon Trimmer 1685f6bc909eSSimon Trimmer return rslt; 1686f6bc909eSSimon Trimmer } 1687e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_get_ctl, FW_CS_DSP); 1688f6bc909eSSimon Trimmer 1689f6bc909eSSimon Trimmer static void cs_dsp_ctl_fixup_base(struct cs_dsp *dsp, 1690f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region) 1691f6bc909eSSimon Trimmer { 1692f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1693f6bc909eSSimon Trimmer 1694f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 1695f6bc909eSSimon Trimmer if (ctl->fw_name == dsp->fw_name && 1696f6bc909eSSimon Trimmer alg_region->alg == ctl->alg_region.alg && 1697f6bc909eSSimon Trimmer alg_region->type == ctl->alg_region.type) { 1698f6bc909eSSimon Trimmer ctl->alg_region.base = alg_region->base; 1699f6bc909eSSimon Trimmer } 1700f6bc909eSSimon Trimmer } 1701f6bc909eSSimon Trimmer } 1702f6bc909eSSimon Trimmer 1703f6bc909eSSimon Trimmer static void *cs_dsp_read_algs(struct cs_dsp *dsp, size_t n_algs, 1704f6bc909eSSimon Trimmer const struct cs_dsp_region *mem, 1705f6bc909eSSimon Trimmer unsigned int pos, unsigned int len) 1706f6bc909eSSimon Trimmer { 1707f6bc909eSSimon Trimmer void *alg; 1708f6bc909eSSimon Trimmer unsigned int reg; 1709f6bc909eSSimon Trimmer int ret; 1710f6bc909eSSimon Trimmer __be32 val; 1711f6bc909eSSimon Trimmer 1712f6bc909eSSimon Trimmer if (n_algs == 0) { 1713f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No algorithms\n"); 1714f6bc909eSSimon Trimmer return ERR_PTR(-EINVAL); 1715f6bc909eSSimon Trimmer } 1716f6bc909eSSimon Trimmer 1717f6bc909eSSimon Trimmer if (n_algs > 1024) { 1718f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1719f6bc909eSSimon Trimmer return ERR_PTR(-EINVAL); 1720f6bc909eSSimon Trimmer } 1721f6bc909eSSimon Trimmer 1722f6bc909eSSimon Trimmer /* Read the terminator first to validate the length */ 1723f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, pos + len); 1724f6bc909eSSimon Trimmer 1725f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1726f6bc909eSSimon Trimmer if (ret != 0) { 1727f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list end: %d\n", 1728f6bc909eSSimon Trimmer ret); 1729f6bc909eSSimon Trimmer return ERR_PTR(ret); 1730f6bc909eSSimon Trimmer } 1731f6bc909eSSimon Trimmer 1732f6bc909eSSimon Trimmer if (be32_to_cpu(val) != 0xbedead) 1733f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 1734f6bc909eSSimon Trimmer reg, be32_to_cpu(val)); 1735f6bc909eSSimon Trimmer 1736f6bc909eSSimon Trimmer /* Convert length from DSP words to bytes */ 1737f6bc909eSSimon Trimmer len *= sizeof(u32); 1738f6bc909eSSimon Trimmer 1739f6bc909eSSimon Trimmer alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 1740f6bc909eSSimon Trimmer if (!alg) 1741f6bc909eSSimon Trimmer return ERR_PTR(-ENOMEM); 1742f6bc909eSSimon Trimmer 1743f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, pos); 1744f6bc909eSSimon Trimmer 1745f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, alg, len); 1746f6bc909eSSimon Trimmer if (ret != 0) { 1747f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1748f6bc909eSSimon Trimmer kfree(alg); 1749f6bc909eSSimon Trimmer return ERR_PTR(ret); 1750f6bc909eSSimon Trimmer } 1751f6bc909eSSimon Trimmer 1752f6bc909eSSimon Trimmer return alg; 1753f6bc909eSSimon Trimmer } 1754f6bc909eSSimon Trimmer 1755f6bc909eSSimon Trimmer /** 1756f6bc909eSSimon Trimmer * cs_dsp_find_alg_region() - Finds a matching algorithm region 1757f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 1758f6bc909eSSimon Trimmer * @type: the algorithm type to match 1759f6bc909eSSimon Trimmer * @id: the algorithm id to match 1760f6bc909eSSimon Trimmer * 1761f6bc909eSSimon Trimmer * Return: Pointer to matching algorithm region, or NULL if not found. 1762f6bc909eSSimon Trimmer */ 1763f6bc909eSSimon Trimmer struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, 1764f6bc909eSSimon Trimmer int type, unsigned int id) 1765f6bc909eSSimon Trimmer { 1766f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1767f6bc909eSSimon Trimmer 17685065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 17695065cfabSCharles Keepax 1770f6bc909eSSimon Trimmer list_for_each_entry(alg_region, &dsp->alg_regions, list) { 1771f6bc909eSSimon Trimmer if (id == alg_region->alg && type == alg_region->type) 1772f6bc909eSSimon Trimmer return alg_region; 1773f6bc909eSSimon Trimmer } 1774f6bc909eSSimon Trimmer 1775f6bc909eSSimon Trimmer return NULL; 1776f6bc909eSSimon Trimmer } 1777e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_find_alg_region, FW_CS_DSP); 1778f6bc909eSSimon Trimmer 1779f6bc909eSSimon Trimmer static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, 1780f6bc909eSSimon Trimmer int type, __be32 id, 17812925748eSCharles Keepax __be32 ver, __be32 base) 1782f6bc909eSSimon Trimmer { 1783f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1784f6bc909eSSimon Trimmer 1785f6bc909eSSimon Trimmer alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1786f6bc909eSSimon Trimmer if (!alg_region) 1787f6bc909eSSimon Trimmer return ERR_PTR(-ENOMEM); 1788f6bc909eSSimon Trimmer 1789f6bc909eSSimon Trimmer alg_region->type = type; 1790f6bc909eSSimon Trimmer alg_region->alg = be32_to_cpu(id); 17912925748eSCharles Keepax alg_region->ver = be32_to_cpu(ver); 1792f6bc909eSSimon Trimmer alg_region->base = be32_to_cpu(base); 1793f6bc909eSSimon Trimmer 1794f6bc909eSSimon Trimmer list_add_tail(&alg_region->list, &dsp->alg_regions); 1795f6bc909eSSimon Trimmer 1796f6bc909eSSimon Trimmer if (dsp->fw_ver > 0) 1797f6bc909eSSimon Trimmer cs_dsp_ctl_fixup_base(dsp, alg_region); 1798f6bc909eSSimon Trimmer 1799f6bc909eSSimon Trimmer return alg_region; 1800f6bc909eSSimon Trimmer } 1801f6bc909eSSimon Trimmer 1802f6bc909eSSimon Trimmer static void cs_dsp_free_alg_regions(struct cs_dsp *dsp) 1803f6bc909eSSimon Trimmer { 1804f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1805f6bc909eSSimon Trimmer 1806f6bc909eSSimon Trimmer while (!list_empty(&dsp->alg_regions)) { 1807f6bc909eSSimon Trimmer alg_region = list_first_entry(&dsp->alg_regions, 1808f6bc909eSSimon Trimmer struct cs_dsp_alg_region, 1809f6bc909eSSimon Trimmer list); 1810f6bc909eSSimon Trimmer list_del(&alg_region->list); 1811f6bc909eSSimon Trimmer kfree(alg_region); 1812f6bc909eSSimon Trimmer } 1813f6bc909eSSimon Trimmer } 1814f6bc909eSSimon Trimmer 1815f6bc909eSSimon Trimmer static void cs_dsp_parse_wmfw_id_header(struct cs_dsp *dsp, 1816f6bc909eSSimon Trimmer struct wmfw_id_hdr *fw, int nalgs) 1817f6bc909eSSimon Trimmer { 1818f6bc909eSSimon Trimmer dsp->fw_id = be32_to_cpu(fw->id); 1819f6bc909eSSimon Trimmer dsp->fw_id_version = be32_to_cpu(fw->ver); 1820f6bc909eSSimon Trimmer 1821f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 1822f6bc909eSSimon Trimmer dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 1823f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 1824f6bc909eSSimon Trimmer nalgs); 1825f6bc909eSSimon Trimmer } 1826f6bc909eSSimon Trimmer 1827f6bc909eSSimon Trimmer static void cs_dsp_parse_wmfw_v3_id_header(struct cs_dsp *dsp, 1828f6bc909eSSimon Trimmer struct wmfw_v3_id_hdr *fw, int nalgs) 1829f6bc909eSSimon Trimmer { 1830f6bc909eSSimon Trimmer dsp->fw_id = be32_to_cpu(fw->id); 1831f6bc909eSSimon Trimmer dsp->fw_id_version = be32_to_cpu(fw->ver); 1832f6bc909eSSimon Trimmer dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 1833f6bc909eSSimon Trimmer 1834f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 1835f6bc909eSSimon Trimmer dsp->fw_id, dsp->fw_vendor_id, 1836f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff0000) >> 16, 1837f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 1838f6bc909eSSimon Trimmer nalgs); 1839f6bc909eSSimon Trimmer } 1840f6bc909eSSimon Trimmer 18412925748eSCharles Keepax static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, 18422925748eSCharles Keepax int nregions, const int *type, __be32 *base) 1843f6bc909eSSimon Trimmer { 1844f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1845f6bc909eSSimon Trimmer int i; 1846f6bc909eSSimon Trimmer 1847f6bc909eSSimon Trimmer for (i = 0; i < nregions; i++) { 18482925748eSCharles Keepax alg_region = cs_dsp_create_region(dsp, type[i], id, ver, base[i]); 1849f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1850f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1851f6bc909eSSimon Trimmer } 1852f6bc909eSSimon Trimmer 1853f6bc909eSSimon Trimmer return 0; 1854f6bc909eSSimon Trimmer } 1855f6bc909eSSimon Trimmer 1856f6bc909eSSimon Trimmer static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) 1857f6bc909eSSimon Trimmer { 1858f6bc909eSSimon Trimmer struct wmfw_adsp1_id_hdr adsp1_id; 1859f6bc909eSSimon Trimmer struct wmfw_adsp1_alg_hdr *adsp1_alg; 1860f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1861f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1862f6bc909eSSimon Trimmer unsigned int pos, len; 1863f6bc909eSSimon Trimmer size_t n_algs; 1864f6bc909eSSimon Trimmer int i, ret; 1865f6bc909eSSimon Trimmer 1866f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP1_DM); 1867f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1868f6bc909eSSimon Trimmer return -EINVAL; 1869f6bc909eSSimon Trimmer 1870f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1871f6bc909eSSimon Trimmer sizeof(adsp1_id)); 1872f6bc909eSSimon Trimmer if (ret != 0) { 1873f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1874f6bc909eSSimon Trimmer ret); 1875f6bc909eSSimon Trimmer return ret; 1876f6bc909eSSimon Trimmer } 1877f6bc909eSSimon Trimmer 1878f6bc909eSSimon Trimmer n_algs = be32_to_cpu(adsp1_id.n_algs); 1879f6bc909eSSimon Trimmer 1880f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp1_id.fw, n_algs); 1881f6bc909eSSimon Trimmer 1882f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 18832925748eSCharles Keepax adsp1_id.fw.id, adsp1_id.fw.ver, 18842925748eSCharles Keepax adsp1_id.zm); 1885f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1886f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1887f6bc909eSSimon Trimmer 1888f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 18892925748eSCharles Keepax adsp1_id.fw.id, adsp1_id.fw.ver, 18902925748eSCharles Keepax adsp1_id.dm); 1891f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1892f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1893f6bc909eSSimon Trimmer 1894f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 1895f6bc909eSSimon Trimmer pos = sizeof(adsp1_id) / sizeof(u32); 1896f6bc909eSSimon Trimmer len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 1897f6bc909eSSimon Trimmer 1898f6bc909eSSimon Trimmer adsp1_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 1899f6bc909eSSimon Trimmer if (IS_ERR(adsp1_alg)) 1900f6bc909eSSimon Trimmer return PTR_ERR(adsp1_alg); 1901f6bc909eSSimon Trimmer 1902f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 1903f6bc909eSSimon Trimmer cs_dsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1904f6bc909eSSimon Trimmer i, be32_to_cpu(adsp1_alg[i].alg.id), 1905f6bc909eSSimon Trimmer (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1906f6bc909eSSimon Trimmer (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1907f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1908f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].dm), 1909f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].zm)); 1910f6bc909eSSimon Trimmer 1911f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 1912f6bc909eSSimon Trimmer adsp1_alg[i].alg.id, 19132925748eSCharles Keepax adsp1_alg[i].alg.ver, 1914f6bc909eSSimon Trimmer adsp1_alg[i].dm); 1915f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1916f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1917f6bc909eSSimon Trimmer goto out; 1918f6bc909eSSimon Trimmer } 1919f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1920f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1921f6bc909eSSimon Trimmer len = be32_to_cpu(adsp1_alg[i + 1].dm); 1922f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp1_alg[i].dm); 1923f6bc909eSSimon Trimmer len *= 4; 1924f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1925f6bc909eSSimon Trimmer len, NULL, 0, 0, 1926f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1927f6bc909eSSimon Trimmer } else { 1928f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region DM with ID %x\n", 1929f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.id)); 1930f6bc909eSSimon Trimmer } 1931f6bc909eSSimon Trimmer } 1932f6bc909eSSimon Trimmer 1933f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 1934f6bc909eSSimon Trimmer adsp1_alg[i].alg.id, 19352925748eSCharles Keepax adsp1_alg[i].alg.ver, 1936f6bc909eSSimon Trimmer adsp1_alg[i].zm); 1937f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1938f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1939f6bc909eSSimon Trimmer goto out; 1940f6bc909eSSimon Trimmer } 1941f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1942f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1943f6bc909eSSimon Trimmer len = be32_to_cpu(adsp1_alg[i + 1].zm); 1944f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp1_alg[i].zm); 1945f6bc909eSSimon Trimmer len *= 4; 1946f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1947f6bc909eSSimon Trimmer len, NULL, 0, 0, 1948f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1949f6bc909eSSimon Trimmer } else { 1950f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 1951f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.id)); 1952f6bc909eSSimon Trimmer } 1953f6bc909eSSimon Trimmer } 1954f6bc909eSSimon Trimmer } 1955f6bc909eSSimon Trimmer 1956f6bc909eSSimon Trimmer out: 1957f6bc909eSSimon Trimmer kfree(adsp1_alg); 1958f6bc909eSSimon Trimmer return ret; 1959f6bc909eSSimon Trimmer } 1960f6bc909eSSimon Trimmer 1961f6bc909eSSimon Trimmer static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) 1962f6bc909eSSimon Trimmer { 1963f6bc909eSSimon Trimmer struct wmfw_adsp2_id_hdr adsp2_id; 1964f6bc909eSSimon Trimmer struct wmfw_adsp2_alg_hdr *adsp2_alg; 1965f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1966f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1967f6bc909eSSimon Trimmer unsigned int pos, len; 1968f6bc909eSSimon Trimmer size_t n_algs; 1969f6bc909eSSimon Trimmer int i, ret; 1970f6bc909eSSimon Trimmer 1971f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 1972f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1973f6bc909eSSimon Trimmer return -EINVAL; 1974f6bc909eSSimon Trimmer 1975f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1976f6bc909eSSimon Trimmer sizeof(adsp2_id)); 1977f6bc909eSSimon Trimmer if (ret != 0) { 1978f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1979f6bc909eSSimon Trimmer ret); 1980f6bc909eSSimon Trimmer return ret; 1981f6bc909eSSimon Trimmer } 1982f6bc909eSSimon Trimmer 1983f6bc909eSSimon Trimmer n_algs = be32_to_cpu(adsp2_id.n_algs); 1984f6bc909eSSimon Trimmer 1985f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp2_id.fw, n_algs); 1986f6bc909eSSimon Trimmer 1987f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 19882925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 19892925748eSCharles Keepax adsp2_id.xm); 1990f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1991f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1992f6bc909eSSimon Trimmer 1993f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 19942925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 19952925748eSCharles Keepax adsp2_id.ym); 1996f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1997f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1998f6bc909eSSimon Trimmer 1999f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 20002925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 20012925748eSCharles Keepax adsp2_id.zm); 2002f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 2003f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 2004f6bc909eSSimon Trimmer 2005f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 2006f6bc909eSSimon Trimmer pos = sizeof(adsp2_id) / sizeof(u32); 2007f6bc909eSSimon Trimmer len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 2008f6bc909eSSimon Trimmer 2009f6bc909eSSimon Trimmer adsp2_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 2010f6bc909eSSimon Trimmer if (IS_ERR(adsp2_alg)) 2011f6bc909eSSimon Trimmer return PTR_ERR(adsp2_alg); 2012f6bc909eSSimon Trimmer 2013f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 201469343ce9SRichard Fitzgerald cs_dsp_dbg(dsp, 2015f6bc909eSSimon Trimmer "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2016f6bc909eSSimon Trimmer i, be32_to_cpu(adsp2_alg[i].alg.id), 2017f6bc909eSSimon Trimmer (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2018f6bc909eSSimon Trimmer (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2019f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2020f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].xm), 2021f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].ym), 2022f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].zm)); 2023f6bc909eSSimon Trimmer 2024f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 2025f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 20262925748eSCharles Keepax adsp2_alg[i].alg.ver, 2027f6bc909eSSimon Trimmer adsp2_alg[i].xm); 2028f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 2029f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 2030f6bc909eSSimon Trimmer goto out; 2031f6bc909eSSimon Trimmer } 2032f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 2033f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 2034f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].xm); 2035f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].xm); 2036f6bc909eSSimon Trimmer len *= 4; 2037f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 2038f6bc909eSSimon Trimmer len, NULL, 0, 0, 2039f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 2040f6bc909eSSimon Trimmer } else { 2041f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region XM with ID %x\n", 2042f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 2043f6bc909eSSimon Trimmer } 2044f6bc909eSSimon Trimmer } 2045f6bc909eSSimon Trimmer 2046f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 2047f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 20482925748eSCharles Keepax adsp2_alg[i].alg.ver, 2049f6bc909eSSimon Trimmer adsp2_alg[i].ym); 2050f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 2051f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 2052f6bc909eSSimon Trimmer goto out; 2053f6bc909eSSimon Trimmer } 2054f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 2055f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 2056f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].ym); 2057f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].ym); 2058f6bc909eSSimon Trimmer len *= 4; 2059f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 2060f6bc909eSSimon Trimmer len, NULL, 0, 0, 2061f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 2062f6bc909eSSimon Trimmer } else { 2063f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region YM with ID %x\n", 2064f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 2065f6bc909eSSimon Trimmer } 2066f6bc909eSSimon Trimmer } 2067f6bc909eSSimon Trimmer 2068f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 2069f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 20702925748eSCharles Keepax adsp2_alg[i].alg.ver, 2071f6bc909eSSimon Trimmer adsp2_alg[i].zm); 2072f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 2073f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 2074f6bc909eSSimon Trimmer goto out; 2075f6bc909eSSimon Trimmer } 2076f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 2077f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 2078f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].zm); 2079f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].zm); 2080f6bc909eSSimon Trimmer len *= 4; 2081f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 2082f6bc909eSSimon Trimmer len, NULL, 0, 0, 2083f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 2084f6bc909eSSimon Trimmer } else { 2085f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 2086f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 2087f6bc909eSSimon Trimmer } 2088f6bc909eSSimon Trimmer } 2089f6bc909eSSimon Trimmer } 2090f6bc909eSSimon Trimmer 2091f6bc909eSSimon Trimmer out: 2092f6bc909eSSimon Trimmer kfree(adsp2_alg); 2093f6bc909eSSimon Trimmer return ret; 2094f6bc909eSSimon Trimmer } 2095f6bc909eSSimon Trimmer 20962925748eSCharles Keepax static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, 2097f6bc909eSSimon Trimmer __be32 xm_base, __be32 ym_base) 2098f6bc909eSSimon Trimmer { 2099f6bc909eSSimon Trimmer static const int types[] = { 2100f6bc909eSSimon Trimmer WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 2101f6bc909eSSimon Trimmer WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 2102f6bc909eSSimon Trimmer }; 2103f6bc909eSSimon Trimmer __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 2104f6bc909eSSimon Trimmer 21052925748eSCharles Keepax return cs_dsp_create_regions(dsp, id, ver, ARRAY_SIZE(types), types, bases); 2106f6bc909eSSimon Trimmer } 2107f6bc909eSSimon Trimmer 2108f6bc909eSSimon Trimmer static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) 2109f6bc909eSSimon Trimmer { 2110f6bc909eSSimon Trimmer struct wmfw_halo_id_hdr halo_id; 2111f6bc909eSSimon Trimmer struct wmfw_halo_alg_hdr *halo_alg; 2112f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 2113f6bc909eSSimon Trimmer unsigned int pos, len; 2114f6bc909eSSimon Trimmer size_t n_algs; 2115f6bc909eSSimon Trimmer int i, ret; 2116f6bc909eSSimon Trimmer 2117f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 2118f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 2119f6bc909eSSimon Trimmer return -EINVAL; 2120f6bc909eSSimon Trimmer 2121f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 2122f6bc909eSSimon Trimmer sizeof(halo_id)); 2123f6bc909eSSimon Trimmer if (ret != 0) { 2124f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 2125f6bc909eSSimon Trimmer ret); 2126f6bc909eSSimon Trimmer return ret; 2127f6bc909eSSimon Trimmer } 2128f6bc909eSSimon Trimmer 2129f6bc909eSSimon Trimmer n_algs = be32_to_cpu(halo_id.n_algs); 2130f6bc909eSSimon Trimmer 2131f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_v3_id_header(dsp, &halo_id.fw, n_algs); 2132f6bc909eSSimon Trimmer 21332925748eSCharles Keepax ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, halo_id.fw.ver, 2134f6bc909eSSimon Trimmer halo_id.xm_base, halo_id.ym_base); 2135f6bc909eSSimon Trimmer if (ret) 2136f6bc909eSSimon Trimmer return ret; 2137f6bc909eSSimon Trimmer 2138f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 2139f6bc909eSSimon Trimmer pos = sizeof(halo_id) / sizeof(u32); 2140f6bc909eSSimon Trimmer len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 2141f6bc909eSSimon Trimmer 2142f6bc909eSSimon Trimmer halo_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 2143f6bc909eSSimon Trimmer if (IS_ERR(halo_alg)) 2144f6bc909eSSimon Trimmer return PTR_ERR(halo_alg); 2145f6bc909eSSimon Trimmer 2146f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 214769343ce9SRichard Fitzgerald cs_dsp_dbg(dsp, 2148f6bc909eSSimon Trimmer "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 2149f6bc909eSSimon Trimmer i, be32_to_cpu(halo_alg[i].alg.id), 2150f6bc909eSSimon Trimmer (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 2151f6bc909eSSimon Trimmer (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 2152f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 2153f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].xm_base), 2154f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].ym_base)); 2155f6bc909eSSimon Trimmer 2156f6bc909eSSimon Trimmer ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id, 21572925748eSCharles Keepax halo_alg[i].alg.ver, 2158f6bc909eSSimon Trimmer halo_alg[i].xm_base, 2159f6bc909eSSimon Trimmer halo_alg[i].ym_base); 2160f6bc909eSSimon Trimmer if (ret) 2161f6bc909eSSimon Trimmer goto out; 2162f6bc909eSSimon Trimmer } 2163f6bc909eSSimon Trimmer 2164f6bc909eSSimon Trimmer out: 2165f6bc909eSSimon Trimmer kfree(halo_alg); 2166f6bc909eSSimon Trimmer return ret; 2167f6bc909eSSimon Trimmer } 2168f6bc909eSSimon Trimmer 2169f6bc909eSSimon Trimmer static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware, 2170f6bc909eSSimon Trimmer const char *file) 2171f6bc909eSSimon Trimmer { 2172f6bc909eSSimon Trimmer LIST_HEAD(buf_list); 2173f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 2174f6bc909eSSimon Trimmer struct wmfw_coeff_hdr *hdr; 2175f6bc909eSSimon Trimmer struct wmfw_coeff_item *blk; 2176f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 2177f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 2178f6bc909eSSimon Trimmer const char *region_name; 21792925748eSCharles Keepax int ret, pos, blocks, type, offset, reg, version; 218040a34ae7SCharles Keepax char *text = NULL; 2181f6bc909eSSimon Trimmer struct cs_dsp_buf *buf; 2182f6bc909eSSimon Trimmer 2183f6bc909eSSimon Trimmer if (!firmware) 2184f6bc909eSSimon Trimmer return 0; 2185f6bc909eSSimon Trimmer 2186f6bc909eSSimon Trimmer ret = -EINVAL; 2187f6bc909eSSimon Trimmer 2188f6bc909eSSimon Trimmer if (sizeof(*hdr) >= firmware->size) { 2189f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: coefficient file too short, %zu bytes\n", 2190f6bc909eSSimon Trimmer file, firmware->size); 2191f6bc909eSSimon Trimmer goto out_fw; 2192f6bc909eSSimon Trimmer } 2193f6bc909eSSimon Trimmer 2194f6bc909eSSimon Trimmer hdr = (void *)&firmware->data[0]; 2195f6bc909eSSimon Trimmer if (memcmp(hdr->magic, "WMDR", 4) != 0) { 2196f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid coefficient magic\n", file); 2197f6bc909eSSimon Trimmer goto out_fw; 2198f6bc909eSSimon Trimmer } 2199f6bc909eSSimon Trimmer 2200f6bc909eSSimon Trimmer switch (be32_to_cpu(hdr->rev) & 0xff) { 2201f6bc909eSSimon Trimmer case 1: 2202dcee7676SCharles Keepax case 2: 2203f6bc909eSSimon Trimmer break; 2204f6bc909eSSimon Trimmer default: 2205f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2206f6bc909eSSimon Trimmer file, be32_to_cpu(hdr->rev) & 0xff); 2207f6bc909eSSimon Trimmer ret = -EINVAL; 2208f6bc909eSSimon Trimmer goto out_fw; 2209f6bc909eSSimon Trimmer } 2210f6bc909eSSimon Trimmer 22118f4007e8SRichard Fitzgerald cs_dsp_info(dsp, "%s: v%d.%d.%d\n", file, 2212f6bc909eSSimon Trimmer (le32_to_cpu(hdr->ver) >> 16) & 0xff, 2213f6bc909eSSimon Trimmer (le32_to_cpu(hdr->ver) >> 8) & 0xff, 2214f6bc909eSSimon Trimmer le32_to_cpu(hdr->ver) & 0xff); 2215f6bc909eSSimon Trimmer 2216f6bc909eSSimon Trimmer pos = le32_to_cpu(hdr->len); 2217f6bc909eSSimon Trimmer 2218f6bc909eSSimon Trimmer blocks = 0; 2219959fe01eSRichard Fitzgerald while (pos < firmware->size) { 2220959fe01eSRichard Fitzgerald /* Is there enough data for a complete block header? */ 2221959fe01eSRichard Fitzgerald if (sizeof(*blk) > firmware->size - pos) { 2222959fe01eSRichard Fitzgerald ret = -EOVERFLOW; 2223959fe01eSRichard Fitzgerald goto out_fw; 2224959fe01eSRichard Fitzgerald } 2225959fe01eSRichard Fitzgerald 2226f6bc909eSSimon Trimmer blk = (void *)(&firmware->data[pos]); 2227f6bc909eSSimon Trimmer 22286598afa9SRichard Fitzgerald if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) { 22296598afa9SRichard Fitzgerald ret = -EOVERFLOW; 22306598afa9SRichard Fitzgerald goto out_fw; 22316598afa9SRichard Fitzgerald } 22326598afa9SRichard Fitzgerald 2233f6bc909eSSimon Trimmer type = le16_to_cpu(blk->type); 2234f6bc909eSSimon Trimmer offset = le16_to_cpu(blk->offset); 22352925748eSCharles Keepax version = le32_to_cpu(blk->ver) >> 8; 2236f6bc909eSSimon Trimmer 2237f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 2238f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->id), 2239f6bc909eSSimon Trimmer (le32_to_cpu(blk->ver) >> 16) & 0xff, 2240f6bc909eSSimon Trimmer (le32_to_cpu(blk->ver) >> 8) & 0xff, 2241f6bc909eSSimon Trimmer le32_to_cpu(blk->ver) & 0xff); 2242f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 2243f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), offset, type); 2244f6bc909eSSimon Trimmer 2245f6bc909eSSimon Trimmer reg = 0; 2246f6bc909eSSimon Trimmer region_name = "Unknown"; 2247f6bc909eSSimon Trimmer switch (type) { 2248f6bc909eSSimon Trimmer case (WMFW_NAME_TEXT << 8): 224940a34ae7SCharles Keepax text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL); 225040a34ae7SCharles Keepax break; 2251f6bc909eSSimon Trimmer case (WMFW_INFO_TEXT << 8): 2252f6bc909eSSimon Trimmer case (WMFW_METADATA << 8): 2253f6bc909eSSimon Trimmer break; 2254f6bc909eSSimon Trimmer case (WMFW_ABSOLUTE << 8): 2255f6bc909eSSimon Trimmer /* 2256f6bc909eSSimon Trimmer * Old files may use this for global 2257f6bc909eSSimon Trimmer * coefficients. 2258f6bc909eSSimon Trimmer */ 2259f6bc909eSSimon Trimmer if (le32_to_cpu(blk->id) == dsp->fw_id && 2260f6bc909eSSimon Trimmer offset == 0) { 2261f6bc909eSSimon Trimmer region_name = "global coefficients"; 2262f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2263f6bc909eSSimon Trimmer if (!mem) { 2264f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No ZM\n"); 2265f6bc909eSSimon Trimmer break; 2266f6bc909eSSimon Trimmer } 2267f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, 0); 2268f6bc909eSSimon Trimmer 2269f6bc909eSSimon Trimmer } else { 2270f6bc909eSSimon Trimmer region_name = "register"; 2271f6bc909eSSimon Trimmer reg = offset; 2272f6bc909eSSimon Trimmer } 2273f6bc909eSSimon Trimmer break; 2274f6bc909eSSimon Trimmer 2275f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 2276f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 2277f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 2278f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 2279f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 2280f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 2281f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 2282f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2283f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), 2284f6bc909eSSimon Trimmer type, le32_to_cpu(blk->id)); 2285f6bc909eSSimon Trimmer 2286ad249195SRichard Fitzgerald region_name = cs_dsp_mem_region_name(type); 2287f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2288f6bc909eSSimon Trimmer if (!mem) { 2289f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", type); 2290f6bc909eSSimon Trimmer break; 2291f6bc909eSSimon Trimmer } 2292f6bc909eSSimon Trimmer 2293f6bc909eSSimon Trimmer alg_region = cs_dsp_find_alg_region(dsp, type, 2294f6bc909eSSimon Trimmer le32_to_cpu(blk->id)); 2295f6bc909eSSimon Trimmer if (alg_region) { 22962925748eSCharles Keepax if (version != alg_region->ver) 22972925748eSCharles Keepax cs_dsp_warn(dsp, 22982925748eSCharles Keepax "Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n", 22992925748eSCharles Keepax (version >> 16) & 0xFF, 23002925748eSCharles Keepax (version >> 8) & 0xFF, 23012925748eSCharles Keepax version & 0xFF, 23022925748eSCharles Keepax (alg_region->ver >> 16) & 0xFF, 23032925748eSCharles Keepax (alg_region->ver >> 8) & 0xFF, 23042925748eSCharles Keepax alg_region->ver & 0xFF); 23052925748eSCharles Keepax 2306f6bc909eSSimon Trimmer reg = alg_region->base; 2307f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, reg); 2308f6bc909eSSimon Trimmer reg += offset; 2309f6bc909eSSimon Trimmer } else { 2310ad249195SRichard Fitzgerald cs_dsp_err(dsp, "No %s for algorithm %x\n", 2311ad249195SRichard Fitzgerald region_name, le32_to_cpu(blk->id)); 2312f6bc909eSSimon Trimmer } 2313f6bc909eSSimon Trimmer break; 2314f6bc909eSSimon Trimmer 2315f6bc909eSSimon Trimmer default: 2316f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 2317f6bc909eSSimon Trimmer file, blocks, type, pos); 2318f6bc909eSSimon Trimmer break; 2319f6bc909eSSimon Trimmer } 2320f6bc909eSSimon Trimmer 232140a34ae7SCharles Keepax if (text) { 232240a34ae7SCharles Keepax memcpy(text, blk->data, le32_to_cpu(blk->len)); 232340a34ae7SCharles Keepax cs_dsp_info(dsp, "%s: %s\n", dsp->fw_name, text); 232440a34ae7SCharles Keepax kfree(text); 232540a34ae7SCharles Keepax text = NULL; 232640a34ae7SCharles Keepax } 232740a34ae7SCharles Keepax 2328f6bc909eSSimon Trimmer if (reg) { 2329f6bc909eSSimon Trimmer buf = cs_dsp_buf_alloc(blk->data, 2330f6bc909eSSimon Trimmer le32_to_cpu(blk->len), 2331f6bc909eSSimon Trimmer &buf_list); 2332f6bc909eSSimon Trimmer if (!buf) { 2333f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 2334f6bc909eSSimon Trimmer ret = -ENOMEM; 2335f6bc909eSSimon Trimmer goto out_fw; 2336f6bc909eSSimon Trimmer } 2337f6bc909eSSimon Trimmer 2338f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 2339f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), 2340f6bc909eSSimon Trimmer reg); 2341f6bc909eSSimon Trimmer ret = regmap_raw_write_async(regmap, reg, buf->buf, 2342f6bc909eSSimon Trimmer le32_to_cpu(blk->len)); 2343f6bc909eSSimon Trimmer if (ret != 0) { 2344f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2345f6bc909eSSimon Trimmer "%s.%d: Failed to write to %x in %s: %d\n", 2346f6bc909eSSimon Trimmer file, blocks, reg, region_name, ret); 2347f6bc909eSSimon Trimmer } 2348f6bc909eSSimon Trimmer } 2349f6bc909eSSimon Trimmer 2350f6bc909eSSimon Trimmer pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 2351f6bc909eSSimon Trimmer blocks++; 2352f6bc909eSSimon Trimmer } 2353f6bc909eSSimon Trimmer 2354f6bc909eSSimon Trimmer ret = regmap_async_complete(regmap); 2355f6bc909eSSimon Trimmer if (ret != 0) 2356f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 2357f6bc909eSSimon Trimmer 2358f6bc909eSSimon Trimmer if (pos > firmware->size) 2359f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 2360f6bc909eSSimon Trimmer file, blocks, pos - firmware->size); 2361f6bc909eSSimon Trimmer 2362f6bc909eSSimon Trimmer cs_dsp_debugfs_save_binname(dsp, file); 2363f6bc909eSSimon Trimmer 2364f6bc909eSSimon Trimmer out_fw: 2365f6bc909eSSimon Trimmer regmap_async_complete(regmap); 2366f6bc909eSSimon Trimmer cs_dsp_buf_free(&buf_list); 236740a34ae7SCharles Keepax kfree(text); 23686598afa9SRichard Fitzgerald 23696598afa9SRichard Fitzgerald if (ret == -EOVERFLOW) 23706598afa9SRichard Fitzgerald cs_dsp_err(dsp, "%s: file content overflows file data\n", file); 23716598afa9SRichard Fitzgerald 2372f6bc909eSSimon Trimmer return ret; 2373f6bc909eSSimon Trimmer } 2374f6bc909eSSimon Trimmer 2375f6bc909eSSimon Trimmer static int cs_dsp_create_name(struct cs_dsp *dsp) 2376f6bc909eSSimon Trimmer { 2377f6bc909eSSimon Trimmer if (!dsp->name) { 2378f6bc909eSSimon Trimmer dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2379f6bc909eSSimon Trimmer dsp->num); 2380f6bc909eSSimon Trimmer if (!dsp->name) 2381f6bc909eSSimon Trimmer return -ENOMEM; 2382f6bc909eSSimon Trimmer } 2383f6bc909eSSimon Trimmer 2384f6bc909eSSimon Trimmer return 0; 2385f6bc909eSSimon Trimmer } 2386f6bc909eSSimon Trimmer 2387f6bc909eSSimon Trimmer static int cs_dsp_common_init(struct cs_dsp *dsp) 2388f6bc909eSSimon Trimmer { 2389f6bc909eSSimon Trimmer int ret; 2390f6bc909eSSimon Trimmer 2391f6bc909eSSimon Trimmer ret = cs_dsp_create_name(dsp); 2392f6bc909eSSimon Trimmer if (ret) 2393f6bc909eSSimon Trimmer return ret; 2394f6bc909eSSimon Trimmer 2395f6bc909eSSimon Trimmer INIT_LIST_HEAD(&dsp->alg_regions); 2396f6bc909eSSimon Trimmer INIT_LIST_HEAD(&dsp->ctl_list); 2397f6bc909eSSimon Trimmer 2398f6bc909eSSimon Trimmer mutex_init(&dsp->pwr_lock); 2399f6bc909eSSimon Trimmer 240066626b15SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 240166626b15SRichard Fitzgerald /* Ensure this is invalid if client never provides a debugfs root */ 240266626b15SRichard Fitzgerald dsp->debugfs_root = ERR_PTR(-ENODEV); 240366626b15SRichard Fitzgerald #endif 240466626b15SRichard Fitzgerald 2405f6bc909eSSimon Trimmer return 0; 2406f6bc909eSSimon Trimmer } 2407f6bc909eSSimon Trimmer 2408f6bc909eSSimon Trimmer /** 2409f6bc909eSSimon Trimmer * cs_dsp_adsp1_init() - Initialise a cs_dsp structure representing a ADSP1 device 2410f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2411f6bc909eSSimon Trimmer * 2412f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2413f6bc909eSSimon Trimmer */ 2414f6bc909eSSimon Trimmer int cs_dsp_adsp1_init(struct cs_dsp *dsp) 2415f6bc909eSSimon Trimmer { 2416f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp1_ops; 2417f6bc909eSSimon Trimmer 2418f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 2419f6bc909eSSimon Trimmer } 2420e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_init, FW_CS_DSP); 2421f6bc909eSSimon Trimmer 2422f6bc909eSSimon Trimmer /** 2423f6bc909eSSimon Trimmer * cs_dsp_adsp1_power_up() - Load and start the named firmware 2424f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2425f6bc909eSSimon Trimmer * @wmfw_firmware: the firmware to be sent 2426f6bc909eSSimon Trimmer * @wmfw_filename: file name of firmware to be sent 2427f6bc909eSSimon Trimmer * @coeff_firmware: the coefficient data to be sent 2428f6bc909eSSimon Trimmer * @coeff_filename: file name of coefficient to data be sent 2429f6bc909eSSimon Trimmer * @fw_name: the user-friendly firmware name 2430f6bc909eSSimon Trimmer * 2431f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2432f6bc909eSSimon Trimmer */ 2433f6bc909eSSimon Trimmer int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, 2434f6bc909eSSimon Trimmer const struct firmware *wmfw_firmware, char *wmfw_filename, 2435f6bc909eSSimon Trimmer const struct firmware *coeff_firmware, char *coeff_filename, 2436f6bc909eSSimon Trimmer const char *fw_name) 2437f6bc909eSSimon Trimmer { 2438f6bc909eSSimon Trimmer unsigned int val; 2439f6bc909eSSimon Trimmer int ret; 2440f6bc909eSSimon Trimmer 2441f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2442f6bc909eSSimon Trimmer 2443f6bc909eSSimon Trimmer dsp->fw_name = fw_name; 2444f6bc909eSSimon Trimmer 2445f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2446f6bc909eSSimon Trimmer ADSP1_SYS_ENA, ADSP1_SYS_ENA); 2447f6bc909eSSimon Trimmer 2448f6bc909eSSimon Trimmer /* 2449f6bc909eSSimon Trimmer * For simplicity set the DSP clock rate to be the 2450f6bc909eSSimon Trimmer * SYSCLK rate rather than making it configurable. 2451f6bc909eSSimon Trimmer */ 2452f6bc909eSSimon Trimmer if (dsp->sysclk_reg) { 2453f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 2454f6bc909eSSimon Trimmer if (ret != 0) { 2455f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 2456f6bc909eSSimon Trimmer goto err_mutex; 2457f6bc909eSSimon Trimmer } 2458f6bc909eSSimon Trimmer 2459f6bc909eSSimon Trimmer val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 2460f6bc909eSSimon Trimmer 2461f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, 2462f6bc909eSSimon Trimmer dsp->base + ADSP1_CONTROL_31, 2463f6bc909eSSimon Trimmer ADSP1_CLK_SEL_MASK, val); 2464f6bc909eSSimon Trimmer if (ret != 0) { 2465f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2466f6bc909eSSimon Trimmer goto err_mutex; 2467f6bc909eSSimon Trimmer } 2468f6bc909eSSimon Trimmer } 2469f6bc909eSSimon Trimmer 2470f6bc909eSSimon Trimmer ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename); 2471f6bc909eSSimon Trimmer if (ret != 0) 2472f6bc909eSSimon Trimmer goto err_ena; 2473f6bc909eSSimon Trimmer 2474f6bc909eSSimon Trimmer ret = cs_dsp_adsp1_setup_algs(dsp); 2475f6bc909eSSimon Trimmer if (ret != 0) 2476f6bc909eSSimon Trimmer goto err_ena; 2477f6bc909eSSimon Trimmer 2478f6bc909eSSimon Trimmer ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename); 2479f6bc909eSSimon Trimmer if (ret != 0) 2480f6bc909eSSimon Trimmer goto err_ena; 2481f6bc909eSSimon Trimmer 2482f6bc909eSSimon Trimmer /* Initialize caches for enabled and unset controls */ 2483f6bc909eSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 2484f6bc909eSSimon Trimmer if (ret != 0) 2485f6bc909eSSimon Trimmer goto err_ena; 2486f6bc909eSSimon Trimmer 2487f6bc909eSSimon Trimmer /* Sync set controls */ 2488f6bc909eSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 2489f6bc909eSSimon Trimmer if (ret != 0) 2490f6bc909eSSimon Trimmer goto err_ena; 2491f6bc909eSSimon Trimmer 2492f6bc909eSSimon Trimmer dsp->booted = true; 2493f6bc909eSSimon Trimmer 2494f6bc909eSSimon Trimmer /* Start the core running */ 2495f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2496f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START, 2497f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START); 2498f6bc909eSSimon Trimmer 2499f6bc909eSSimon Trimmer dsp->running = true; 2500f6bc909eSSimon Trimmer 2501f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2502f6bc909eSSimon Trimmer 2503f6bc909eSSimon Trimmer return 0; 2504f6bc909eSSimon Trimmer 2505f6bc909eSSimon Trimmer err_ena: 2506f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2507f6bc909eSSimon Trimmer ADSP1_SYS_ENA, 0); 2508f6bc909eSSimon Trimmer err_mutex: 2509f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2510f6bc909eSSimon Trimmer return ret; 2511f6bc909eSSimon Trimmer } 2512e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_up, FW_CS_DSP); 2513f6bc909eSSimon Trimmer 2514f6bc909eSSimon Trimmer /** 2515f6bc909eSSimon Trimmer * cs_dsp_adsp1_power_down() - Halts the DSP 2516f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2517f6bc909eSSimon Trimmer */ 2518f6bc909eSSimon Trimmer void cs_dsp_adsp1_power_down(struct cs_dsp *dsp) 2519f6bc909eSSimon Trimmer { 2520f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2521f6bc909eSSimon Trimmer 2522f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2523f6bc909eSSimon Trimmer 2524f6bc909eSSimon Trimmer dsp->running = false; 2525f6bc909eSSimon Trimmer dsp->booted = false; 2526f6bc909eSSimon Trimmer 2527f6bc909eSSimon Trimmer /* Halt the core */ 2528f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2529f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START, 0); 2530f6bc909eSSimon Trimmer 2531f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 2532f6bc909eSSimon Trimmer ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 2533f6bc909eSSimon Trimmer 2534f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2535f6bc909eSSimon Trimmer ADSP1_SYS_ENA, 0); 2536f6bc909eSSimon Trimmer 2537f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) 2538f6bc909eSSimon Trimmer ctl->enabled = 0; 2539f6bc909eSSimon Trimmer 2540f6bc909eSSimon Trimmer cs_dsp_free_alg_regions(dsp); 2541f6bc909eSSimon Trimmer 2542f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2543f6bc909eSSimon Trimmer } 2544e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_down, FW_CS_DSP); 2545f6bc909eSSimon Trimmer 2546f6bc909eSSimon Trimmer static int cs_dsp_adsp2v2_enable_core(struct cs_dsp *dsp) 2547f6bc909eSSimon Trimmer { 2548f6bc909eSSimon Trimmer unsigned int val; 2549f6bc909eSSimon Trimmer int ret, count; 2550f6bc909eSSimon Trimmer 2551f6bc909eSSimon Trimmer /* Wait for the RAM to start, should be near instantaneous */ 2552f6bc909eSSimon Trimmer for (count = 0; count < 10; ++count) { 2553f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 2554f6bc909eSSimon Trimmer if (ret != 0) 2555f6bc909eSSimon Trimmer return ret; 2556f6bc909eSSimon Trimmer 2557f6bc909eSSimon Trimmer if (val & ADSP2_RAM_RDY) 2558f6bc909eSSimon Trimmer break; 2559f6bc909eSSimon Trimmer 2560f6bc909eSSimon Trimmer usleep_range(250, 500); 2561f6bc909eSSimon Trimmer } 2562f6bc909eSSimon Trimmer 2563f6bc909eSSimon Trimmer if (!(val & ADSP2_RAM_RDY)) { 2564f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to start DSP RAM\n"); 2565f6bc909eSSimon Trimmer return -EBUSY; 2566f6bc909eSSimon Trimmer } 2567f6bc909eSSimon Trimmer 2568f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "RAM ready after %d polls\n", count); 2569f6bc909eSSimon Trimmer 2570f6bc909eSSimon Trimmer return 0; 2571f6bc909eSSimon Trimmer } 2572f6bc909eSSimon Trimmer 2573f6bc909eSSimon Trimmer static int cs_dsp_adsp2_enable_core(struct cs_dsp *dsp) 2574f6bc909eSSimon Trimmer { 2575f6bc909eSSimon Trimmer int ret; 2576f6bc909eSSimon Trimmer 2577f6bc909eSSimon Trimmer ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 2578f6bc909eSSimon Trimmer ADSP2_SYS_ENA, ADSP2_SYS_ENA); 2579f6bc909eSSimon Trimmer if (ret != 0) 2580f6bc909eSSimon Trimmer return ret; 2581f6bc909eSSimon Trimmer 2582f6bc909eSSimon Trimmer return cs_dsp_adsp2v2_enable_core(dsp); 2583f6bc909eSSimon Trimmer } 2584f6bc909eSSimon Trimmer 2585f6bc909eSSimon Trimmer static int cs_dsp_adsp2_lock(struct cs_dsp *dsp, unsigned int lock_regions) 2586f6bc909eSSimon Trimmer { 2587f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 2588f6bc909eSSimon Trimmer unsigned int code0, code1, lock_reg; 2589f6bc909eSSimon Trimmer 2590f6bc909eSSimon Trimmer if (!(lock_regions & CS_ADSP2_REGION_ALL)) 2591f6bc909eSSimon Trimmer return 0; 2592f6bc909eSSimon Trimmer 2593f6bc909eSSimon Trimmer lock_regions &= CS_ADSP2_REGION_ALL; 2594f6bc909eSSimon Trimmer lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 2595f6bc909eSSimon Trimmer 2596f6bc909eSSimon Trimmer while (lock_regions) { 2597f6bc909eSSimon Trimmer code0 = code1 = 0; 2598f6bc909eSSimon Trimmer if (lock_regions & BIT(0)) { 2599f6bc909eSSimon Trimmer code0 = ADSP2_LOCK_CODE_0; 2600f6bc909eSSimon Trimmer code1 = ADSP2_LOCK_CODE_1; 2601f6bc909eSSimon Trimmer } 2602f6bc909eSSimon Trimmer if (lock_regions & BIT(1)) { 2603f6bc909eSSimon Trimmer code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 2604f6bc909eSSimon Trimmer code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 2605f6bc909eSSimon Trimmer } 2606f6bc909eSSimon Trimmer regmap_write(regmap, lock_reg, code0); 2607f6bc909eSSimon Trimmer regmap_write(regmap, lock_reg, code1); 2608f6bc909eSSimon Trimmer lock_regions >>= 2; 2609f6bc909eSSimon Trimmer lock_reg += 2; 2610f6bc909eSSimon Trimmer } 2611f6bc909eSSimon Trimmer 2612f6bc909eSSimon Trimmer return 0; 2613f6bc909eSSimon Trimmer } 2614f6bc909eSSimon Trimmer 2615f6bc909eSSimon Trimmer static int cs_dsp_adsp2_enable_memory(struct cs_dsp *dsp) 2616f6bc909eSSimon Trimmer { 2617f6bc909eSSimon Trimmer return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2618f6bc909eSSimon Trimmer ADSP2_MEM_ENA, ADSP2_MEM_ENA); 2619f6bc909eSSimon Trimmer } 2620f6bc909eSSimon Trimmer 2621f6bc909eSSimon Trimmer static void cs_dsp_adsp2_disable_memory(struct cs_dsp *dsp) 2622f6bc909eSSimon Trimmer { 2623f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2624f6bc909eSSimon Trimmer ADSP2_MEM_ENA, 0); 2625f6bc909eSSimon Trimmer } 2626f6bc909eSSimon Trimmer 2627f6bc909eSSimon Trimmer static void cs_dsp_adsp2_disable_core(struct cs_dsp *dsp) 2628f6bc909eSSimon Trimmer { 2629f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2630f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2631f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 2632f6bc909eSSimon Trimmer 2633f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2634f6bc909eSSimon Trimmer ADSP2_SYS_ENA, 0); 2635f6bc909eSSimon Trimmer } 2636f6bc909eSSimon Trimmer 2637f6bc909eSSimon Trimmer static void cs_dsp_adsp2v2_disable_core(struct cs_dsp *dsp) 2638f6bc909eSSimon Trimmer { 2639f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2640f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2641f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 2642f6bc909eSSimon Trimmer } 2643f6bc909eSSimon Trimmer 2644f6bc909eSSimon Trimmer static int cs_dsp_halo_configure_mpu(struct cs_dsp *dsp, unsigned int lock_regions) 2645f6bc909eSSimon Trimmer { 2646f6bc909eSSimon Trimmer struct reg_sequence config[] = { 2647f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 2648f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 2649f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 2650f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 2651f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 2652f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 2653f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 2654f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 2655f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 2656f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 2657f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 2658f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 2659f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 2660f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 2661f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 2662f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 2663f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 2664f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 2665f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 2666f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 2667f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 2668f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 2669f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 2670f6bc909eSSimon Trimmer }; 2671f6bc909eSSimon Trimmer 2672f6bc909eSSimon Trimmer return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 2673f6bc909eSSimon Trimmer } 2674f6bc909eSSimon Trimmer 2675f6bc909eSSimon Trimmer /** 2676f6bc909eSSimon Trimmer * cs_dsp_set_dspclk() - Applies the given frequency to the given cs_dsp 2677f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2678f6bc909eSSimon Trimmer * @freq: clock rate to set 2679f6bc909eSSimon Trimmer * 2680f6bc909eSSimon Trimmer * This is only for use on ADSP2 cores. 2681f6bc909eSSimon Trimmer * 2682f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2683f6bc909eSSimon Trimmer */ 2684f6bc909eSSimon Trimmer int cs_dsp_set_dspclk(struct cs_dsp *dsp, unsigned int freq) 2685f6bc909eSSimon Trimmer { 2686f6bc909eSSimon Trimmer int ret; 2687f6bc909eSSimon Trimmer 2688f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 2689f6bc909eSSimon Trimmer ADSP2_CLK_SEL_MASK, 2690f6bc909eSSimon Trimmer freq << ADSP2_CLK_SEL_SHIFT); 2691f6bc909eSSimon Trimmer if (ret) 2692f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2693f6bc909eSSimon Trimmer 2694f6bc909eSSimon Trimmer return ret; 2695f6bc909eSSimon Trimmer } 2696e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_set_dspclk, FW_CS_DSP); 2697f6bc909eSSimon Trimmer 2698f6bc909eSSimon Trimmer static void cs_dsp_stop_watchdog(struct cs_dsp *dsp) 2699f6bc909eSSimon Trimmer { 2700f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 2701f6bc909eSSimon Trimmer ADSP2_WDT_ENA_MASK, 0); 2702f6bc909eSSimon Trimmer } 2703f6bc909eSSimon Trimmer 2704f6bc909eSSimon Trimmer static void cs_dsp_halo_stop_watchdog(struct cs_dsp *dsp) 2705f6bc909eSSimon Trimmer { 2706f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 2707f6bc909eSSimon Trimmer HALO_WDT_EN_MASK, 0); 2708f6bc909eSSimon Trimmer } 2709f6bc909eSSimon Trimmer 2710f6bc909eSSimon Trimmer /** 2711f6bc909eSSimon Trimmer * cs_dsp_power_up() - Downloads firmware to the DSP 2712f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2713f6bc909eSSimon Trimmer * @wmfw_firmware: the firmware to be sent 2714f6bc909eSSimon Trimmer * @wmfw_filename: file name of firmware to be sent 2715f6bc909eSSimon Trimmer * @coeff_firmware: the coefficient data to be sent 2716f6bc909eSSimon Trimmer * @coeff_filename: file name of coefficient to data be sent 2717f6bc909eSSimon Trimmer * @fw_name: the user-friendly firmware name 2718f6bc909eSSimon Trimmer * 2719f6bc909eSSimon Trimmer * This function is used on ADSP2 and Halo DSP cores, it powers-up the DSP core 2720f6bc909eSSimon Trimmer * and downloads the firmware but does not start the firmware running. The 2721f6bc909eSSimon Trimmer * cs_dsp booted flag will be set once completed and if the core has a low-power 2722f6bc909eSSimon Trimmer * memory retention mode it will be put into this state after the firmware is 2723f6bc909eSSimon Trimmer * downloaded. 2724f6bc909eSSimon Trimmer * 2725f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2726f6bc909eSSimon Trimmer */ 2727f6bc909eSSimon Trimmer int cs_dsp_power_up(struct cs_dsp *dsp, 2728f6bc909eSSimon Trimmer const struct firmware *wmfw_firmware, char *wmfw_filename, 2729f6bc909eSSimon Trimmer const struct firmware *coeff_firmware, char *coeff_filename, 2730f6bc909eSSimon Trimmer const char *fw_name) 2731f6bc909eSSimon Trimmer { 2732f6bc909eSSimon Trimmer int ret; 2733f6bc909eSSimon Trimmer 2734f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2735f6bc909eSSimon Trimmer 2736f6bc909eSSimon Trimmer dsp->fw_name = fw_name; 2737f6bc909eSSimon Trimmer 2738f6bc909eSSimon Trimmer if (dsp->ops->enable_memory) { 2739f6bc909eSSimon Trimmer ret = dsp->ops->enable_memory(dsp); 2740f6bc909eSSimon Trimmer if (ret != 0) 2741f6bc909eSSimon Trimmer goto err_mutex; 2742f6bc909eSSimon Trimmer } 2743f6bc909eSSimon Trimmer 2744f6bc909eSSimon Trimmer if (dsp->ops->enable_core) { 2745f6bc909eSSimon Trimmer ret = dsp->ops->enable_core(dsp); 2746f6bc909eSSimon Trimmer if (ret != 0) 2747f6bc909eSSimon Trimmer goto err_mem; 2748f6bc909eSSimon Trimmer } 2749f6bc909eSSimon Trimmer 2750f6bc909eSSimon Trimmer ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename); 2751f6bc909eSSimon Trimmer if (ret != 0) 2752f6bc909eSSimon Trimmer goto err_ena; 2753f6bc909eSSimon Trimmer 2754f6bc909eSSimon Trimmer ret = dsp->ops->setup_algs(dsp); 2755f6bc909eSSimon Trimmer if (ret != 0) 2756f6bc909eSSimon Trimmer goto err_ena; 2757f6bc909eSSimon Trimmer 2758f6bc909eSSimon Trimmer ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename); 2759f6bc909eSSimon Trimmer if (ret != 0) 2760f6bc909eSSimon Trimmer goto err_ena; 2761f6bc909eSSimon Trimmer 2762f6bc909eSSimon Trimmer /* Initialize caches for enabled and unset controls */ 2763f6bc909eSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 2764f6bc909eSSimon Trimmer if (ret != 0) 2765f6bc909eSSimon Trimmer goto err_ena; 2766f6bc909eSSimon Trimmer 2767f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2768f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2769f6bc909eSSimon Trimmer 2770f6bc909eSSimon Trimmer dsp->booted = true; 2771f6bc909eSSimon Trimmer 2772f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2773f6bc909eSSimon Trimmer 2774f6bc909eSSimon Trimmer return 0; 2775f6bc909eSSimon Trimmer err_ena: 2776f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2777f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2778f6bc909eSSimon Trimmer err_mem: 2779f6bc909eSSimon Trimmer if (dsp->ops->disable_memory) 2780f6bc909eSSimon Trimmer dsp->ops->disable_memory(dsp); 2781f6bc909eSSimon Trimmer err_mutex: 2782f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2783f6bc909eSSimon Trimmer 2784f6bc909eSSimon Trimmer return ret; 2785f6bc909eSSimon Trimmer } 2786e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_power_up, FW_CS_DSP); 2787f6bc909eSSimon Trimmer 2788f6bc909eSSimon Trimmer /** 2789f6bc909eSSimon Trimmer * cs_dsp_power_down() - Powers-down the DSP 2790f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2791f6bc909eSSimon Trimmer * 2792f6bc909eSSimon Trimmer * cs_dsp_stop() must have been called before this function. The core will be 2793f6bc909eSSimon Trimmer * fully powered down and so the memory will not be retained. 2794f6bc909eSSimon Trimmer */ 2795f6bc909eSSimon Trimmer void cs_dsp_power_down(struct cs_dsp *dsp) 2796f6bc909eSSimon Trimmer { 2797f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2798f6bc909eSSimon Trimmer 2799f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2800f6bc909eSSimon Trimmer 2801f6bc909eSSimon Trimmer cs_dsp_debugfs_clear(dsp); 2802f6bc909eSSimon Trimmer 2803f6bc909eSSimon Trimmer dsp->fw_id = 0; 2804f6bc909eSSimon Trimmer dsp->fw_id_version = 0; 2805f6bc909eSSimon Trimmer 2806f6bc909eSSimon Trimmer dsp->booted = false; 2807f6bc909eSSimon Trimmer 2808f6bc909eSSimon Trimmer if (dsp->ops->disable_memory) 2809f6bc909eSSimon Trimmer dsp->ops->disable_memory(dsp); 2810f6bc909eSSimon Trimmer 2811f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) 2812f6bc909eSSimon Trimmer ctl->enabled = 0; 2813f6bc909eSSimon Trimmer 2814f6bc909eSSimon Trimmer cs_dsp_free_alg_regions(dsp); 2815f6bc909eSSimon Trimmer 2816f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2817f6bc909eSSimon Trimmer 2818f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Shutdown complete\n"); 2819f6bc909eSSimon Trimmer } 2820e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_power_down, FW_CS_DSP); 2821f6bc909eSSimon Trimmer 2822f6bc909eSSimon Trimmer static int cs_dsp_adsp2_start_core(struct cs_dsp *dsp) 2823f6bc909eSSimon Trimmer { 2824f6bc909eSSimon Trimmer return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2825f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START, 2826f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START); 2827f6bc909eSSimon Trimmer } 2828f6bc909eSSimon Trimmer 2829f6bc909eSSimon Trimmer static void cs_dsp_adsp2_stop_core(struct cs_dsp *dsp) 2830f6bc909eSSimon Trimmer { 2831f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2832f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START, 0); 2833f6bc909eSSimon Trimmer } 2834f6bc909eSSimon Trimmer 2835f6bc909eSSimon Trimmer /** 2836f6bc909eSSimon Trimmer * cs_dsp_run() - Starts the firmware running 2837f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2838f6bc909eSSimon Trimmer * 2839f6bc909eSSimon Trimmer * cs_dsp_power_up() must have previously been called successfully. 2840f6bc909eSSimon Trimmer * 2841f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2842f6bc909eSSimon Trimmer */ 2843f6bc909eSSimon Trimmer int cs_dsp_run(struct cs_dsp *dsp) 2844f6bc909eSSimon Trimmer { 2845f6bc909eSSimon Trimmer int ret; 2846f6bc909eSSimon Trimmer 2847f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2848f6bc909eSSimon Trimmer 2849f6bc909eSSimon Trimmer if (!dsp->booted) { 2850f6bc909eSSimon Trimmer ret = -EIO; 2851f6bc909eSSimon Trimmer goto err; 2852f6bc909eSSimon Trimmer } 2853f6bc909eSSimon Trimmer 2854f6bc909eSSimon Trimmer if (dsp->ops->enable_core) { 2855f6bc909eSSimon Trimmer ret = dsp->ops->enable_core(dsp); 2856f6bc909eSSimon Trimmer if (ret != 0) 2857f6bc909eSSimon Trimmer goto err; 2858f6bc909eSSimon Trimmer } 2859f6bc909eSSimon Trimmer 286014055b5aSCharles Keepax if (dsp->client_ops->pre_run) { 286114055b5aSCharles Keepax ret = dsp->client_ops->pre_run(dsp); 286214055b5aSCharles Keepax if (ret) 286314055b5aSCharles Keepax goto err; 286414055b5aSCharles Keepax } 286514055b5aSCharles Keepax 2866f6bc909eSSimon Trimmer /* Sync set controls */ 2867f6bc909eSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 2868f6bc909eSSimon Trimmer if (ret != 0) 2869f6bc909eSSimon Trimmer goto err; 2870f6bc909eSSimon Trimmer 2871f6bc909eSSimon Trimmer if (dsp->ops->lock_memory) { 2872f6bc909eSSimon Trimmer ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 2873f6bc909eSSimon Trimmer if (ret != 0) { 2874f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Error configuring MPU: %d\n", ret); 2875f6bc909eSSimon Trimmer goto err; 2876f6bc909eSSimon Trimmer } 2877f6bc909eSSimon Trimmer } 2878f6bc909eSSimon Trimmer 2879f6bc909eSSimon Trimmer if (dsp->ops->start_core) { 2880f6bc909eSSimon Trimmer ret = dsp->ops->start_core(dsp); 2881f6bc909eSSimon Trimmer if (ret != 0) 2882f6bc909eSSimon Trimmer goto err; 2883f6bc909eSSimon Trimmer } 2884f6bc909eSSimon Trimmer 2885f6bc909eSSimon Trimmer dsp->running = true; 2886f6bc909eSSimon Trimmer 2887f6bc909eSSimon Trimmer if (dsp->client_ops->post_run) { 2888f6bc909eSSimon Trimmer ret = dsp->client_ops->post_run(dsp); 2889f6bc909eSSimon Trimmer if (ret) 2890f6bc909eSSimon Trimmer goto err; 2891f6bc909eSSimon Trimmer } 2892f6bc909eSSimon Trimmer 2893f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2894f6bc909eSSimon Trimmer 2895f6bc909eSSimon Trimmer return 0; 2896f6bc909eSSimon Trimmer 2897f6bc909eSSimon Trimmer err: 2898f6bc909eSSimon Trimmer if (dsp->ops->stop_core) 2899f6bc909eSSimon Trimmer dsp->ops->stop_core(dsp); 2900f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2901f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2902f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2903f6bc909eSSimon Trimmer 2904f6bc909eSSimon Trimmer return ret; 2905f6bc909eSSimon Trimmer } 2906e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_run, FW_CS_DSP); 2907f6bc909eSSimon Trimmer 2908f6bc909eSSimon Trimmer /** 2909f6bc909eSSimon Trimmer * cs_dsp_stop() - Stops the firmware 2910f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2911f6bc909eSSimon Trimmer * 2912f6bc909eSSimon Trimmer * Memory will not be disabled so firmware will remain loaded. 2913f6bc909eSSimon Trimmer */ 2914f6bc909eSSimon Trimmer void cs_dsp_stop(struct cs_dsp *dsp) 2915f6bc909eSSimon Trimmer { 2916f6bc909eSSimon Trimmer /* Tell the firmware to cleanup */ 2917f6bc909eSSimon Trimmer cs_dsp_signal_event_controls(dsp, CS_DSP_FW_EVENT_SHUTDOWN); 2918f6bc909eSSimon Trimmer 2919f6bc909eSSimon Trimmer if (dsp->ops->stop_watchdog) 2920f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 2921f6bc909eSSimon Trimmer 2922f6bc909eSSimon Trimmer /* Log firmware state, it can be useful for analysis */ 2923f6bc909eSSimon Trimmer if (dsp->ops->show_fw_status) 2924f6bc909eSSimon Trimmer dsp->ops->show_fw_status(dsp); 2925f6bc909eSSimon Trimmer 2926f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2927f6bc909eSSimon Trimmer 2928dea99773SCharles Keepax if (dsp->client_ops->pre_stop) 2929dea99773SCharles Keepax dsp->client_ops->pre_stop(dsp); 2930dea99773SCharles Keepax 2931f6bc909eSSimon Trimmer dsp->running = false; 2932f6bc909eSSimon Trimmer 2933f6bc909eSSimon Trimmer if (dsp->ops->stop_core) 2934f6bc909eSSimon Trimmer dsp->ops->stop_core(dsp); 2935f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2936f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2937f6bc909eSSimon Trimmer 2938f6bc909eSSimon Trimmer if (dsp->client_ops->post_stop) 2939f6bc909eSSimon Trimmer dsp->client_ops->post_stop(dsp); 2940f6bc909eSSimon Trimmer 2941f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2942f6bc909eSSimon Trimmer 2943f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Execution stopped\n"); 2944f6bc909eSSimon Trimmer } 2945e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_stop, FW_CS_DSP); 2946f6bc909eSSimon Trimmer 2947f6bc909eSSimon Trimmer static int cs_dsp_halo_start_core(struct cs_dsp *dsp) 2948f6bc909eSSimon Trimmer { 29497aa1cc10SCharles Keepax int ret; 29507aa1cc10SCharles Keepax 29517aa1cc10SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 2952f6bc909eSSimon Trimmer HALO_CORE_RESET | HALO_CORE_EN, 2953f6bc909eSSimon Trimmer HALO_CORE_RESET | HALO_CORE_EN); 29547aa1cc10SCharles Keepax if (ret) 29557aa1cc10SCharles Keepax return ret; 29567aa1cc10SCharles Keepax 29577aa1cc10SCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 29587aa1cc10SCharles Keepax HALO_CORE_RESET, 0); 2959f6bc909eSSimon Trimmer } 2960f6bc909eSSimon Trimmer 2961f6bc909eSSimon Trimmer static void cs_dsp_halo_stop_core(struct cs_dsp *dsp) 2962f6bc909eSSimon Trimmer { 2963f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 2964f6bc909eSSimon Trimmer HALO_CORE_EN, 0); 2965f6bc909eSSimon Trimmer 2966f6bc909eSSimon Trimmer /* reset halo core with CORE_SOFT_RESET */ 2967f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 2968f6bc909eSSimon Trimmer HALO_CORE_SOFT_RESET_MASK, 1); 2969f6bc909eSSimon Trimmer } 2970f6bc909eSSimon Trimmer 2971f6bc909eSSimon Trimmer /** 2972f6bc909eSSimon Trimmer * cs_dsp_adsp2_init() - Initialise a cs_dsp structure representing a ADSP2 core 2973f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2974f6bc909eSSimon Trimmer * 2975f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2976f6bc909eSSimon Trimmer */ 2977f6bc909eSSimon Trimmer int cs_dsp_adsp2_init(struct cs_dsp *dsp) 2978f6bc909eSSimon Trimmer { 2979f6bc909eSSimon Trimmer int ret; 2980f6bc909eSSimon Trimmer 2981f6bc909eSSimon Trimmer switch (dsp->rev) { 2982f6bc909eSSimon Trimmer case 0: 2983f6bc909eSSimon Trimmer /* 2984f6bc909eSSimon Trimmer * Disable the DSP memory by default when in reset for a small 2985f6bc909eSSimon Trimmer * power saving. 2986f6bc909eSSimon Trimmer */ 2987f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2988f6bc909eSSimon Trimmer ADSP2_MEM_ENA, 0); 2989f6bc909eSSimon Trimmer if (ret) { 2990f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2991f6bc909eSSimon Trimmer "Failed to clear memory retention: %d\n", ret); 2992f6bc909eSSimon Trimmer return ret; 2993f6bc909eSSimon Trimmer } 2994f6bc909eSSimon Trimmer 2995f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[0]; 2996f6bc909eSSimon Trimmer break; 2997f6bc909eSSimon Trimmer case 1: 2998f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[1]; 2999f6bc909eSSimon Trimmer break; 3000f6bc909eSSimon Trimmer default: 3001f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[2]; 3002f6bc909eSSimon Trimmer break; 3003f6bc909eSSimon Trimmer } 3004f6bc909eSSimon Trimmer 3005f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 3006f6bc909eSSimon Trimmer } 3007e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_init, FW_CS_DSP); 3008f6bc909eSSimon Trimmer 3009f6bc909eSSimon Trimmer /** 3010f6bc909eSSimon Trimmer * cs_dsp_halo_init() - Initialise a cs_dsp structure representing a HALO Core DSP 3011f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3012f6bc909eSSimon Trimmer * 3013f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 3014f6bc909eSSimon Trimmer */ 3015f6bc909eSSimon Trimmer int cs_dsp_halo_init(struct cs_dsp *dsp) 3016f6bc909eSSimon Trimmer { 30177062e1c7SSimon Trimmer if (dsp->no_core_startstop) 30187062e1c7SSimon Trimmer dsp->ops = &cs_dsp_halo_ao_ops; 30197062e1c7SSimon Trimmer else 3020f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_halo_ops; 3021f6bc909eSSimon Trimmer 3022f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 3023f6bc909eSSimon Trimmer } 3024e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_init, FW_CS_DSP); 3025f6bc909eSSimon Trimmer 3026f6bc909eSSimon Trimmer /** 3027f6bc909eSSimon Trimmer * cs_dsp_remove() - Clean a cs_dsp before deletion 3028f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3029f6bc909eSSimon Trimmer */ 3030f6bc909eSSimon Trimmer void cs_dsp_remove(struct cs_dsp *dsp) 3031f6bc909eSSimon Trimmer { 3032f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 3033f6bc909eSSimon Trimmer 3034f6bc909eSSimon Trimmer while (!list_empty(&dsp->ctl_list)) { 3035f6bc909eSSimon Trimmer ctl = list_first_entry(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list); 3036f6bc909eSSimon Trimmer 3037f6bc909eSSimon Trimmer if (dsp->client_ops->control_remove) 3038f6bc909eSSimon Trimmer dsp->client_ops->control_remove(ctl); 3039f6bc909eSSimon Trimmer 3040f6bc909eSSimon Trimmer list_del(&ctl->list); 3041f6bc909eSSimon Trimmer cs_dsp_free_ctl_blk(ctl); 3042f6bc909eSSimon Trimmer } 3043f6bc909eSSimon Trimmer } 3044e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_remove, FW_CS_DSP); 3045f6bc909eSSimon Trimmer 3046f6bc909eSSimon Trimmer /** 3047f6bc909eSSimon Trimmer * cs_dsp_read_raw_data_block() - Reads a block of data from DSP memory 3048f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3049f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be read 3050f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 3051f6bc909eSSimon Trimmer * @num_words: the length of the data to read 3052f6bc909eSSimon Trimmer * @data: a buffer to store the fetched data 3053f6bc909eSSimon Trimmer * 3054f6bc909eSSimon Trimmer * If this is used to read unpacked 24-bit memory, each 24-bit DSP word will 3055f6bc909eSSimon Trimmer * occupy 32-bits in data (MSbyte will be 0). This padding can be removed using 3056f6bc909eSSimon Trimmer * cs_dsp_remove_padding() 3057f6bc909eSSimon Trimmer * 3058f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 3059f6bc909eSSimon Trimmer */ 3060f6bc909eSSimon Trimmer int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, 3061f6bc909eSSimon Trimmer unsigned int num_words, __be32 *data) 3062f6bc909eSSimon Trimmer { 3063f6bc909eSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 3064f6bc909eSSimon Trimmer unsigned int reg; 3065f6bc909eSSimon Trimmer int ret; 3066f6bc909eSSimon Trimmer 30675065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 30685065cfabSCharles Keepax 3069f6bc909eSSimon Trimmer if (!mem) 3070f6bc909eSSimon Trimmer return -EINVAL; 3071f6bc909eSSimon Trimmer 3072f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, mem_addr); 3073f6bc909eSSimon Trimmer 3074f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, data, 3075f6bc909eSSimon Trimmer sizeof(*data) * num_words); 3076f6bc909eSSimon Trimmer if (ret < 0) 3077f6bc909eSSimon Trimmer return ret; 3078f6bc909eSSimon Trimmer 3079f6bc909eSSimon Trimmer return 0; 3080f6bc909eSSimon Trimmer } 3081e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_read_raw_data_block, FW_CS_DSP); 3082f6bc909eSSimon Trimmer 3083f6bc909eSSimon Trimmer /** 3084f6bc909eSSimon Trimmer * cs_dsp_read_data_word() - Reads a word from DSP memory 3085f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3086f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be read 3087f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 3088f6bc909eSSimon Trimmer * @data: a buffer to store the fetched data 3089f6bc909eSSimon Trimmer * 3090f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 3091f6bc909eSSimon Trimmer */ 3092f6bc909eSSimon Trimmer int cs_dsp_read_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 *data) 3093f6bc909eSSimon Trimmer { 3094f6bc909eSSimon Trimmer __be32 raw; 3095f6bc909eSSimon Trimmer int ret; 3096f6bc909eSSimon Trimmer 3097f6bc909eSSimon Trimmer ret = cs_dsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw); 3098f6bc909eSSimon Trimmer if (ret < 0) 3099f6bc909eSSimon Trimmer return ret; 3100f6bc909eSSimon Trimmer 3101f6bc909eSSimon Trimmer *data = be32_to_cpu(raw) & 0x00ffffffu; 3102f6bc909eSSimon Trimmer 3103f6bc909eSSimon Trimmer return 0; 3104f6bc909eSSimon Trimmer } 3105e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_read_data_word, FW_CS_DSP); 3106f6bc909eSSimon Trimmer 3107f6bc909eSSimon Trimmer /** 3108f6bc909eSSimon Trimmer * cs_dsp_write_data_word() - Writes a word to DSP memory 3109f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3110f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be written 3111f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 3112f6bc909eSSimon Trimmer * @data: the data to be written 3113f6bc909eSSimon Trimmer * 3114f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 3115f6bc909eSSimon Trimmer */ 3116f6bc909eSSimon Trimmer int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 data) 3117f6bc909eSSimon Trimmer { 3118f6bc909eSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 3119f6bc909eSSimon Trimmer __be32 val = cpu_to_be32(data & 0x00ffffffu); 3120f6bc909eSSimon Trimmer unsigned int reg; 3121f6bc909eSSimon Trimmer 31225065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 31235065cfabSCharles Keepax 3124f6bc909eSSimon Trimmer if (!mem) 3125f6bc909eSSimon Trimmer return -EINVAL; 3126f6bc909eSSimon Trimmer 3127f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, mem_addr); 3128f6bc909eSSimon Trimmer 3129f6bc909eSSimon Trimmer return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 3130f6bc909eSSimon Trimmer } 3131e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_write_data_word, FW_CS_DSP); 3132f6bc909eSSimon Trimmer 3133f6bc909eSSimon Trimmer /** 3134f6bc909eSSimon Trimmer * cs_dsp_remove_padding() - Convert unpacked words to packed bytes 3135f6bc909eSSimon Trimmer * @buf: buffer containing DSP words read from DSP memory 3136f6bc909eSSimon Trimmer * @nwords: number of words to convert 3137f6bc909eSSimon Trimmer * 3138f6bc909eSSimon Trimmer * DSP words from the register map have pad bytes and the data bytes 3139f6bc909eSSimon Trimmer * are in swapped order. This swaps to the native endian order and 3140f6bc909eSSimon Trimmer * strips the pad bytes. 3141f6bc909eSSimon Trimmer */ 3142f6bc909eSSimon Trimmer void cs_dsp_remove_padding(u32 *buf, int nwords) 3143f6bc909eSSimon Trimmer { 3144f6bc909eSSimon Trimmer const __be32 *pack_in = (__be32 *)buf; 3145f6bc909eSSimon Trimmer u8 *pack_out = (u8 *)buf; 3146f6bc909eSSimon Trimmer int i; 3147f6bc909eSSimon Trimmer 3148f6bc909eSSimon Trimmer for (i = 0; i < nwords; i++) { 3149f6bc909eSSimon Trimmer u32 word = be32_to_cpu(*pack_in++); 3150f6bc909eSSimon Trimmer *pack_out++ = (u8)word; 3151f6bc909eSSimon Trimmer *pack_out++ = (u8)(word >> 8); 3152f6bc909eSSimon Trimmer *pack_out++ = (u8)(word >> 16); 3153f6bc909eSSimon Trimmer } 3154f6bc909eSSimon Trimmer } 3155e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_remove_padding, FW_CS_DSP); 3156f6bc909eSSimon Trimmer 3157f6bc909eSSimon Trimmer /** 3158f6bc909eSSimon Trimmer * cs_dsp_adsp2_bus_error() - Handle a DSP bus error interrupt 3159f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3160f6bc909eSSimon Trimmer * 3161f6bc909eSSimon Trimmer * The firmware and DSP state will be logged for future analysis. 3162f6bc909eSSimon Trimmer */ 3163f6bc909eSSimon Trimmer void cs_dsp_adsp2_bus_error(struct cs_dsp *dsp) 3164f6bc909eSSimon Trimmer { 3165f6bc909eSSimon Trimmer unsigned int val; 3166f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 3167f6bc909eSSimon Trimmer int ret = 0; 3168f6bc909eSSimon Trimmer 3169f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 3170f6bc909eSSimon Trimmer 3171f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 3172f6bc909eSSimon Trimmer if (ret) { 3173f6bc909eSSimon Trimmer cs_dsp_err(dsp, 3174f6bc909eSSimon Trimmer "Failed to read Region Lock Ctrl register: %d\n", ret); 3175f6bc909eSSimon Trimmer goto error; 3176f6bc909eSSimon Trimmer } 3177f6bc909eSSimon Trimmer 3178f6bc909eSSimon Trimmer if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 3179f6bc909eSSimon Trimmer cs_dsp_err(dsp, "watchdog timeout error\n"); 3180f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 3181f6bc909eSSimon Trimmer if (dsp->client_ops->watchdog_expired) 3182f6bc909eSSimon Trimmer dsp->client_ops->watchdog_expired(dsp); 3183f6bc909eSSimon Trimmer } 3184f6bc909eSSimon Trimmer 3185f6bc909eSSimon Trimmer if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 3186f6bc909eSSimon Trimmer if (val & ADSP2_ADDR_ERR_MASK) 3187f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error: address error\n"); 3188f6bc909eSSimon Trimmer else 3189f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error: region lock error\n"); 3190f6bc909eSSimon Trimmer 3191f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 3192f6bc909eSSimon Trimmer if (ret) { 3193f6bc909eSSimon Trimmer cs_dsp_err(dsp, 3194f6bc909eSSimon Trimmer "Failed to read Bus Err Addr register: %d\n", 3195f6bc909eSSimon Trimmer ret); 3196f6bc909eSSimon Trimmer goto error; 3197f6bc909eSSimon Trimmer } 3198f6bc909eSSimon Trimmer 3199f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error address = 0x%x\n", 3200f6bc909eSSimon Trimmer val & ADSP2_BUS_ERR_ADDR_MASK); 3201f6bc909eSSimon Trimmer 3202f6bc909eSSimon Trimmer ret = regmap_read(regmap, 3203f6bc909eSSimon Trimmer dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 3204f6bc909eSSimon Trimmer &val); 3205f6bc909eSSimon Trimmer if (ret) { 3206f6bc909eSSimon Trimmer cs_dsp_err(dsp, 3207f6bc909eSSimon Trimmer "Failed to read Pmem Xmem Err Addr register: %d\n", 3208f6bc909eSSimon Trimmer ret); 3209f6bc909eSSimon Trimmer goto error; 3210f6bc909eSSimon Trimmer } 3211f6bc909eSSimon Trimmer 3212f6bc909eSSimon Trimmer cs_dsp_err(dsp, "xmem error address = 0x%x\n", 3213f6bc909eSSimon Trimmer val & ADSP2_XMEM_ERR_ADDR_MASK); 3214f6bc909eSSimon Trimmer cs_dsp_err(dsp, "pmem error address = 0x%x\n", 3215f6bc909eSSimon Trimmer (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 3216f6bc909eSSimon Trimmer ADSP2_PMEM_ERR_ADDR_SHIFT); 3217f6bc909eSSimon Trimmer } 3218f6bc909eSSimon Trimmer 3219f6bc909eSSimon Trimmer regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 3220f6bc909eSSimon Trimmer ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 3221f6bc909eSSimon Trimmer 3222f6bc909eSSimon Trimmer error: 3223f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3224f6bc909eSSimon Trimmer } 3225e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_bus_error, FW_CS_DSP); 3226f6bc909eSSimon Trimmer 3227f6bc909eSSimon Trimmer /** 3228f6bc909eSSimon Trimmer * cs_dsp_halo_bus_error() - Handle a DSP bus error interrupt 3229f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3230f6bc909eSSimon Trimmer * 3231f6bc909eSSimon Trimmer * The firmware and DSP state will be logged for future analysis. 3232f6bc909eSSimon Trimmer */ 3233f6bc909eSSimon Trimmer void cs_dsp_halo_bus_error(struct cs_dsp *dsp) 3234f6bc909eSSimon Trimmer { 3235f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 3236f6bc909eSSimon Trimmer unsigned int fault[6]; 3237f6bc909eSSimon Trimmer struct reg_sequence clear[] = { 3238f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 3239f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 3240f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 3241f6bc909eSSimon Trimmer }; 3242f6bc909eSSimon Trimmer int ret; 3243f6bc909eSSimon Trimmer 3244f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 3245f6bc909eSSimon Trimmer 3246f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 3247f6bc909eSSimon Trimmer fault); 3248f6bc909eSSimon Trimmer if (ret) { 3249f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 3250f6bc909eSSimon Trimmer goto exit_unlock; 3251f6bc909eSSimon Trimmer } 3252f6bc909eSSimon Trimmer 3253f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 3254f6bc909eSSimon Trimmer *fault & HALO_AHBM_FLAGS_ERR_MASK, 3255f6bc909eSSimon Trimmer (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 3256f6bc909eSSimon Trimmer HALO_AHBM_CORE_ERR_ADDR_SHIFT); 3257f6bc909eSSimon Trimmer 3258f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 3259f6bc909eSSimon Trimmer fault); 3260f6bc909eSSimon Trimmer if (ret) { 3261f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 3262f6bc909eSSimon Trimmer goto exit_unlock; 3263f6bc909eSSimon Trimmer } 3264f6bc909eSSimon Trimmer 3265f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 3266f6bc909eSSimon Trimmer 3267f6bc909eSSimon Trimmer ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 3268f6bc909eSSimon Trimmer fault, ARRAY_SIZE(fault)); 3269f6bc909eSSimon Trimmer if (ret) { 3270f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 3271f6bc909eSSimon Trimmer goto exit_unlock; 3272f6bc909eSSimon Trimmer } 3273f6bc909eSSimon Trimmer 3274f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 3275f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 3276f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 3277f6bc909eSSimon Trimmer 3278f6bc909eSSimon Trimmer ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 3279f6bc909eSSimon Trimmer if (ret) 3280f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 3281f6bc909eSSimon Trimmer 3282f6bc909eSSimon Trimmer exit_unlock: 3283f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3284f6bc909eSSimon Trimmer } 3285e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_bus_error, FW_CS_DSP); 3286f6bc909eSSimon Trimmer 3287f6bc909eSSimon Trimmer /** 3288f6bc909eSSimon Trimmer * cs_dsp_halo_wdt_expire() - Handle DSP watchdog expiry 3289f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3290f6bc909eSSimon Trimmer * 3291f6bc909eSSimon Trimmer * This is logged for future analysis. 3292f6bc909eSSimon Trimmer */ 3293f6bc909eSSimon Trimmer void cs_dsp_halo_wdt_expire(struct cs_dsp *dsp) 3294f6bc909eSSimon Trimmer { 3295f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 3296f6bc909eSSimon Trimmer 3297f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "WDT Expiry Fault\n"); 3298f6bc909eSSimon Trimmer 3299f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 3300f6bc909eSSimon Trimmer if (dsp->client_ops->watchdog_expired) 3301f6bc909eSSimon Trimmer dsp->client_ops->watchdog_expired(dsp); 3302f6bc909eSSimon Trimmer 3303f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3304f6bc909eSSimon Trimmer } 3305e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_wdt_expire, FW_CS_DSP); 3306f6bc909eSSimon Trimmer 3307f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp1_ops = { 3308f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3309f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp1_parse_sizes, 3310f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3311f6bc909eSSimon Trimmer }; 3312f6bc909eSSimon Trimmer 3313f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp2_ops[] = { 3314f6bc909eSSimon Trimmer { 3315f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3316f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3317f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3318f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3319f6bc909eSSimon Trimmer 3320f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2_show_fw_status, 3321f6bc909eSSimon Trimmer 3322f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3323f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3324f6bc909eSSimon Trimmer 3325f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2_enable_core, 3326f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2_disable_core, 3327f6bc909eSSimon Trimmer 3328f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3329f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3330f6bc909eSSimon Trimmer 3331f6bc909eSSimon Trimmer }, 3332f6bc909eSSimon Trimmer { 3333f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3334f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3335f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3336f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3337f6bc909eSSimon Trimmer 3338f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 3339f6bc909eSSimon Trimmer 3340f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3341f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3342f6bc909eSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 3343f6bc909eSSimon Trimmer 3344f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 3345f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 3346f6bc909eSSimon Trimmer 3347f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3348f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3349f6bc909eSSimon Trimmer }, 3350f6bc909eSSimon Trimmer { 3351f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3352f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3353f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3354f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3355f6bc909eSSimon Trimmer 3356f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 3357f6bc909eSSimon Trimmer .stop_watchdog = cs_dsp_stop_watchdog, 3358f6bc909eSSimon Trimmer 3359f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3360f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3361f6bc909eSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 3362f6bc909eSSimon Trimmer 3363f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 3364f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 3365f6bc909eSSimon Trimmer 3366f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3367f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3368f6bc909eSSimon Trimmer }, 3369f6bc909eSSimon Trimmer }; 3370f6bc909eSSimon Trimmer 3371f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ops = { 3372f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3373f6bc909eSSimon Trimmer .validate_version = cs_dsp_halo_validate_version, 3374f6bc909eSSimon Trimmer .setup_algs = cs_dsp_halo_setup_algs, 3375f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_halo_region_to_reg, 3376f6bc909eSSimon Trimmer 3377f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_halo_show_fw_status, 3378f6bc909eSSimon Trimmer .stop_watchdog = cs_dsp_halo_stop_watchdog, 3379f6bc909eSSimon Trimmer 3380f6bc909eSSimon Trimmer .lock_memory = cs_dsp_halo_configure_mpu, 3381f6bc909eSSimon Trimmer 3382f6bc909eSSimon Trimmer .start_core = cs_dsp_halo_start_core, 3383f6bc909eSSimon Trimmer .stop_core = cs_dsp_halo_stop_core, 3384f6bc909eSSimon Trimmer }; 3385f6bc909eSSimon Trimmer 33867062e1c7SSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ao_ops = { 33877062e1c7SSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 33887062e1c7SSimon Trimmer .validate_version = cs_dsp_halo_validate_version, 33897062e1c7SSimon Trimmer .setup_algs = cs_dsp_halo_setup_algs, 33907062e1c7SSimon Trimmer .region_to_reg = cs_dsp_halo_region_to_reg, 33917062e1c7SSimon Trimmer .show_fw_status = cs_dsp_halo_show_fw_status, 33927062e1c7SSimon Trimmer }; 33937062e1c7SSimon Trimmer 3394a4b97655SCharles Keepax /** 3395a4b97655SCharles Keepax * cs_dsp_chunk_write() - Format data to a DSP memory chunk 3396a4b97655SCharles Keepax * @ch: Pointer to the chunk structure 3397a4b97655SCharles Keepax * @nbits: Number of bits to write 3398a4b97655SCharles Keepax * @val: Value to write 3399a4b97655SCharles Keepax * 3400a4b97655SCharles Keepax * This function sequentially writes values into the format required for DSP 3401a4b97655SCharles Keepax * memory, it handles both inserting of the padding bytes and converting to 3402a4b97655SCharles Keepax * big endian. Note that data is only committed to the chunk when a whole DSP 3403a4b97655SCharles Keepax * words worth of data is available. 3404a4b97655SCharles Keepax * 3405a4b97655SCharles Keepax * Return: Zero for success, a negative number on error. 3406a4b97655SCharles Keepax */ 3407a4b97655SCharles Keepax int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val) 3408a4b97655SCharles Keepax { 3409a4b97655SCharles Keepax int nwrite, i; 3410a4b97655SCharles Keepax 3411a4b97655SCharles Keepax nwrite = min(CS_DSP_DATA_WORD_BITS - ch->cachebits, nbits); 3412a4b97655SCharles Keepax 3413a4b97655SCharles Keepax ch->cache <<= nwrite; 3414a4b97655SCharles Keepax ch->cache |= val >> (nbits - nwrite); 3415a4b97655SCharles Keepax ch->cachebits += nwrite; 3416a4b97655SCharles Keepax nbits -= nwrite; 3417a4b97655SCharles Keepax 3418a4b97655SCharles Keepax if (ch->cachebits == CS_DSP_DATA_WORD_BITS) { 3419a4b97655SCharles Keepax if (cs_dsp_chunk_end(ch)) 3420a4b97655SCharles Keepax return -ENOSPC; 3421a4b97655SCharles Keepax 3422a4b97655SCharles Keepax ch->cache &= 0xFFFFFF; 3423a4b97655SCharles Keepax for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) 3424a4b97655SCharles Keepax *ch->data++ = (ch->cache & 0xFF000000) >> CS_DSP_DATA_WORD_BITS; 3425a4b97655SCharles Keepax 3426a4b97655SCharles Keepax ch->bytes += sizeof(ch->cache); 3427a4b97655SCharles Keepax ch->cachebits = 0; 3428a4b97655SCharles Keepax } 3429a4b97655SCharles Keepax 3430a4b97655SCharles Keepax if (nbits) 3431a4b97655SCharles Keepax return cs_dsp_chunk_write(ch, nbits, val); 3432a4b97655SCharles Keepax 3433a4b97655SCharles Keepax return 0; 3434a4b97655SCharles Keepax } 3435e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_write, FW_CS_DSP); 3436a4b97655SCharles Keepax 3437a4b97655SCharles Keepax /** 3438a4b97655SCharles Keepax * cs_dsp_chunk_flush() - Pad remaining data with zero and commit to chunk 3439a4b97655SCharles Keepax * @ch: Pointer to the chunk structure 3440a4b97655SCharles Keepax * 3441a4b97655SCharles Keepax * As cs_dsp_chunk_write only writes data when a whole DSP word is ready to 3442a4b97655SCharles Keepax * be written out it is possible that some data will remain in the cache, this 3443a4b97655SCharles Keepax * function will pad that data with zeros upto a whole DSP word and write out. 3444a4b97655SCharles Keepax * 3445a4b97655SCharles Keepax * Return: Zero for success, a negative number on error. 3446a4b97655SCharles Keepax */ 3447a4b97655SCharles Keepax int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch) 3448a4b97655SCharles Keepax { 3449a4b97655SCharles Keepax if (!ch->cachebits) 3450a4b97655SCharles Keepax return 0; 3451a4b97655SCharles Keepax 3452a4b97655SCharles Keepax return cs_dsp_chunk_write(ch, CS_DSP_DATA_WORD_BITS - ch->cachebits, 0); 3453a4b97655SCharles Keepax } 3454e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_flush, FW_CS_DSP); 3455a4b97655SCharles Keepax 3456a4b97655SCharles Keepax /** 3457a4b97655SCharles Keepax * cs_dsp_chunk_read() - Parse data from a DSP memory chunk 3458a4b97655SCharles Keepax * @ch: Pointer to the chunk structure 3459a4b97655SCharles Keepax * @nbits: Number of bits to read 3460a4b97655SCharles Keepax * 3461a4b97655SCharles Keepax * This function sequentially reads values from a DSP memory formatted buffer, 3462a4b97655SCharles Keepax * it handles both removing of the padding bytes and converting from big endian. 3463a4b97655SCharles Keepax * 3464a4b97655SCharles Keepax * Return: A negative number is returned on error, otherwise the read value. 3465a4b97655SCharles Keepax */ 3466a4b97655SCharles Keepax int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits) 3467a4b97655SCharles Keepax { 3468a4b97655SCharles Keepax int nread, i; 3469a4b97655SCharles Keepax u32 result; 3470a4b97655SCharles Keepax 3471a4b97655SCharles Keepax if (!ch->cachebits) { 3472a4b97655SCharles Keepax if (cs_dsp_chunk_end(ch)) 3473a4b97655SCharles Keepax return -ENOSPC; 3474a4b97655SCharles Keepax 3475a4b97655SCharles Keepax ch->cache = 0; 3476a4b97655SCharles Keepax ch->cachebits = CS_DSP_DATA_WORD_BITS; 3477a4b97655SCharles Keepax 3478a4b97655SCharles Keepax for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) 3479a4b97655SCharles Keepax ch->cache |= *ch->data++; 3480a4b97655SCharles Keepax 3481a4b97655SCharles Keepax ch->bytes += sizeof(ch->cache); 3482a4b97655SCharles Keepax } 3483a4b97655SCharles Keepax 3484a4b97655SCharles Keepax nread = min(ch->cachebits, nbits); 3485a4b97655SCharles Keepax nbits -= nread; 3486a4b97655SCharles Keepax 3487a4b97655SCharles Keepax result = ch->cache >> ((sizeof(ch->cache) * BITS_PER_BYTE) - nread); 3488a4b97655SCharles Keepax ch->cache <<= nread; 3489a4b97655SCharles Keepax ch->cachebits -= nread; 3490a4b97655SCharles Keepax 3491a4b97655SCharles Keepax if (nbits) 3492a4b97655SCharles Keepax result = (result << nbits) | cs_dsp_chunk_read(ch, nbits); 3493a4b97655SCharles Keepax 3494a4b97655SCharles Keepax return result; 3495a4b97655SCharles Keepax } 3496e57d904aSRichard Fitzgerald EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP); 3497a4b97655SCharles Keepax 3498f6bc909eSSimon Trimmer MODULE_DESCRIPTION("Cirrus Logic DSP Support"); 3499f6bc909eSSimon Trimmer MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); 3500f6bc909eSSimon Trimmer MODULE_LICENSE("GPL v2"); 3501