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> 17f6bc909eSSimon Trimmer #include <linux/slab.h> 18f6bc909eSSimon Trimmer #include <linux/vmalloc.h> 19f6bc909eSSimon Trimmer 20f6bc909eSSimon Trimmer #include <linux/firmware/cirrus/cs_dsp.h> 21f6bc909eSSimon Trimmer #include <linux/firmware/cirrus/wmfw.h> 22f6bc909eSSimon Trimmer 23f6bc909eSSimon Trimmer #define cs_dsp_err(_dsp, fmt, ...) \ 24f6bc909eSSimon Trimmer dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 25f6bc909eSSimon Trimmer #define cs_dsp_warn(_dsp, fmt, ...) \ 26f6bc909eSSimon Trimmer dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 27f6bc909eSSimon Trimmer #define cs_dsp_info(_dsp, fmt, ...) \ 28f6bc909eSSimon Trimmer dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 29f6bc909eSSimon Trimmer #define cs_dsp_dbg(_dsp, fmt, ...) \ 30f6bc909eSSimon Trimmer dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 31f6bc909eSSimon Trimmer 32f6bc909eSSimon Trimmer #define ADSP1_CONTROL_1 0x00 33f6bc909eSSimon Trimmer #define ADSP1_CONTROL_2 0x02 34f6bc909eSSimon Trimmer #define ADSP1_CONTROL_3 0x03 35f6bc909eSSimon Trimmer #define ADSP1_CONTROL_4 0x04 36f6bc909eSSimon Trimmer #define ADSP1_CONTROL_5 0x06 37f6bc909eSSimon Trimmer #define ADSP1_CONTROL_6 0x07 38f6bc909eSSimon Trimmer #define ADSP1_CONTROL_7 0x08 39f6bc909eSSimon Trimmer #define ADSP1_CONTROL_8 0x09 40f6bc909eSSimon Trimmer #define ADSP1_CONTROL_9 0x0A 41f6bc909eSSimon Trimmer #define ADSP1_CONTROL_10 0x0B 42f6bc909eSSimon Trimmer #define ADSP1_CONTROL_11 0x0C 43f6bc909eSSimon Trimmer #define ADSP1_CONTROL_12 0x0D 44f6bc909eSSimon Trimmer #define ADSP1_CONTROL_13 0x0F 45f6bc909eSSimon Trimmer #define ADSP1_CONTROL_14 0x10 46f6bc909eSSimon Trimmer #define ADSP1_CONTROL_15 0x11 47f6bc909eSSimon Trimmer #define ADSP1_CONTROL_16 0x12 48f6bc909eSSimon Trimmer #define ADSP1_CONTROL_17 0x13 49f6bc909eSSimon Trimmer #define ADSP1_CONTROL_18 0x14 50f6bc909eSSimon Trimmer #define ADSP1_CONTROL_19 0x16 51f6bc909eSSimon Trimmer #define ADSP1_CONTROL_20 0x17 52f6bc909eSSimon Trimmer #define ADSP1_CONTROL_21 0x18 53f6bc909eSSimon Trimmer #define ADSP1_CONTROL_22 0x1A 54f6bc909eSSimon Trimmer #define ADSP1_CONTROL_23 0x1B 55f6bc909eSSimon Trimmer #define ADSP1_CONTROL_24 0x1C 56f6bc909eSSimon Trimmer #define ADSP1_CONTROL_25 0x1E 57f6bc909eSSimon Trimmer #define ADSP1_CONTROL_26 0x20 58f6bc909eSSimon Trimmer #define ADSP1_CONTROL_27 0x21 59f6bc909eSSimon Trimmer #define ADSP1_CONTROL_28 0x22 60f6bc909eSSimon Trimmer #define ADSP1_CONTROL_29 0x23 61f6bc909eSSimon Trimmer #define ADSP1_CONTROL_30 0x24 62f6bc909eSSimon Trimmer #define ADSP1_CONTROL_31 0x26 63f6bc909eSSimon Trimmer 64f6bc909eSSimon Trimmer /* 65f6bc909eSSimon Trimmer * ADSP1 Control 19 66f6bc909eSSimon Trimmer */ 67f6bc909eSSimon Trimmer #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 68f6bc909eSSimon Trimmer #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 69f6bc909eSSimon Trimmer #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 70f6bc909eSSimon Trimmer 71f6bc909eSSimon Trimmer /* 72f6bc909eSSimon Trimmer * ADSP1 Control 30 73f6bc909eSSimon Trimmer */ 74f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 75f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 76f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 77f6bc909eSSimon Trimmer #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 78f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 79f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 80f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 81f6bc909eSSimon Trimmer #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 82f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 83f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 84f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 85f6bc909eSSimon Trimmer #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 86f6bc909eSSimon Trimmer #define ADSP1_START 0x0001 /* DSP1_START */ 87f6bc909eSSimon Trimmer #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 88f6bc909eSSimon Trimmer #define ADSP1_START_SHIFT 0 /* DSP1_START */ 89f6bc909eSSimon Trimmer #define ADSP1_START_WIDTH 1 /* DSP1_START */ 90f6bc909eSSimon Trimmer 91f6bc909eSSimon Trimmer /* 92f6bc909eSSimon Trimmer * ADSP1 Control 31 93f6bc909eSSimon Trimmer */ 94f6bc909eSSimon Trimmer #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 95f6bc909eSSimon Trimmer #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 96f6bc909eSSimon Trimmer #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 97f6bc909eSSimon Trimmer 98f6bc909eSSimon Trimmer #define ADSP2_CONTROL 0x0 99f6bc909eSSimon Trimmer #define ADSP2_CLOCKING 0x1 100f6bc909eSSimon Trimmer #define ADSP2V2_CLOCKING 0x2 101f6bc909eSSimon Trimmer #define ADSP2_STATUS1 0x4 102f6bc909eSSimon Trimmer #define ADSP2_WDMA_CONFIG_1 0x30 103f6bc909eSSimon Trimmer #define ADSP2_WDMA_CONFIG_2 0x31 104f6bc909eSSimon Trimmer #define ADSP2V2_WDMA_CONFIG_2 0x32 105f6bc909eSSimon Trimmer #define ADSP2_RDMA_CONFIG_1 0x34 106f6bc909eSSimon Trimmer 107f6bc909eSSimon Trimmer #define ADSP2_SCRATCH0 0x40 108f6bc909eSSimon Trimmer #define ADSP2_SCRATCH1 0x41 109f6bc909eSSimon Trimmer #define ADSP2_SCRATCH2 0x42 110f6bc909eSSimon Trimmer #define ADSP2_SCRATCH3 0x43 111f6bc909eSSimon Trimmer 112f6bc909eSSimon Trimmer #define ADSP2V2_SCRATCH0_1 0x40 113f6bc909eSSimon Trimmer #define ADSP2V2_SCRATCH2_3 0x42 114f6bc909eSSimon Trimmer 115f6bc909eSSimon Trimmer /* 116f6bc909eSSimon Trimmer * ADSP2 Control 117f6bc909eSSimon Trimmer */ 118f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 119f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 120f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 121f6bc909eSSimon Trimmer #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 122f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 123f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 124f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 125f6bc909eSSimon Trimmer #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 126f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 127f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 128f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 129f6bc909eSSimon Trimmer #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 130f6bc909eSSimon Trimmer #define ADSP2_START 0x0001 /* DSP1_START */ 131f6bc909eSSimon Trimmer #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 132f6bc909eSSimon Trimmer #define ADSP2_START_SHIFT 0 /* DSP1_START */ 133f6bc909eSSimon Trimmer #define ADSP2_START_WIDTH 1 /* DSP1_START */ 134f6bc909eSSimon Trimmer 135f6bc909eSSimon Trimmer /* 136f6bc909eSSimon Trimmer * ADSP2 clocking 137f6bc909eSSimon Trimmer */ 138f6bc909eSSimon Trimmer #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 139f6bc909eSSimon Trimmer #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 140f6bc909eSSimon Trimmer #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 141f6bc909eSSimon Trimmer 142f6bc909eSSimon Trimmer /* 143f6bc909eSSimon Trimmer * ADSP2V2 clocking 144f6bc909eSSimon Trimmer */ 145f6bc909eSSimon Trimmer #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 146f6bc909eSSimon Trimmer #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 147f6bc909eSSimon Trimmer #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 148f6bc909eSSimon Trimmer 149f6bc909eSSimon Trimmer #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 150f6bc909eSSimon Trimmer #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 151f6bc909eSSimon Trimmer #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 152f6bc909eSSimon Trimmer 153f6bc909eSSimon Trimmer /* 154f6bc909eSSimon Trimmer * ADSP2 Status 1 155f6bc909eSSimon Trimmer */ 156f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY 0x0001 157f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY_MASK 0x0001 158f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY_SHIFT 0 159f6bc909eSSimon Trimmer #define ADSP2_RAM_RDY_WIDTH 1 160f6bc909eSSimon Trimmer 161f6bc909eSSimon Trimmer /* 162f6bc909eSSimon Trimmer * ADSP2 Lock support 163f6bc909eSSimon Trimmer */ 164f6bc909eSSimon Trimmer #define ADSP2_LOCK_CODE_0 0x5555 165f6bc909eSSimon Trimmer #define ADSP2_LOCK_CODE_1 0xAAAA 166f6bc909eSSimon Trimmer 167f6bc909eSSimon Trimmer #define ADSP2_WATCHDOG 0x0A 168f6bc909eSSimon Trimmer #define ADSP2_BUS_ERR_ADDR 0x52 169f6bc909eSSimon Trimmer #define ADSP2_REGION_LOCK_STATUS 0x64 170f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 171f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 172f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 173f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 174f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 175f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_CTRL 0x7A 176f6bc909eSSimon Trimmer #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 177f6bc909eSSimon Trimmer 178f6bc909eSSimon Trimmer #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 179f6bc909eSSimon Trimmer #define ADSP2_ADDR_ERR_MASK 0x4000 180f6bc909eSSimon Trimmer #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 181f6bc909eSSimon Trimmer #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 182f6bc909eSSimon Trimmer #define ADSP2_CTRL_ERR_EINT 0x0001 183f6bc909eSSimon Trimmer 184f6bc909eSSimon Trimmer #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 185f6bc909eSSimon Trimmer #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 186f6bc909eSSimon Trimmer #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 187f6bc909eSSimon Trimmer #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 188f6bc909eSSimon Trimmer #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 189f6bc909eSSimon Trimmer 190f6bc909eSSimon Trimmer #define ADSP2_LOCK_REGION_SHIFT 16 191f6bc909eSSimon Trimmer 192f6bc909eSSimon Trimmer /* 193f6bc909eSSimon Trimmer * Event control messages 194f6bc909eSSimon Trimmer */ 195f6bc909eSSimon Trimmer #define CS_DSP_FW_EVENT_SHUTDOWN 0x000001 196f6bc909eSSimon Trimmer 197f6bc909eSSimon Trimmer /* 198f6bc909eSSimon Trimmer * HALO system info 199f6bc909eSSimon Trimmer */ 200f6bc909eSSimon Trimmer #define HALO_AHBM_WINDOW_DEBUG_0 0x02040 201f6bc909eSSimon Trimmer #define HALO_AHBM_WINDOW_DEBUG_1 0x02044 202f6bc909eSSimon Trimmer 203f6bc909eSSimon Trimmer /* 204f6bc909eSSimon Trimmer * HALO core 205f6bc909eSSimon Trimmer */ 206f6bc909eSSimon Trimmer #define HALO_SCRATCH1 0x005c0 207f6bc909eSSimon Trimmer #define HALO_SCRATCH2 0x005c8 208f6bc909eSSimon Trimmer #define HALO_SCRATCH3 0x005d0 209f6bc909eSSimon Trimmer #define HALO_SCRATCH4 0x005d8 210f6bc909eSSimon Trimmer #define HALO_CCM_CORE_CONTROL 0x41000 211f6bc909eSSimon Trimmer #define HALO_CORE_SOFT_RESET 0x00010 212f6bc909eSSimon Trimmer #define HALO_WDT_CONTROL 0x47000 213f6bc909eSSimon Trimmer 214f6bc909eSSimon Trimmer /* 215f6bc909eSSimon Trimmer * HALO MPU banks 216f6bc909eSSimon Trimmer */ 217f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_0 0x43000 218f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_0 0x43004 219f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_0 0x43008 220f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_0 0x4300C 221f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_0 0x43014 222f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_1 0x43018 223f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_1 0x4301C 224f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_1 0x43020 225f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_1 0x43024 226f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_1 0x4302C 227f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_2 0x43030 228f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_2 0x43034 229f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_2 0x43038 230f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_2 0x4303C 231f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_2 0x43044 232f6bc909eSSimon Trimmer #define HALO_MPU_XMEM_ACCESS_3 0x43048 233f6bc909eSSimon Trimmer #define HALO_MPU_YMEM_ACCESS_3 0x4304C 234f6bc909eSSimon Trimmer #define HALO_MPU_WINDOW_ACCESS_3 0x43050 235f6bc909eSSimon Trimmer #define HALO_MPU_XREG_ACCESS_3 0x43054 236f6bc909eSSimon Trimmer #define HALO_MPU_YREG_ACCESS_3 0x4305C 237f6bc909eSSimon Trimmer #define HALO_MPU_XM_VIO_ADDR 0x43100 238f6bc909eSSimon Trimmer #define HALO_MPU_XM_VIO_STATUS 0x43104 239f6bc909eSSimon Trimmer #define HALO_MPU_YM_VIO_ADDR 0x43108 240f6bc909eSSimon Trimmer #define HALO_MPU_YM_VIO_STATUS 0x4310C 241f6bc909eSSimon Trimmer #define HALO_MPU_PM_VIO_ADDR 0x43110 242f6bc909eSSimon Trimmer #define HALO_MPU_PM_VIO_STATUS 0x43114 243f6bc909eSSimon Trimmer #define HALO_MPU_LOCK_CONFIG 0x43140 244f6bc909eSSimon Trimmer 245f6bc909eSSimon Trimmer /* 246f6bc909eSSimon Trimmer * HALO_AHBM_WINDOW_DEBUG_1 247f6bc909eSSimon Trimmer */ 248f6bc909eSSimon Trimmer #define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 249f6bc909eSSimon Trimmer #define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 250f6bc909eSSimon Trimmer #define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff 251f6bc909eSSimon Trimmer 252f6bc909eSSimon Trimmer /* 253f6bc909eSSimon Trimmer * HALO_CCM_CORE_CONTROL 254f6bc909eSSimon Trimmer */ 255f6bc909eSSimon Trimmer #define HALO_CORE_RESET 0x00000200 256f6bc909eSSimon Trimmer #define HALO_CORE_EN 0x00000001 257f6bc909eSSimon Trimmer 258f6bc909eSSimon Trimmer /* 259f6bc909eSSimon Trimmer * HALO_CORE_SOFT_RESET 260f6bc909eSSimon Trimmer */ 261f6bc909eSSimon Trimmer #define HALO_CORE_SOFT_RESET_MASK 0x00000001 262f6bc909eSSimon Trimmer 263f6bc909eSSimon Trimmer /* 264f6bc909eSSimon Trimmer * HALO_WDT_CONTROL 265f6bc909eSSimon Trimmer */ 266f6bc909eSSimon Trimmer #define HALO_WDT_EN_MASK 0x00000001 267f6bc909eSSimon Trimmer 268f6bc909eSSimon Trimmer /* 269f6bc909eSSimon Trimmer * HALO_MPU_?M_VIO_STATUS 270f6bc909eSSimon Trimmer */ 271f6bc909eSSimon Trimmer #define HALO_MPU_VIO_STS_MASK 0x007e0000 272f6bc909eSSimon Trimmer #define HALO_MPU_VIO_STS_SHIFT 17 273f6bc909eSSimon Trimmer #define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 274f6bc909eSSimon Trimmer #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff 275f6bc909eSSimon Trimmer #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 276f6bc909eSSimon Trimmer 277f6bc909eSSimon Trimmer struct cs_dsp_ops { 278f6bc909eSSimon Trimmer bool (*validate_version)(struct cs_dsp *dsp, unsigned int version); 279f6bc909eSSimon Trimmer unsigned int (*parse_sizes)(struct cs_dsp *dsp, 280f6bc909eSSimon Trimmer const char * const file, 281f6bc909eSSimon Trimmer unsigned int pos, 282f6bc909eSSimon Trimmer const struct firmware *firmware); 283f6bc909eSSimon Trimmer int (*setup_algs)(struct cs_dsp *dsp); 284f6bc909eSSimon Trimmer unsigned int (*region_to_reg)(struct cs_dsp_region const *mem, 285f6bc909eSSimon Trimmer unsigned int offset); 286f6bc909eSSimon Trimmer 287f6bc909eSSimon Trimmer void (*show_fw_status)(struct cs_dsp *dsp); 288f6bc909eSSimon Trimmer void (*stop_watchdog)(struct cs_dsp *dsp); 289f6bc909eSSimon Trimmer 290f6bc909eSSimon Trimmer int (*enable_memory)(struct cs_dsp *dsp); 291f6bc909eSSimon Trimmer void (*disable_memory)(struct cs_dsp *dsp); 292f6bc909eSSimon Trimmer int (*lock_memory)(struct cs_dsp *dsp, unsigned int lock_regions); 293f6bc909eSSimon Trimmer 294f6bc909eSSimon Trimmer int (*enable_core)(struct cs_dsp *dsp); 295f6bc909eSSimon Trimmer void (*disable_core)(struct cs_dsp *dsp); 296f6bc909eSSimon Trimmer 297f6bc909eSSimon Trimmer int (*start_core)(struct cs_dsp *dsp); 298f6bc909eSSimon Trimmer void (*stop_core)(struct cs_dsp *dsp); 299f6bc909eSSimon Trimmer }; 300f6bc909eSSimon Trimmer 301f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp1_ops; 302f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp2_ops[]; 303f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ops; 304f6bc909eSSimon Trimmer 305f6bc909eSSimon Trimmer struct cs_dsp_buf { 306f6bc909eSSimon Trimmer struct list_head list; 307f6bc909eSSimon Trimmer void *buf; 308f6bc909eSSimon Trimmer }; 309f6bc909eSSimon Trimmer 310f6bc909eSSimon Trimmer static struct cs_dsp_buf *cs_dsp_buf_alloc(const void *src, size_t len, 311f6bc909eSSimon Trimmer struct list_head *list) 312f6bc909eSSimon Trimmer { 313f6bc909eSSimon Trimmer struct cs_dsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 314f6bc909eSSimon Trimmer 315f6bc909eSSimon Trimmer if (buf == NULL) 316f6bc909eSSimon Trimmer return NULL; 317f6bc909eSSimon Trimmer 318f6bc909eSSimon Trimmer buf->buf = vmalloc(len); 319f6bc909eSSimon Trimmer if (!buf->buf) { 320f6bc909eSSimon Trimmer kfree(buf); 321f6bc909eSSimon Trimmer return NULL; 322f6bc909eSSimon Trimmer } 323f6bc909eSSimon Trimmer memcpy(buf->buf, src, len); 324f6bc909eSSimon Trimmer 325f6bc909eSSimon Trimmer if (list) 326f6bc909eSSimon Trimmer list_add_tail(&buf->list, list); 327f6bc909eSSimon Trimmer 328f6bc909eSSimon Trimmer return buf; 329f6bc909eSSimon Trimmer } 330f6bc909eSSimon Trimmer 331f6bc909eSSimon Trimmer static void cs_dsp_buf_free(struct list_head *list) 332f6bc909eSSimon Trimmer { 333f6bc909eSSimon Trimmer while (!list_empty(list)) { 334f6bc909eSSimon Trimmer struct cs_dsp_buf *buf = list_first_entry(list, 335f6bc909eSSimon Trimmer struct cs_dsp_buf, 336f6bc909eSSimon Trimmer list); 337f6bc909eSSimon Trimmer list_del(&buf->list); 338f6bc909eSSimon Trimmer vfree(buf->buf); 339f6bc909eSSimon Trimmer kfree(buf); 340f6bc909eSSimon Trimmer } 341f6bc909eSSimon Trimmer } 342f6bc909eSSimon Trimmer 343f6bc909eSSimon Trimmer /** 344f6bc909eSSimon Trimmer * cs_dsp_mem_region_name() - Return a name string for a memory type 345f6bc909eSSimon Trimmer * @type: the memory type to match 346f6bc909eSSimon Trimmer * 347f6bc909eSSimon Trimmer * Return: A const string identifying the memory region. 348f6bc909eSSimon Trimmer */ 349f6bc909eSSimon Trimmer const char *cs_dsp_mem_region_name(unsigned int type) 350f6bc909eSSimon Trimmer { 351f6bc909eSSimon Trimmer switch (type) { 352f6bc909eSSimon Trimmer case WMFW_ADSP1_PM: 353f6bc909eSSimon Trimmer return "PM"; 354f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 355f6bc909eSSimon Trimmer return "PM_PACKED"; 356f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 357f6bc909eSSimon Trimmer return "DM"; 358f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 359f6bc909eSSimon Trimmer return "XM"; 360f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 361f6bc909eSSimon Trimmer return "XM_PACKED"; 362f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 363f6bc909eSSimon Trimmer return "YM"; 364f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 365f6bc909eSSimon Trimmer return "YM_PACKED"; 366f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 367f6bc909eSSimon Trimmer return "ZM"; 368f6bc909eSSimon Trimmer default: 369f6bc909eSSimon Trimmer return NULL; 370f6bc909eSSimon Trimmer } 371f6bc909eSSimon Trimmer } 372f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_mem_region_name); 373f6bc909eSSimon Trimmer 374f6bc909eSSimon Trimmer #ifdef CONFIG_DEBUG_FS 375f6bc909eSSimon Trimmer static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s) 376f6bc909eSSimon Trimmer { 377f6bc909eSSimon Trimmer char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 378f6bc909eSSimon Trimmer 379f6bc909eSSimon Trimmer kfree(dsp->wmfw_file_name); 380f6bc909eSSimon Trimmer dsp->wmfw_file_name = tmp; 381f6bc909eSSimon Trimmer } 382f6bc909eSSimon Trimmer 383f6bc909eSSimon Trimmer static void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, const char *s) 384f6bc909eSSimon Trimmer { 385f6bc909eSSimon Trimmer char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 386f6bc909eSSimon Trimmer 387f6bc909eSSimon Trimmer kfree(dsp->bin_file_name); 388f6bc909eSSimon Trimmer dsp->bin_file_name = tmp; 389f6bc909eSSimon Trimmer } 390f6bc909eSSimon Trimmer 391f6bc909eSSimon Trimmer static void cs_dsp_debugfs_clear(struct cs_dsp *dsp) 392f6bc909eSSimon Trimmer { 393f6bc909eSSimon Trimmer kfree(dsp->wmfw_file_name); 394f6bc909eSSimon Trimmer kfree(dsp->bin_file_name); 395f6bc909eSSimon Trimmer dsp->wmfw_file_name = NULL; 396f6bc909eSSimon Trimmer dsp->bin_file_name = NULL; 397f6bc909eSSimon Trimmer } 398f6bc909eSSimon Trimmer 399f6bc909eSSimon Trimmer static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file, 400f6bc909eSSimon Trimmer char __user *user_buf, 401f6bc909eSSimon Trimmer size_t count, loff_t *ppos) 402f6bc909eSSimon Trimmer { 403f6bc909eSSimon Trimmer struct cs_dsp *dsp = file->private_data; 404f6bc909eSSimon Trimmer ssize_t ret; 405f6bc909eSSimon Trimmer 406f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 407f6bc909eSSimon Trimmer 408f6bc909eSSimon Trimmer if (!dsp->wmfw_file_name || !dsp->booted) 409f6bc909eSSimon Trimmer ret = 0; 410f6bc909eSSimon Trimmer else 411f6bc909eSSimon Trimmer ret = simple_read_from_buffer(user_buf, count, ppos, 412f6bc909eSSimon Trimmer dsp->wmfw_file_name, 413f6bc909eSSimon Trimmer strlen(dsp->wmfw_file_name)); 414f6bc909eSSimon Trimmer 415f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 416f6bc909eSSimon Trimmer return ret; 417f6bc909eSSimon Trimmer } 418f6bc909eSSimon Trimmer 419f6bc909eSSimon Trimmer static ssize_t cs_dsp_debugfs_bin_read(struct file *file, 420f6bc909eSSimon Trimmer char __user *user_buf, 421f6bc909eSSimon Trimmer size_t count, loff_t *ppos) 422f6bc909eSSimon Trimmer { 423f6bc909eSSimon Trimmer struct cs_dsp *dsp = file->private_data; 424f6bc909eSSimon Trimmer ssize_t ret; 425f6bc909eSSimon Trimmer 426f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 427f6bc909eSSimon Trimmer 428f6bc909eSSimon Trimmer if (!dsp->bin_file_name || !dsp->booted) 429f6bc909eSSimon Trimmer ret = 0; 430f6bc909eSSimon Trimmer else 431f6bc909eSSimon Trimmer ret = simple_read_from_buffer(user_buf, count, ppos, 432f6bc909eSSimon Trimmer dsp->bin_file_name, 433f6bc909eSSimon Trimmer strlen(dsp->bin_file_name)); 434f6bc909eSSimon Trimmer 435f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 436f6bc909eSSimon Trimmer return ret; 437f6bc909eSSimon Trimmer } 438f6bc909eSSimon Trimmer 439f6bc909eSSimon Trimmer static const struct { 440f6bc909eSSimon Trimmer const char *name; 441f6bc909eSSimon Trimmer const struct file_operations fops; 442f6bc909eSSimon Trimmer } cs_dsp_debugfs_fops[] = { 443f6bc909eSSimon Trimmer { 444f6bc909eSSimon Trimmer .name = "wmfw_file_name", 445f6bc909eSSimon Trimmer .fops = { 446f6bc909eSSimon Trimmer .open = simple_open, 447f6bc909eSSimon Trimmer .read = cs_dsp_debugfs_wmfw_read, 448f6bc909eSSimon Trimmer }, 449f6bc909eSSimon Trimmer }, 450f6bc909eSSimon Trimmer { 451f6bc909eSSimon Trimmer .name = "bin_file_name", 452f6bc909eSSimon Trimmer .fops = { 453f6bc909eSSimon Trimmer .open = simple_open, 454f6bc909eSSimon Trimmer .read = cs_dsp_debugfs_bin_read, 455f6bc909eSSimon Trimmer }, 456f6bc909eSSimon Trimmer }, 457f6bc909eSSimon Trimmer }; 458f6bc909eSSimon Trimmer 459f6bc909eSSimon Trimmer /** 460f6bc909eSSimon Trimmer * cs_dsp_init_debugfs() - Create and populate DSP representation in debugfs 461f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 462f6bc909eSSimon Trimmer * @debugfs_root: pointer to debugfs directory in which to create this DSP 463f6bc909eSSimon Trimmer * representation 464f6bc909eSSimon Trimmer */ 465f6bc909eSSimon Trimmer void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) 466f6bc909eSSimon Trimmer { 467f6bc909eSSimon Trimmer struct dentry *root = NULL; 468f6bc909eSSimon Trimmer int i; 469f6bc909eSSimon Trimmer 470f6bc909eSSimon Trimmer root = debugfs_create_dir(dsp->name, debugfs_root); 471f6bc909eSSimon Trimmer 472f6bc909eSSimon Trimmer debugfs_create_bool("booted", 0444, root, &dsp->booted); 473f6bc909eSSimon Trimmer debugfs_create_bool("running", 0444, root, &dsp->running); 474f6bc909eSSimon Trimmer debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); 475f6bc909eSSimon Trimmer debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); 476f6bc909eSSimon Trimmer 477f6bc909eSSimon Trimmer for (i = 0; i < ARRAY_SIZE(cs_dsp_debugfs_fops); ++i) 478f6bc909eSSimon Trimmer debugfs_create_file(cs_dsp_debugfs_fops[i].name, 0444, root, 479f6bc909eSSimon Trimmer dsp, &cs_dsp_debugfs_fops[i].fops); 480f6bc909eSSimon Trimmer 481f6bc909eSSimon Trimmer dsp->debugfs_root = root; 482f6bc909eSSimon Trimmer } 483f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_init_debugfs); 484f6bc909eSSimon Trimmer 485f6bc909eSSimon Trimmer /** 486f6bc909eSSimon Trimmer * cs_dsp_cleanup_debugfs() - Removes DSP representation from debugfs 487f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 488f6bc909eSSimon Trimmer */ 489f6bc909eSSimon Trimmer void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) 490f6bc909eSSimon Trimmer { 491f6bc909eSSimon Trimmer cs_dsp_debugfs_clear(dsp); 492f6bc909eSSimon Trimmer debugfs_remove_recursive(dsp->debugfs_root); 493f6bc909eSSimon Trimmer dsp->debugfs_root = NULL; 494f6bc909eSSimon Trimmer } 495f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_cleanup_debugfs); 496f6bc909eSSimon Trimmer #else 497f6bc909eSSimon Trimmer void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) 498f6bc909eSSimon Trimmer { 499f6bc909eSSimon Trimmer } 500f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_init_debugfs); 501f6bc909eSSimon Trimmer 502f6bc909eSSimon Trimmer void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) 503f6bc909eSSimon Trimmer { 504f6bc909eSSimon Trimmer } 505f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_cleanup_debugfs); 506f6bc909eSSimon Trimmer 507f6bc909eSSimon Trimmer static inline void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, 508f6bc909eSSimon Trimmer const char *s) 509f6bc909eSSimon Trimmer { 510f6bc909eSSimon Trimmer } 511f6bc909eSSimon Trimmer 512f6bc909eSSimon Trimmer static inline void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, 513f6bc909eSSimon Trimmer const char *s) 514f6bc909eSSimon Trimmer { 515f6bc909eSSimon Trimmer } 516f6bc909eSSimon Trimmer 517f6bc909eSSimon Trimmer static inline void cs_dsp_debugfs_clear(struct cs_dsp *dsp) 518f6bc909eSSimon Trimmer { 519f6bc909eSSimon Trimmer } 520f6bc909eSSimon Trimmer #endif 521f6bc909eSSimon Trimmer 522f6bc909eSSimon Trimmer static const struct cs_dsp_region *cs_dsp_find_region(struct cs_dsp *dsp, 523f6bc909eSSimon Trimmer int type) 524f6bc909eSSimon Trimmer { 525f6bc909eSSimon Trimmer int i; 526f6bc909eSSimon Trimmer 527f6bc909eSSimon Trimmer for (i = 0; i < dsp->num_mems; i++) 528f6bc909eSSimon Trimmer if (dsp->mem[i].type == type) 529f6bc909eSSimon Trimmer return &dsp->mem[i]; 530f6bc909eSSimon Trimmer 531f6bc909eSSimon Trimmer return NULL; 532f6bc909eSSimon Trimmer } 533f6bc909eSSimon Trimmer 534f6bc909eSSimon Trimmer static unsigned int cs_dsp_region_to_reg(struct cs_dsp_region const *mem, 535f6bc909eSSimon Trimmer unsigned int offset) 536f6bc909eSSimon Trimmer { 537f6bc909eSSimon Trimmer switch (mem->type) { 538f6bc909eSSimon Trimmer case WMFW_ADSP1_PM: 539f6bc909eSSimon Trimmer return mem->base + (offset * 3); 540f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 541f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 542f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 543f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 544f6bc909eSSimon Trimmer return mem->base + (offset * 2); 545f6bc909eSSimon Trimmer default: 546f6bc909eSSimon Trimmer WARN(1, "Unknown memory region type"); 547f6bc909eSSimon Trimmer return offset; 548f6bc909eSSimon Trimmer } 549f6bc909eSSimon Trimmer } 550f6bc909eSSimon Trimmer 551f6bc909eSSimon Trimmer static unsigned int cs_dsp_halo_region_to_reg(struct cs_dsp_region const *mem, 552f6bc909eSSimon Trimmer unsigned int offset) 553f6bc909eSSimon Trimmer { 554f6bc909eSSimon Trimmer switch (mem->type) { 555f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 556f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 557f6bc909eSSimon Trimmer return mem->base + (offset * 4); 558f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 559f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 560f6bc909eSSimon Trimmer return (mem->base + (offset * 3)) & ~0x3; 561f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 562f6bc909eSSimon Trimmer return mem->base + (offset * 5); 563f6bc909eSSimon Trimmer default: 564f6bc909eSSimon Trimmer WARN(1, "Unknown memory region type"); 565f6bc909eSSimon Trimmer return offset; 566f6bc909eSSimon Trimmer } 567f6bc909eSSimon Trimmer } 568f6bc909eSSimon Trimmer 569f6bc909eSSimon Trimmer static void cs_dsp_read_fw_status(struct cs_dsp *dsp, 570f6bc909eSSimon Trimmer int noffs, unsigned int *offs) 571f6bc909eSSimon Trimmer { 572f6bc909eSSimon Trimmer unsigned int i; 573f6bc909eSSimon Trimmer int ret; 574f6bc909eSSimon Trimmer 575f6bc909eSSimon Trimmer for (i = 0; i < noffs; ++i) { 576f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 577f6bc909eSSimon Trimmer if (ret) { 578f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 579f6bc909eSSimon Trimmer return; 580f6bc909eSSimon Trimmer } 581f6bc909eSSimon Trimmer } 582f6bc909eSSimon Trimmer } 583f6bc909eSSimon Trimmer 584f6bc909eSSimon Trimmer static void cs_dsp_adsp2_show_fw_status(struct cs_dsp *dsp) 585f6bc909eSSimon Trimmer { 586f6bc909eSSimon Trimmer unsigned int offs[] = { 587f6bc909eSSimon Trimmer ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 588f6bc909eSSimon Trimmer }; 589f6bc909eSSimon Trimmer 590f6bc909eSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 591f6bc909eSSimon Trimmer 592f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 593f6bc909eSSimon Trimmer offs[0], offs[1], offs[2], offs[3]); 594f6bc909eSSimon Trimmer } 595f6bc909eSSimon Trimmer 596f6bc909eSSimon Trimmer static void cs_dsp_adsp2v2_show_fw_status(struct cs_dsp *dsp) 597f6bc909eSSimon Trimmer { 598f6bc909eSSimon Trimmer unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 599f6bc909eSSimon Trimmer 600f6bc909eSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 601f6bc909eSSimon Trimmer 602f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 603f6bc909eSSimon Trimmer offs[0] & 0xFFFF, offs[0] >> 16, 604f6bc909eSSimon Trimmer offs[1] & 0xFFFF, offs[1] >> 16); 605f6bc909eSSimon Trimmer } 606f6bc909eSSimon Trimmer 607f6bc909eSSimon Trimmer static void cs_dsp_halo_show_fw_status(struct cs_dsp *dsp) 608f6bc909eSSimon Trimmer { 609f6bc909eSSimon Trimmer unsigned int offs[] = { 610f6bc909eSSimon Trimmer HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, 611f6bc909eSSimon Trimmer }; 612f6bc909eSSimon Trimmer 613f6bc909eSSimon Trimmer cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 614f6bc909eSSimon Trimmer 615f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 616f6bc909eSSimon Trimmer offs[0], offs[1], offs[2], offs[3]); 617f6bc909eSSimon Trimmer } 618f6bc909eSSimon Trimmer 619f6bc909eSSimon Trimmer static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg) 620f6bc909eSSimon Trimmer { 621f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region = &ctl->alg_region; 622f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 623f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 624f6bc909eSSimon Trimmer 625f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, alg_region->type); 626f6bc909eSSimon Trimmer if (!mem) { 627f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", 628f6bc909eSSimon Trimmer alg_region->type); 629f6bc909eSSimon Trimmer return -EINVAL; 630f6bc909eSSimon Trimmer } 631f6bc909eSSimon Trimmer 632f6bc909eSSimon Trimmer *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); 633f6bc909eSSimon Trimmer 634f6bc909eSSimon Trimmer return 0; 635f6bc909eSSimon Trimmer } 636f6bc909eSSimon Trimmer 637f6bc909eSSimon Trimmer /** 638f6bc909eSSimon Trimmer * cs_dsp_coeff_write_acked_control() - Sends event_id to the acked control 639f6bc909eSSimon Trimmer * @ctl: pointer to acked coefficient control 640f6bc909eSSimon Trimmer * @event_id: the value to write to the given acked control 641f6bc909eSSimon Trimmer * 642f6bc909eSSimon Trimmer * Once the value has been written to the control the function shall block 643f6bc909eSSimon Trimmer * until the running firmware acknowledges the write or timeout is exceeded. 644f6bc909eSSimon Trimmer * 645f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 646f6bc909eSSimon Trimmer * 647f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 648f6bc909eSSimon Trimmer */ 649f6bc909eSSimon Trimmer int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int event_id) 650f6bc909eSSimon Trimmer { 651f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 652f6bc909eSSimon Trimmer __be32 val = cpu_to_be32(event_id); 653f6bc909eSSimon Trimmer unsigned int reg; 654f6bc909eSSimon Trimmer int i, ret; 655f6bc909eSSimon Trimmer 6565065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 6575065cfabSCharles Keepax 658f6bc909eSSimon Trimmer if (!dsp->running) 659f6bc909eSSimon Trimmer return -EPERM; 660f6bc909eSSimon Trimmer 661f6bc909eSSimon Trimmer ret = cs_dsp_coeff_base_reg(ctl, ®); 662f6bc909eSSimon Trimmer if (ret) 663f6bc909eSSimon Trimmer return ret; 664f6bc909eSSimon Trimmer 665f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 666f6bc909eSSimon Trimmer event_id, ctl->alg_region.alg, 667f6bc909eSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), ctl->offset); 668f6bc909eSSimon Trimmer 669f6bc909eSSimon Trimmer ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 670f6bc909eSSimon Trimmer if (ret) { 671f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 672f6bc909eSSimon Trimmer return ret; 673f6bc909eSSimon Trimmer } 674f6bc909eSSimon Trimmer 675f6bc909eSSimon Trimmer /* 676f6bc909eSSimon Trimmer * Poll for ack, we initially poll at ~1ms intervals for firmwares 677f6bc909eSSimon Trimmer * that respond quickly, then go to ~10ms polls. A firmware is unlikely 678f6bc909eSSimon Trimmer * to ack instantly so we do the first 1ms delay before reading the 679f6bc909eSSimon Trimmer * control to avoid a pointless bus transaction 680f6bc909eSSimon Trimmer */ 681f6bc909eSSimon Trimmer for (i = 0; i < CS_DSP_ACKED_CTL_TIMEOUT_MS;) { 682f6bc909eSSimon Trimmer switch (i) { 683f6bc909eSSimon Trimmer case 0 ... CS_DSP_ACKED_CTL_N_QUICKPOLLS - 1: 684f6bc909eSSimon Trimmer usleep_range(1000, 2000); 685f6bc909eSSimon Trimmer i++; 686f6bc909eSSimon Trimmer break; 687f6bc909eSSimon Trimmer default: 688f6bc909eSSimon Trimmer usleep_range(10000, 20000); 689f6bc909eSSimon Trimmer i += 10; 690f6bc909eSSimon Trimmer break; 691f6bc909eSSimon Trimmer } 692f6bc909eSSimon Trimmer 693f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 694f6bc909eSSimon Trimmer if (ret) { 695f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 696f6bc909eSSimon Trimmer return ret; 697f6bc909eSSimon Trimmer } 698f6bc909eSSimon Trimmer 699f6bc909eSSimon Trimmer if (val == 0) { 700f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 701f6bc909eSSimon Trimmer return 0; 702f6bc909eSSimon Trimmer } 703f6bc909eSSimon Trimmer } 704f6bc909eSSimon Trimmer 705f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 706f6bc909eSSimon Trimmer reg, ctl->alg_region.alg, 707f6bc909eSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), 708f6bc909eSSimon Trimmer ctl->offset); 709f6bc909eSSimon Trimmer 710f6bc909eSSimon Trimmer return -ETIMEDOUT; 711f6bc909eSSimon Trimmer } 712f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_acked_control); 713f6bc909eSSimon Trimmer 714f6bc909eSSimon Trimmer static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, 715f6bc909eSSimon Trimmer const void *buf, size_t len) 716f6bc909eSSimon Trimmer { 717f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 718f6bc909eSSimon Trimmer void *scratch; 719f6bc909eSSimon Trimmer int ret; 720f6bc909eSSimon Trimmer unsigned int reg; 721f6bc909eSSimon Trimmer 722f6bc909eSSimon Trimmer ret = cs_dsp_coeff_base_reg(ctl, ®); 723f6bc909eSSimon Trimmer if (ret) 724f6bc909eSSimon Trimmer return ret; 725f6bc909eSSimon Trimmer 726f6bc909eSSimon Trimmer scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 727f6bc909eSSimon Trimmer if (!scratch) 728f6bc909eSSimon Trimmer return -ENOMEM; 729f6bc909eSSimon Trimmer 730f6bc909eSSimon Trimmer ret = regmap_raw_write(dsp->regmap, reg, scratch, 731f6bc909eSSimon Trimmer len); 732f6bc909eSSimon Trimmer if (ret) { 733f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 734f6bc909eSSimon Trimmer len, reg, ret); 735f6bc909eSSimon Trimmer kfree(scratch); 736f6bc909eSSimon Trimmer return ret; 737f6bc909eSSimon Trimmer } 738f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 739f6bc909eSSimon Trimmer 740f6bc909eSSimon Trimmer kfree(scratch); 741f6bc909eSSimon Trimmer 742f6bc909eSSimon Trimmer return 0; 743f6bc909eSSimon Trimmer } 744f6bc909eSSimon Trimmer 745f6bc909eSSimon Trimmer /** 746f6bc909eSSimon Trimmer * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control 747f6bc909eSSimon Trimmer * @ctl: pointer to coefficient control 748f6bc909eSSimon Trimmer * @buf: the buffer to write to the given control 749f6bc909eSSimon Trimmer * @len: the length of the buffer 750f6bc909eSSimon Trimmer * 751f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 752f6bc909eSSimon Trimmer * 753f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 754f6bc909eSSimon Trimmer */ 755f6bc909eSSimon Trimmer int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, const void *buf, size_t len) 756f6bc909eSSimon Trimmer { 757f6bc909eSSimon Trimmer int ret = 0; 758f6bc909eSSimon Trimmer 7595065cfabSCharles Keepax lockdep_assert_held(&ctl->dsp->pwr_lock); 7605065cfabSCharles Keepax 761f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 762f6bc909eSSimon Trimmer ret = -EPERM; 763f6bc909eSSimon Trimmer else if (buf != ctl->cache) 764f6bc909eSSimon Trimmer memcpy(ctl->cache, buf, len); 765f6bc909eSSimon Trimmer 766f6bc909eSSimon Trimmer ctl->set = 1; 767f6bc909eSSimon Trimmer if (ctl->enabled && ctl->dsp->running) 768f6bc909eSSimon Trimmer ret = cs_dsp_coeff_write_ctrl_raw(ctl, buf, len); 769f6bc909eSSimon Trimmer 770f6bc909eSSimon Trimmer return ret; 771f6bc909eSSimon Trimmer } 772f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_ctrl); 773f6bc909eSSimon Trimmer 774f6bc909eSSimon Trimmer static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) 775f6bc909eSSimon Trimmer { 776f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 777f6bc909eSSimon Trimmer void *scratch; 778f6bc909eSSimon Trimmer int ret; 779f6bc909eSSimon Trimmer unsigned int reg; 780f6bc909eSSimon Trimmer 781f6bc909eSSimon Trimmer ret = cs_dsp_coeff_base_reg(ctl, ®); 782f6bc909eSSimon Trimmer if (ret) 783f6bc909eSSimon Trimmer return ret; 784f6bc909eSSimon Trimmer 785f6bc909eSSimon Trimmer scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 786f6bc909eSSimon Trimmer if (!scratch) 787f6bc909eSSimon Trimmer return -ENOMEM; 788f6bc909eSSimon Trimmer 789f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 790f6bc909eSSimon Trimmer if (ret) { 791f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 792f6bc909eSSimon Trimmer len, reg, ret); 793f6bc909eSSimon Trimmer kfree(scratch); 794f6bc909eSSimon Trimmer return ret; 795f6bc909eSSimon Trimmer } 796f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 797f6bc909eSSimon Trimmer 798f6bc909eSSimon Trimmer memcpy(buf, scratch, len); 799f6bc909eSSimon Trimmer kfree(scratch); 800f6bc909eSSimon Trimmer 801f6bc909eSSimon Trimmer return 0; 802f6bc909eSSimon Trimmer } 803f6bc909eSSimon Trimmer 804f6bc909eSSimon Trimmer /** 805f6bc909eSSimon Trimmer * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer 806f6bc909eSSimon Trimmer * @ctl: pointer to coefficient control 807f6bc909eSSimon Trimmer * @buf: the buffer to store to the given control 808f6bc909eSSimon Trimmer * @len: the length of the buffer 809f6bc909eSSimon Trimmer * 810f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 811f6bc909eSSimon Trimmer * 812f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 813f6bc909eSSimon Trimmer */ 814f6bc909eSSimon Trimmer int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, void *buf, size_t len) 815f6bc909eSSimon Trimmer { 816f6bc909eSSimon Trimmer int ret = 0; 817f6bc909eSSimon Trimmer 8185065cfabSCharles Keepax lockdep_assert_held(&ctl->dsp->pwr_lock); 8195065cfabSCharles Keepax 820f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 821f6bc909eSSimon Trimmer if (ctl->enabled && ctl->dsp->running) 822f6bc909eSSimon Trimmer return cs_dsp_coeff_read_ctrl_raw(ctl, buf, len); 823f6bc909eSSimon Trimmer else 824f6bc909eSSimon Trimmer return -EPERM; 825f6bc909eSSimon Trimmer } else { 826f6bc909eSSimon Trimmer if (!ctl->flags && ctl->enabled && ctl->dsp->running) 827f6bc909eSSimon Trimmer ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 828f6bc909eSSimon Trimmer 829f6bc909eSSimon Trimmer if (buf != ctl->cache) 830f6bc909eSSimon Trimmer memcpy(buf, ctl->cache, len); 831f6bc909eSSimon Trimmer } 832f6bc909eSSimon Trimmer 833f6bc909eSSimon Trimmer return ret; 834f6bc909eSSimon Trimmer } 835f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_read_ctrl); 836f6bc909eSSimon Trimmer 837f6bc909eSSimon Trimmer static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp) 838f6bc909eSSimon Trimmer { 839f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 840f6bc909eSSimon Trimmer int ret; 841f6bc909eSSimon Trimmer 842f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 843f6bc909eSSimon Trimmer if (!ctl->enabled || ctl->set) 844f6bc909eSSimon Trimmer continue; 845f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 846f6bc909eSSimon Trimmer continue; 847f6bc909eSSimon Trimmer 848f6bc909eSSimon Trimmer /* 849f6bc909eSSimon Trimmer * For readable controls populate the cache from the DSP memory. 850f6bc909eSSimon Trimmer * For non-readable controls the cache was zero-filled when 851f6bc909eSSimon Trimmer * created so we don't need to do anything. 852f6bc909eSSimon Trimmer */ 853f6bc909eSSimon Trimmer if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 854f6bc909eSSimon Trimmer ret = cs_dsp_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); 855f6bc909eSSimon Trimmer if (ret < 0) 856f6bc909eSSimon Trimmer return ret; 857f6bc909eSSimon Trimmer } 858f6bc909eSSimon Trimmer } 859f6bc909eSSimon Trimmer 860f6bc909eSSimon Trimmer return 0; 861f6bc909eSSimon Trimmer } 862f6bc909eSSimon Trimmer 863f6bc909eSSimon Trimmer static int cs_dsp_coeff_sync_controls(struct cs_dsp *dsp) 864f6bc909eSSimon Trimmer { 865f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 866f6bc909eSSimon Trimmer int ret; 867f6bc909eSSimon Trimmer 868f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 869f6bc909eSSimon Trimmer if (!ctl->enabled) 870f6bc909eSSimon Trimmer continue; 871f6bc909eSSimon Trimmer if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 872f6bc909eSSimon Trimmer ret = cs_dsp_coeff_write_ctrl_raw(ctl, ctl->cache, 873f6bc909eSSimon Trimmer ctl->len); 874f6bc909eSSimon Trimmer if (ret < 0) 875f6bc909eSSimon Trimmer return ret; 876f6bc909eSSimon Trimmer } 877f6bc909eSSimon Trimmer } 878f6bc909eSSimon Trimmer 879f6bc909eSSimon Trimmer return 0; 880f6bc909eSSimon Trimmer } 881f6bc909eSSimon Trimmer 882f6bc909eSSimon Trimmer static void cs_dsp_signal_event_controls(struct cs_dsp *dsp, 883f6bc909eSSimon Trimmer unsigned int event) 884f6bc909eSSimon Trimmer { 885f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 886f6bc909eSSimon Trimmer int ret; 887f6bc909eSSimon Trimmer 888f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 889f6bc909eSSimon Trimmer if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 890f6bc909eSSimon Trimmer continue; 891f6bc909eSSimon Trimmer 892f6bc909eSSimon Trimmer if (!ctl->enabled) 893f6bc909eSSimon Trimmer continue; 894f6bc909eSSimon Trimmer 895f6bc909eSSimon Trimmer ret = cs_dsp_coeff_write_acked_control(ctl, event); 896f6bc909eSSimon Trimmer if (ret) 897f6bc909eSSimon Trimmer cs_dsp_warn(dsp, 898f6bc909eSSimon Trimmer "Failed to send 0x%x event to alg 0x%x (%d)\n", 899f6bc909eSSimon Trimmer event, ctl->alg_region.alg, ret); 900f6bc909eSSimon Trimmer } 901f6bc909eSSimon Trimmer } 902f6bc909eSSimon Trimmer 903f6bc909eSSimon Trimmer static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl) 904f6bc909eSSimon Trimmer { 905f6bc909eSSimon Trimmer kfree(ctl->cache); 906f6bc909eSSimon Trimmer kfree(ctl->subname); 907f6bc909eSSimon Trimmer kfree(ctl); 908f6bc909eSSimon Trimmer } 909f6bc909eSSimon Trimmer 910f6bc909eSSimon Trimmer static int cs_dsp_create_control(struct cs_dsp *dsp, 911f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region, 912f6bc909eSSimon Trimmer unsigned int offset, unsigned int len, 913f6bc909eSSimon Trimmer const char *subname, unsigned int subname_len, 914f6bc909eSSimon Trimmer unsigned int flags, unsigned int type) 915f6bc909eSSimon Trimmer { 916f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 917f6bc909eSSimon Trimmer int ret; 918f6bc909eSSimon Trimmer 919f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 920f6bc909eSSimon Trimmer if (ctl->fw_name == dsp->fw_name && 921f6bc909eSSimon Trimmer ctl->alg_region.alg == alg_region->alg && 922f6bc909eSSimon Trimmer ctl->alg_region.type == alg_region->type) { 923f6bc909eSSimon Trimmer if ((!subname && !ctl->subname) || 924f6bc909eSSimon Trimmer (subname && !strncmp(ctl->subname, subname, ctl->subname_len))) { 925f6bc909eSSimon Trimmer if (!ctl->enabled) 926f6bc909eSSimon Trimmer ctl->enabled = 1; 927f6bc909eSSimon Trimmer return 0; 928f6bc909eSSimon Trimmer } 929f6bc909eSSimon Trimmer } 930f6bc909eSSimon Trimmer } 931f6bc909eSSimon Trimmer 932f6bc909eSSimon Trimmer ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 933f6bc909eSSimon Trimmer if (!ctl) 934f6bc909eSSimon Trimmer return -ENOMEM; 935f6bc909eSSimon Trimmer 936f6bc909eSSimon Trimmer ctl->fw_name = dsp->fw_name; 937f6bc909eSSimon Trimmer ctl->alg_region = *alg_region; 938f6bc909eSSimon Trimmer if (subname && dsp->fw_ver >= 2) { 939f6bc909eSSimon Trimmer ctl->subname_len = subname_len; 940f6bc909eSSimon Trimmer ctl->subname = kmemdup(subname, 941f6bc909eSSimon Trimmer strlen(subname) + 1, GFP_KERNEL); 942f6bc909eSSimon Trimmer if (!ctl->subname) { 943f6bc909eSSimon Trimmer ret = -ENOMEM; 944f6bc909eSSimon Trimmer goto err_ctl; 945f6bc909eSSimon Trimmer } 946f6bc909eSSimon Trimmer } 947f6bc909eSSimon Trimmer ctl->enabled = 1; 948f6bc909eSSimon Trimmer ctl->set = 0; 949f6bc909eSSimon Trimmer ctl->dsp = dsp; 950f6bc909eSSimon Trimmer 951f6bc909eSSimon Trimmer ctl->flags = flags; 952f6bc909eSSimon Trimmer ctl->type = type; 953f6bc909eSSimon Trimmer ctl->offset = offset; 954f6bc909eSSimon Trimmer ctl->len = len; 955f6bc909eSSimon Trimmer ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 956f6bc909eSSimon Trimmer if (!ctl->cache) { 957f6bc909eSSimon Trimmer ret = -ENOMEM; 958f6bc909eSSimon Trimmer goto err_ctl_subname; 959f6bc909eSSimon Trimmer } 960f6bc909eSSimon Trimmer 961f6bc909eSSimon Trimmer list_add(&ctl->list, &dsp->ctl_list); 962f6bc909eSSimon Trimmer 963f6bc909eSSimon Trimmer if (dsp->client_ops->control_add) { 964f6bc909eSSimon Trimmer ret = dsp->client_ops->control_add(ctl); 965f6bc909eSSimon Trimmer if (ret) 966f6bc909eSSimon Trimmer goto err_list_del; 967f6bc909eSSimon Trimmer } 968f6bc909eSSimon Trimmer 969f6bc909eSSimon Trimmer return 0; 970f6bc909eSSimon Trimmer 971f6bc909eSSimon Trimmer err_list_del: 972f6bc909eSSimon Trimmer list_del(&ctl->list); 973f6bc909eSSimon Trimmer kfree(ctl->cache); 974f6bc909eSSimon Trimmer err_ctl_subname: 975f6bc909eSSimon Trimmer kfree(ctl->subname); 976f6bc909eSSimon Trimmer err_ctl: 977f6bc909eSSimon Trimmer kfree(ctl); 978f6bc909eSSimon Trimmer 979f6bc909eSSimon Trimmer return ret; 980f6bc909eSSimon Trimmer } 981f6bc909eSSimon Trimmer 982f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg { 983f6bc909eSSimon Trimmer int id; 984f6bc909eSSimon Trimmer const u8 *name; 985f6bc909eSSimon Trimmer int name_len; 986f6bc909eSSimon Trimmer int ncoeff; 987f6bc909eSSimon Trimmer }; 988f6bc909eSSimon Trimmer 989f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff { 990f6bc909eSSimon Trimmer int offset; 991f6bc909eSSimon Trimmer int mem_type; 992f6bc909eSSimon Trimmer const u8 *name; 993f6bc909eSSimon Trimmer int name_len; 994f6bc909eSSimon Trimmer unsigned int ctl_type; 995f6bc909eSSimon Trimmer int flags; 996f6bc909eSSimon Trimmer int len; 997f6bc909eSSimon Trimmer }; 998f6bc909eSSimon Trimmer 999f6bc909eSSimon Trimmer static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1000f6bc909eSSimon Trimmer { 1001f6bc909eSSimon Trimmer int length; 1002f6bc909eSSimon Trimmer 1003f6bc909eSSimon Trimmer switch (bytes) { 1004f6bc909eSSimon Trimmer case 1: 1005f6bc909eSSimon Trimmer length = **pos; 1006f6bc909eSSimon Trimmer break; 1007f6bc909eSSimon Trimmer case 2: 1008f6bc909eSSimon Trimmer length = le16_to_cpu(*((__le16 *)*pos)); 1009f6bc909eSSimon Trimmer break; 1010f6bc909eSSimon Trimmer default: 1011f6bc909eSSimon Trimmer return 0; 1012f6bc909eSSimon Trimmer } 1013f6bc909eSSimon Trimmer 1014f6bc909eSSimon Trimmer if (str) 1015f6bc909eSSimon Trimmer *str = *pos + bytes; 1016f6bc909eSSimon Trimmer 1017f6bc909eSSimon Trimmer *pos += ((length + bytes) + 3) & ~0x03; 1018f6bc909eSSimon Trimmer 1019f6bc909eSSimon Trimmer return length; 1020f6bc909eSSimon Trimmer } 1021f6bc909eSSimon Trimmer 1022f6bc909eSSimon Trimmer static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos) 1023f6bc909eSSimon Trimmer { 1024f6bc909eSSimon Trimmer int val = 0; 1025f6bc909eSSimon Trimmer 1026f6bc909eSSimon Trimmer switch (bytes) { 1027f6bc909eSSimon Trimmer case 2: 1028f6bc909eSSimon Trimmer val = le16_to_cpu(*((__le16 *)*pos)); 1029f6bc909eSSimon Trimmer break; 1030f6bc909eSSimon Trimmer case 4: 1031f6bc909eSSimon Trimmer val = le32_to_cpu(*((__le32 *)*pos)); 1032f6bc909eSSimon Trimmer break; 1033f6bc909eSSimon Trimmer default: 1034f6bc909eSSimon Trimmer break; 1035f6bc909eSSimon Trimmer } 1036f6bc909eSSimon Trimmer 1037f6bc909eSSimon Trimmer *pos += bytes; 1038f6bc909eSSimon Trimmer 1039f6bc909eSSimon Trimmer return val; 1040f6bc909eSSimon Trimmer } 1041f6bc909eSSimon Trimmer 1042f6bc909eSSimon Trimmer static inline void cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, const u8 **data, 1043f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg *blk) 1044f6bc909eSSimon Trimmer { 1045f6bc909eSSimon Trimmer const struct wmfw_adsp_alg_data *raw; 1046f6bc909eSSimon Trimmer 1047f6bc909eSSimon Trimmer switch (dsp->fw_ver) { 1048f6bc909eSSimon Trimmer case 0: 1049f6bc909eSSimon Trimmer case 1: 1050f6bc909eSSimon Trimmer raw = (const struct wmfw_adsp_alg_data *)*data; 1051f6bc909eSSimon Trimmer *data = raw->data; 1052f6bc909eSSimon Trimmer 1053f6bc909eSSimon Trimmer blk->id = le32_to_cpu(raw->id); 1054f6bc909eSSimon Trimmer blk->name = raw->name; 1055f6bc909eSSimon Trimmer blk->name_len = strlen(raw->name); 1056f6bc909eSSimon Trimmer blk->ncoeff = le32_to_cpu(raw->ncoeff); 1057f6bc909eSSimon Trimmer break; 1058f6bc909eSSimon Trimmer default: 1059f6bc909eSSimon Trimmer blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), data); 1060f6bc909eSSimon Trimmer blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), data, 1061f6bc909eSSimon Trimmer &blk->name); 1062f6bc909eSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u16), data, NULL); 1063f6bc909eSSimon Trimmer blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), data); 1064f6bc909eSSimon Trimmer break; 1065f6bc909eSSimon Trimmer } 1066f6bc909eSSimon Trimmer 1067f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 1068f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 1069f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 1070f6bc909eSSimon Trimmer } 1071f6bc909eSSimon Trimmer 1072f6bc909eSSimon Trimmer static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data, 1073f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff *blk) 1074f6bc909eSSimon Trimmer { 1075f6bc909eSSimon Trimmer const struct wmfw_adsp_coeff_data *raw; 1076f6bc909eSSimon Trimmer const u8 *tmp; 1077f6bc909eSSimon Trimmer int length; 1078f6bc909eSSimon Trimmer 1079f6bc909eSSimon Trimmer switch (dsp->fw_ver) { 1080f6bc909eSSimon Trimmer case 0: 1081f6bc909eSSimon Trimmer case 1: 1082f6bc909eSSimon Trimmer raw = (const struct wmfw_adsp_coeff_data *)*data; 1083f6bc909eSSimon Trimmer *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 1084f6bc909eSSimon Trimmer 1085f6bc909eSSimon Trimmer blk->offset = le16_to_cpu(raw->hdr.offset); 1086f6bc909eSSimon Trimmer blk->mem_type = le16_to_cpu(raw->hdr.type); 1087f6bc909eSSimon Trimmer blk->name = raw->name; 1088f6bc909eSSimon Trimmer blk->name_len = strlen(raw->name); 1089f6bc909eSSimon Trimmer blk->ctl_type = le16_to_cpu(raw->ctl_type); 1090f6bc909eSSimon Trimmer blk->flags = le16_to_cpu(raw->flags); 1091f6bc909eSSimon Trimmer blk->len = le32_to_cpu(raw->len); 1092f6bc909eSSimon Trimmer break; 1093f6bc909eSSimon Trimmer default: 1094f6bc909eSSimon Trimmer tmp = *data; 1095f6bc909eSSimon Trimmer blk->offset = cs_dsp_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1096f6bc909eSSimon Trimmer blk->mem_type = cs_dsp_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1097f6bc909eSSimon Trimmer length = cs_dsp_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1098f6bc909eSSimon Trimmer blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, 1099f6bc909eSSimon Trimmer &blk->name); 1100f6bc909eSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u8), &tmp, NULL); 1101f6bc909eSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u16), &tmp, NULL); 1102f6bc909eSSimon Trimmer blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1103f6bc909eSSimon Trimmer blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp); 1104f6bc909eSSimon Trimmer blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp); 1105f6bc909eSSimon Trimmer 1106f6bc909eSSimon Trimmer *data = *data + sizeof(raw->hdr) + length; 1107f6bc909eSSimon Trimmer break; 1108f6bc909eSSimon Trimmer } 1109f6bc909eSSimon Trimmer 1110f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 1111f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 1112f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 1113f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 1114f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 1115f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 1116f6bc909eSSimon Trimmer } 1117f6bc909eSSimon Trimmer 1118f6bc909eSSimon Trimmer static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp, 1119f6bc909eSSimon Trimmer const struct cs_dsp_coeff_parsed_coeff *coeff_blk, 1120f6bc909eSSimon Trimmer unsigned int f_required, 1121f6bc909eSSimon Trimmer unsigned int f_illegal) 1122f6bc909eSSimon Trimmer { 1123f6bc909eSSimon Trimmer if ((coeff_blk->flags & f_illegal) || 1124f6bc909eSSimon Trimmer ((coeff_blk->flags & f_required) != f_required)) { 1125f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1126f6bc909eSSimon Trimmer coeff_blk->flags, coeff_blk->ctl_type); 1127f6bc909eSSimon Trimmer return -EINVAL; 1128f6bc909eSSimon Trimmer } 1129f6bc909eSSimon Trimmer 1130f6bc909eSSimon Trimmer return 0; 1131f6bc909eSSimon Trimmer } 1132f6bc909eSSimon Trimmer 1133f6bc909eSSimon Trimmer static int cs_dsp_parse_coeff(struct cs_dsp *dsp, 1134f6bc909eSSimon Trimmer const struct wmfw_region *region) 1135f6bc909eSSimon Trimmer { 1136f6bc909eSSimon Trimmer struct cs_dsp_alg_region alg_region = {}; 1137f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg alg_blk; 1138f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff coeff_blk; 1139f6bc909eSSimon Trimmer const u8 *data = region->data; 1140f6bc909eSSimon Trimmer int i, ret; 1141f6bc909eSSimon Trimmer 1142f6bc909eSSimon Trimmer cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk); 1143f6bc909eSSimon Trimmer for (i = 0; i < alg_blk.ncoeff; i++) { 1144f6bc909eSSimon Trimmer cs_dsp_coeff_parse_coeff(dsp, &data, &coeff_blk); 1145f6bc909eSSimon Trimmer 1146f6bc909eSSimon Trimmer switch (coeff_blk.ctl_type) { 1147f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_BYTES: 1148f6bc909eSSimon Trimmer break; 1149f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_ACKED: 1150f6bc909eSSimon Trimmer if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1151f6bc909eSSimon Trimmer continue; /* ignore */ 1152f6bc909eSSimon Trimmer 1153f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1154f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1155f6bc909eSSimon Trimmer WMFW_CTL_FLAG_WRITEABLE | 1156f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1157f6bc909eSSimon Trimmer 0); 1158f6bc909eSSimon Trimmer if (ret) 1159f6bc909eSSimon Trimmer return -EINVAL; 1160f6bc909eSSimon Trimmer break; 1161f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_HOSTEVENT: 1162f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1163f6bc909eSSimon Trimmer WMFW_CTL_FLAG_SYS | 1164f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1165f6bc909eSSimon Trimmer WMFW_CTL_FLAG_WRITEABLE | 1166f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1167f6bc909eSSimon Trimmer 0); 1168f6bc909eSSimon Trimmer if (ret) 1169f6bc909eSSimon Trimmer return -EINVAL; 1170f6bc909eSSimon Trimmer break; 1171f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_HOST_BUFFER: 1172f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1173f6bc909eSSimon Trimmer WMFW_CTL_FLAG_SYS | 1174f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1175f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1176f6bc909eSSimon Trimmer 0); 1177f6bc909eSSimon Trimmer if (ret) 1178f6bc909eSSimon Trimmer return -EINVAL; 1179f6bc909eSSimon Trimmer break; 1180f6bc909eSSimon Trimmer default: 1181f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Unknown control type: %d\n", 1182f6bc909eSSimon Trimmer coeff_blk.ctl_type); 1183f6bc909eSSimon Trimmer return -EINVAL; 1184f6bc909eSSimon Trimmer } 1185f6bc909eSSimon Trimmer 1186f6bc909eSSimon Trimmer alg_region.type = coeff_blk.mem_type; 1187f6bc909eSSimon Trimmer alg_region.alg = alg_blk.id; 1188f6bc909eSSimon Trimmer 1189f6bc909eSSimon Trimmer ret = cs_dsp_create_control(dsp, &alg_region, 1190f6bc909eSSimon Trimmer coeff_blk.offset, 1191f6bc909eSSimon Trimmer coeff_blk.len, 1192f6bc909eSSimon Trimmer coeff_blk.name, 1193f6bc909eSSimon Trimmer coeff_blk.name_len, 1194f6bc909eSSimon Trimmer coeff_blk.flags, 1195f6bc909eSSimon Trimmer coeff_blk.ctl_type); 1196f6bc909eSSimon Trimmer if (ret < 0) 1197f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to create control: %.*s, %d\n", 1198f6bc909eSSimon Trimmer coeff_blk.name_len, coeff_blk.name, ret); 1199f6bc909eSSimon Trimmer } 1200f6bc909eSSimon Trimmer 1201f6bc909eSSimon Trimmer return 0; 1202f6bc909eSSimon Trimmer } 1203f6bc909eSSimon Trimmer 1204f6bc909eSSimon Trimmer static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp, 1205f6bc909eSSimon Trimmer const char * const file, 1206f6bc909eSSimon Trimmer unsigned int pos, 1207f6bc909eSSimon Trimmer const struct firmware *firmware) 1208f6bc909eSSimon Trimmer { 1209f6bc909eSSimon Trimmer const struct wmfw_adsp1_sizes *adsp1_sizes; 1210f6bc909eSSimon Trimmer 1211f6bc909eSSimon Trimmer adsp1_sizes = (void *)&firmware->data[pos]; 1212f6bc909eSSimon Trimmer 1213f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 1214f6bc909eSSimon Trimmer le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 1215f6bc909eSSimon Trimmer le32_to_cpu(adsp1_sizes->zm)); 1216f6bc909eSSimon Trimmer 1217f6bc909eSSimon Trimmer return pos + sizeof(*adsp1_sizes); 1218f6bc909eSSimon Trimmer } 1219f6bc909eSSimon Trimmer 1220f6bc909eSSimon Trimmer static unsigned int cs_dsp_adsp2_parse_sizes(struct cs_dsp *dsp, 1221f6bc909eSSimon Trimmer const char * const file, 1222f6bc909eSSimon Trimmer unsigned int pos, 1223f6bc909eSSimon Trimmer const struct firmware *firmware) 1224f6bc909eSSimon Trimmer { 1225f6bc909eSSimon Trimmer const struct wmfw_adsp2_sizes *adsp2_sizes; 1226f6bc909eSSimon Trimmer 1227f6bc909eSSimon Trimmer adsp2_sizes = (void *)&firmware->data[pos]; 1228f6bc909eSSimon Trimmer 1229f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 1230f6bc909eSSimon Trimmer le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 1231f6bc909eSSimon Trimmer le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 1232f6bc909eSSimon Trimmer 1233f6bc909eSSimon Trimmer return pos + sizeof(*adsp2_sizes); 1234f6bc909eSSimon Trimmer } 1235f6bc909eSSimon Trimmer 1236f6bc909eSSimon Trimmer static bool cs_dsp_validate_version(struct cs_dsp *dsp, unsigned int version) 1237f6bc909eSSimon Trimmer { 1238f6bc909eSSimon Trimmer switch (version) { 1239f6bc909eSSimon Trimmer case 0: 1240f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Deprecated file format %d\n", version); 1241f6bc909eSSimon Trimmer return true; 1242f6bc909eSSimon Trimmer case 1: 1243f6bc909eSSimon Trimmer case 2: 1244f6bc909eSSimon Trimmer return true; 1245f6bc909eSSimon Trimmer default: 1246f6bc909eSSimon Trimmer return false; 1247f6bc909eSSimon Trimmer } 1248f6bc909eSSimon Trimmer } 1249f6bc909eSSimon Trimmer 1250f6bc909eSSimon Trimmer static bool cs_dsp_halo_validate_version(struct cs_dsp *dsp, unsigned int version) 1251f6bc909eSSimon Trimmer { 1252f6bc909eSSimon Trimmer switch (version) { 1253f6bc909eSSimon Trimmer case 3: 1254f6bc909eSSimon Trimmer return true; 1255f6bc909eSSimon Trimmer default: 1256f6bc909eSSimon Trimmer return false; 1257f6bc909eSSimon Trimmer } 1258f6bc909eSSimon Trimmer } 1259f6bc909eSSimon Trimmer 1260f6bc909eSSimon Trimmer static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, 1261f6bc909eSSimon Trimmer const char *file) 1262f6bc909eSSimon Trimmer { 1263f6bc909eSSimon Trimmer LIST_HEAD(buf_list); 1264f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 1265f6bc909eSSimon Trimmer unsigned int pos = 0; 1266f6bc909eSSimon Trimmer const struct wmfw_header *header; 1267f6bc909eSSimon Trimmer const struct wmfw_adsp1_sizes *adsp1_sizes; 1268f6bc909eSSimon Trimmer const struct wmfw_footer *footer; 1269f6bc909eSSimon Trimmer const struct wmfw_region *region; 1270f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1271f6bc909eSSimon Trimmer const char *region_name; 1272f6bc909eSSimon Trimmer char *text = NULL; 1273f6bc909eSSimon Trimmer struct cs_dsp_buf *buf; 1274f6bc909eSSimon Trimmer unsigned int reg; 1275f6bc909eSSimon Trimmer int regions = 0; 1276f6bc909eSSimon Trimmer int ret, offset, type; 1277f6bc909eSSimon Trimmer 1278f6bc909eSSimon Trimmer ret = -EINVAL; 1279f6bc909eSSimon Trimmer 1280f6bc909eSSimon Trimmer pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 1281f6bc909eSSimon Trimmer if (pos >= firmware->size) { 1282f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: file too short, %zu bytes\n", 1283f6bc909eSSimon Trimmer file, firmware->size); 1284f6bc909eSSimon Trimmer goto out_fw; 1285f6bc909eSSimon Trimmer } 1286f6bc909eSSimon Trimmer 1287f6bc909eSSimon Trimmer header = (void *)&firmware->data[0]; 1288f6bc909eSSimon Trimmer 1289f6bc909eSSimon Trimmer if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 1290f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid magic\n", file); 1291f6bc909eSSimon Trimmer goto out_fw; 1292f6bc909eSSimon Trimmer } 1293f6bc909eSSimon Trimmer 1294f6bc909eSSimon Trimmer if (!dsp->ops->validate_version(dsp, header->ver)) { 1295f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: unknown file format %d\n", 1296f6bc909eSSimon Trimmer file, header->ver); 1297f6bc909eSSimon Trimmer goto out_fw; 1298f6bc909eSSimon Trimmer } 1299f6bc909eSSimon Trimmer 1300f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware version: %d\n", header->ver); 1301f6bc909eSSimon Trimmer dsp->fw_ver = header->ver; 1302f6bc909eSSimon Trimmer 1303f6bc909eSSimon Trimmer if (header->core != dsp->type) { 1304f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid core %d != %d\n", 1305f6bc909eSSimon Trimmer file, header->core, dsp->type); 1306f6bc909eSSimon Trimmer goto out_fw; 1307f6bc909eSSimon Trimmer } 1308f6bc909eSSimon Trimmer 1309f6bc909eSSimon Trimmer pos = sizeof(*header); 1310f6bc909eSSimon Trimmer pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 1311f6bc909eSSimon Trimmer 1312f6bc909eSSimon Trimmer footer = (void *)&firmware->data[pos]; 1313f6bc909eSSimon Trimmer pos += sizeof(*footer); 1314f6bc909eSSimon Trimmer 1315f6bc909eSSimon Trimmer if (le32_to_cpu(header->len) != pos) { 1316f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: unexpected header length %d\n", 1317f6bc909eSSimon Trimmer file, le32_to_cpu(header->len)); 1318f6bc909eSSimon Trimmer goto out_fw; 1319f6bc909eSSimon Trimmer } 1320f6bc909eSSimon Trimmer 1321f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, 1322f6bc909eSSimon Trimmer le64_to_cpu(footer->timestamp)); 1323f6bc909eSSimon Trimmer 1324f6bc909eSSimon Trimmer while (pos < firmware->size && 1325f6bc909eSSimon Trimmer sizeof(*region) < firmware->size - pos) { 1326f6bc909eSSimon Trimmer region = (void *)&(firmware->data[pos]); 1327f6bc909eSSimon Trimmer region_name = "Unknown"; 1328f6bc909eSSimon Trimmer reg = 0; 1329f6bc909eSSimon Trimmer text = NULL; 1330f6bc909eSSimon Trimmer offset = le32_to_cpu(region->offset) & 0xffffff; 1331f6bc909eSSimon Trimmer type = be32_to_cpu(region->type) & 0xff; 1332f6bc909eSSimon Trimmer 1333f6bc909eSSimon Trimmer switch (type) { 1334f6bc909eSSimon Trimmer case WMFW_NAME_TEXT: 1335f6bc909eSSimon Trimmer region_name = "Firmware name"; 1336f6bc909eSSimon Trimmer text = kzalloc(le32_to_cpu(region->len) + 1, 1337f6bc909eSSimon Trimmer GFP_KERNEL); 1338f6bc909eSSimon Trimmer break; 1339f6bc909eSSimon Trimmer case WMFW_ALGORITHM_DATA: 1340f6bc909eSSimon Trimmer region_name = "Algorithm"; 1341f6bc909eSSimon Trimmer ret = cs_dsp_parse_coeff(dsp, region); 1342f6bc909eSSimon Trimmer if (ret != 0) 1343f6bc909eSSimon Trimmer goto out_fw; 1344f6bc909eSSimon Trimmer break; 1345f6bc909eSSimon Trimmer case WMFW_INFO_TEXT: 1346f6bc909eSSimon Trimmer region_name = "Information"; 1347f6bc909eSSimon Trimmer text = kzalloc(le32_to_cpu(region->len) + 1, 1348f6bc909eSSimon Trimmer GFP_KERNEL); 1349f6bc909eSSimon Trimmer break; 1350f6bc909eSSimon Trimmer case WMFW_ABSOLUTE: 1351f6bc909eSSimon Trimmer region_name = "Absolute"; 1352f6bc909eSSimon Trimmer reg = offset; 1353f6bc909eSSimon Trimmer break; 1354f6bc909eSSimon Trimmer case WMFW_ADSP1_PM: 1355f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 1356f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 1357f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 1358f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 1359f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 1360f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 1361f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 1362f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 1363f6bc909eSSimon Trimmer if (!mem) { 1364f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No region of type: %x\n", type); 1365f6bc909eSSimon Trimmer ret = -EINVAL; 1366f6bc909eSSimon Trimmer goto out_fw; 1367f6bc909eSSimon Trimmer } 1368f6bc909eSSimon Trimmer 1369f6bc909eSSimon Trimmer region_name = cs_dsp_mem_region_name(type); 1370f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, offset); 1371f6bc909eSSimon Trimmer break; 1372f6bc909eSSimon Trimmer default: 1373f6bc909eSSimon Trimmer cs_dsp_warn(dsp, 1374f6bc909eSSimon Trimmer "%s.%d: Unknown region type %x at %d(%x)\n", 1375f6bc909eSSimon Trimmer file, regions, type, pos, pos); 1376f6bc909eSSimon Trimmer break; 1377f6bc909eSSimon Trimmer } 1378f6bc909eSSimon Trimmer 1379f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 1380f6bc909eSSimon Trimmer regions, le32_to_cpu(region->len), offset, 1381f6bc909eSSimon Trimmer region_name); 1382f6bc909eSSimon Trimmer 1383f6bc909eSSimon Trimmer if (le32_to_cpu(region->len) > 1384f6bc909eSSimon Trimmer firmware->size - pos - sizeof(*region)) { 1385f6bc909eSSimon Trimmer cs_dsp_err(dsp, 1386f6bc909eSSimon Trimmer "%s.%d: %s region len %d bytes exceeds file length %zu\n", 1387f6bc909eSSimon Trimmer file, regions, region_name, 1388f6bc909eSSimon Trimmer le32_to_cpu(region->len), firmware->size); 1389f6bc909eSSimon Trimmer ret = -EINVAL; 1390f6bc909eSSimon Trimmer goto out_fw; 1391f6bc909eSSimon Trimmer } 1392f6bc909eSSimon Trimmer 1393f6bc909eSSimon Trimmer if (text) { 1394f6bc909eSSimon Trimmer memcpy(text, region->data, le32_to_cpu(region->len)); 1395f6bc909eSSimon Trimmer cs_dsp_info(dsp, "%s: %s\n", file, text); 1396f6bc909eSSimon Trimmer kfree(text); 1397f6bc909eSSimon Trimmer text = NULL; 1398f6bc909eSSimon Trimmer } 1399f6bc909eSSimon Trimmer 1400f6bc909eSSimon Trimmer if (reg) { 1401f6bc909eSSimon Trimmer buf = cs_dsp_buf_alloc(region->data, 1402f6bc909eSSimon Trimmer le32_to_cpu(region->len), 1403f6bc909eSSimon Trimmer &buf_list); 1404f6bc909eSSimon Trimmer if (!buf) { 1405f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 1406f6bc909eSSimon Trimmer ret = -ENOMEM; 1407f6bc909eSSimon Trimmer goto out_fw; 1408f6bc909eSSimon Trimmer } 1409f6bc909eSSimon Trimmer 1410f6bc909eSSimon Trimmer ret = regmap_raw_write_async(regmap, reg, buf->buf, 1411f6bc909eSSimon Trimmer le32_to_cpu(region->len)); 1412f6bc909eSSimon Trimmer if (ret != 0) { 1413f6bc909eSSimon Trimmer cs_dsp_err(dsp, 1414f6bc909eSSimon Trimmer "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 1415f6bc909eSSimon Trimmer file, regions, 1416f6bc909eSSimon Trimmer le32_to_cpu(region->len), offset, 1417f6bc909eSSimon Trimmer region_name, ret); 1418f6bc909eSSimon Trimmer goto out_fw; 1419f6bc909eSSimon Trimmer } 1420f6bc909eSSimon Trimmer } 1421f6bc909eSSimon Trimmer 1422f6bc909eSSimon Trimmer pos += le32_to_cpu(region->len) + sizeof(*region); 1423f6bc909eSSimon Trimmer regions++; 1424f6bc909eSSimon Trimmer } 1425f6bc909eSSimon Trimmer 1426f6bc909eSSimon Trimmer ret = regmap_async_complete(regmap); 1427f6bc909eSSimon Trimmer if (ret != 0) { 1428f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 1429f6bc909eSSimon Trimmer goto out_fw; 1430f6bc909eSSimon Trimmer } 1431f6bc909eSSimon Trimmer 1432f6bc909eSSimon Trimmer if (pos > firmware->size) 1433f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 1434f6bc909eSSimon Trimmer file, regions, pos - firmware->size); 1435f6bc909eSSimon Trimmer 1436f6bc909eSSimon Trimmer cs_dsp_debugfs_save_wmfwname(dsp, file); 1437f6bc909eSSimon Trimmer 1438f6bc909eSSimon Trimmer out_fw: 1439f6bc909eSSimon Trimmer regmap_async_complete(regmap); 1440f6bc909eSSimon Trimmer cs_dsp_buf_free(&buf_list); 1441f6bc909eSSimon Trimmer kfree(text); 1442f6bc909eSSimon Trimmer 1443f6bc909eSSimon Trimmer return ret; 1444f6bc909eSSimon Trimmer } 1445f6bc909eSSimon Trimmer 1446f6bc909eSSimon Trimmer /** 1447f6bc909eSSimon Trimmer * cs_dsp_get_ctl() - Finds a matching coefficient control 1448f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 1449f6bc909eSSimon Trimmer * @name: pointer to string to match with a control's subname 1450f6bc909eSSimon Trimmer * @type: the algorithm type to match 1451f6bc909eSSimon Trimmer * @alg: the algorithm id to match 1452f6bc909eSSimon Trimmer * 1453f6bc909eSSimon Trimmer * Find cs_dsp_coeff_ctl with input name as its subname 1454f6bc909eSSimon Trimmer * 1455f6bc909eSSimon Trimmer * Return: pointer to the control on success, NULL if not found 1456f6bc909eSSimon Trimmer */ 1457f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, int type, 1458f6bc909eSSimon Trimmer unsigned int alg) 1459f6bc909eSSimon Trimmer { 1460f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *pos, *rslt = NULL; 1461f6bc909eSSimon Trimmer 14625065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 14635065cfabSCharles Keepax 1464f6bc909eSSimon Trimmer list_for_each_entry(pos, &dsp->ctl_list, list) { 1465f6bc909eSSimon Trimmer if (!pos->subname) 1466f6bc909eSSimon Trimmer continue; 1467f6bc909eSSimon Trimmer if (strncmp(pos->subname, name, pos->subname_len) == 0 && 1468f6bc909eSSimon Trimmer pos->fw_name == dsp->fw_name && 1469f6bc909eSSimon Trimmer pos->alg_region.alg == alg && 1470f6bc909eSSimon Trimmer pos->alg_region.type == type) { 1471f6bc909eSSimon Trimmer rslt = pos; 1472f6bc909eSSimon Trimmer break; 1473f6bc909eSSimon Trimmer } 1474f6bc909eSSimon Trimmer } 1475f6bc909eSSimon Trimmer 1476f6bc909eSSimon Trimmer return rslt; 1477f6bc909eSSimon Trimmer } 1478f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_get_ctl); 1479f6bc909eSSimon Trimmer 1480f6bc909eSSimon Trimmer static void cs_dsp_ctl_fixup_base(struct cs_dsp *dsp, 1481f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region) 1482f6bc909eSSimon Trimmer { 1483f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1484f6bc909eSSimon Trimmer 1485f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 1486f6bc909eSSimon Trimmer if (ctl->fw_name == dsp->fw_name && 1487f6bc909eSSimon Trimmer alg_region->alg == ctl->alg_region.alg && 1488f6bc909eSSimon Trimmer alg_region->type == ctl->alg_region.type) { 1489f6bc909eSSimon Trimmer ctl->alg_region.base = alg_region->base; 1490f6bc909eSSimon Trimmer } 1491f6bc909eSSimon Trimmer } 1492f6bc909eSSimon Trimmer } 1493f6bc909eSSimon Trimmer 1494f6bc909eSSimon Trimmer static void *cs_dsp_read_algs(struct cs_dsp *dsp, size_t n_algs, 1495f6bc909eSSimon Trimmer const struct cs_dsp_region *mem, 1496f6bc909eSSimon Trimmer unsigned int pos, unsigned int len) 1497f6bc909eSSimon Trimmer { 1498f6bc909eSSimon Trimmer void *alg; 1499f6bc909eSSimon Trimmer unsigned int reg; 1500f6bc909eSSimon Trimmer int ret; 1501f6bc909eSSimon Trimmer __be32 val; 1502f6bc909eSSimon Trimmer 1503f6bc909eSSimon Trimmer if (n_algs == 0) { 1504f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No algorithms\n"); 1505f6bc909eSSimon Trimmer return ERR_PTR(-EINVAL); 1506f6bc909eSSimon Trimmer } 1507f6bc909eSSimon Trimmer 1508f6bc909eSSimon Trimmer if (n_algs > 1024) { 1509f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1510f6bc909eSSimon Trimmer return ERR_PTR(-EINVAL); 1511f6bc909eSSimon Trimmer } 1512f6bc909eSSimon Trimmer 1513f6bc909eSSimon Trimmer /* Read the terminator first to validate the length */ 1514f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, pos + len); 1515f6bc909eSSimon Trimmer 1516f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1517f6bc909eSSimon Trimmer if (ret != 0) { 1518f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list end: %d\n", 1519f6bc909eSSimon Trimmer ret); 1520f6bc909eSSimon Trimmer return ERR_PTR(ret); 1521f6bc909eSSimon Trimmer } 1522f6bc909eSSimon Trimmer 1523f6bc909eSSimon Trimmer if (be32_to_cpu(val) != 0xbedead) 1524f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 1525f6bc909eSSimon Trimmer reg, be32_to_cpu(val)); 1526f6bc909eSSimon Trimmer 1527f6bc909eSSimon Trimmer /* Convert length from DSP words to bytes */ 1528f6bc909eSSimon Trimmer len *= sizeof(u32); 1529f6bc909eSSimon Trimmer 1530f6bc909eSSimon Trimmer alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 1531f6bc909eSSimon Trimmer if (!alg) 1532f6bc909eSSimon Trimmer return ERR_PTR(-ENOMEM); 1533f6bc909eSSimon Trimmer 1534f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, pos); 1535f6bc909eSSimon Trimmer 1536f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, alg, len); 1537f6bc909eSSimon Trimmer if (ret != 0) { 1538f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1539f6bc909eSSimon Trimmer kfree(alg); 1540f6bc909eSSimon Trimmer return ERR_PTR(ret); 1541f6bc909eSSimon Trimmer } 1542f6bc909eSSimon Trimmer 1543f6bc909eSSimon Trimmer return alg; 1544f6bc909eSSimon Trimmer } 1545f6bc909eSSimon Trimmer 1546f6bc909eSSimon Trimmer /** 1547f6bc909eSSimon Trimmer * cs_dsp_find_alg_region() - Finds a matching algorithm region 1548f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 1549f6bc909eSSimon Trimmer * @type: the algorithm type to match 1550f6bc909eSSimon Trimmer * @id: the algorithm id to match 1551f6bc909eSSimon Trimmer * 1552f6bc909eSSimon Trimmer * Return: Pointer to matching algorithm region, or NULL if not found. 1553f6bc909eSSimon Trimmer */ 1554f6bc909eSSimon Trimmer struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, 1555f6bc909eSSimon Trimmer int type, unsigned int id) 1556f6bc909eSSimon Trimmer { 1557f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1558f6bc909eSSimon Trimmer 15595065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 15605065cfabSCharles Keepax 1561f6bc909eSSimon Trimmer list_for_each_entry(alg_region, &dsp->alg_regions, list) { 1562f6bc909eSSimon Trimmer if (id == alg_region->alg && type == alg_region->type) 1563f6bc909eSSimon Trimmer return alg_region; 1564f6bc909eSSimon Trimmer } 1565f6bc909eSSimon Trimmer 1566f6bc909eSSimon Trimmer return NULL; 1567f6bc909eSSimon Trimmer } 1568f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_find_alg_region); 1569f6bc909eSSimon Trimmer 1570f6bc909eSSimon Trimmer static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, 1571f6bc909eSSimon Trimmer int type, __be32 id, 1572*2925748eSCharles Keepax __be32 ver, __be32 base) 1573f6bc909eSSimon Trimmer { 1574f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1575f6bc909eSSimon Trimmer 1576f6bc909eSSimon Trimmer alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1577f6bc909eSSimon Trimmer if (!alg_region) 1578f6bc909eSSimon Trimmer return ERR_PTR(-ENOMEM); 1579f6bc909eSSimon Trimmer 1580f6bc909eSSimon Trimmer alg_region->type = type; 1581f6bc909eSSimon Trimmer alg_region->alg = be32_to_cpu(id); 1582*2925748eSCharles Keepax alg_region->ver = be32_to_cpu(ver); 1583f6bc909eSSimon Trimmer alg_region->base = be32_to_cpu(base); 1584f6bc909eSSimon Trimmer 1585f6bc909eSSimon Trimmer list_add_tail(&alg_region->list, &dsp->alg_regions); 1586f6bc909eSSimon Trimmer 1587f6bc909eSSimon Trimmer if (dsp->fw_ver > 0) 1588f6bc909eSSimon Trimmer cs_dsp_ctl_fixup_base(dsp, alg_region); 1589f6bc909eSSimon Trimmer 1590f6bc909eSSimon Trimmer return alg_region; 1591f6bc909eSSimon Trimmer } 1592f6bc909eSSimon Trimmer 1593f6bc909eSSimon Trimmer static void cs_dsp_free_alg_regions(struct cs_dsp *dsp) 1594f6bc909eSSimon Trimmer { 1595f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1596f6bc909eSSimon Trimmer 1597f6bc909eSSimon Trimmer while (!list_empty(&dsp->alg_regions)) { 1598f6bc909eSSimon Trimmer alg_region = list_first_entry(&dsp->alg_regions, 1599f6bc909eSSimon Trimmer struct cs_dsp_alg_region, 1600f6bc909eSSimon Trimmer list); 1601f6bc909eSSimon Trimmer list_del(&alg_region->list); 1602f6bc909eSSimon Trimmer kfree(alg_region); 1603f6bc909eSSimon Trimmer } 1604f6bc909eSSimon Trimmer } 1605f6bc909eSSimon Trimmer 1606f6bc909eSSimon Trimmer static void cs_dsp_parse_wmfw_id_header(struct cs_dsp *dsp, 1607f6bc909eSSimon Trimmer struct wmfw_id_hdr *fw, int nalgs) 1608f6bc909eSSimon Trimmer { 1609f6bc909eSSimon Trimmer dsp->fw_id = be32_to_cpu(fw->id); 1610f6bc909eSSimon Trimmer dsp->fw_id_version = be32_to_cpu(fw->ver); 1611f6bc909eSSimon Trimmer 1612f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 1613f6bc909eSSimon Trimmer dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 1614f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 1615f6bc909eSSimon Trimmer nalgs); 1616f6bc909eSSimon Trimmer } 1617f6bc909eSSimon Trimmer 1618f6bc909eSSimon Trimmer static void cs_dsp_parse_wmfw_v3_id_header(struct cs_dsp *dsp, 1619f6bc909eSSimon Trimmer struct wmfw_v3_id_hdr *fw, int nalgs) 1620f6bc909eSSimon Trimmer { 1621f6bc909eSSimon Trimmer dsp->fw_id = be32_to_cpu(fw->id); 1622f6bc909eSSimon Trimmer dsp->fw_id_version = be32_to_cpu(fw->ver); 1623f6bc909eSSimon Trimmer dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 1624f6bc909eSSimon Trimmer 1625f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 1626f6bc909eSSimon Trimmer dsp->fw_id, dsp->fw_vendor_id, 1627f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff0000) >> 16, 1628f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 1629f6bc909eSSimon Trimmer nalgs); 1630f6bc909eSSimon Trimmer } 1631f6bc909eSSimon Trimmer 1632*2925748eSCharles Keepax static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, 1633*2925748eSCharles Keepax int nregions, const int *type, __be32 *base) 1634f6bc909eSSimon Trimmer { 1635f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1636f6bc909eSSimon Trimmer int i; 1637f6bc909eSSimon Trimmer 1638f6bc909eSSimon Trimmer for (i = 0; i < nregions; i++) { 1639*2925748eSCharles Keepax alg_region = cs_dsp_create_region(dsp, type[i], id, ver, base[i]); 1640f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1641f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1642f6bc909eSSimon Trimmer } 1643f6bc909eSSimon Trimmer 1644f6bc909eSSimon Trimmer return 0; 1645f6bc909eSSimon Trimmer } 1646f6bc909eSSimon Trimmer 1647f6bc909eSSimon Trimmer static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) 1648f6bc909eSSimon Trimmer { 1649f6bc909eSSimon Trimmer struct wmfw_adsp1_id_hdr adsp1_id; 1650f6bc909eSSimon Trimmer struct wmfw_adsp1_alg_hdr *adsp1_alg; 1651f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1652f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1653f6bc909eSSimon Trimmer unsigned int pos, len; 1654f6bc909eSSimon Trimmer size_t n_algs; 1655f6bc909eSSimon Trimmer int i, ret; 1656f6bc909eSSimon Trimmer 1657f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP1_DM); 1658f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1659f6bc909eSSimon Trimmer return -EINVAL; 1660f6bc909eSSimon Trimmer 1661f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1662f6bc909eSSimon Trimmer sizeof(adsp1_id)); 1663f6bc909eSSimon Trimmer if (ret != 0) { 1664f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1665f6bc909eSSimon Trimmer ret); 1666f6bc909eSSimon Trimmer return ret; 1667f6bc909eSSimon Trimmer } 1668f6bc909eSSimon Trimmer 1669f6bc909eSSimon Trimmer n_algs = be32_to_cpu(adsp1_id.n_algs); 1670f6bc909eSSimon Trimmer 1671f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp1_id.fw, n_algs); 1672f6bc909eSSimon Trimmer 1673f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 1674*2925748eSCharles Keepax adsp1_id.fw.id, adsp1_id.fw.ver, 1675*2925748eSCharles Keepax adsp1_id.zm); 1676f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1677f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1678f6bc909eSSimon Trimmer 1679f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 1680*2925748eSCharles Keepax adsp1_id.fw.id, adsp1_id.fw.ver, 1681*2925748eSCharles Keepax adsp1_id.dm); 1682f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1683f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1684f6bc909eSSimon Trimmer 1685f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 1686f6bc909eSSimon Trimmer pos = sizeof(adsp1_id) / sizeof(u32); 1687f6bc909eSSimon Trimmer len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 1688f6bc909eSSimon Trimmer 1689f6bc909eSSimon Trimmer adsp1_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 1690f6bc909eSSimon Trimmer if (IS_ERR(adsp1_alg)) 1691f6bc909eSSimon Trimmer return PTR_ERR(adsp1_alg); 1692f6bc909eSSimon Trimmer 1693f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 1694f6bc909eSSimon Trimmer cs_dsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1695f6bc909eSSimon Trimmer i, be32_to_cpu(adsp1_alg[i].alg.id), 1696f6bc909eSSimon Trimmer (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1697f6bc909eSSimon Trimmer (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1698f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1699f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].dm), 1700f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].zm)); 1701f6bc909eSSimon Trimmer 1702f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 1703f6bc909eSSimon Trimmer adsp1_alg[i].alg.id, 1704*2925748eSCharles Keepax adsp1_alg[i].alg.ver, 1705f6bc909eSSimon Trimmer adsp1_alg[i].dm); 1706f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1707f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1708f6bc909eSSimon Trimmer goto out; 1709f6bc909eSSimon Trimmer } 1710f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1711f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1712f6bc909eSSimon Trimmer len = be32_to_cpu(adsp1_alg[i + 1].dm); 1713f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp1_alg[i].dm); 1714f6bc909eSSimon Trimmer len *= 4; 1715f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1716f6bc909eSSimon Trimmer len, NULL, 0, 0, 1717f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1718f6bc909eSSimon Trimmer } else { 1719f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region DM with ID %x\n", 1720f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.id)); 1721f6bc909eSSimon Trimmer } 1722f6bc909eSSimon Trimmer } 1723f6bc909eSSimon Trimmer 1724f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 1725f6bc909eSSimon Trimmer adsp1_alg[i].alg.id, 1726*2925748eSCharles Keepax adsp1_alg[i].alg.ver, 1727f6bc909eSSimon Trimmer adsp1_alg[i].zm); 1728f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1729f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1730f6bc909eSSimon Trimmer goto out; 1731f6bc909eSSimon Trimmer } 1732f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1733f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1734f6bc909eSSimon Trimmer len = be32_to_cpu(adsp1_alg[i + 1].zm); 1735f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp1_alg[i].zm); 1736f6bc909eSSimon Trimmer len *= 4; 1737f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1738f6bc909eSSimon Trimmer len, NULL, 0, 0, 1739f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1740f6bc909eSSimon Trimmer } else { 1741f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 1742f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.id)); 1743f6bc909eSSimon Trimmer } 1744f6bc909eSSimon Trimmer } 1745f6bc909eSSimon Trimmer } 1746f6bc909eSSimon Trimmer 1747f6bc909eSSimon Trimmer out: 1748f6bc909eSSimon Trimmer kfree(adsp1_alg); 1749f6bc909eSSimon Trimmer return ret; 1750f6bc909eSSimon Trimmer } 1751f6bc909eSSimon Trimmer 1752f6bc909eSSimon Trimmer static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) 1753f6bc909eSSimon Trimmer { 1754f6bc909eSSimon Trimmer struct wmfw_adsp2_id_hdr adsp2_id; 1755f6bc909eSSimon Trimmer struct wmfw_adsp2_alg_hdr *adsp2_alg; 1756f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1757f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1758f6bc909eSSimon Trimmer unsigned int pos, len; 1759f6bc909eSSimon Trimmer size_t n_algs; 1760f6bc909eSSimon Trimmer int i, ret; 1761f6bc909eSSimon Trimmer 1762f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 1763f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1764f6bc909eSSimon Trimmer return -EINVAL; 1765f6bc909eSSimon Trimmer 1766f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1767f6bc909eSSimon Trimmer sizeof(adsp2_id)); 1768f6bc909eSSimon Trimmer if (ret != 0) { 1769f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1770f6bc909eSSimon Trimmer ret); 1771f6bc909eSSimon Trimmer return ret; 1772f6bc909eSSimon Trimmer } 1773f6bc909eSSimon Trimmer 1774f6bc909eSSimon Trimmer n_algs = be32_to_cpu(adsp2_id.n_algs); 1775f6bc909eSSimon Trimmer 1776f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp2_id.fw, n_algs); 1777f6bc909eSSimon Trimmer 1778f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 1779*2925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 1780*2925748eSCharles Keepax adsp2_id.xm); 1781f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1782f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1783f6bc909eSSimon Trimmer 1784f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 1785*2925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 1786*2925748eSCharles Keepax adsp2_id.ym); 1787f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1788f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1789f6bc909eSSimon Trimmer 1790f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 1791*2925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 1792*2925748eSCharles Keepax adsp2_id.zm); 1793f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1794f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1795f6bc909eSSimon Trimmer 1796f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 1797f6bc909eSSimon Trimmer pos = sizeof(adsp2_id) / sizeof(u32); 1798f6bc909eSSimon Trimmer len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 1799f6bc909eSSimon Trimmer 1800f6bc909eSSimon Trimmer adsp2_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 1801f6bc909eSSimon Trimmer if (IS_ERR(adsp2_alg)) 1802f6bc909eSSimon Trimmer return PTR_ERR(adsp2_alg); 1803f6bc909eSSimon Trimmer 1804f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 1805f6bc909eSSimon Trimmer cs_dsp_info(dsp, 1806f6bc909eSSimon Trimmer "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1807f6bc909eSSimon Trimmer i, be32_to_cpu(adsp2_alg[i].alg.id), 1808f6bc909eSSimon Trimmer (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1809f6bc909eSSimon Trimmer (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1810f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1811f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].xm), 1812f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].ym), 1813f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].zm)); 1814f6bc909eSSimon Trimmer 1815f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 1816f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 1817*2925748eSCharles Keepax adsp2_alg[i].alg.ver, 1818f6bc909eSSimon Trimmer adsp2_alg[i].xm); 1819f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1820f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1821f6bc909eSSimon Trimmer goto out; 1822f6bc909eSSimon Trimmer } 1823f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1824f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1825f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].xm); 1826f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].xm); 1827f6bc909eSSimon Trimmer len *= 4; 1828f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1829f6bc909eSSimon Trimmer len, NULL, 0, 0, 1830f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1831f6bc909eSSimon Trimmer } else { 1832f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region XM with ID %x\n", 1833f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 1834f6bc909eSSimon Trimmer } 1835f6bc909eSSimon Trimmer } 1836f6bc909eSSimon Trimmer 1837f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 1838f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 1839*2925748eSCharles Keepax adsp2_alg[i].alg.ver, 1840f6bc909eSSimon Trimmer adsp2_alg[i].ym); 1841f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1842f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1843f6bc909eSSimon Trimmer goto out; 1844f6bc909eSSimon Trimmer } 1845f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1846f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1847f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].ym); 1848f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].ym); 1849f6bc909eSSimon Trimmer len *= 4; 1850f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1851f6bc909eSSimon Trimmer len, NULL, 0, 0, 1852f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1853f6bc909eSSimon Trimmer } else { 1854f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region YM with ID %x\n", 1855f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 1856f6bc909eSSimon Trimmer } 1857f6bc909eSSimon Trimmer } 1858f6bc909eSSimon Trimmer 1859f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 1860f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 1861*2925748eSCharles Keepax adsp2_alg[i].alg.ver, 1862f6bc909eSSimon Trimmer adsp2_alg[i].zm); 1863f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1864f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1865f6bc909eSSimon Trimmer goto out; 1866f6bc909eSSimon Trimmer } 1867f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1868f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1869f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].zm); 1870f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].zm); 1871f6bc909eSSimon Trimmer len *= 4; 1872f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1873f6bc909eSSimon Trimmer len, NULL, 0, 0, 1874f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1875f6bc909eSSimon Trimmer } else { 1876f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 1877f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 1878f6bc909eSSimon Trimmer } 1879f6bc909eSSimon Trimmer } 1880f6bc909eSSimon Trimmer } 1881f6bc909eSSimon Trimmer 1882f6bc909eSSimon Trimmer out: 1883f6bc909eSSimon Trimmer kfree(adsp2_alg); 1884f6bc909eSSimon Trimmer return ret; 1885f6bc909eSSimon Trimmer } 1886f6bc909eSSimon Trimmer 1887*2925748eSCharles Keepax static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, 1888f6bc909eSSimon Trimmer __be32 xm_base, __be32 ym_base) 1889f6bc909eSSimon Trimmer { 1890f6bc909eSSimon Trimmer static const int types[] = { 1891f6bc909eSSimon Trimmer WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 1892f6bc909eSSimon Trimmer WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 1893f6bc909eSSimon Trimmer }; 1894f6bc909eSSimon Trimmer __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 1895f6bc909eSSimon Trimmer 1896*2925748eSCharles Keepax return cs_dsp_create_regions(dsp, id, ver, ARRAY_SIZE(types), types, bases); 1897f6bc909eSSimon Trimmer } 1898f6bc909eSSimon Trimmer 1899f6bc909eSSimon Trimmer static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) 1900f6bc909eSSimon Trimmer { 1901f6bc909eSSimon Trimmer struct wmfw_halo_id_hdr halo_id; 1902f6bc909eSSimon Trimmer struct wmfw_halo_alg_hdr *halo_alg; 1903f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1904f6bc909eSSimon Trimmer unsigned int pos, len; 1905f6bc909eSSimon Trimmer size_t n_algs; 1906f6bc909eSSimon Trimmer int i, ret; 1907f6bc909eSSimon Trimmer 1908f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 1909f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1910f6bc909eSSimon Trimmer return -EINVAL; 1911f6bc909eSSimon Trimmer 1912f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 1913f6bc909eSSimon Trimmer sizeof(halo_id)); 1914f6bc909eSSimon Trimmer if (ret != 0) { 1915f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1916f6bc909eSSimon Trimmer ret); 1917f6bc909eSSimon Trimmer return ret; 1918f6bc909eSSimon Trimmer } 1919f6bc909eSSimon Trimmer 1920f6bc909eSSimon Trimmer n_algs = be32_to_cpu(halo_id.n_algs); 1921f6bc909eSSimon Trimmer 1922f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_v3_id_header(dsp, &halo_id.fw, n_algs); 1923f6bc909eSSimon Trimmer 1924*2925748eSCharles Keepax ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, halo_id.fw.ver, 1925f6bc909eSSimon Trimmer halo_id.xm_base, halo_id.ym_base); 1926f6bc909eSSimon Trimmer if (ret) 1927f6bc909eSSimon Trimmer return ret; 1928f6bc909eSSimon Trimmer 1929f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 1930f6bc909eSSimon Trimmer pos = sizeof(halo_id) / sizeof(u32); 1931f6bc909eSSimon Trimmer len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 1932f6bc909eSSimon Trimmer 1933f6bc909eSSimon Trimmer halo_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 1934f6bc909eSSimon Trimmer if (IS_ERR(halo_alg)) 1935f6bc909eSSimon Trimmer return PTR_ERR(halo_alg); 1936f6bc909eSSimon Trimmer 1937f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 1938f6bc909eSSimon Trimmer cs_dsp_info(dsp, 1939f6bc909eSSimon Trimmer "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 1940f6bc909eSSimon Trimmer i, be32_to_cpu(halo_alg[i].alg.id), 1941f6bc909eSSimon Trimmer (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 1942f6bc909eSSimon Trimmer (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 1943f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 1944f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].xm_base), 1945f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].ym_base)); 1946f6bc909eSSimon Trimmer 1947f6bc909eSSimon Trimmer ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id, 1948*2925748eSCharles Keepax halo_alg[i].alg.ver, 1949f6bc909eSSimon Trimmer halo_alg[i].xm_base, 1950f6bc909eSSimon Trimmer halo_alg[i].ym_base); 1951f6bc909eSSimon Trimmer if (ret) 1952f6bc909eSSimon Trimmer goto out; 1953f6bc909eSSimon Trimmer } 1954f6bc909eSSimon Trimmer 1955f6bc909eSSimon Trimmer out: 1956f6bc909eSSimon Trimmer kfree(halo_alg); 1957f6bc909eSSimon Trimmer return ret; 1958f6bc909eSSimon Trimmer } 1959f6bc909eSSimon Trimmer 1960f6bc909eSSimon Trimmer static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware, 1961f6bc909eSSimon Trimmer const char *file) 1962f6bc909eSSimon Trimmer { 1963f6bc909eSSimon Trimmer LIST_HEAD(buf_list); 1964f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 1965f6bc909eSSimon Trimmer struct wmfw_coeff_hdr *hdr; 1966f6bc909eSSimon Trimmer struct wmfw_coeff_item *blk; 1967f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1968f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1969f6bc909eSSimon Trimmer const char *region_name; 1970*2925748eSCharles Keepax int ret, pos, blocks, type, offset, reg, version; 1971f6bc909eSSimon Trimmer struct cs_dsp_buf *buf; 1972f6bc909eSSimon Trimmer 1973f6bc909eSSimon Trimmer if (!firmware) 1974f6bc909eSSimon Trimmer return 0; 1975f6bc909eSSimon Trimmer 1976f6bc909eSSimon Trimmer ret = -EINVAL; 1977f6bc909eSSimon Trimmer 1978f6bc909eSSimon Trimmer if (sizeof(*hdr) >= firmware->size) { 1979f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: coefficient file too short, %zu bytes\n", 1980f6bc909eSSimon Trimmer file, firmware->size); 1981f6bc909eSSimon Trimmer goto out_fw; 1982f6bc909eSSimon Trimmer } 1983f6bc909eSSimon Trimmer 1984f6bc909eSSimon Trimmer hdr = (void *)&firmware->data[0]; 1985f6bc909eSSimon Trimmer if (memcmp(hdr->magic, "WMDR", 4) != 0) { 1986f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid coefficient magic\n", file); 1987f6bc909eSSimon Trimmer goto out_fw; 1988f6bc909eSSimon Trimmer } 1989f6bc909eSSimon Trimmer 1990f6bc909eSSimon Trimmer switch (be32_to_cpu(hdr->rev) & 0xff) { 1991f6bc909eSSimon Trimmer case 1: 1992f6bc909eSSimon Trimmer break; 1993f6bc909eSSimon Trimmer default: 1994f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 1995f6bc909eSSimon Trimmer file, be32_to_cpu(hdr->rev) & 0xff); 1996f6bc909eSSimon Trimmer ret = -EINVAL; 1997f6bc909eSSimon Trimmer goto out_fw; 1998f6bc909eSSimon Trimmer } 1999f6bc909eSSimon Trimmer 2000f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 2001f6bc909eSSimon Trimmer (le32_to_cpu(hdr->ver) >> 16) & 0xff, 2002f6bc909eSSimon Trimmer (le32_to_cpu(hdr->ver) >> 8) & 0xff, 2003f6bc909eSSimon Trimmer le32_to_cpu(hdr->ver) & 0xff); 2004f6bc909eSSimon Trimmer 2005f6bc909eSSimon Trimmer pos = le32_to_cpu(hdr->len); 2006f6bc909eSSimon Trimmer 2007f6bc909eSSimon Trimmer blocks = 0; 2008f6bc909eSSimon Trimmer while (pos < firmware->size && 2009f6bc909eSSimon Trimmer sizeof(*blk) < firmware->size - pos) { 2010f6bc909eSSimon Trimmer blk = (void *)(&firmware->data[pos]); 2011f6bc909eSSimon Trimmer 2012f6bc909eSSimon Trimmer type = le16_to_cpu(blk->type); 2013f6bc909eSSimon Trimmer offset = le16_to_cpu(blk->offset); 2014*2925748eSCharles Keepax version = le32_to_cpu(blk->ver) >> 8; 2015f6bc909eSSimon Trimmer 2016f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 2017f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->id), 2018f6bc909eSSimon Trimmer (le32_to_cpu(blk->ver) >> 16) & 0xff, 2019f6bc909eSSimon Trimmer (le32_to_cpu(blk->ver) >> 8) & 0xff, 2020f6bc909eSSimon Trimmer le32_to_cpu(blk->ver) & 0xff); 2021f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 2022f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), offset, type); 2023f6bc909eSSimon Trimmer 2024f6bc909eSSimon Trimmer reg = 0; 2025f6bc909eSSimon Trimmer region_name = "Unknown"; 2026f6bc909eSSimon Trimmer switch (type) { 2027f6bc909eSSimon Trimmer case (WMFW_NAME_TEXT << 8): 2028f6bc909eSSimon Trimmer case (WMFW_INFO_TEXT << 8): 2029f6bc909eSSimon Trimmer case (WMFW_METADATA << 8): 2030f6bc909eSSimon Trimmer break; 2031f6bc909eSSimon Trimmer case (WMFW_ABSOLUTE << 8): 2032f6bc909eSSimon Trimmer /* 2033f6bc909eSSimon Trimmer * Old files may use this for global 2034f6bc909eSSimon Trimmer * coefficients. 2035f6bc909eSSimon Trimmer */ 2036f6bc909eSSimon Trimmer if (le32_to_cpu(blk->id) == dsp->fw_id && 2037f6bc909eSSimon Trimmer offset == 0) { 2038f6bc909eSSimon Trimmer region_name = "global coefficients"; 2039f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2040f6bc909eSSimon Trimmer if (!mem) { 2041f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No ZM\n"); 2042f6bc909eSSimon Trimmer break; 2043f6bc909eSSimon Trimmer } 2044f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, 0); 2045f6bc909eSSimon Trimmer 2046f6bc909eSSimon Trimmer } else { 2047f6bc909eSSimon Trimmer region_name = "register"; 2048f6bc909eSSimon Trimmer reg = offset; 2049f6bc909eSSimon Trimmer } 2050f6bc909eSSimon Trimmer break; 2051f6bc909eSSimon Trimmer 2052f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 2053f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 2054f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 2055f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 2056f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 2057f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 2058f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 2059f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2060f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), 2061f6bc909eSSimon Trimmer type, le32_to_cpu(blk->id)); 2062f6bc909eSSimon Trimmer 2063f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2064f6bc909eSSimon Trimmer if (!mem) { 2065f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", type); 2066f6bc909eSSimon Trimmer break; 2067f6bc909eSSimon Trimmer } 2068f6bc909eSSimon Trimmer 2069f6bc909eSSimon Trimmer alg_region = cs_dsp_find_alg_region(dsp, type, 2070f6bc909eSSimon Trimmer le32_to_cpu(blk->id)); 2071f6bc909eSSimon Trimmer if (alg_region) { 2072*2925748eSCharles Keepax if (version != alg_region->ver) 2073*2925748eSCharles Keepax cs_dsp_warn(dsp, 2074*2925748eSCharles Keepax "Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n", 2075*2925748eSCharles Keepax (version >> 16) & 0xFF, 2076*2925748eSCharles Keepax (version >> 8) & 0xFF, 2077*2925748eSCharles Keepax version & 0xFF, 2078*2925748eSCharles Keepax (alg_region->ver >> 16) & 0xFF, 2079*2925748eSCharles Keepax (alg_region->ver >> 8) & 0xFF, 2080*2925748eSCharles Keepax alg_region->ver & 0xFF); 2081*2925748eSCharles Keepax 2082f6bc909eSSimon Trimmer reg = alg_region->base; 2083f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, reg); 2084f6bc909eSSimon Trimmer reg += offset; 2085f6bc909eSSimon Trimmer } else { 2086f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No %x for algorithm %x\n", 2087f6bc909eSSimon Trimmer type, le32_to_cpu(blk->id)); 2088f6bc909eSSimon Trimmer } 2089f6bc909eSSimon Trimmer break; 2090f6bc909eSSimon Trimmer 2091f6bc909eSSimon Trimmer default: 2092f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 2093f6bc909eSSimon Trimmer file, blocks, type, pos); 2094f6bc909eSSimon Trimmer break; 2095f6bc909eSSimon Trimmer } 2096f6bc909eSSimon Trimmer 2097f6bc909eSSimon Trimmer if (reg) { 2098f6bc909eSSimon Trimmer if (le32_to_cpu(blk->len) > 2099f6bc909eSSimon Trimmer firmware->size - pos - sizeof(*blk)) { 2100f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2101f6bc909eSSimon Trimmer "%s.%d: %s region len %d bytes exceeds file length %zu\n", 2102f6bc909eSSimon Trimmer file, blocks, region_name, 2103f6bc909eSSimon Trimmer le32_to_cpu(blk->len), 2104f6bc909eSSimon Trimmer firmware->size); 2105f6bc909eSSimon Trimmer ret = -EINVAL; 2106f6bc909eSSimon Trimmer goto out_fw; 2107f6bc909eSSimon Trimmer } 2108f6bc909eSSimon Trimmer 2109f6bc909eSSimon Trimmer buf = cs_dsp_buf_alloc(blk->data, 2110f6bc909eSSimon Trimmer le32_to_cpu(blk->len), 2111f6bc909eSSimon Trimmer &buf_list); 2112f6bc909eSSimon Trimmer if (!buf) { 2113f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 2114f6bc909eSSimon Trimmer ret = -ENOMEM; 2115f6bc909eSSimon Trimmer goto out_fw; 2116f6bc909eSSimon Trimmer } 2117f6bc909eSSimon Trimmer 2118f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 2119f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), 2120f6bc909eSSimon Trimmer reg); 2121f6bc909eSSimon Trimmer ret = regmap_raw_write_async(regmap, reg, buf->buf, 2122f6bc909eSSimon Trimmer le32_to_cpu(blk->len)); 2123f6bc909eSSimon Trimmer if (ret != 0) { 2124f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2125f6bc909eSSimon Trimmer "%s.%d: Failed to write to %x in %s: %d\n", 2126f6bc909eSSimon Trimmer file, blocks, reg, region_name, ret); 2127f6bc909eSSimon Trimmer } 2128f6bc909eSSimon Trimmer } 2129f6bc909eSSimon Trimmer 2130f6bc909eSSimon Trimmer pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 2131f6bc909eSSimon Trimmer blocks++; 2132f6bc909eSSimon Trimmer } 2133f6bc909eSSimon Trimmer 2134f6bc909eSSimon Trimmer ret = regmap_async_complete(regmap); 2135f6bc909eSSimon Trimmer if (ret != 0) 2136f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 2137f6bc909eSSimon Trimmer 2138f6bc909eSSimon Trimmer if (pos > firmware->size) 2139f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 2140f6bc909eSSimon Trimmer file, blocks, pos - firmware->size); 2141f6bc909eSSimon Trimmer 2142f6bc909eSSimon Trimmer cs_dsp_debugfs_save_binname(dsp, file); 2143f6bc909eSSimon Trimmer 2144f6bc909eSSimon Trimmer out_fw: 2145f6bc909eSSimon Trimmer regmap_async_complete(regmap); 2146f6bc909eSSimon Trimmer cs_dsp_buf_free(&buf_list); 2147f6bc909eSSimon Trimmer return ret; 2148f6bc909eSSimon Trimmer } 2149f6bc909eSSimon Trimmer 2150f6bc909eSSimon Trimmer static int cs_dsp_create_name(struct cs_dsp *dsp) 2151f6bc909eSSimon Trimmer { 2152f6bc909eSSimon Trimmer if (!dsp->name) { 2153f6bc909eSSimon Trimmer dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2154f6bc909eSSimon Trimmer dsp->num); 2155f6bc909eSSimon Trimmer if (!dsp->name) 2156f6bc909eSSimon Trimmer return -ENOMEM; 2157f6bc909eSSimon Trimmer } 2158f6bc909eSSimon Trimmer 2159f6bc909eSSimon Trimmer return 0; 2160f6bc909eSSimon Trimmer } 2161f6bc909eSSimon Trimmer 2162f6bc909eSSimon Trimmer static int cs_dsp_common_init(struct cs_dsp *dsp) 2163f6bc909eSSimon Trimmer { 2164f6bc909eSSimon Trimmer int ret; 2165f6bc909eSSimon Trimmer 2166f6bc909eSSimon Trimmer ret = cs_dsp_create_name(dsp); 2167f6bc909eSSimon Trimmer if (ret) 2168f6bc909eSSimon Trimmer return ret; 2169f6bc909eSSimon Trimmer 2170f6bc909eSSimon Trimmer INIT_LIST_HEAD(&dsp->alg_regions); 2171f6bc909eSSimon Trimmer INIT_LIST_HEAD(&dsp->ctl_list); 2172f6bc909eSSimon Trimmer 2173f6bc909eSSimon Trimmer mutex_init(&dsp->pwr_lock); 2174f6bc909eSSimon Trimmer 2175f6bc909eSSimon Trimmer return 0; 2176f6bc909eSSimon Trimmer } 2177f6bc909eSSimon Trimmer 2178f6bc909eSSimon Trimmer /** 2179f6bc909eSSimon Trimmer * cs_dsp_adsp1_init() - Initialise a cs_dsp structure representing a ADSP1 device 2180f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2181f6bc909eSSimon Trimmer * 2182f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2183f6bc909eSSimon Trimmer */ 2184f6bc909eSSimon Trimmer int cs_dsp_adsp1_init(struct cs_dsp *dsp) 2185f6bc909eSSimon Trimmer { 2186f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp1_ops; 2187f6bc909eSSimon Trimmer 2188f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 2189f6bc909eSSimon Trimmer } 2190f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp1_init); 2191f6bc909eSSimon Trimmer 2192f6bc909eSSimon Trimmer /** 2193f6bc909eSSimon Trimmer * cs_dsp_adsp1_power_up() - Load and start the named firmware 2194f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2195f6bc909eSSimon Trimmer * @wmfw_firmware: the firmware to be sent 2196f6bc909eSSimon Trimmer * @wmfw_filename: file name of firmware to be sent 2197f6bc909eSSimon Trimmer * @coeff_firmware: the coefficient data to be sent 2198f6bc909eSSimon Trimmer * @coeff_filename: file name of coefficient to data be sent 2199f6bc909eSSimon Trimmer * @fw_name: the user-friendly firmware name 2200f6bc909eSSimon Trimmer * 2201f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2202f6bc909eSSimon Trimmer */ 2203f6bc909eSSimon Trimmer int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, 2204f6bc909eSSimon Trimmer const struct firmware *wmfw_firmware, char *wmfw_filename, 2205f6bc909eSSimon Trimmer const struct firmware *coeff_firmware, char *coeff_filename, 2206f6bc909eSSimon Trimmer const char *fw_name) 2207f6bc909eSSimon Trimmer { 2208f6bc909eSSimon Trimmer unsigned int val; 2209f6bc909eSSimon Trimmer int ret; 2210f6bc909eSSimon Trimmer 2211f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2212f6bc909eSSimon Trimmer 2213f6bc909eSSimon Trimmer dsp->fw_name = fw_name; 2214f6bc909eSSimon Trimmer 2215f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2216f6bc909eSSimon Trimmer ADSP1_SYS_ENA, ADSP1_SYS_ENA); 2217f6bc909eSSimon Trimmer 2218f6bc909eSSimon Trimmer /* 2219f6bc909eSSimon Trimmer * For simplicity set the DSP clock rate to be the 2220f6bc909eSSimon Trimmer * SYSCLK rate rather than making it configurable. 2221f6bc909eSSimon Trimmer */ 2222f6bc909eSSimon Trimmer if (dsp->sysclk_reg) { 2223f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 2224f6bc909eSSimon Trimmer if (ret != 0) { 2225f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 2226f6bc909eSSimon Trimmer goto err_mutex; 2227f6bc909eSSimon Trimmer } 2228f6bc909eSSimon Trimmer 2229f6bc909eSSimon Trimmer val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 2230f6bc909eSSimon Trimmer 2231f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, 2232f6bc909eSSimon Trimmer dsp->base + ADSP1_CONTROL_31, 2233f6bc909eSSimon Trimmer ADSP1_CLK_SEL_MASK, val); 2234f6bc909eSSimon Trimmer if (ret != 0) { 2235f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2236f6bc909eSSimon Trimmer goto err_mutex; 2237f6bc909eSSimon Trimmer } 2238f6bc909eSSimon Trimmer } 2239f6bc909eSSimon Trimmer 2240f6bc909eSSimon Trimmer ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename); 2241f6bc909eSSimon Trimmer if (ret != 0) 2242f6bc909eSSimon Trimmer goto err_ena; 2243f6bc909eSSimon Trimmer 2244f6bc909eSSimon Trimmer ret = cs_dsp_adsp1_setup_algs(dsp); 2245f6bc909eSSimon Trimmer if (ret != 0) 2246f6bc909eSSimon Trimmer goto err_ena; 2247f6bc909eSSimon Trimmer 2248f6bc909eSSimon Trimmer ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename); 2249f6bc909eSSimon Trimmer if (ret != 0) 2250f6bc909eSSimon Trimmer goto err_ena; 2251f6bc909eSSimon Trimmer 2252f6bc909eSSimon Trimmer /* Initialize caches for enabled and unset controls */ 2253f6bc909eSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 2254f6bc909eSSimon Trimmer if (ret != 0) 2255f6bc909eSSimon Trimmer goto err_ena; 2256f6bc909eSSimon Trimmer 2257f6bc909eSSimon Trimmer /* Sync set controls */ 2258f6bc909eSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 2259f6bc909eSSimon Trimmer if (ret != 0) 2260f6bc909eSSimon Trimmer goto err_ena; 2261f6bc909eSSimon Trimmer 2262f6bc909eSSimon Trimmer dsp->booted = true; 2263f6bc909eSSimon Trimmer 2264f6bc909eSSimon Trimmer /* Start the core running */ 2265f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2266f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START, 2267f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START); 2268f6bc909eSSimon Trimmer 2269f6bc909eSSimon Trimmer dsp->running = true; 2270f6bc909eSSimon Trimmer 2271f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2272f6bc909eSSimon Trimmer 2273f6bc909eSSimon Trimmer return 0; 2274f6bc909eSSimon Trimmer 2275f6bc909eSSimon Trimmer err_ena: 2276f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2277f6bc909eSSimon Trimmer ADSP1_SYS_ENA, 0); 2278f6bc909eSSimon Trimmer err_mutex: 2279f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2280f6bc909eSSimon Trimmer return ret; 2281f6bc909eSSimon Trimmer } 2282f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp1_power_up); 2283f6bc909eSSimon Trimmer 2284f6bc909eSSimon Trimmer /** 2285f6bc909eSSimon Trimmer * cs_dsp_adsp1_power_down() - Halts the DSP 2286f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2287f6bc909eSSimon Trimmer */ 2288f6bc909eSSimon Trimmer void cs_dsp_adsp1_power_down(struct cs_dsp *dsp) 2289f6bc909eSSimon Trimmer { 2290f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2291f6bc909eSSimon Trimmer 2292f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2293f6bc909eSSimon Trimmer 2294f6bc909eSSimon Trimmer dsp->running = false; 2295f6bc909eSSimon Trimmer dsp->booted = false; 2296f6bc909eSSimon Trimmer 2297f6bc909eSSimon Trimmer /* Halt the core */ 2298f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2299f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START, 0); 2300f6bc909eSSimon Trimmer 2301f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 2302f6bc909eSSimon Trimmer ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 2303f6bc909eSSimon Trimmer 2304f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2305f6bc909eSSimon Trimmer ADSP1_SYS_ENA, 0); 2306f6bc909eSSimon Trimmer 2307f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) 2308f6bc909eSSimon Trimmer ctl->enabled = 0; 2309f6bc909eSSimon Trimmer 2310f6bc909eSSimon Trimmer cs_dsp_free_alg_regions(dsp); 2311f6bc909eSSimon Trimmer 2312f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2313f6bc909eSSimon Trimmer } 2314f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp1_power_down); 2315f6bc909eSSimon Trimmer 2316f6bc909eSSimon Trimmer static int cs_dsp_adsp2v2_enable_core(struct cs_dsp *dsp) 2317f6bc909eSSimon Trimmer { 2318f6bc909eSSimon Trimmer unsigned int val; 2319f6bc909eSSimon Trimmer int ret, count; 2320f6bc909eSSimon Trimmer 2321f6bc909eSSimon Trimmer /* Wait for the RAM to start, should be near instantaneous */ 2322f6bc909eSSimon Trimmer for (count = 0; count < 10; ++count) { 2323f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 2324f6bc909eSSimon Trimmer if (ret != 0) 2325f6bc909eSSimon Trimmer return ret; 2326f6bc909eSSimon Trimmer 2327f6bc909eSSimon Trimmer if (val & ADSP2_RAM_RDY) 2328f6bc909eSSimon Trimmer break; 2329f6bc909eSSimon Trimmer 2330f6bc909eSSimon Trimmer usleep_range(250, 500); 2331f6bc909eSSimon Trimmer } 2332f6bc909eSSimon Trimmer 2333f6bc909eSSimon Trimmer if (!(val & ADSP2_RAM_RDY)) { 2334f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to start DSP RAM\n"); 2335f6bc909eSSimon Trimmer return -EBUSY; 2336f6bc909eSSimon Trimmer } 2337f6bc909eSSimon Trimmer 2338f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "RAM ready after %d polls\n", count); 2339f6bc909eSSimon Trimmer 2340f6bc909eSSimon Trimmer return 0; 2341f6bc909eSSimon Trimmer } 2342f6bc909eSSimon Trimmer 2343f6bc909eSSimon Trimmer static int cs_dsp_adsp2_enable_core(struct cs_dsp *dsp) 2344f6bc909eSSimon Trimmer { 2345f6bc909eSSimon Trimmer int ret; 2346f6bc909eSSimon Trimmer 2347f6bc909eSSimon Trimmer ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 2348f6bc909eSSimon Trimmer ADSP2_SYS_ENA, ADSP2_SYS_ENA); 2349f6bc909eSSimon Trimmer if (ret != 0) 2350f6bc909eSSimon Trimmer return ret; 2351f6bc909eSSimon Trimmer 2352f6bc909eSSimon Trimmer return cs_dsp_adsp2v2_enable_core(dsp); 2353f6bc909eSSimon Trimmer } 2354f6bc909eSSimon Trimmer 2355f6bc909eSSimon Trimmer static int cs_dsp_adsp2_lock(struct cs_dsp *dsp, unsigned int lock_regions) 2356f6bc909eSSimon Trimmer { 2357f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 2358f6bc909eSSimon Trimmer unsigned int code0, code1, lock_reg; 2359f6bc909eSSimon Trimmer 2360f6bc909eSSimon Trimmer if (!(lock_regions & CS_ADSP2_REGION_ALL)) 2361f6bc909eSSimon Trimmer return 0; 2362f6bc909eSSimon Trimmer 2363f6bc909eSSimon Trimmer lock_regions &= CS_ADSP2_REGION_ALL; 2364f6bc909eSSimon Trimmer lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 2365f6bc909eSSimon Trimmer 2366f6bc909eSSimon Trimmer while (lock_regions) { 2367f6bc909eSSimon Trimmer code0 = code1 = 0; 2368f6bc909eSSimon Trimmer if (lock_regions & BIT(0)) { 2369f6bc909eSSimon Trimmer code0 = ADSP2_LOCK_CODE_0; 2370f6bc909eSSimon Trimmer code1 = ADSP2_LOCK_CODE_1; 2371f6bc909eSSimon Trimmer } 2372f6bc909eSSimon Trimmer if (lock_regions & BIT(1)) { 2373f6bc909eSSimon Trimmer code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 2374f6bc909eSSimon Trimmer code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 2375f6bc909eSSimon Trimmer } 2376f6bc909eSSimon Trimmer regmap_write(regmap, lock_reg, code0); 2377f6bc909eSSimon Trimmer regmap_write(regmap, lock_reg, code1); 2378f6bc909eSSimon Trimmer lock_regions >>= 2; 2379f6bc909eSSimon Trimmer lock_reg += 2; 2380f6bc909eSSimon Trimmer } 2381f6bc909eSSimon Trimmer 2382f6bc909eSSimon Trimmer return 0; 2383f6bc909eSSimon Trimmer } 2384f6bc909eSSimon Trimmer 2385f6bc909eSSimon Trimmer static int cs_dsp_adsp2_enable_memory(struct cs_dsp *dsp) 2386f6bc909eSSimon Trimmer { 2387f6bc909eSSimon Trimmer return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2388f6bc909eSSimon Trimmer ADSP2_MEM_ENA, ADSP2_MEM_ENA); 2389f6bc909eSSimon Trimmer } 2390f6bc909eSSimon Trimmer 2391f6bc909eSSimon Trimmer static void cs_dsp_adsp2_disable_memory(struct cs_dsp *dsp) 2392f6bc909eSSimon Trimmer { 2393f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2394f6bc909eSSimon Trimmer ADSP2_MEM_ENA, 0); 2395f6bc909eSSimon Trimmer } 2396f6bc909eSSimon Trimmer 2397f6bc909eSSimon Trimmer static void cs_dsp_adsp2_disable_core(struct cs_dsp *dsp) 2398f6bc909eSSimon Trimmer { 2399f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2400f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2401f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 2402f6bc909eSSimon Trimmer 2403f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2404f6bc909eSSimon Trimmer ADSP2_SYS_ENA, 0); 2405f6bc909eSSimon Trimmer } 2406f6bc909eSSimon Trimmer 2407f6bc909eSSimon Trimmer static void cs_dsp_adsp2v2_disable_core(struct cs_dsp *dsp) 2408f6bc909eSSimon Trimmer { 2409f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2410f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2411f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 2412f6bc909eSSimon Trimmer } 2413f6bc909eSSimon Trimmer 2414f6bc909eSSimon Trimmer static int cs_dsp_halo_configure_mpu(struct cs_dsp *dsp, unsigned int lock_regions) 2415f6bc909eSSimon Trimmer { 2416f6bc909eSSimon Trimmer struct reg_sequence config[] = { 2417f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 2418f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 2419f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 2420f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 2421f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 2422f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 2423f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 2424f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 2425f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 2426f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 2427f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 2428f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 2429f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 2430f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 2431f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 2432f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 2433f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 2434f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 2435f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 2436f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 2437f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 2438f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 2439f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 2440f6bc909eSSimon Trimmer }; 2441f6bc909eSSimon Trimmer 2442f6bc909eSSimon Trimmer return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 2443f6bc909eSSimon Trimmer } 2444f6bc909eSSimon Trimmer 2445f6bc909eSSimon Trimmer /** 2446f6bc909eSSimon Trimmer * cs_dsp_set_dspclk() - Applies the given frequency to the given cs_dsp 2447f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2448f6bc909eSSimon Trimmer * @freq: clock rate to set 2449f6bc909eSSimon Trimmer * 2450f6bc909eSSimon Trimmer * This is only for use on ADSP2 cores. 2451f6bc909eSSimon Trimmer * 2452f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2453f6bc909eSSimon Trimmer */ 2454f6bc909eSSimon Trimmer int cs_dsp_set_dspclk(struct cs_dsp *dsp, unsigned int freq) 2455f6bc909eSSimon Trimmer { 2456f6bc909eSSimon Trimmer int ret; 2457f6bc909eSSimon Trimmer 2458f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 2459f6bc909eSSimon Trimmer ADSP2_CLK_SEL_MASK, 2460f6bc909eSSimon Trimmer freq << ADSP2_CLK_SEL_SHIFT); 2461f6bc909eSSimon Trimmer if (ret) 2462f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2463f6bc909eSSimon Trimmer 2464f6bc909eSSimon Trimmer return ret; 2465f6bc909eSSimon Trimmer } 2466f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_set_dspclk); 2467f6bc909eSSimon Trimmer 2468f6bc909eSSimon Trimmer static void cs_dsp_stop_watchdog(struct cs_dsp *dsp) 2469f6bc909eSSimon Trimmer { 2470f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 2471f6bc909eSSimon Trimmer ADSP2_WDT_ENA_MASK, 0); 2472f6bc909eSSimon Trimmer } 2473f6bc909eSSimon Trimmer 2474f6bc909eSSimon Trimmer static void cs_dsp_halo_stop_watchdog(struct cs_dsp *dsp) 2475f6bc909eSSimon Trimmer { 2476f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 2477f6bc909eSSimon Trimmer HALO_WDT_EN_MASK, 0); 2478f6bc909eSSimon Trimmer } 2479f6bc909eSSimon Trimmer 2480f6bc909eSSimon Trimmer /** 2481f6bc909eSSimon Trimmer * cs_dsp_power_up() - Downloads firmware to the DSP 2482f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2483f6bc909eSSimon Trimmer * @wmfw_firmware: the firmware to be sent 2484f6bc909eSSimon Trimmer * @wmfw_filename: file name of firmware to be sent 2485f6bc909eSSimon Trimmer * @coeff_firmware: the coefficient data to be sent 2486f6bc909eSSimon Trimmer * @coeff_filename: file name of coefficient to data be sent 2487f6bc909eSSimon Trimmer * @fw_name: the user-friendly firmware name 2488f6bc909eSSimon Trimmer * 2489f6bc909eSSimon Trimmer * This function is used on ADSP2 and Halo DSP cores, it powers-up the DSP core 2490f6bc909eSSimon Trimmer * and downloads the firmware but does not start the firmware running. The 2491f6bc909eSSimon Trimmer * cs_dsp booted flag will be set once completed and if the core has a low-power 2492f6bc909eSSimon Trimmer * memory retention mode it will be put into this state after the firmware is 2493f6bc909eSSimon Trimmer * downloaded. 2494f6bc909eSSimon Trimmer * 2495f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2496f6bc909eSSimon Trimmer */ 2497f6bc909eSSimon Trimmer int cs_dsp_power_up(struct cs_dsp *dsp, 2498f6bc909eSSimon Trimmer const struct firmware *wmfw_firmware, char *wmfw_filename, 2499f6bc909eSSimon Trimmer const struct firmware *coeff_firmware, char *coeff_filename, 2500f6bc909eSSimon Trimmer const char *fw_name) 2501f6bc909eSSimon Trimmer { 2502f6bc909eSSimon Trimmer int ret; 2503f6bc909eSSimon Trimmer 2504f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2505f6bc909eSSimon Trimmer 2506f6bc909eSSimon Trimmer dsp->fw_name = fw_name; 2507f6bc909eSSimon Trimmer 2508f6bc909eSSimon Trimmer if (dsp->ops->enable_memory) { 2509f6bc909eSSimon Trimmer ret = dsp->ops->enable_memory(dsp); 2510f6bc909eSSimon Trimmer if (ret != 0) 2511f6bc909eSSimon Trimmer goto err_mutex; 2512f6bc909eSSimon Trimmer } 2513f6bc909eSSimon Trimmer 2514f6bc909eSSimon Trimmer if (dsp->ops->enable_core) { 2515f6bc909eSSimon Trimmer ret = dsp->ops->enable_core(dsp); 2516f6bc909eSSimon Trimmer if (ret != 0) 2517f6bc909eSSimon Trimmer goto err_mem; 2518f6bc909eSSimon Trimmer } 2519f6bc909eSSimon Trimmer 2520f6bc909eSSimon Trimmer ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename); 2521f6bc909eSSimon Trimmer if (ret != 0) 2522f6bc909eSSimon Trimmer goto err_ena; 2523f6bc909eSSimon Trimmer 2524f6bc909eSSimon Trimmer ret = dsp->ops->setup_algs(dsp); 2525f6bc909eSSimon Trimmer if (ret != 0) 2526f6bc909eSSimon Trimmer goto err_ena; 2527f6bc909eSSimon Trimmer 2528f6bc909eSSimon Trimmer ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename); 2529f6bc909eSSimon Trimmer if (ret != 0) 2530f6bc909eSSimon Trimmer goto err_ena; 2531f6bc909eSSimon Trimmer 2532f6bc909eSSimon Trimmer /* Initialize caches for enabled and unset controls */ 2533f6bc909eSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 2534f6bc909eSSimon Trimmer if (ret != 0) 2535f6bc909eSSimon Trimmer goto err_ena; 2536f6bc909eSSimon Trimmer 2537f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2538f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2539f6bc909eSSimon Trimmer 2540f6bc909eSSimon Trimmer dsp->booted = true; 2541f6bc909eSSimon Trimmer 2542f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2543f6bc909eSSimon Trimmer 2544f6bc909eSSimon Trimmer return 0; 2545f6bc909eSSimon Trimmer err_ena: 2546f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2547f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2548f6bc909eSSimon Trimmer err_mem: 2549f6bc909eSSimon Trimmer if (dsp->ops->disable_memory) 2550f6bc909eSSimon Trimmer dsp->ops->disable_memory(dsp); 2551f6bc909eSSimon Trimmer err_mutex: 2552f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2553f6bc909eSSimon Trimmer 2554f6bc909eSSimon Trimmer return ret; 2555f6bc909eSSimon Trimmer } 2556f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_power_up); 2557f6bc909eSSimon Trimmer 2558f6bc909eSSimon Trimmer /** 2559f6bc909eSSimon Trimmer * cs_dsp_power_down() - Powers-down the DSP 2560f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2561f6bc909eSSimon Trimmer * 2562f6bc909eSSimon Trimmer * cs_dsp_stop() must have been called before this function. The core will be 2563f6bc909eSSimon Trimmer * fully powered down and so the memory will not be retained. 2564f6bc909eSSimon Trimmer */ 2565f6bc909eSSimon Trimmer void cs_dsp_power_down(struct cs_dsp *dsp) 2566f6bc909eSSimon Trimmer { 2567f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2568f6bc909eSSimon Trimmer 2569f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2570f6bc909eSSimon Trimmer 2571f6bc909eSSimon Trimmer cs_dsp_debugfs_clear(dsp); 2572f6bc909eSSimon Trimmer 2573f6bc909eSSimon Trimmer dsp->fw_id = 0; 2574f6bc909eSSimon Trimmer dsp->fw_id_version = 0; 2575f6bc909eSSimon Trimmer 2576f6bc909eSSimon Trimmer dsp->booted = false; 2577f6bc909eSSimon Trimmer 2578f6bc909eSSimon Trimmer if (dsp->ops->disable_memory) 2579f6bc909eSSimon Trimmer dsp->ops->disable_memory(dsp); 2580f6bc909eSSimon Trimmer 2581f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) 2582f6bc909eSSimon Trimmer ctl->enabled = 0; 2583f6bc909eSSimon Trimmer 2584f6bc909eSSimon Trimmer cs_dsp_free_alg_regions(dsp); 2585f6bc909eSSimon Trimmer 2586f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2587f6bc909eSSimon Trimmer 2588f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Shutdown complete\n"); 2589f6bc909eSSimon Trimmer } 2590f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_power_down); 2591f6bc909eSSimon Trimmer 2592f6bc909eSSimon Trimmer static int cs_dsp_adsp2_start_core(struct cs_dsp *dsp) 2593f6bc909eSSimon Trimmer { 2594f6bc909eSSimon Trimmer return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2595f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START, 2596f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START); 2597f6bc909eSSimon Trimmer } 2598f6bc909eSSimon Trimmer 2599f6bc909eSSimon Trimmer static void cs_dsp_adsp2_stop_core(struct cs_dsp *dsp) 2600f6bc909eSSimon Trimmer { 2601f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2602f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START, 0); 2603f6bc909eSSimon Trimmer } 2604f6bc909eSSimon Trimmer 2605f6bc909eSSimon Trimmer /** 2606f6bc909eSSimon Trimmer * cs_dsp_run() - Starts the firmware running 2607f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2608f6bc909eSSimon Trimmer * 2609f6bc909eSSimon Trimmer * cs_dsp_power_up() must have previously been called successfully. 2610f6bc909eSSimon Trimmer * 2611f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2612f6bc909eSSimon Trimmer */ 2613f6bc909eSSimon Trimmer int cs_dsp_run(struct cs_dsp *dsp) 2614f6bc909eSSimon Trimmer { 2615f6bc909eSSimon Trimmer int ret; 2616f6bc909eSSimon Trimmer 2617f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2618f6bc909eSSimon Trimmer 2619f6bc909eSSimon Trimmer if (!dsp->booted) { 2620f6bc909eSSimon Trimmer ret = -EIO; 2621f6bc909eSSimon Trimmer goto err; 2622f6bc909eSSimon Trimmer } 2623f6bc909eSSimon Trimmer 2624f6bc909eSSimon Trimmer if (dsp->ops->enable_core) { 2625f6bc909eSSimon Trimmer ret = dsp->ops->enable_core(dsp); 2626f6bc909eSSimon Trimmer if (ret != 0) 2627f6bc909eSSimon Trimmer goto err; 2628f6bc909eSSimon Trimmer } 2629f6bc909eSSimon Trimmer 2630f6bc909eSSimon Trimmer /* Sync set controls */ 2631f6bc909eSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 2632f6bc909eSSimon Trimmer if (ret != 0) 2633f6bc909eSSimon Trimmer goto err; 2634f6bc909eSSimon Trimmer 2635f6bc909eSSimon Trimmer if (dsp->ops->lock_memory) { 2636f6bc909eSSimon Trimmer ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 2637f6bc909eSSimon Trimmer if (ret != 0) { 2638f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Error configuring MPU: %d\n", ret); 2639f6bc909eSSimon Trimmer goto err; 2640f6bc909eSSimon Trimmer } 2641f6bc909eSSimon Trimmer } 2642f6bc909eSSimon Trimmer 2643f6bc909eSSimon Trimmer if (dsp->ops->start_core) { 2644f6bc909eSSimon Trimmer ret = dsp->ops->start_core(dsp); 2645f6bc909eSSimon Trimmer if (ret != 0) 2646f6bc909eSSimon Trimmer goto err; 2647f6bc909eSSimon Trimmer } 2648f6bc909eSSimon Trimmer 2649f6bc909eSSimon Trimmer dsp->running = true; 2650f6bc909eSSimon Trimmer 2651f6bc909eSSimon Trimmer if (dsp->client_ops->post_run) { 2652f6bc909eSSimon Trimmer ret = dsp->client_ops->post_run(dsp); 2653f6bc909eSSimon Trimmer if (ret) 2654f6bc909eSSimon Trimmer goto err; 2655f6bc909eSSimon Trimmer } 2656f6bc909eSSimon Trimmer 2657f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2658f6bc909eSSimon Trimmer 2659f6bc909eSSimon Trimmer return 0; 2660f6bc909eSSimon Trimmer 2661f6bc909eSSimon Trimmer err: 2662f6bc909eSSimon Trimmer if (dsp->ops->stop_core) 2663f6bc909eSSimon Trimmer dsp->ops->stop_core(dsp); 2664f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2665f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2666f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2667f6bc909eSSimon Trimmer 2668f6bc909eSSimon Trimmer return ret; 2669f6bc909eSSimon Trimmer } 2670f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_run); 2671f6bc909eSSimon Trimmer 2672f6bc909eSSimon Trimmer /** 2673f6bc909eSSimon Trimmer * cs_dsp_stop() - Stops the firmware 2674f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2675f6bc909eSSimon Trimmer * 2676f6bc909eSSimon Trimmer * Memory will not be disabled so firmware will remain loaded. 2677f6bc909eSSimon Trimmer */ 2678f6bc909eSSimon Trimmer void cs_dsp_stop(struct cs_dsp *dsp) 2679f6bc909eSSimon Trimmer { 2680f6bc909eSSimon Trimmer /* Tell the firmware to cleanup */ 2681f6bc909eSSimon Trimmer cs_dsp_signal_event_controls(dsp, CS_DSP_FW_EVENT_SHUTDOWN); 2682f6bc909eSSimon Trimmer 2683f6bc909eSSimon Trimmer if (dsp->ops->stop_watchdog) 2684f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 2685f6bc909eSSimon Trimmer 2686f6bc909eSSimon Trimmer /* Log firmware state, it can be useful for analysis */ 2687f6bc909eSSimon Trimmer if (dsp->ops->show_fw_status) 2688f6bc909eSSimon Trimmer dsp->ops->show_fw_status(dsp); 2689f6bc909eSSimon Trimmer 2690f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2691f6bc909eSSimon Trimmer 2692f6bc909eSSimon Trimmer dsp->running = false; 2693f6bc909eSSimon Trimmer 2694f6bc909eSSimon Trimmer if (dsp->ops->stop_core) 2695f6bc909eSSimon Trimmer dsp->ops->stop_core(dsp); 2696f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2697f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2698f6bc909eSSimon Trimmer 2699f6bc909eSSimon Trimmer if (dsp->client_ops->post_stop) 2700f6bc909eSSimon Trimmer dsp->client_ops->post_stop(dsp); 2701f6bc909eSSimon Trimmer 2702f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2703f6bc909eSSimon Trimmer 2704f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Execution stopped\n"); 2705f6bc909eSSimon Trimmer } 2706f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_stop); 2707f6bc909eSSimon Trimmer 2708f6bc909eSSimon Trimmer static int cs_dsp_halo_start_core(struct cs_dsp *dsp) 2709f6bc909eSSimon Trimmer { 2710f6bc909eSSimon Trimmer return regmap_update_bits(dsp->regmap, 2711f6bc909eSSimon Trimmer dsp->base + HALO_CCM_CORE_CONTROL, 2712f6bc909eSSimon Trimmer HALO_CORE_RESET | HALO_CORE_EN, 2713f6bc909eSSimon Trimmer HALO_CORE_RESET | HALO_CORE_EN); 2714f6bc909eSSimon Trimmer } 2715f6bc909eSSimon Trimmer 2716f6bc909eSSimon Trimmer static void cs_dsp_halo_stop_core(struct cs_dsp *dsp) 2717f6bc909eSSimon Trimmer { 2718f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 2719f6bc909eSSimon Trimmer HALO_CORE_EN, 0); 2720f6bc909eSSimon Trimmer 2721f6bc909eSSimon Trimmer /* reset halo core with CORE_SOFT_RESET */ 2722f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 2723f6bc909eSSimon Trimmer HALO_CORE_SOFT_RESET_MASK, 1); 2724f6bc909eSSimon Trimmer } 2725f6bc909eSSimon Trimmer 2726f6bc909eSSimon Trimmer /** 2727f6bc909eSSimon Trimmer * cs_dsp_adsp2_init() - Initialise a cs_dsp structure representing a ADSP2 core 2728f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2729f6bc909eSSimon Trimmer * 2730f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2731f6bc909eSSimon Trimmer */ 2732f6bc909eSSimon Trimmer int cs_dsp_adsp2_init(struct cs_dsp *dsp) 2733f6bc909eSSimon Trimmer { 2734f6bc909eSSimon Trimmer int ret; 2735f6bc909eSSimon Trimmer 2736f6bc909eSSimon Trimmer switch (dsp->rev) { 2737f6bc909eSSimon Trimmer case 0: 2738f6bc909eSSimon Trimmer /* 2739f6bc909eSSimon Trimmer * Disable the DSP memory by default when in reset for a small 2740f6bc909eSSimon Trimmer * power saving. 2741f6bc909eSSimon Trimmer */ 2742f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2743f6bc909eSSimon Trimmer ADSP2_MEM_ENA, 0); 2744f6bc909eSSimon Trimmer if (ret) { 2745f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2746f6bc909eSSimon Trimmer "Failed to clear memory retention: %d\n", ret); 2747f6bc909eSSimon Trimmer return ret; 2748f6bc909eSSimon Trimmer } 2749f6bc909eSSimon Trimmer 2750f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[0]; 2751f6bc909eSSimon Trimmer break; 2752f6bc909eSSimon Trimmer case 1: 2753f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[1]; 2754f6bc909eSSimon Trimmer break; 2755f6bc909eSSimon Trimmer default: 2756f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[2]; 2757f6bc909eSSimon Trimmer break; 2758f6bc909eSSimon Trimmer } 2759f6bc909eSSimon Trimmer 2760f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 2761f6bc909eSSimon Trimmer } 2762f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp2_init); 2763f6bc909eSSimon Trimmer 2764f6bc909eSSimon Trimmer /** 2765f6bc909eSSimon Trimmer * cs_dsp_halo_init() - Initialise a cs_dsp structure representing a HALO Core DSP 2766f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2767f6bc909eSSimon Trimmer * 2768f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2769f6bc909eSSimon Trimmer */ 2770f6bc909eSSimon Trimmer int cs_dsp_halo_init(struct cs_dsp *dsp) 2771f6bc909eSSimon Trimmer { 2772f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_halo_ops; 2773f6bc909eSSimon Trimmer 2774f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 2775f6bc909eSSimon Trimmer } 2776f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_halo_init); 2777f6bc909eSSimon Trimmer 2778f6bc909eSSimon Trimmer /** 2779f6bc909eSSimon Trimmer * cs_dsp_remove() - Clean a cs_dsp before deletion 2780f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2781f6bc909eSSimon Trimmer */ 2782f6bc909eSSimon Trimmer void cs_dsp_remove(struct cs_dsp *dsp) 2783f6bc909eSSimon Trimmer { 2784f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2785f6bc909eSSimon Trimmer 2786f6bc909eSSimon Trimmer while (!list_empty(&dsp->ctl_list)) { 2787f6bc909eSSimon Trimmer ctl = list_first_entry(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list); 2788f6bc909eSSimon Trimmer 2789f6bc909eSSimon Trimmer if (dsp->client_ops->control_remove) 2790f6bc909eSSimon Trimmer dsp->client_ops->control_remove(ctl); 2791f6bc909eSSimon Trimmer 2792f6bc909eSSimon Trimmer list_del(&ctl->list); 2793f6bc909eSSimon Trimmer cs_dsp_free_ctl_blk(ctl); 2794f6bc909eSSimon Trimmer } 2795f6bc909eSSimon Trimmer } 2796f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_remove); 2797f6bc909eSSimon Trimmer 2798f6bc909eSSimon Trimmer /** 2799f6bc909eSSimon Trimmer * cs_dsp_read_raw_data_block() - Reads a block of data from DSP memory 2800f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2801f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be read 2802f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 2803f6bc909eSSimon Trimmer * @num_words: the length of the data to read 2804f6bc909eSSimon Trimmer * @data: a buffer to store the fetched data 2805f6bc909eSSimon Trimmer * 2806f6bc909eSSimon Trimmer * If this is used to read unpacked 24-bit memory, each 24-bit DSP word will 2807f6bc909eSSimon Trimmer * occupy 32-bits in data (MSbyte will be 0). This padding can be removed using 2808f6bc909eSSimon Trimmer * cs_dsp_remove_padding() 2809f6bc909eSSimon Trimmer * 2810f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2811f6bc909eSSimon Trimmer */ 2812f6bc909eSSimon Trimmer int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, 2813f6bc909eSSimon Trimmer unsigned int num_words, __be32 *data) 2814f6bc909eSSimon Trimmer { 2815f6bc909eSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 2816f6bc909eSSimon Trimmer unsigned int reg; 2817f6bc909eSSimon Trimmer int ret; 2818f6bc909eSSimon Trimmer 28195065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 28205065cfabSCharles Keepax 2821f6bc909eSSimon Trimmer if (!mem) 2822f6bc909eSSimon Trimmer return -EINVAL; 2823f6bc909eSSimon Trimmer 2824f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, mem_addr); 2825f6bc909eSSimon Trimmer 2826f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, data, 2827f6bc909eSSimon Trimmer sizeof(*data) * num_words); 2828f6bc909eSSimon Trimmer if (ret < 0) 2829f6bc909eSSimon Trimmer return ret; 2830f6bc909eSSimon Trimmer 2831f6bc909eSSimon Trimmer return 0; 2832f6bc909eSSimon Trimmer } 2833f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_read_raw_data_block); 2834f6bc909eSSimon Trimmer 2835f6bc909eSSimon Trimmer /** 2836f6bc909eSSimon Trimmer * cs_dsp_read_data_word() - Reads a word from DSP memory 2837f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2838f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be read 2839f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 2840f6bc909eSSimon Trimmer * @data: a buffer to store the fetched data 2841f6bc909eSSimon Trimmer * 2842f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2843f6bc909eSSimon Trimmer */ 2844f6bc909eSSimon Trimmer int cs_dsp_read_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 *data) 2845f6bc909eSSimon Trimmer { 2846f6bc909eSSimon Trimmer __be32 raw; 2847f6bc909eSSimon Trimmer int ret; 2848f6bc909eSSimon Trimmer 2849f6bc909eSSimon Trimmer ret = cs_dsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw); 2850f6bc909eSSimon Trimmer if (ret < 0) 2851f6bc909eSSimon Trimmer return ret; 2852f6bc909eSSimon Trimmer 2853f6bc909eSSimon Trimmer *data = be32_to_cpu(raw) & 0x00ffffffu; 2854f6bc909eSSimon Trimmer 2855f6bc909eSSimon Trimmer return 0; 2856f6bc909eSSimon Trimmer } 2857f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_read_data_word); 2858f6bc909eSSimon Trimmer 2859f6bc909eSSimon Trimmer /** 2860f6bc909eSSimon Trimmer * cs_dsp_write_data_word() - Writes a word to DSP memory 2861f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2862f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be written 2863f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 2864f6bc909eSSimon Trimmer * @data: the data to be written 2865f6bc909eSSimon Trimmer * 2866f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2867f6bc909eSSimon Trimmer */ 2868f6bc909eSSimon Trimmer int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 data) 2869f6bc909eSSimon Trimmer { 2870f6bc909eSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 2871f6bc909eSSimon Trimmer __be32 val = cpu_to_be32(data & 0x00ffffffu); 2872f6bc909eSSimon Trimmer unsigned int reg; 2873f6bc909eSSimon Trimmer 28745065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 28755065cfabSCharles Keepax 2876f6bc909eSSimon Trimmer if (!mem) 2877f6bc909eSSimon Trimmer return -EINVAL; 2878f6bc909eSSimon Trimmer 2879f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, mem_addr); 2880f6bc909eSSimon Trimmer 2881f6bc909eSSimon Trimmer return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 2882f6bc909eSSimon Trimmer } 2883f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_write_data_word); 2884f6bc909eSSimon Trimmer 2885f6bc909eSSimon Trimmer /** 2886f6bc909eSSimon Trimmer * cs_dsp_remove_padding() - Convert unpacked words to packed bytes 2887f6bc909eSSimon Trimmer * @buf: buffer containing DSP words read from DSP memory 2888f6bc909eSSimon Trimmer * @nwords: number of words to convert 2889f6bc909eSSimon Trimmer * 2890f6bc909eSSimon Trimmer * DSP words from the register map have pad bytes and the data bytes 2891f6bc909eSSimon Trimmer * are in swapped order. This swaps to the native endian order and 2892f6bc909eSSimon Trimmer * strips the pad bytes. 2893f6bc909eSSimon Trimmer */ 2894f6bc909eSSimon Trimmer void cs_dsp_remove_padding(u32 *buf, int nwords) 2895f6bc909eSSimon Trimmer { 2896f6bc909eSSimon Trimmer const __be32 *pack_in = (__be32 *)buf; 2897f6bc909eSSimon Trimmer u8 *pack_out = (u8 *)buf; 2898f6bc909eSSimon Trimmer int i; 2899f6bc909eSSimon Trimmer 2900f6bc909eSSimon Trimmer for (i = 0; i < nwords; i++) { 2901f6bc909eSSimon Trimmer u32 word = be32_to_cpu(*pack_in++); 2902f6bc909eSSimon Trimmer *pack_out++ = (u8)word; 2903f6bc909eSSimon Trimmer *pack_out++ = (u8)(word >> 8); 2904f6bc909eSSimon Trimmer *pack_out++ = (u8)(word >> 16); 2905f6bc909eSSimon Trimmer } 2906f6bc909eSSimon Trimmer } 2907f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_remove_padding); 2908f6bc909eSSimon Trimmer 2909f6bc909eSSimon Trimmer /** 2910f6bc909eSSimon Trimmer * cs_dsp_adsp2_bus_error() - Handle a DSP bus error interrupt 2911f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2912f6bc909eSSimon Trimmer * 2913f6bc909eSSimon Trimmer * The firmware and DSP state will be logged for future analysis. 2914f6bc909eSSimon Trimmer */ 2915f6bc909eSSimon Trimmer void cs_dsp_adsp2_bus_error(struct cs_dsp *dsp) 2916f6bc909eSSimon Trimmer { 2917f6bc909eSSimon Trimmer unsigned int val; 2918f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 2919f6bc909eSSimon Trimmer int ret = 0; 2920f6bc909eSSimon Trimmer 2921f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2922f6bc909eSSimon Trimmer 2923f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 2924f6bc909eSSimon Trimmer if (ret) { 2925f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2926f6bc909eSSimon Trimmer "Failed to read Region Lock Ctrl register: %d\n", ret); 2927f6bc909eSSimon Trimmer goto error; 2928f6bc909eSSimon Trimmer } 2929f6bc909eSSimon Trimmer 2930f6bc909eSSimon Trimmer if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 2931f6bc909eSSimon Trimmer cs_dsp_err(dsp, "watchdog timeout error\n"); 2932f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 2933f6bc909eSSimon Trimmer if (dsp->client_ops->watchdog_expired) 2934f6bc909eSSimon Trimmer dsp->client_ops->watchdog_expired(dsp); 2935f6bc909eSSimon Trimmer } 2936f6bc909eSSimon Trimmer 2937f6bc909eSSimon Trimmer if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 2938f6bc909eSSimon Trimmer if (val & ADSP2_ADDR_ERR_MASK) 2939f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error: address error\n"); 2940f6bc909eSSimon Trimmer else 2941f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error: region lock error\n"); 2942f6bc909eSSimon Trimmer 2943f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 2944f6bc909eSSimon Trimmer if (ret) { 2945f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2946f6bc909eSSimon Trimmer "Failed to read Bus Err Addr register: %d\n", 2947f6bc909eSSimon Trimmer ret); 2948f6bc909eSSimon Trimmer goto error; 2949f6bc909eSSimon Trimmer } 2950f6bc909eSSimon Trimmer 2951f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error address = 0x%x\n", 2952f6bc909eSSimon Trimmer val & ADSP2_BUS_ERR_ADDR_MASK); 2953f6bc909eSSimon Trimmer 2954f6bc909eSSimon Trimmer ret = regmap_read(regmap, 2955f6bc909eSSimon Trimmer dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 2956f6bc909eSSimon Trimmer &val); 2957f6bc909eSSimon Trimmer if (ret) { 2958f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2959f6bc909eSSimon Trimmer "Failed to read Pmem Xmem Err Addr register: %d\n", 2960f6bc909eSSimon Trimmer ret); 2961f6bc909eSSimon Trimmer goto error; 2962f6bc909eSSimon Trimmer } 2963f6bc909eSSimon Trimmer 2964f6bc909eSSimon Trimmer cs_dsp_err(dsp, "xmem error address = 0x%x\n", 2965f6bc909eSSimon Trimmer val & ADSP2_XMEM_ERR_ADDR_MASK); 2966f6bc909eSSimon Trimmer cs_dsp_err(dsp, "pmem error address = 0x%x\n", 2967f6bc909eSSimon Trimmer (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 2968f6bc909eSSimon Trimmer ADSP2_PMEM_ERR_ADDR_SHIFT); 2969f6bc909eSSimon Trimmer } 2970f6bc909eSSimon Trimmer 2971f6bc909eSSimon Trimmer regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 2972f6bc909eSSimon Trimmer ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 2973f6bc909eSSimon Trimmer 2974f6bc909eSSimon Trimmer error: 2975f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2976f6bc909eSSimon Trimmer } 2977f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp2_bus_error); 2978f6bc909eSSimon Trimmer 2979f6bc909eSSimon Trimmer /** 2980f6bc909eSSimon Trimmer * cs_dsp_halo_bus_error() - Handle a DSP bus error interrupt 2981f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2982f6bc909eSSimon Trimmer * 2983f6bc909eSSimon Trimmer * The firmware and DSP state will be logged for future analysis. 2984f6bc909eSSimon Trimmer */ 2985f6bc909eSSimon Trimmer void cs_dsp_halo_bus_error(struct cs_dsp *dsp) 2986f6bc909eSSimon Trimmer { 2987f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 2988f6bc909eSSimon Trimmer unsigned int fault[6]; 2989f6bc909eSSimon Trimmer struct reg_sequence clear[] = { 2990f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 2991f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 2992f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 2993f6bc909eSSimon Trimmer }; 2994f6bc909eSSimon Trimmer int ret; 2995f6bc909eSSimon Trimmer 2996f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2997f6bc909eSSimon Trimmer 2998f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 2999f6bc909eSSimon Trimmer fault); 3000f6bc909eSSimon Trimmer if (ret) { 3001f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 3002f6bc909eSSimon Trimmer goto exit_unlock; 3003f6bc909eSSimon Trimmer } 3004f6bc909eSSimon Trimmer 3005f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 3006f6bc909eSSimon Trimmer *fault & HALO_AHBM_FLAGS_ERR_MASK, 3007f6bc909eSSimon Trimmer (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 3008f6bc909eSSimon Trimmer HALO_AHBM_CORE_ERR_ADDR_SHIFT); 3009f6bc909eSSimon Trimmer 3010f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 3011f6bc909eSSimon Trimmer fault); 3012f6bc909eSSimon Trimmer if (ret) { 3013f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 3014f6bc909eSSimon Trimmer goto exit_unlock; 3015f6bc909eSSimon Trimmer } 3016f6bc909eSSimon Trimmer 3017f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 3018f6bc909eSSimon Trimmer 3019f6bc909eSSimon Trimmer ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 3020f6bc909eSSimon Trimmer fault, ARRAY_SIZE(fault)); 3021f6bc909eSSimon Trimmer if (ret) { 3022f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 3023f6bc909eSSimon Trimmer goto exit_unlock; 3024f6bc909eSSimon Trimmer } 3025f6bc909eSSimon Trimmer 3026f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 3027f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 3028f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 3029f6bc909eSSimon Trimmer 3030f6bc909eSSimon Trimmer ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 3031f6bc909eSSimon Trimmer if (ret) 3032f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 3033f6bc909eSSimon Trimmer 3034f6bc909eSSimon Trimmer exit_unlock: 3035f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3036f6bc909eSSimon Trimmer } 3037f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_halo_bus_error); 3038f6bc909eSSimon Trimmer 3039f6bc909eSSimon Trimmer /** 3040f6bc909eSSimon Trimmer * cs_dsp_halo_wdt_expire() - Handle DSP watchdog expiry 3041f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3042f6bc909eSSimon Trimmer * 3043f6bc909eSSimon Trimmer * This is logged for future analysis. 3044f6bc909eSSimon Trimmer */ 3045f6bc909eSSimon Trimmer void cs_dsp_halo_wdt_expire(struct cs_dsp *dsp) 3046f6bc909eSSimon Trimmer { 3047f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 3048f6bc909eSSimon Trimmer 3049f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "WDT Expiry Fault\n"); 3050f6bc909eSSimon Trimmer 3051f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 3052f6bc909eSSimon Trimmer if (dsp->client_ops->watchdog_expired) 3053f6bc909eSSimon Trimmer dsp->client_ops->watchdog_expired(dsp); 3054f6bc909eSSimon Trimmer 3055f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3056f6bc909eSSimon Trimmer } 3057f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_halo_wdt_expire); 3058f6bc909eSSimon Trimmer 3059f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp1_ops = { 3060f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3061f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp1_parse_sizes, 3062f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3063f6bc909eSSimon Trimmer }; 3064f6bc909eSSimon Trimmer 3065f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp2_ops[] = { 3066f6bc909eSSimon Trimmer { 3067f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3068f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3069f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3070f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3071f6bc909eSSimon Trimmer 3072f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2_show_fw_status, 3073f6bc909eSSimon Trimmer 3074f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3075f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3076f6bc909eSSimon Trimmer 3077f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2_enable_core, 3078f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2_disable_core, 3079f6bc909eSSimon Trimmer 3080f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3081f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3082f6bc909eSSimon Trimmer 3083f6bc909eSSimon Trimmer }, 3084f6bc909eSSimon Trimmer { 3085f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3086f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3087f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3088f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3089f6bc909eSSimon Trimmer 3090f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 3091f6bc909eSSimon Trimmer 3092f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3093f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3094f6bc909eSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 3095f6bc909eSSimon Trimmer 3096f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 3097f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 3098f6bc909eSSimon Trimmer 3099f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3100f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3101f6bc909eSSimon Trimmer }, 3102f6bc909eSSimon Trimmer { 3103f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3104f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3105f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3106f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3107f6bc909eSSimon Trimmer 3108f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 3109f6bc909eSSimon Trimmer .stop_watchdog = cs_dsp_stop_watchdog, 3110f6bc909eSSimon Trimmer 3111f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3112f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3113f6bc909eSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 3114f6bc909eSSimon Trimmer 3115f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 3116f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 3117f6bc909eSSimon Trimmer 3118f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3119f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3120f6bc909eSSimon Trimmer }, 3121f6bc909eSSimon Trimmer }; 3122f6bc909eSSimon Trimmer 3123f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ops = { 3124f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3125f6bc909eSSimon Trimmer .validate_version = cs_dsp_halo_validate_version, 3126f6bc909eSSimon Trimmer .setup_algs = cs_dsp_halo_setup_algs, 3127f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_halo_region_to_reg, 3128f6bc909eSSimon Trimmer 3129f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_halo_show_fw_status, 3130f6bc909eSSimon Trimmer .stop_watchdog = cs_dsp_halo_stop_watchdog, 3131f6bc909eSSimon Trimmer 3132f6bc909eSSimon Trimmer .lock_memory = cs_dsp_halo_configure_mpu, 3133f6bc909eSSimon Trimmer 3134f6bc909eSSimon Trimmer .start_core = cs_dsp_halo_start_core, 3135f6bc909eSSimon Trimmer .stop_core = cs_dsp_halo_stop_core, 3136f6bc909eSSimon Trimmer }; 3137f6bc909eSSimon Trimmer 3138f6bc909eSSimon Trimmer MODULE_DESCRIPTION("Cirrus Logic DSP Support"); 3139f6bc909eSSimon Trimmer MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); 3140f6bc909eSSimon Trimmer MODULE_LICENSE("GPL v2"); 3141