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 619f444da38SCharles Keepax static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg, 620f444da38SCharles Keepax unsigned int off) 621f6bc909eSSimon Trimmer { 622f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region = &ctl->alg_region; 623f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 624f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 625f6bc909eSSimon Trimmer 626f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, alg_region->type); 627f6bc909eSSimon Trimmer if (!mem) { 628f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", 629f6bc909eSSimon Trimmer alg_region->type); 630f6bc909eSSimon Trimmer return -EINVAL; 631f6bc909eSSimon Trimmer } 632f6bc909eSSimon Trimmer 633f444da38SCharles Keepax *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset + off); 634f6bc909eSSimon Trimmer 635f6bc909eSSimon Trimmer return 0; 636f6bc909eSSimon Trimmer } 637f6bc909eSSimon Trimmer 638f6bc909eSSimon Trimmer /** 639f6bc909eSSimon Trimmer * cs_dsp_coeff_write_acked_control() - Sends event_id to the acked control 640f6bc909eSSimon Trimmer * @ctl: pointer to acked coefficient control 641f6bc909eSSimon Trimmer * @event_id: the value to write to the given acked control 642f6bc909eSSimon Trimmer * 643f6bc909eSSimon Trimmer * Once the value has been written to the control the function shall block 644f6bc909eSSimon Trimmer * until the running firmware acknowledges the write or timeout is exceeded. 645f6bc909eSSimon Trimmer * 646f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 647f6bc909eSSimon Trimmer * 648f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 649f6bc909eSSimon Trimmer */ 650f6bc909eSSimon Trimmer int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int event_id) 651f6bc909eSSimon Trimmer { 652f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 653f6bc909eSSimon Trimmer __be32 val = cpu_to_be32(event_id); 654f6bc909eSSimon Trimmer unsigned int reg; 655f6bc909eSSimon Trimmer int i, ret; 656f6bc909eSSimon Trimmer 6575065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 6585065cfabSCharles Keepax 659f6bc909eSSimon Trimmer if (!dsp->running) 660f6bc909eSSimon Trimmer return -EPERM; 661f6bc909eSSimon Trimmer 662f444da38SCharles Keepax ret = cs_dsp_coeff_base_reg(ctl, ®, 0); 663f6bc909eSSimon Trimmer if (ret) 664f6bc909eSSimon Trimmer return ret; 665f6bc909eSSimon Trimmer 666f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 667f6bc909eSSimon Trimmer event_id, ctl->alg_region.alg, 668f6bc909eSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), ctl->offset); 669f6bc909eSSimon Trimmer 670f6bc909eSSimon Trimmer ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 671f6bc909eSSimon Trimmer if (ret) { 672f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 673f6bc909eSSimon Trimmer return ret; 674f6bc909eSSimon Trimmer } 675f6bc909eSSimon Trimmer 676f6bc909eSSimon Trimmer /* 677f6bc909eSSimon Trimmer * Poll for ack, we initially poll at ~1ms intervals for firmwares 678f6bc909eSSimon Trimmer * that respond quickly, then go to ~10ms polls. A firmware is unlikely 679f6bc909eSSimon Trimmer * to ack instantly so we do the first 1ms delay before reading the 680f6bc909eSSimon Trimmer * control to avoid a pointless bus transaction 681f6bc909eSSimon Trimmer */ 682f6bc909eSSimon Trimmer for (i = 0; i < CS_DSP_ACKED_CTL_TIMEOUT_MS;) { 683f6bc909eSSimon Trimmer switch (i) { 684f6bc909eSSimon Trimmer case 0 ... CS_DSP_ACKED_CTL_N_QUICKPOLLS - 1: 685f6bc909eSSimon Trimmer usleep_range(1000, 2000); 686f6bc909eSSimon Trimmer i++; 687f6bc909eSSimon Trimmer break; 688f6bc909eSSimon Trimmer default: 689f6bc909eSSimon Trimmer usleep_range(10000, 20000); 690f6bc909eSSimon Trimmer i += 10; 691f6bc909eSSimon Trimmer break; 692f6bc909eSSimon Trimmer } 693f6bc909eSSimon Trimmer 694f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 695f6bc909eSSimon Trimmer if (ret) { 696f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 697f6bc909eSSimon Trimmer return ret; 698f6bc909eSSimon Trimmer } 699f6bc909eSSimon Trimmer 700f6bc909eSSimon Trimmer if (val == 0) { 701f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 702f6bc909eSSimon Trimmer return 0; 703f6bc909eSSimon Trimmer } 704f6bc909eSSimon Trimmer } 705f6bc909eSSimon Trimmer 706f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 707f6bc909eSSimon Trimmer reg, ctl->alg_region.alg, 708f6bc909eSSimon Trimmer cs_dsp_mem_region_name(ctl->alg_region.type), 709f6bc909eSSimon Trimmer ctl->offset); 710f6bc909eSSimon Trimmer 711f6bc909eSSimon Trimmer return -ETIMEDOUT; 712f6bc909eSSimon Trimmer } 713f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_acked_control); 714f6bc909eSSimon Trimmer 715f6bc909eSSimon Trimmer static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, 716f444da38SCharles Keepax unsigned int off, const void *buf, size_t len) 717f6bc909eSSimon Trimmer { 718f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 719f6bc909eSSimon Trimmer void *scratch; 720f6bc909eSSimon Trimmer int ret; 721f6bc909eSSimon Trimmer unsigned int reg; 722f6bc909eSSimon Trimmer 723f444da38SCharles Keepax ret = cs_dsp_coeff_base_reg(ctl, ®, off); 724f6bc909eSSimon Trimmer if (ret) 725f6bc909eSSimon Trimmer return ret; 726f6bc909eSSimon Trimmer 727f6bc909eSSimon Trimmer scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 728f6bc909eSSimon Trimmer if (!scratch) 729f6bc909eSSimon Trimmer return -ENOMEM; 730f6bc909eSSimon Trimmer 731f6bc909eSSimon Trimmer ret = regmap_raw_write(dsp->regmap, reg, scratch, 732f6bc909eSSimon Trimmer len); 733f6bc909eSSimon Trimmer if (ret) { 734f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 735f6bc909eSSimon Trimmer len, reg, ret); 736f6bc909eSSimon Trimmer kfree(scratch); 737f6bc909eSSimon Trimmer return ret; 738f6bc909eSSimon Trimmer } 739f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 740f6bc909eSSimon Trimmer 741f6bc909eSSimon Trimmer kfree(scratch); 742f6bc909eSSimon Trimmer 743f6bc909eSSimon Trimmer return 0; 744f6bc909eSSimon Trimmer } 745f6bc909eSSimon Trimmer 746f6bc909eSSimon Trimmer /** 747f6bc909eSSimon Trimmer * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control 748f6bc909eSSimon Trimmer * @ctl: pointer to coefficient control 749f444da38SCharles Keepax * @off: word offset at which data should be written 750f6bc909eSSimon Trimmer * @buf: the buffer to write to the given control 751b329b3d3SCharles Keepax * @len: the length of the buffer in bytes 752f6bc909eSSimon Trimmer * 753f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 754f6bc909eSSimon Trimmer * 755f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 756f6bc909eSSimon Trimmer */ 757f444da38SCharles Keepax int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, 758f444da38SCharles Keepax unsigned int off, const void *buf, size_t len) 759f6bc909eSSimon Trimmer { 760f6bc909eSSimon Trimmer int ret = 0; 761f6bc909eSSimon Trimmer 76286c60804SCharles Keepax if (!ctl) 76386c60804SCharles Keepax return -ENOENT; 76486c60804SCharles Keepax 765043c0a62SCharles Keepax lockdep_assert_held(&ctl->dsp->pwr_lock); 766043c0a62SCharles Keepax 767f444da38SCharles Keepax if (len + off * sizeof(u32) > ctl->len) 768f444da38SCharles Keepax return -EINVAL; 769f444da38SCharles Keepax 770f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 771f6bc909eSSimon Trimmer ret = -EPERM; 772f6bc909eSSimon Trimmer else if (buf != ctl->cache) 773f444da38SCharles Keepax memcpy(ctl->cache + off * sizeof(u32), buf, len); 774f6bc909eSSimon Trimmer 775f6bc909eSSimon Trimmer ctl->set = 1; 776f6bc909eSSimon Trimmer if (ctl->enabled && ctl->dsp->running) 777f444da38SCharles Keepax ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); 778f6bc909eSSimon Trimmer 779f6bc909eSSimon Trimmer return ret; 780f6bc909eSSimon Trimmer } 781f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_write_ctrl); 782f6bc909eSSimon Trimmer 783f444da38SCharles Keepax static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, 784f444da38SCharles Keepax unsigned int off, void *buf, size_t len) 785f6bc909eSSimon Trimmer { 786f6bc909eSSimon Trimmer struct cs_dsp *dsp = ctl->dsp; 787f6bc909eSSimon Trimmer void *scratch; 788f6bc909eSSimon Trimmer int ret; 789f6bc909eSSimon Trimmer unsigned int reg; 790f6bc909eSSimon Trimmer 791f444da38SCharles Keepax ret = cs_dsp_coeff_base_reg(ctl, ®, off); 792f6bc909eSSimon Trimmer if (ret) 793f6bc909eSSimon Trimmer return ret; 794f6bc909eSSimon Trimmer 795f6bc909eSSimon Trimmer scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 796f6bc909eSSimon Trimmer if (!scratch) 797f6bc909eSSimon Trimmer return -ENOMEM; 798f6bc909eSSimon Trimmer 799f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 800f6bc909eSSimon Trimmer if (ret) { 801f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 802f6bc909eSSimon Trimmer len, reg, ret); 803f6bc909eSSimon Trimmer kfree(scratch); 804f6bc909eSSimon Trimmer return ret; 805f6bc909eSSimon Trimmer } 806f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 807f6bc909eSSimon Trimmer 808f6bc909eSSimon Trimmer memcpy(buf, scratch, len); 809f6bc909eSSimon Trimmer kfree(scratch); 810f6bc909eSSimon Trimmer 811f6bc909eSSimon Trimmer return 0; 812f6bc909eSSimon Trimmer } 813f6bc909eSSimon Trimmer 814f6bc909eSSimon Trimmer /** 815f6bc909eSSimon Trimmer * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer 816f6bc909eSSimon Trimmer * @ctl: pointer to coefficient control 817f444da38SCharles Keepax * @off: word offset at which data should be read 818f6bc909eSSimon Trimmer * @buf: the buffer to store to the given control 819b329b3d3SCharles Keepax * @len: the length of the buffer in bytes 820f6bc909eSSimon Trimmer * 821f6bc909eSSimon Trimmer * Must be called with pwr_lock held. 822f6bc909eSSimon Trimmer * 823f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 824f6bc909eSSimon Trimmer */ 825f444da38SCharles Keepax int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, 826f444da38SCharles Keepax unsigned int off, void *buf, size_t len) 827f6bc909eSSimon Trimmer { 828f6bc909eSSimon Trimmer int ret = 0; 829f6bc909eSSimon Trimmer 83086c60804SCharles Keepax if (!ctl) 83186c60804SCharles Keepax return -ENOENT; 83286c60804SCharles Keepax 833043c0a62SCharles Keepax lockdep_assert_held(&ctl->dsp->pwr_lock); 834043c0a62SCharles Keepax 835f444da38SCharles Keepax if (len + off * sizeof(u32) > ctl->len) 836f444da38SCharles Keepax return -EINVAL; 837f444da38SCharles Keepax 838f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 839f6bc909eSSimon Trimmer if (ctl->enabled && ctl->dsp->running) 840f444da38SCharles Keepax return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); 841f6bc909eSSimon Trimmer else 842f6bc909eSSimon Trimmer return -EPERM; 843f6bc909eSSimon Trimmer } else { 844f6bc909eSSimon Trimmer if (!ctl->flags && ctl->enabled && ctl->dsp->running) 845f444da38SCharles Keepax ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); 846f6bc909eSSimon Trimmer 847f6bc909eSSimon Trimmer if (buf != ctl->cache) 848f444da38SCharles Keepax memcpy(buf, ctl->cache + off * sizeof(u32), len); 849f6bc909eSSimon Trimmer } 850f6bc909eSSimon Trimmer 851f6bc909eSSimon Trimmer return ret; 852f6bc909eSSimon Trimmer } 853f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_coeff_read_ctrl); 854f6bc909eSSimon Trimmer 855f6bc909eSSimon Trimmer static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp) 856f6bc909eSSimon Trimmer { 857f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 858f6bc909eSSimon Trimmer int ret; 859f6bc909eSSimon Trimmer 860f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 861f6bc909eSSimon Trimmer if (!ctl->enabled || ctl->set) 862f6bc909eSSimon Trimmer continue; 863f6bc909eSSimon Trimmer if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 864f6bc909eSSimon Trimmer continue; 865f6bc909eSSimon Trimmer 866f6bc909eSSimon Trimmer /* 867f6bc909eSSimon Trimmer * For readable controls populate the cache from the DSP memory. 868f6bc909eSSimon Trimmer * For non-readable controls the cache was zero-filled when 869f6bc909eSSimon Trimmer * created so we don't need to do anything. 870f6bc909eSSimon Trimmer */ 871f6bc909eSSimon Trimmer if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 872f444da38SCharles Keepax ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); 873f6bc909eSSimon Trimmer if (ret < 0) 874f6bc909eSSimon Trimmer return ret; 875f6bc909eSSimon Trimmer } 876f6bc909eSSimon Trimmer } 877f6bc909eSSimon Trimmer 878f6bc909eSSimon Trimmer return 0; 879f6bc909eSSimon Trimmer } 880f6bc909eSSimon Trimmer 881f6bc909eSSimon Trimmer static int cs_dsp_coeff_sync_controls(struct cs_dsp *dsp) 882f6bc909eSSimon Trimmer { 883f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 884f6bc909eSSimon Trimmer int ret; 885f6bc909eSSimon Trimmer 886f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 887f6bc909eSSimon Trimmer if (!ctl->enabled) 888f6bc909eSSimon Trimmer continue; 889f6bc909eSSimon Trimmer if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 890f444da38SCharles Keepax ret = cs_dsp_coeff_write_ctrl_raw(ctl, 0, ctl->cache, 891f6bc909eSSimon Trimmer ctl->len); 892f6bc909eSSimon Trimmer if (ret < 0) 893f6bc909eSSimon Trimmer return ret; 894f6bc909eSSimon Trimmer } 895f6bc909eSSimon Trimmer } 896f6bc909eSSimon Trimmer 897f6bc909eSSimon Trimmer return 0; 898f6bc909eSSimon Trimmer } 899f6bc909eSSimon Trimmer 900f6bc909eSSimon Trimmer static void cs_dsp_signal_event_controls(struct cs_dsp *dsp, 901f6bc909eSSimon Trimmer unsigned int event) 902f6bc909eSSimon Trimmer { 903f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 904f6bc909eSSimon Trimmer int ret; 905f6bc909eSSimon Trimmer 906f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 907f6bc909eSSimon Trimmer if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 908f6bc909eSSimon Trimmer continue; 909f6bc909eSSimon Trimmer 910f6bc909eSSimon Trimmer if (!ctl->enabled) 911f6bc909eSSimon Trimmer continue; 912f6bc909eSSimon Trimmer 913f6bc909eSSimon Trimmer ret = cs_dsp_coeff_write_acked_control(ctl, event); 914f6bc909eSSimon Trimmer if (ret) 915f6bc909eSSimon Trimmer cs_dsp_warn(dsp, 916f6bc909eSSimon Trimmer "Failed to send 0x%x event to alg 0x%x (%d)\n", 917f6bc909eSSimon Trimmer event, ctl->alg_region.alg, ret); 918f6bc909eSSimon Trimmer } 919f6bc909eSSimon Trimmer } 920f6bc909eSSimon Trimmer 921f6bc909eSSimon Trimmer static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl) 922f6bc909eSSimon Trimmer { 923f6bc909eSSimon Trimmer kfree(ctl->cache); 924f6bc909eSSimon Trimmer kfree(ctl->subname); 925f6bc909eSSimon Trimmer kfree(ctl); 926f6bc909eSSimon Trimmer } 927f6bc909eSSimon Trimmer 928f6bc909eSSimon Trimmer static int cs_dsp_create_control(struct cs_dsp *dsp, 929f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region, 930f6bc909eSSimon Trimmer unsigned int offset, unsigned int len, 931f6bc909eSSimon Trimmer const char *subname, unsigned int subname_len, 932f6bc909eSSimon Trimmer unsigned int flags, unsigned int type) 933f6bc909eSSimon Trimmer { 934f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 935f6bc909eSSimon Trimmer int ret; 936f6bc909eSSimon Trimmer 937f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 938f6bc909eSSimon Trimmer if (ctl->fw_name == dsp->fw_name && 939f6bc909eSSimon Trimmer ctl->alg_region.alg == alg_region->alg && 940f6bc909eSSimon Trimmer ctl->alg_region.type == alg_region->type) { 941f6bc909eSSimon Trimmer if ((!subname && !ctl->subname) || 942f6bc909eSSimon Trimmer (subname && !strncmp(ctl->subname, subname, ctl->subname_len))) { 943f6bc909eSSimon Trimmer if (!ctl->enabled) 944f6bc909eSSimon Trimmer ctl->enabled = 1; 945f6bc909eSSimon Trimmer return 0; 946f6bc909eSSimon Trimmer } 947f6bc909eSSimon Trimmer } 948f6bc909eSSimon Trimmer } 949f6bc909eSSimon Trimmer 950f6bc909eSSimon Trimmer ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 951f6bc909eSSimon Trimmer if (!ctl) 952f6bc909eSSimon Trimmer return -ENOMEM; 953f6bc909eSSimon Trimmer 954f6bc909eSSimon Trimmer ctl->fw_name = dsp->fw_name; 955f6bc909eSSimon Trimmer ctl->alg_region = *alg_region; 956f6bc909eSSimon Trimmer if (subname && dsp->fw_ver >= 2) { 957f6bc909eSSimon Trimmer ctl->subname_len = subname_len; 958*5b933c72SRichard Fitzgerald ctl->subname = kasprintf(GFP_KERNEL, "%.*s", subname_len, subname); 959f6bc909eSSimon Trimmer if (!ctl->subname) { 960f6bc909eSSimon Trimmer ret = -ENOMEM; 961f6bc909eSSimon Trimmer goto err_ctl; 962f6bc909eSSimon Trimmer } 963f6bc909eSSimon Trimmer } 964f6bc909eSSimon Trimmer ctl->enabled = 1; 965f6bc909eSSimon Trimmer ctl->set = 0; 966f6bc909eSSimon Trimmer ctl->dsp = dsp; 967f6bc909eSSimon Trimmer 968f6bc909eSSimon Trimmer ctl->flags = flags; 969f6bc909eSSimon Trimmer ctl->type = type; 970f6bc909eSSimon Trimmer ctl->offset = offset; 971f6bc909eSSimon Trimmer ctl->len = len; 972f6bc909eSSimon Trimmer ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 973f6bc909eSSimon Trimmer if (!ctl->cache) { 974f6bc909eSSimon Trimmer ret = -ENOMEM; 975f6bc909eSSimon Trimmer goto err_ctl_subname; 976f6bc909eSSimon Trimmer } 977f6bc909eSSimon Trimmer 978f6bc909eSSimon Trimmer list_add(&ctl->list, &dsp->ctl_list); 979f6bc909eSSimon Trimmer 980f6bc909eSSimon Trimmer if (dsp->client_ops->control_add) { 981f6bc909eSSimon Trimmer ret = dsp->client_ops->control_add(ctl); 982f6bc909eSSimon Trimmer if (ret) 983f6bc909eSSimon Trimmer goto err_list_del; 984f6bc909eSSimon Trimmer } 985f6bc909eSSimon Trimmer 986f6bc909eSSimon Trimmer return 0; 987f6bc909eSSimon Trimmer 988f6bc909eSSimon Trimmer err_list_del: 989f6bc909eSSimon Trimmer list_del(&ctl->list); 990f6bc909eSSimon Trimmer kfree(ctl->cache); 991f6bc909eSSimon Trimmer err_ctl_subname: 992f6bc909eSSimon Trimmer kfree(ctl->subname); 993f6bc909eSSimon Trimmer err_ctl: 994f6bc909eSSimon Trimmer kfree(ctl); 995f6bc909eSSimon Trimmer 996f6bc909eSSimon Trimmer return ret; 997f6bc909eSSimon Trimmer } 998f6bc909eSSimon Trimmer 999f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg { 1000f6bc909eSSimon Trimmer int id; 1001f6bc909eSSimon Trimmer const u8 *name; 1002f6bc909eSSimon Trimmer int name_len; 1003f6bc909eSSimon Trimmer int ncoeff; 1004f6bc909eSSimon Trimmer }; 1005f6bc909eSSimon Trimmer 1006f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff { 1007f6bc909eSSimon Trimmer int offset; 1008f6bc909eSSimon Trimmer int mem_type; 1009f6bc909eSSimon Trimmer const u8 *name; 1010f6bc909eSSimon Trimmer int name_len; 1011f6bc909eSSimon Trimmer unsigned int ctl_type; 1012f6bc909eSSimon Trimmer int flags; 1013f6bc909eSSimon Trimmer int len; 1014f6bc909eSSimon Trimmer }; 1015f6bc909eSSimon Trimmer 1016f6bc909eSSimon Trimmer static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1017f6bc909eSSimon Trimmer { 1018f6bc909eSSimon Trimmer int length; 1019f6bc909eSSimon Trimmer 1020f6bc909eSSimon Trimmer switch (bytes) { 1021f6bc909eSSimon Trimmer case 1: 1022f6bc909eSSimon Trimmer length = **pos; 1023f6bc909eSSimon Trimmer break; 1024f6bc909eSSimon Trimmer case 2: 1025f6bc909eSSimon Trimmer length = le16_to_cpu(*((__le16 *)*pos)); 1026f6bc909eSSimon Trimmer break; 1027f6bc909eSSimon Trimmer default: 1028f6bc909eSSimon Trimmer return 0; 1029f6bc909eSSimon Trimmer } 1030f6bc909eSSimon Trimmer 1031f6bc909eSSimon Trimmer if (str) 1032f6bc909eSSimon Trimmer *str = *pos + bytes; 1033f6bc909eSSimon Trimmer 1034f6bc909eSSimon Trimmer *pos += ((length + bytes) + 3) & ~0x03; 1035f6bc909eSSimon Trimmer 1036f6bc909eSSimon Trimmer return length; 1037f6bc909eSSimon Trimmer } 1038f6bc909eSSimon Trimmer 1039f6bc909eSSimon Trimmer static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos) 1040f6bc909eSSimon Trimmer { 1041f6bc909eSSimon Trimmer int val = 0; 1042f6bc909eSSimon Trimmer 1043f6bc909eSSimon Trimmer switch (bytes) { 1044f6bc909eSSimon Trimmer case 2: 1045f6bc909eSSimon Trimmer val = le16_to_cpu(*((__le16 *)*pos)); 1046f6bc909eSSimon Trimmer break; 1047f6bc909eSSimon Trimmer case 4: 1048f6bc909eSSimon Trimmer val = le32_to_cpu(*((__le32 *)*pos)); 1049f6bc909eSSimon Trimmer break; 1050f6bc909eSSimon Trimmer default: 1051f6bc909eSSimon Trimmer break; 1052f6bc909eSSimon Trimmer } 1053f6bc909eSSimon Trimmer 1054f6bc909eSSimon Trimmer *pos += bytes; 1055f6bc909eSSimon Trimmer 1056f6bc909eSSimon Trimmer return val; 1057f6bc909eSSimon Trimmer } 1058f6bc909eSSimon Trimmer 1059f6bc909eSSimon Trimmer static inline void cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, const u8 **data, 1060f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg *blk) 1061f6bc909eSSimon Trimmer { 1062f6bc909eSSimon Trimmer const struct wmfw_adsp_alg_data *raw; 1063f6bc909eSSimon Trimmer 1064f6bc909eSSimon Trimmer switch (dsp->fw_ver) { 1065f6bc909eSSimon Trimmer case 0: 1066f6bc909eSSimon Trimmer case 1: 1067f6bc909eSSimon Trimmer raw = (const struct wmfw_adsp_alg_data *)*data; 1068f6bc909eSSimon Trimmer *data = raw->data; 1069f6bc909eSSimon Trimmer 1070f6bc909eSSimon Trimmer blk->id = le32_to_cpu(raw->id); 1071f6bc909eSSimon Trimmer blk->name = raw->name; 1072f6bc909eSSimon Trimmer blk->name_len = strlen(raw->name); 1073f6bc909eSSimon Trimmer blk->ncoeff = le32_to_cpu(raw->ncoeff); 1074f6bc909eSSimon Trimmer break; 1075f6bc909eSSimon Trimmer default: 1076f6bc909eSSimon Trimmer blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), data); 1077f6bc909eSSimon Trimmer blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), data, 1078f6bc909eSSimon Trimmer &blk->name); 1079f6bc909eSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u16), data, NULL); 1080f6bc909eSSimon Trimmer blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), data); 1081f6bc909eSSimon Trimmer break; 1082f6bc909eSSimon Trimmer } 1083f6bc909eSSimon Trimmer 1084f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 1085f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 1086f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 1087f6bc909eSSimon Trimmer } 1088f6bc909eSSimon Trimmer 1089f6bc909eSSimon Trimmer static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data, 1090f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff *blk) 1091f6bc909eSSimon Trimmer { 1092f6bc909eSSimon Trimmer const struct wmfw_adsp_coeff_data *raw; 1093f6bc909eSSimon Trimmer const u8 *tmp; 1094f6bc909eSSimon Trimmer int length; 1095f6bc909eSSimon Trimmer 1096f6bc909eSSimon Trimmer switch (dsp->fw_ver) { 1097f6bc909eSSimon Trimmer case 0: 1098f6bc909eSSimon Trimmer case 1: 1099f6bc909eSSimon Trimmer raw = (const struct wmfw_adsp_coeff_data *)*data; 1100f6bc909eSSimon Trimmer *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 1101f6bc909eSSimon Trimmer 1102f6bc909eSSimon Trimmer blk->offset = le16_to_cpu(raw->hdr.offset); 1103f6bc909eSSimon Trimmer blk->mem_type = le16_to_cpu(raw->hdr.type); 1104f6bc909eSSimon Trimmer blk->name = raw->name; 1105f6bc909eSSimon Trimmer blk->name_len = strlen(raw->name); 1106f6bc909eSSimon Trimmer blk->ctl_type = le16_to_cpu(raw->ctl_type); 1107f6bc909eSSimon Trimmer blk->flags = le16_to_cpu(raw->flags); 1108f6bc909eSSimon Trimmer blk->len = le32_to_cpu(raw->len); 1109f6bc909eSSimon Trimmer break; 1110f6bc909eSSimon Trimmer default: 1111f6bc909eSSimon Trimmer tmp = *data; 1112f6bc909eSSimon Trimmer blk->offset = cs_dsp_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1113f6bc909eSSimon Trimmer blk->mem_type = cs_dsp_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1114f6bc909eSSimon Trimmer length = cs_dsp_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1115f6bc909eSSimon Trimmer blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, 1116f6bc909eSSimon Trimmer &blk->name); 1117f6bc909eSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u8), &tmp, NULL); 1118f6bc909eSSimon Trimmer cs_dsp_coeff_parse_string(sizeof(u16), &tmp, NULL); 1119f6bc909eSSimon Trimmer blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1120f6bc909eSSimon Trimmer blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp); 1121f6bc909eSSimon Trimmer blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp); 1122f6bc909eSSimon Trimmer 1123f6bc909eSSimon Trimmer *data = *data + sizeof(raw->hdr) + length; 1124f6bc909eSSimon Trimmer break; 1125f6bc909eSSimon Trimmer } 1126f6bc909eSSimon Trimmer 1127f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 1128f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 1129f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 1130f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 1131f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 1132f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 1133f6bc909eSSimon Trimmer } 1134f6bc909eSSimon Trimmer 1135f6bc909eSSimon Trimmer static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp, 1136f6bc909eSSimon Trimmer const struct cs_dsp_coeff_parsed_coeff *coeff_blk, 1137f6bc909eSSimon Trimmer unsigned int f_required, 1138f6bc909eSSimon Trimmer unsigned int f_illegal) 1139f6bc909eSSimon Trimmer { 1140f6bc909eSSimon Trimmer if ((coeff_blk->flags & f_illegal) || 1141f6bc909eSSimon Trimmer ((coeff_blk->flags & f_required) != f_required)) { 1142f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1143f6bc909eSSimon Trimmer coeff_blk->flags, coeff_blk->ctl_type); 1144f6bc909eSSimon Trimmer return -EINVAL; 1145f6bc909eSSimon Trimmer } 1146f6bc909eSSimon Trimmer 1147f6bc909eSSimon Trimmer return 0; 1148f6bc909eSSimon Trimmer } 1149f6bc909eSSimon Trimmer 1150f6bc909eSSimon Trimmer static int cs_dsp_parse_coeff(struct cs_dsp *dsp, 1151f6bc909eSSimon Trimmer const struct wmfw_region *region) 1152f6bc909eSSimon Trimmer { 1153f6bc909eSSimon Trimmer struct cs_dsp_alg_region alg_region = {}; 1154f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_alg alg_blk; 1155f6bc909eSSimon Trimmer struct cs_dsp_coeff_parsed_coeff coeff_blk; 1156f6bc909eSSimon Trimmer const u8 *data = region->data; 1157f6bc909eSSimon Trimmer int i, ret; 1158f6bc909eSSimon Trimmer 1159f6bc909eSSimon Trimmer cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk); 1160f6bc909eSSimon Trimmer for (i = 0; i < alg_blk.ncoeff; i++) { 1161f6bc909eSSimon Trimmer cs_dsp_coeff_parse_coeff(dsp, &data, &coeff_blk); 1162f6bc909eSSimon Trimmer 1163f6bc909eSSimon Trimmer switch (coeff_blk.ctl_type) { 1164f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_BYTES: 1165f6bc909eSSimon Trimmer break; 1166f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_ACKED: 1167f6bc909eSSimon Trimmer if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1168f6bc909eSSimon Trimmer continue; /* ignore */ 1169f6bc909eSSimon Trimmer 1170f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1171f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1172f6bc909eSSimon Trimmer WMFW_CTL_FLAG_WRITEABLE | 1173f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1174f6bc909eSSimon Trimmer 0); 1175f6bc909eSSimon Trimmer if (ret) 1176f6bc909eSSimon Trimmer return -EINVAL; 1177f6bc909eSSimon Trimmer break; 1178f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_HOSTEVENT: 11795c903f64SCharles Keepax case WMFW_CTL_TYPE_FWEVENT: 1180f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1181f6bc909eSSimon Trimmer WMFW_CTL_FLAG_SYS | 1182f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1183f6bc909eSSimon Trimmer WMFW_CTL_FLAG_WRITEABLE | 1184f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1185f6bc909eSSimon Trimmer 0); 1186f6bc909eSSimon Trimmer if (ret) 1187f6bc909eSSimon Trimmer return -EINVAL; 1188f6bc909eSSimon Trimmer break; 1189f6bc909eSSimon Trimmer case WMFW_CTL_TYPE_HOST_BUFFER: 1190f6bc909eSSimon Trimmer ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk, 1191f6bc909eSSimon Trimmer WMFW_CTL_FLAG_SYS | 1192f6bc909eSSimon Trimmer WMFW_CTL_FLAG_VOLATILE | 1193f6bc909eSSimon Trimmer WMFW_CTL_FLAG_READABLE, 1194f6bc909eSSimon Trimmer 0); 1195f6bc909eSSimon Trimmer if (ret) 1196f6bc909eSSimon Trimmer return -EINVAL; 1197f6bc909eSSimon Trimmer break; 1198f6bc909eSSimon Trimmer default: 1199f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Unknown control type: %d\n", 1200f6bc909eSSimon Trimmer coeff_blk.ctl_type); 1201f6bc909eSSimon Trimmer return -EINVAL; 1202f6bc909eSSimon Trimmer } 1203f6bc909eSSimon Trimmer 1204f6bc909eSSimon Trimmer alg_region.type = coeff_blk.mem_type; 1205f6bc909eSSimon Trimmer alg_region.alg = alg_blk.id; 1206f6bc909eSSimon Trimmer 1207f6bc909eSSimon Trimmer ret = cs_dsp_create_control(dsp, &alg_region, 1208f6bc909eSSimon Trimmer coeff_blk.offset, 1209f6bc909eSSimon Trimmer coeff_blk.len, 1210f6bc909eSSimon Trimmer coeff_blk.name, 1211f6bc909eSSimon Trimmer coeff_blk.name_len, 1212f6bc909eSSimon Trimmer coeff_blk.flags, 1213f6bc909eSSimon Trimmer coeff_blk.ctl_type); 1214f6bc909eSSimon Trimmer if (ret < 0) 1215f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to create control: %.*s, %d\n", 1216f6bc909eSSimon Trimmer coeff_blk.name_len, coeff_blk.name, ret); 1217f6bc909eSSimon Trimmer } 1218f6bc909eSSimon Trimmer 1219f6bc909eSSimon Trimmer return 0; 1220f6bc909eSSimon Trimmer } 1221f6bc909eSSimon Trimmer 1222f6bc909eSSimon Trimmer static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp, 1223f6bc909eSSimon Trimmer const char * const file, 1224f6bc909eSSimon Trimmer unsigned int pos, 1225f6bc909eSSimon Trimmer const struct firmware *firmware) 1226f6bc909eSSimon Trimmer { 1227f6bc909eSSimon Trimmer const struct wmfw_adsp1_sizes *adsp1_sizes; 1228f6bc909eSSimon Trimmer 1229f6bc909eSSimon Trimmer adsp1_sizes = (void *)&firmware->data[pos]; 1230f6bc909eSSimon Trimmer 1231f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, 1232f6bc909eSSimon Trimmer le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 1233f6bc909eSSimon Trimmer le32_to_cpu(adsp1_sizes->zm)); 1234f6bc909eSSimon Trimmer 1235f6bc909eSSimon Trimmer return pos + sizeof(*adsp1_sizes); 1236f6bc909eSSimon Trimmer } 1237f6bc909eSSimon Trimmer 1238f6bc909eSSimon Trimmer static unsigned int cs_dsp_adsp2_parse_sizes(struct cs_dsp *dsp, 1239f6bc909eSSimon Trimmer const char * const file, 1240f6bc909eSSimon Trimmer unsigned int pos, 1241f6bc909eSSimon Trimmer const struct firmware *firmware) 1242f6bc909eSSimon Trimmer { 1243f6bc909eSSimon Trimmer const struct wmfw_adsp2_sizes *adsp2_sizes; 1244f6bc909eSSimon Trimmer 1245f6bc909eSSimon Trimmer adsp2_sizes = (void *)&firmware->data[pos]; 1246f6bc909eSSimon Trimmer 1247f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, 1248f6bc909eSSimon Trimmer le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 1249f6bc909eSSimon Trimmer le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); 1250f6bc909eSSimon Trimmer 1251f6bc909eSSimon Trimmer return pos + sizeof(*adsp2_sizes); 1252f6bc909eSSimon Trimmer } 1253f6bc909eSSimon Trimmer 1254f6bc909eSSimon Trimmer static bool cs_dsp_validate_version(struct cs_dsp *dsp, unsigned int version) 1255f6bc909eSSimon Trimmer { 1256f6bc909eSSimon Trimmer switch (version) { 1257f6bc909eSSimon Trimmer case 0: 1258f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Deprecated file format %d\n", version); 1259f6bc909eSSimon Trimmer return true; 1260f6bc909eSSimon Trimmer case 1: 1261f6bc909eSSimon Trimmer case 2: 1262f6bc909eSSimon Trimmer return true; 1263f6bc909eSSimon Trimmer default: 1264f6bc909eSSimon Trimmer return false; 1265f6bc909eSSimon Trimmer } 1266f6bc909eSSimon Trimmer } 1267f6bc909eSSimon Trimmer 1268f6bc909eSSimon Trimmer static bool cs_dsp_halo_validate_version(struct cs_dsp *dsp, unsigned int version) 1269f6bc909eSSimon Trimmer { 1270f6bc909eSSimon Trimmer switch (version) { 1271f6bc909eSSimon Trimmer case 3: 1272f6bc909eSSimon Trimmer return true; 1273f6bc909eSSimon Trimmer default: 1274f6bc909eSSimon Trimmer return false; 1275f6bc909eSSimon Trimmer } 1276f6bc909eSSimon Trimmer } 1277f6bc909eSSimon Trimmer 1278f6bc909eSSimon Trimmer static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, 1279f6bc909eSSimon Trimmer const char *file) 1280f6bc909eSSimon Trimmer { 1281f6bc909eSSimon Trimmer LIST_HEAD(buf_list); 1282f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 1283f6bc909eSSimon Trimmer unsigned int pos = 0; 1284f6bc909eSSimon Trimmer const struct wmfw_header *header; 1285f6bc909eSSimon Trimmer const struct wmfw_adsp1_sizes *adsp1_sizes; 1286f6bc909eSSimon Trimmer const struct wmfw_footer *footer; 1287f6bc909eSSimon Trimmer const struct wmfw_region *region; 1288f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1289f6bc909eSSimon Trimmer const char *region_name; 1290f6bc909eSSimon Trimmer char *text = NULL; 1291f6bc909eSSimon Trimmer struct cs_dsp_buf *buf; 1292f6bc909eSSimon Trimmer unsigned int reg; 1293f6bc909eSSimon Trimmer int regions = 0; 1294f6bc909eSSimon Trimmer int ret, offset, type; 1295f6bc909eSSimon Trimmer 1296f6bc909eSSimon Trimmer ret = -EINVAL; 1297f6bc909eSSimon Trimmer 1298f6bc909eSSimon Trimmer pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 1299f6bc909eSSimon Trimmer if (pos >= firmware->size) { 1300f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: file too short, %zu bytes\n", 1301f6bc909eSSimon Trimmer file, firmware->size); 1302f6bc909eSSimon Trimmer goto out_fw; 1303f6bc909eSSimon Trimmer } 1304f6bc909eSSimon Trimmer 1305f6bc909eSSimon Trimmer header = (void *)&firmware->data[0]; 1306f6bc909eSSimon Trimmer 1307f6bc909eSSimon Trimmer if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 1308f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid magic\n", file); 1309f6bc909eSSimon Trimmer goto out_fw; 1310f6bc909eSSimon Trimmer } 1311f6bc909eSSimon Trimmer 1312f6bc909eSSimon Trimmer if (!dsp->ops->validate_version(dsp, header->ver)) { 1313f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: unknown file format %d\n", 1314f6bc909eSSimon Trimmer file, header->ver); 1315f6bc909eSSimon Trimmer goto out_fw; 1316f6bc909eSSimon Trimmer } 1317f6bc909eSSimon Trimmer 1318f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware version: %d\n", header->ver); 1319f6bc909eSSimon Trimmer dsp->fw_ver = header->ver; 1320f6bc909eSSimon Trimmer 1321f6bc909eSSimon Trimmer if (header->core != dsp->type) { 1322f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid core %d != %d\n", 1323f6bc909eSSimon Trimmer file, header->core, dsp->type); 1324f6bc909eSSimon Trimmer goto out_fw; 1325f6bc909eSSimon Trimmer } 1326f6bc909eSSimon Trimmer 1327f6bc909eSSimon Trimmer pos = sizeof(*header); 1328f6bc909eSSimon Trimmer pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); 1329f6bc909eSSimon Trimmer 1330f6bc909eSSimon Trimmer footer = (void *)&firmware->data[pos]; 1331f6bc909eSSimon Trimmer pos += sizeof(*footer); 1332f6bc909eSSimon Trimmer 1333f6bc909eSSimon Trimmer if (le32_to_cpu(header->len) != pos) { 1334f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: unexpected header length %d\n", 1335f6bc909eSSimon Trimmer file, le32_to_cpu(header->len)); 1336f6bc909eSSimon Trimmer goto out_fw; 1337f6bc909eSSimon Trimmer } 1338f6bc909eSSimon Trimmer 1339f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, 1340f6bc909eSSimon Trimmer le64_to_cpu(footer->timestamp)); 1341f6bc909eSSimon Trimmer 1342f6bc909eSSimon Trimmer while (pos < firmware->size && 1343f6bc909eSSimon Trimmer sizeof(*region) < firmware->size - pos) { 1344f6bc909eSSimon Trimmer region = (void *)&(firmware->data[pos]); 1345f6bc909eSSimon Trimmer region_name = "Unknown"; 1346f6bc909eSSimon Trimmer reg = 0; 1347f6bc909eSSimon Trimmer text = NULL; 1348f6bc909eSSimon Trimmer offset = le32_to_cpu(region->offset) & 0xffffff; 1349f6bc909eSSimon Trimmer type = be32_to_cpu(region->type) & 0xff; 1350f6bc909eSSimon Trimmer 1351f6bc909eSSimon Trimmer switch (type) { 1352f6bc909eSSimon Trimmer case WMFW_NAME_TEXT: 1353f6bc909eSSimon Trimmer region_name = "Firmware name"; 1354f6bc909eSSimon Trimmer text = kzalloc(le32_to_cpu(region->len) + 1, 1355f6bc909eSSimon Trimmer GFP_KERNEL); 1356f6bc909eSSimon Trimmer break; 1357f6bc909eSSimon Trimmer case WMFW_ALGORITHM_DATA: 1358f6bc909eSSimon Trimmer region_name = "Algorithm"; 1359f6bc909eSSimon Trimmer ret = cs_dsp_parse_coeff(dsp, region); 1360f6bc909eSSimon Trimmer if (ret != 0) 1361f6bc909eSSimon Trimmer goto out_fw; 1362f6bc909eSSimon Trimmer break; 1363f6bc909eSSimon Trimmer case WMFW_INFO_TEXT: 1364f6bc909eSSimon Trimmer region_name = "Information"; 1365f6bc909eSSimon Trimmer text = kzalloc(le32_to_cpu(region->len) + 1, 1366f6bc909eSSimon Trimmer GFP_KERNEL); 1367f6bc909eSSimon Trimmer break; 1368f6bc909eSSimon Trimmer case WMFW_ABSOLUTE: 1369f6bc909eSSimon Trimmer region_name = "Absolute"; 1370f6bc909eSSimon Trimmer reg = offset; 1371f6bc909eSSimon Trimmer break; 1372f6bc909eSSimon Trimmer case WMFW_ADSP1_PM: 1373f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 1374f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 1375f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 1376f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 1377f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 1378f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 1379f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 1380f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 1381f6bc909eSSimon Trimmer if (!mem) { 1382f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No region of type: %x\n", type); 1383f6bc909eSSimon Trimmer ret = -EINVAL; 1384f6bc909eSSimon Trimmer goto out_fw; 1385f6bc909eSSimon Trimmer } 1386f6bc909eSSimon Trimmer 1387f6bc909eSSimon Trimmer region_name = cs_dsp_mem_region_name(type); 1388f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, offset); 1389f6bc909eSSimon Trimmer break; 1390f6bc909eSSimon Trimmer default: 1391f6bc909eSSimon Trimmer cs_dsp_warn(dsp, 1392f6bc909eSSimon Trimmer "%s.%d: Unknown region type %x at %d(%x)\n", 1393f6bc909eSSimon Trimmer file, regions, type, pos, pos); 1394f6bc909eSSimon Trimmer break; 1395f6bc909eSSimon Trimmer } 1396f6bc909eSSimon Trimmer 1397f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 1398f6bc909eSSimon Trimmer regions, le32_to_cpu(region->len), offset, 1399f6bc909eSSimon Trimmer region_name); 1400f6bc909eSSimon Trimmer 1401f6bc909eSSimon Trimmer if (le32_to_cpu(region->len) > 1402f6bc909eSSimon Trimmer firmware->size - pos - sizeof(*region)) { 1403f6bc909eSSimon Trimmer cs_dsp_err(dsp, 1404f6bc909eSSimon Trimmer "%s.%d: %s region len %d bytes exceeds file length %zu\n", 1405f6bc909eSSimon Trimmer file, regions, region_name, 1406f6bc909eSSimon Trimmer le32_to_cpu(region->len), firmware->size); 1407f6bc909eSSimon Trimmer ret = -EINVAL; 1408f6bc909eSSimon Trimmer goto out_fw; 1409f6bc909eSSimon Trimmer } 1410f6bc909eSSimon Trimmer 1411f6bc909eSSimon Trimmer if (text) { 1412f6bc909eSSimon Trimmer memcpy(text, region->data, le32_to_cpu(region->len)); 1413f6bc909eSSimon Trimmer cs_dsp_info(dsp, "%s: %s\n", file, text); 1414f6bc909eSSimon Trimmer kfree(text); 1415f6bc909eSSimon Trimmer text = NULL; 1416f6bc909eSSimon Trimmer } 1417f6bc909eSSimon Trimmer 1418f6bc909eSSimon Trimmer if (reg) { 1419f6bc909eSSimon Trimmer buf = cs_dsp_buf_alloc(region->data, 1420f6bc909eSSimon Trimmer le32_to_cpu(region->len), 1421f6bc909eSSimon Trimmer &buf_list); 1422f6bc909eSSimon Trimmer if (!buf) { 1423f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 1424f6bc909eSSimon Trimmer ret = -ENOMEM; 1425f6bc909eSSimon Trimmer goto out_fw; 1426f6bc909eSSimon Trimmer } 1427f6bc909eSSimon Trimmer 1428f6bc909eSSimon Trimmer ret = regmap_raw_write_async(regmap, reg, buf->buf, 1429f6bc909eSSimon Trimmer le32_to_cpu(region->len)); 1430f6bc909eSSimon Trimmer if (ret != 0) { 1431f6bc909eSSimon Trimmer cs_dsp_err(dsp, 1432f6bc909eSSimon Trimmer "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 1433f6bc909eSSimon Trimmer file, regions, 1434f6bc909eSSimon Trimmer le32_to_cpu(region->len), offset, 1435f6bc909eSSimon Trimmer region_name, ret); 1436f6bc909eSSimon Trimmer goto out_fw; 1437f6bc909eSSimon Trimmer } 1438f6bc909eSSimon Trimmer } 1439f6bc909eSSimon Trimmer 1440f6bc909eSSimon Trimmer pos += le32_to_cpu(region->len) + sizeof(*region); 1441f6bc909eSSimon Trimmer regions++; 1442f6bc909eSSimon Trimmer } 1443f6bc909eSSimon Trimmer 1444f6bc909eSSimon Trimmer ret = regmap_async_complete(regmap); 1445f6bc909eSSimon Trimmer if (ret != 0) { 1446f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 1447f6bc909eSSimon Trimmer goto out_fw; 1448f6bc909eSSimon Trimmer } 1449f6bc909eSSimon Trimmer 1450f6bc909eSSimon Trimmer if (pos > firmware->size) 1451f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 1452f6bc909eSSimon Trimmer file, regions, pos - firmware->size); 1453f6bc909eSSimon Trimmer 1454f6bc909eSSimon Trimmer cs_dsp_debugfs_save_wmfwname(dsp, file); 1455f6bc909eSSimon Trimmer 1456f6bc909eSSimon Trimmer out_fw: 1457f6bc909eSSimon Trimmer regmap_async_complete(regmap); 1458f6bc909eSSimon Trimmer cs_dsp_buf_free(&buf_list); 1459f6bc909eSSimon Trimmer kfree(text); 1460f6bc909eSSimon Trimmer 1461f6bc909eSSimon Trimmer return ret; 1462f6bc909eSSimon Trimmer } 1463f6bc909eSSimon Trimmer 1464f6bc909eSSimon Trimmer /** 1465f6bc909eSSimon Trimmer * cs_dsp_get_ctl() - Finds a matching coefficient control 1466f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 1467f6bc909eSSimon Trimmer * @name: pointer to string to match with a control's subname 1468f6bc909eSSimon Trimmer * @type: the algorithm type to match 1469f6bc909eSSimon Trimmer * @alg: the algorithm id to match 1470f6bc909eSSimon Trimmer * 1471f6bc909eSSimon Trimmer * Find cs_dsp_coeff_ctl with input name as its subname 1472f6bc909eSSimon Trimmer * 1473f6bc909eSSimon Trimmer * Return: pointer to the control on success, NULL if not found 1474f6bc909eSSimon Trimmer */ 1475f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, int type, 1476f6bc909eSSimon Trimmer unsigned int alg) 1477f6bc909eSSimon Trimmer { 1478f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *pos, *rslt = NULL; 1479f6bc909eSSimon Trimmer 14805065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 14815065cfabSCharles Keepax 1482f6bc909eSSimon Trimmer list_for_each_entry(pos, &dsp->ctl_list, list) { 1483f6bc909eSSimon Trimmer if (!pos->subname) 1484f6bc909eSSimon Trimmer continue; 1485f6bc909eSSimon Trimmer if (strncmp(pos->subname, name, pos->subname_len) == 0 && 1486f6bc909eSSimon Trimmer pos->fw_name == dsp->fw_name && 1487f6bc909eSSimon Trimmer pos->alg_region.alg == alg && 1488f6bc909eSSimon Trimmer pos->alg_region.type == type) { 1489f6bc909eSSimon Trimmer rslt = pos; 1490f6bc909eSSimon Trimmer break; 1491f6bc909eSSimon Trimmer } 1492f6bc909eSSimon Trimmer } 1493f6bc909eSSimon Trimmer 1494f6bc909eSSimon Trimmer return rslt; 1495f6bc909eSSimon Trimmer } 1496f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_get_ctl); 1497f6bc909eSSimon Trimmer 1498f6bc909eSSimon Trimmer static void cs_dsp_ctl_fixup_base(struct cs_dsp *dsp, 1499f6bc909eSSimon Trimmer const struct cs_dsp_alg_region *alg_region) 1500f6bc909eSSimon Trimmer { 1501f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 1502f6bc909eSSimon Trimmer 1503f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) { 1504f6bc909eSSimon Trimmer if (ctl->fw_name == dsp->fw_name && 1505f6bc909eSSimon Trimmer alg_region->alg == ctl->alg_region.alg && 1506f6bc909eSSimon Trimmer alg_region->type == ctl->alg_region.type) { 1507f6bc909eSSimon Trimmer ctl->alg_region.base = alg_region->base; 1508f6bc909eSSimon Trimmer } 1509f6bc909eSSimon Trimmer } 1510f6bc909eSSimon Trimmer } 1511f6bc909eSSimon Trimmer 1512f6bc909eSSimon Trimmer static void *cs_dsp_read_algs(struct cs_dsp *dsp, size_t n_algs, 1513f6bc909eSSimon Trimmer const struct cs_dsp_region *mem, 1514f6bc909eSSimon Trimmer unsigned int pos, unsigned int len) 1515f6bc909eSSimon Trimmer { 1516f6bc909eSSimon Trimmer void *alg; 1517f6bc909eSSimon Trimmer unsigned int reg; 1518f6bc909eSSimon Trimmer int ret; 1519f6bc909eSSimon Trimmer __be32 val; 1520f6bc909eSSimon Trimmer 1521f6bc909eSSimon Trimmer if (n_algs == 0) { 1522f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No algorithms\n"); 1523f6bc909eSSimon Trimmer return ERR_PTR(-EINVAL); 1524f6bc909eSSimon Trimmer } 1525f6bc909eSSimon Trimmer 1526f6bc909eSSimon Trimmer if (n_algs > 1024) { 1527f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1528f6bc909eSSimon Trimmer return ERR_PTR(-EINVAL); 1529f6bc909eSSimon Trimmer } 1530f6bc909eSSimon Trimmer 1531f6bc909eSSimon Trimmer /* Read the terminator first to validate the length */ 1532f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, pos + len); 1533f6bc909eSSimon Trimmer 1534f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1535f6bc909eSSimon Trimmer if (ret != 0) { 1536f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list end: %d\n", 1537f6bc909eSSimon Trimmer ret); 1538f6bc909eSSimon Trimmer return ERR_PTR(ret); 1539f6bc909eSSimon Trimmer } 1540f6bc909eSSimon Trimmer 1541f6bc909eSSimon Trimmer if (be32_to_cpu(val) != 0xbedead) 1542f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 1543f6bc909eSSimon Trimmer reg, be32_to_cpu(val)); 1544f6bc909eSSimon Trimmer 1545f6bc909eSSimon Trimmer /* Convert length from DSP words to bytes */ 1546f6bc909eSSimon Trimmer len *= sizeof(u32); 1547f6bc909eSSimon Trimmer 1548f6bc909eSSimon Trimmer alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 1549f6bc909eSSimon Trimmer if (!alg) 1550f6bc909eSSimon Trimmer return ERR_PTR(-ENOMEM); 1551f6bc909eSSimon Trimmer 1552f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, pos); 1553f6bc909eSSimon Trimmer 1554f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, alg, len); 1555f6bc909eSSimon Trimmer if (ret != 0) { 1556f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1557f6bc909eSSimon Trimmer kfree(alg); 1558f6bc909eSSimon Trimmer return ERR_PTR(ret); 1559f6bc909eSSimon Trimmer } 1560f6bc909eSSimon Trimmer 1561f6bc909eSSimon Trimmer return alg; 1562f6bc909eSSimon Trimmer } 1563f6bc909eSSimon Trimmer 1564f6bc909eSSimon Trimmer /** 1565f6bc909eSSimon Trimmer * cs_dsp_find_alg_region() - Finds a matching algorithm region 1566f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 1567f6bc909eSSimon Trimmer * @type: the algorithm type to match 1568f6bc909eSSimon Trimmer * @id: the algorithm id to match 1569f6bc909eSSimon Trimmer * 1570f6bc909eSSimon Trimmer * Return: Pointer to matching algorithm region, or NULL if not found. 1571f6bc909eSSimon Trimmer */ 1572f6bc909eSSimon Trimmer struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, 1573f6bc909eSSimon Trimmer int type, unsigned int id) 1574f6bc909eSSimon Trimmer { 1575f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1576f6bc909eSSimon Trimmer 15775065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 15785065cfabSCharles Keepax 1579f6bc909eSSimon Trimmer list_for_each_entry(alg_region, &dsp->alg_regions, list) { 1580f6bc909eSSimon Trimmer if (id == alg_region->alg && type == alg_region->type) 1581f6bc909eSSimon Trimmer return alg_region; 1582f6bc909eSSimon Trimmer } 1583f6bc909eSSimon Trimmer 1584f6bc909eSSimon Trimmer return NULL; 1585f6bc909eSSimon Trimmer } 1586f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_find_alg_region); 1587f6bc909eSSimon Trimmer 1588f6bc909eSSimon Trimmer static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, 1589f6bc909eSSimon Trimmer int type, __be32 id, 15902925748eSCharles Keepax __be32 ver, __be32 base) 1591f6bc909eSSimon Trimmer { 1592f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1593f6bc909eSSimon Trimmer 1594f6bc909eSSimon Trimmer alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1595f6bc909eSSimon Trimmer if (!alg_region) 1596f6bc909eSSimon Trimmer return ERR_PTR(-ENOMEM); 1597f6bc909eSSimon Trimmer 1598f6bc909eSSimon Trimmer alg_region->type = type; 1599f6bc909eSSimon Trimmer alg_region->alg = be32_to_cpu(id); 16002925748eSCharles Keepax alg_region->ver = be32_to_cpu(ver); 1601f6bc909eSSimon Trimmer alg_region->base = be32_to_cpu(base); 1602f6bc909eSSimon Trimmer 1603f6bc909eSSimon Trimmer list_add_tail(&alg_region->list, &dsp->alg_regions); 1604f6bc909eSSimon Trimmer 1605f6bc909eSSimon Trimmer if (dsp->fw_ver > 0) 1606f6bc909eSSimon Trimmer cs_dsp_ctl_fixup_base(dsp, alg_region); 1607f6bc909eSSimon Trimmer 1608f6bc909eSSimon Trimmer return alg_region; 1609f6bc909eSSimon Trimmer } 1610f6bc909eSSimon Trimmer 1611f6bc909eSSimon Trimmer static void cs_dsp_free_alg_regions(struct cs_dsp *dsp) 1612f6bc909eSSimon Trimmer { 1613f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1614f6bc909eSSimon Trimmer 1615f6bc909eSSimon Trimmer while (!list_empty(&dsp->alg_regions)) { 1616f6bc909eSSimon Trimmer alg_region = list_first_entry(&dsp->alg_regions, 1617f6bc909eSSimon Trimmer struct cs_dsp_alg_region, 1618f6bc909eSSimon Trimmer list); 1619f6bc909eSSimon Trimmer list_del(&alg_region->list); 1620f6bc909eSSimon Trimmer kfree(alg_region); 1621f6bc909eSSimon Trimmer } 1622f6bc909eSSimon Trimmer } 1623f6bc909eSSimon Trimmer 1624f6bc909eSSimon Trimmer static void cs_dsp_parse_wmfw_id_header(struct cs_dsp *dsp, 1625f6bc909eSSimon Trimmer struct wmfw_id_hdr *fw, int nalgs) 1626f6bc909eSSimon Trimmer { 1627f6bc909eSSimon Trimmer dsp->fw_id = be32_to_cpu(fw->id); 1628f6bc909eSSimon Trimmer dsp->fw_id_version = be32_to_cpu(fw->ver); 1629f6bc909eSSimon Trimmer 1630f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", 1631f6bc909eSSimon Trimmer dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 1632f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 1633f6bc909eSSimon Trimmer nalgs); 1634f6bc909eSSimon Trimmer } 1635f6bc909eSSimon Trimmer 1636f6bc909eSSimon Trimmer static void cs_dsp_parse_wmfw_v3_id_header(struct cs_dsp *dsp, 1637f6bc909eSSimon Trimmer struct wmfw_v3_id_hdr *fw, int nalgs) 1638f6bc909eSSimon Trimmer { 1639f6bc909eSSimon Trimmer dsp->fw_id = be32_to_cpu(fw->id); 1640f6bc909eSSimon Trimmer dsp->fw_id_version = be32_to_cpu(fw->ver); 1641f6bc909eSSimon Trimmer dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); 1642f6bc909eSSimon Trimmer 1643f6bc909eSSimon Trimmer cs_dsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", 1644f6bc909eSSimon Trimmer dsp->fw_id, dsp->fw_vendor_id, 1645f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff0000) >> 16, 1646f6bc909eSSimon Trimmer (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 1647f6bc909eSSimon Trimmer nalgs); 1648f6bc909eSSimon Trimmer } 1649f6bc909eSSimon Trimmer 16502925748eSCharles Keepax static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, 16512925748eSCharles Keepax int nregions, const int *type, __be32 *base) 1652f6bc909eSSimon Trimmer { 1653f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1654f6bc909eSSimon Trimmer int i; 1655f6bc909eSSimon Trimmer 1656f6bc909eSSimon Trimmer for (i = 0; i < nregions; i++) { 16572925748eSCharles Keepax alg_region = cs_dsp_create_region(dsp, type[i], id, ver, base[i]); 1658f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1659f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1660f6bc909eSSimon Trimmer } 1661f6bc909eSSimon Trimmer 1662f6bc909eSSimon Trimmer return 0; 1663f6bc909eSSimon Trimmer } 1664f6bc909eSSimon Trimmer 1665f6bc909eSSimon Trimmer static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) 1666f6bc909eSSimon Trimmer { 1667f6bc909eSSimon Trimmer struct wmfw_adsp1_id_hdr adsp1_id; 1668f6bc909eSSimon Trimmer struct wmfw_adsp1_alg_hdr *adsp1_alg; 1669f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1670f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1671f6bc909eSSimon Trimmer unsigned int pos, len; 1672f6bc909eSSimon Trimmer size_t n_algs; 1673f6bc909eSSimon Trimmer int i, ret; 1674f6bc909eSSimon Trimmer 1675f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP1_DM); 1676f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1677f6bc909eSSimon Trimmer return -EINVAL; 1678f6bc909eSSimon Trimmer 1679f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1680f6bc909eSSimon Trimmer sizeof(adsp1_id)); 1681f6bc909eSSimon Trimmer if (ret != 0) { 1682f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1683f6bc909eSSimon Trimmer ret); 1684f6bc909eSSimon Trimmer return ret; 1685f6bc909eSSimon Trimmer } 1686f6bc909eSSimon Trimmer 1687f6bc909eSSimon Trimmer n_algs = be32_to_cpu(adsp1_id.n_algs); 1688f6bc909eSSimon Trimmer 1689f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp1_id.fw, n_algs); 1690f6bc909eSSimon Trimmer 1691f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 16922925748eSCharles Keepax adsp1_id.fw.id, adsp1_id.fw.ver, 16932925748eSCharles Keepax adsp1_id.zm); 1694f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1695f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1696f6bc909eSSimon Trimmer 1697f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 16982925748eSCharles Keepax adsp1_id.fw.id, adsp1_id.fw.ver, 16992925748eSCharles Keepax adsp1_id.dm); 1700f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1701f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1702f6bc909eSSimon Trimmer 1703f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 1704f6bc909eSSimon Trimmer pos = sizeof(adsp1_id) / sizeof(u32); 1705f6bc909eSSimon Trimmer len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 1706f6bc909eSSimon Trimmer 1707f6bc909eSSimon Trimmer adsp1_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 1708f6bc909eSSimon Trimmer if (IS_ERR(adsp1_alg)) 1709f6bc909eSSimon Trimmer return PTR_ERR(adsp1_alg); 1710f6bc909eSSimon Trimmer 1711f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 1712f6bc909eSSimon Trimmer cs_dsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1713f6bc909eSSimon Trimmer i, be32_to_cpu(adsp1_alg[i].alg.id), 1714f6bc909eSSimon Trimmer (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1715f6bc909eSSimon Trimmer (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1716f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1717f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].dm), 1718f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].zm)); 1719f6bc909eSSimon Trimmer 1720f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, 1721f6bc909eSSimon Trimmer adsp1_alg[i].alg.id, 17222925748eSCharles Keepax adsp1_alg[i].alg.ver, 1723f6bc909eSSimon Trimmer adsp1_alg[i].dm); 1724f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1725f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1726f6bc909eSSimon Trimmer goto out; 1727f6bc909eSSimon Trimmer } 1728f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1729f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1730f6bc909eSSimon Trimmer len = be32_to_cpu(adsp1_alg[i + 1].dm); 1731f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp1_alg[i].dm); 1732f6bc909eSSimon Trimmer len *= 4; 1733f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1734f6bc909eSSimon Trimmer len, NULL, 0, 0, 1735f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1736f6bc909eSSimon Trimmer } else { 1737f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region DM with ID %x\n", 1738f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.id)); 1739f6bc909eSSimon Trimmer } 1740f6bc909eSSimon Trimmer } 1741f6bc909eSSimon Trimmer 1742f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, 1743f6bc909eSSimon Trimmer adsp1_alg[i].alg.id, 17442925748eSCharles Keepax adsp1_alg[i].alg.ver, 1745f6bc909eSSimon Trimmer adsp1_alg[i].zm); 1746f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1747f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1748f6bc909eSSimon Trimmer goto out; 1749f6bc909eSSimon Trimmer } 1750f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1751f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1752f6bc909eSSimon Trimmer len = be32_to_cpu(adsp1_alg[i + 1].zm); 1753f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp1_alg[i].zm); 1754f6bc909eSSimon Trimmer len *= 4; 1755f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1756f6bc909eSSimon Trimmer len, NULL, 0, 0, 1757f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1758f6bc909eSSimon Trimmer } else { 1759f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 1760f6bc909eSSimon Trimmer be32_to_cpu(adsp1_alg[i].alg.id)); 1761f6bc909eSSimon Trimmer } 1762f6bc909eSSimon Trimmer } 1763f6bc909eSSimon Trimmer } 1764f6bc909eSSimon Trimmer 1765f6bc909eSSimon Trimmer out: 1766f6bc909eSSimon Trimmer kfree(adsp1_alg); 1767f6bc909eSSimon Trimmer return ret; 1768f6bc909eSSimon Trimmer } 1769f6bc909eSSimon Trimmer 1770f6bc909eSSimon Trimmer static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) 1771f6bc909eSSimon Trimmer { 1772f6bc909eSSimon Trimmer struct wmfw_adsp2_id_hdr adsp2_id; 1773f6bc909eSSimon Trimmer struct wmfw_adsp2_alg_hdr *adsp2_alg; 1774f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1775f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1776f6bc909eSSimon Trimmer unsigned int pos, len; 1777f6bc909eSSimon Trimmer size_t n_algs; 1778f6bc909eSSimon Trimmer int i, ret; 1779f6bc909eSSimon Trimmer 1780f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 1781f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1782f6bc909eSSimon Trimmer return -EINVAL; 1783f6bc909eSSimon Trimmer 1784f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1785f6bc909eSSimon Trimmer sizeof(adsp2_id)); 1786f6bc909eSSimon Trimmer if (ret != 0) { 1787f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1788f6bc909eSSimon Trimmer ret); 1789f6bc909eSSimon Trimmer return ret; 1790f6bc909eSSimon Trimmer } 1791f6bc909eSSimon Trimmer 1792f6bc909eSSimon Trimmer n_algs = be32_to_cpu(adsp2_id.n_algs); 1793f6bc909eSSimon Trimmer 1794f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_id_header(dsp, &adsp2_id.fw, n_algs); 1795f6bc909eSSimon Trimmer 1796f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 17972925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 17982925748eSCharles Keepax adsp2_id.xm); 1799f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1800f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1801f6bc909eSSimon Trimmer 1802f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 18032925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 18042925748eSCharles Keepax adsp2_id.ym); 1805f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1806f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1807f6bc909eSSimon Trimmer 1808f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 18092925748eSCharles Keepax adsp2_id.fw.id, adsp2_id.fw.ver, 18102925748eSCharles Keepax adsp2_id.zm); 1811f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) 1812f6bc909eSSimon Trimmer return PTR_ERR(alg_region); 1813f6bc909eSSimon Trimmer 1814f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 1815f6bc909eSSimon Trimmer pos = sizeof(adsp2_id) / sizeof(u32); 1816f6bc909eSSimon Trimmer len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 1817f6bc909eSSimon Trimmer 1818f6bc909eSSimon Trimmer adsp2_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 1819f6bc909eSSimon Trimmer if (IS_ERR(adsp2_alg)) 1820f6bc909eSSimon Trimmer return PTR_ERR(adsp2_alg); 1821f6bc909eSSimon Trimmer 1822f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 1823f6bc909eSSimon Trimmer cs_dsp_info(dsp, 1824f6bc909eSSimon Trimmer "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1825f6bc909eSSimon Trimmer i, be32_to_cpu(adsp2_alg[i].alg.id), 1826f6bc909eSSimon Trimmer (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1827f6bc909eSSimon Trimmer (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1828f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1829f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].xm), 1830f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].ym), 1831f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].zm)); 1832f6bc909eSSimon Trimmer 1833f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, 1834f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 18352925748eSCharles Keepax adsp2_alg[i].alg.ver, 1836f6bc909eSSimon Trimmer adsp2_alg[i].xm); 1837f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1838f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1839f6bc909eSSimon Trimmer goto out; 1840f6bc909eSSimon Trimmer } 1841f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1842f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1843f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].xm); 1844f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].xm); 1845f6bc909eSSimon Trimmer len *= 4; 1846f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1847f6bc909eSSimon Trimmer len, NULL, 0, 0, 1848f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1849f6bc909eSSimon Trimmer } else { 1850f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region XM with ID %x\n", 1851f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 1852f6bc909eSSimon Trimmer } 1853f6bc909eSSimon Trimmer } 1854f6bc909eSSimon Trimmer 1855f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, 1856f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 18572925748eSCharles Keepax adsp2_alg[i].alg.ver, 1858f6bc909eSSimon Trimmer adsp2_alg[i].ym); 1859f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1860f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1861f6bc909eSSimon Trimmer goto out; 1862f6bc909eSSimon Trimmer } 1863f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1864f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1865f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].ym); 1866f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].ym); 1867f6bc909eSSimon Trimmer len *= 4; 1868f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1869f6bc909eSSimon Trimmer len, NULL, 0, 0, 1870f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1871f6bc909eSSimon Trimmer } else { 1872f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region YM with ID %x\n", 1873f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 1874f6bc909eSSimon Trimmer } 1875f6bc909eSSimon Trimmer } 1876f6bc909eSSimon Trimmer 1877f6bc909eSSimon Trimmer alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, 1878f6bc909eSSimon Trimmer adsp2_alg[i].alg.id, 18792925748eSCharles Keepax adsp2_alg[i].alg.ver, 1880f6bc909eSSimon Trimmer adsp2_alg[i].zm); 1881f6bc909eSSimon Trimmer if (IS_ERR(alg_region)) { 1882f6bc909eSSimon Trimmer ret = PTR_ERR(alg_region); 1883f6bc909eSSimon Trimmer goto out; 1884f6bc909eSSimon Trimmer } 1885f6bc909eSSimon Trimmer if (dsp->fw_ver == 0) { 1886f6bc909eSSimon Trimmer if (i + 1 < n_algs) { 1887f6bc909eSSimon Trimmer len = be32_to_cpu(adsp2_alg[i + 1].zm); 1888f6bc909eSSimon Trimmer len -= be32_to_cpu(adsp2_alg[i].zm); 1889f6bc909eSSimon Trimmer len *= 4; 1890f6bc909eSSimon Trimmer cs_dsp_create_control(dsp, alg_region, 0, 1891f6bc909eSSimon Trimmer len, NULL, 0, 0, 1892f6bc909eSSimon Trimmer WMFW_CTL_TYPE_BYTES); 1893f6bc909eSSimon Trimmer } else { 1894f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 1895f6bc909eSSimon Trimmer be32_to_cpu(adsp2_alg[i].alg.id)); 1896f6bc909eSSimon Trimmer } 1897f6bc909eSSimon Trimmer } 1898f6bc909eSSimon Trimmer } 1899f6bc909eSSimon Trimmer 1900f6bc909eSSimon Trimmer out: 1901f6bc909eSSimon Trimmer kfree(adsp2_alg); 1902f6bc909eSSimon Trimmer return ret; 1903f6bc909eSSimon Trimmer } 1904f6bc909eSSimon Trimmer 19052925748eSCharles Keepax static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, 1906f6bc909eSSimon Trimmer __be32 xm_base, __be32 ym_base) 1907f6bc909eSSimon Trimmer { 1908f6bc909eSSimon Trimmer static const int types[] = { 1909f6bc909eSSimon Trimmer WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, 1910f6bc909eSSimon Trimmer WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED 1911f6bc909eSSimon Trimmer }; 1912f6bc909eSSimon Trimmer __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; 1913f6bc909eSSimon Trimmer 19142925748eSCharles Keepax return cs_dsp_create_regions(dsp, id, ver, ARRAY_SIZE(types), types, bases); 1915f6bc909eSSimon Trimmer } 1916f6bc909eSSimon Trimmer 1917f6bc909eSSimon Trimmer static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) 1918f6bc909eSSimon Trimmer { 1919f6bc909eSSimon Trimmer struct wmfw_halo_id_hdr halo_id; 1920f6bc909eSSimon Trimmer struct wmfw_halo_alg_hdr *halo_alg; 1921f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1922f6bc909eSSimon Trimmer unsigned int pos, len; 1923f6bc909eSSimon Trimmer size_t n_algs; 1924f6bc909eSSimon Trimmer int i, ret; 1925f6bc909eSSimon Trimmer 1926f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); 1927f6bc909eSSimon Trimmer if (WARN_ON(!mem)) 1928f6bc909eSSimon Trimmer return -EINVAL; 1929f6bc909eSSimon Trimmer 1930f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, 1931f6bc909eSSimon Trimmer sizeof(halo_id)); 1932f6bc909eSSimon Trimmer if (ret != 0) { 1933f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read algorithm info: %d\n", 1934f6bc909eSSimon Trimmer ret); 1935f6bc909eSSimon Trimmer return ret; 1936f6bc909eSSimon Trimmer } 1937f6bc909eSSimon Trimmer 1938f6bc909eSSimon Trimmer n_algs = be32_to_cpu(halo_id.n_algs); 1939f6bc909eSSimon Trimmer 1940f6bc909eSSimon Trimmer cs_dsp_parse_wmfw_v3_id_header(dsp, &halo_id.fw, n_algs); 1941f6bc909eSSimon Trimmer 19422925748eSCharles Keepax ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, halo_id.fw.ver, 1943f6bc909eSSimon Trimmer halo_id.xm_base, halo_id.ym_base); 1944f6bc909eSSimon Trimmer if (ret) 1945f6bc909eSSimon Trimmer return ret; 1946f6bc909eSSimon Trimmer 1947f6bc909eSSimon Trimmer /* Calculate offset and length in DSP words */ 1948f6bc909eSSimon Trimmer pos = sizeof(halo_id) / sizeof(u32); 1949f6bc909eSSimon Trimmer len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); 1950f6bc909eSSimon Trimmer 1951f6bc909eSSimon Trimmer halo_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); 1952f6bc909eSSimon Trimmer if (IS_ERR(halo_alg)) 1953f6bc909eSSimon Trimmer return PTR_ERR(halo_alg); 1954f6bc909eSSimon Trimmer 1955f6bc909eSSimon Trimmer for (i = 0; i < n_algs; i++) { 1956f6bc909eSSimon Trimmer cs_dsp_info(dsp, 1957f6bc909eSSimon Trimmer "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", 1958f6bc909eSSimon Trimmer i, be32_to_cpu(halo_alg[i].alg.id), 1959f6bc909eSSimon Trimmer (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, 1960f6bc909eSSimon Trimmer (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, 1961f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].alg.ver) & 0xff, 1962f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].xm_base), 1963f6bc909eSSimon Trimmer be32_to_cpu(halo_alg[i].ym_base)); 1964f6bc909eSSimon Trimmer 1965f6bc909eSSimon Trimmer ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id, 19662925748eSCharles Keepax halo_alg[i].alg.ver, 1967f6bc909eSSimon Trimmer halo_alg[i].xm_base, 1968f6bc909eSSimon Trimmer halo_alg[i].ym_base); 1969f6bc909eSSimon Trimmer if (ret) 1970f6bc909eSSimon Trimmer goto out; 1971f6bc909eSSimon Trimmer } 1972f6bc909eSSimon Trimmer 1973f6bc909eSSimon Trimmer out: 1974f6bc909eSSimon Trimmer kfree(halo_alg); 1975f6bc909eSSimon Trimmer return ret; 1976f6bc909eSSimon Trimmer } 1977f6bc909eSSimon Trimmer 1978f6bc909eSSimon Trimmer static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware, 1979f6bc909eSSimon Trimmer const char *file) 1980f6bc909eSSimon Trimmer { 1981f6bc909eSSimon Trimmer LIST_HEAD(buf_list); 1982f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 1983f6bc909eSSimon Trimmer struct wmfw_coeff_hdr *hdr; 1984f6bc909eSSimon Trimmer struct wmfw_coeff_item *blk; 1985f6bc909eSSimon Trimmer const struct cs_dsp_region *mem; 1986f6bc909eSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1987f6bc909eSSimon Trimmer const char *region_name; 19882925748eSCharles Keepax int ret, pos, blocks, type, offset, reg, version; 198940a34ae7SCharles Keepax char *text = NULL; 1990f6bc909eSSimon Trimmer struct cs_dsp_buf *buf; 1991f6bc909eSSimon Trimmer 1992f6bc909eSSimon Trimmer if (!firmware) 1993f6bc909eSSimon Trimmer return 0; 1994f6bc909eSSimon Trimmer 1995f6bc909eSSimon Trimmer ret = -EINVAL; 1996f6bc909eSSimon Trimmer 1997f6bc909eSSimon Trimmer if (sizeof(*hdr) >= firmware->size) { 1998f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: coefficient file too short, %zu bytes\n", 1999f6bc909eSSimon Trimmer file, firmware->size); 2000f6bc909eSSimon Trimmer goto out_fw; 2001f6bc909eSSimon Trimmer } 2002f6bc909eSSimon Trimmer 2003f6bc909eSSimon Trimmer hdr = (void *)&firmware->data[0]; 2004f6bc909eSSimon Trimmer if (memcmp(hdr->magic, "WMDR", 4) != 0) { 2005f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: invalid coefficient magic\n", file); 2006f6bc909eSSimon Trimmer goto out_fw; 2007f6bc909eSSimon Trimmer } 2008f6bc909eSSimon Trimmer 2009f6bc909eSSimon Trimmer switch (be32_to_cpu(hdr->rev) & 0xff) { 2010f6bc909eSSimon Trimmer case 1: 2011dcee7676SCharles Keepax case 2: 2012f6bc909eSSimon Trimmer break; 2013f6bc909eSSimon Trimmer default: 2014f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2015f6bc909eSSimon Trimmer file, be32_to_cpu(hdr->rev) & 0xff); 2016f6bc909eSSimon Trimmer ret = -EINVAL; 2017f6bc909eSSimon Trimmer goto out_fw; 2018f6bc909eSSimon Trimmer } 2019f6bc909eSSimon Trimmer 2020f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 2021f6bc909eSSimon Trimmer (le32_to_cpu(hdr->ver) >> 16) & 0xff, 2022f6bc909eSSimon Trimmer (le32_to_cpu(hdr->ver) >> 8) & 0xff, 2023f6bc909eSSimon Trimmer le32_to_cpu(hdr->ver) & 0xff); 2024f6bc909eSSimon Trimmer 2025f6bc909eSSimon Trimmer pos = le32_to_cpu(hdr->len); 2026f6bc909eSSimon Trimmer 2027f6bc909eSSimon Trimmer blocks = 0; 2028f6bc909eSSimon Trimmer while (pos < firmware->size && 2029f6bc909eSSimon Trimmer sizeof(*blk) < firmware->size - pos) { 2030f6bc909eSSimon Trimmer blk = (void *)(&firmware->data[pos]); 2031f6bc909eSSimon Trimmer 2032f6bc909eSSimon Trimmer type = le16_to_cpu(blk->type); 2033f6bc909eSSimon Trimmer offset = le16_to_cpu(blk->offset); 20342925748eSCharles Keepax version = le32_to_cpu(blk->ver) >> 8; 2035f6bc909eSSimon Trimmer 2036f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 2037f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->id), 2038f6bc909eSSimon Trimmer (le32_to_cpu(blk->ver) >> 16) & 0xff, 2039f6bc909eSSimon Trimmer (le32_to_cpu(blk->ver) >> 8) & 0xff, 2040f6bc909eSSimon Trimmer le32_to_cpu(blk->ver) & 0xff); 2041f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 2042f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), offset, type); 2043f6bc909eSSimon Trimmer 2044f6bc909eSSimon Trimmer reg = 0; 2045f6bc909eSSimon Trimmer region_name = "Unknown"; 2046f6bc909eSSimon Trimmer switch (type) { 2047f6bc909eSSimon Trimmer case (WMFW_NAME_TEXT << 8): 204840a34ae7SCharles Keepax text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL); 204940a34ae7SCharles Keepax break; 2050f6bc909eSSimon Trimmer case (WMFW_INFO_TEXT << 8): 2051f6bc909eSSimon Trimmer case (WMFW_METADATA << 8): 2052f6bc909eSSimon Trimmer break; 2053f6bc909eSSimon Trimmer case (WMFW_ABSOLUTE << 8): 2054f6bc909eSSimon Trimmer /* 2055f6bc909eSSimon Trimmer * Old files may use this for global 2056f6bc909eSSimon Trimmer * coefficients. 2057f6bc909eSSimon Trimmer */ 2058f6bc909eSSimon Trimmer if (le32_to_cpu(blk->id) == dsp->fw_id && 2059f6bc909eSSimon Trimmer offset == 0) { 2060f6bc909eSSimon Trimmer region_name = "global coefficients"; 2061f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2062f6bc909eSSimon Trimmer if (!mem) { 2063f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No ZM\n"); 2064f6bc909eSSimon Trimmer break; 2065f6bc909eSSimon Trimmer } 2066f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, 0); 2067f6bc909eSSimon Trimmer 2068f6bc909eSSimon Trimmer } else { 2069f6bc909eSSimon Trimmer region_name = "register"; 2070f6bc909eSSimon Trimmer reg = offset; 2071f6bc909eSSimon Trimmer } 2072f6bc909eSSimon Trimmer break; 2073f6bc909eSSimon Trimmer 2074f6bc909eSSimon Trimmer case WMFW_ADSP1_DM: 2075f6bc909eSSimon Trimmer case WMFW_ADSP1_ZM: 2076f6bc909eSSimon Trimmer case WMFW_ADSP2_XM: 2077f6bc909eSSimon Trimmer case WMFW_ADSP2_YM: 2078f6bc909eSSimon Trimmer case WMFW_HALO_XM_PACKED: 2079f6bc909eSSimon Trimmer case WMFW_HALO_YM_PACKED: 2080f6bc909eSSimon Trimmer case WMFW_HALO_PM_PACKED: 2081f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2082f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), 2083f6bc909eSSimon Trimmer type, le32_to_cpu(blk->id)); 2084f6bc909eSSimon Trimmer 2085f6bc909eSSimon Trimmer mem = cs_dsp_find_region(dsp, type); 2086f6bc909eSSimon Trimmer if (!mem) { 2087f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No base for region %x\n", type); 2088f6bc909eSSimon Trimmer break; 2089f6bc909eSSimon Trimmer } 2090f6bc909eSSimon Trimmer 2091f6bc909eSSimon Trimmer alg_region = cs_dsp_find_alg_region(dsp, type, 2092f6bc909eSSimon Trimmer le32_to_cpu(blk->id)); 2093f6bc909eSSimon Trimmer if (alg_region) { 20942925748eSCharles Keepax if (version != alg_region->ver) 20952925748eSCharles Keepax cs_dsp_warn(dsp, 20962925748eSCharles Keepax "Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n", 20972925748eSCharles Keepax (version >> 16) & 0xFF, 20982925748eSCharles Keepax (version >> 8) & 0xFF, 20992925748eSCharles Keepax version & 0xFF, 21002925748eSCharles Keepax (alg_region->ver >> 16) & 0xFF, 21012925748eSCharles Keepax (alg_region->ver >> 8) & 0xFF, 21022925748eSCharles Keepax alg_region->ver & 0xFF); 21032925748eSCharles Keepax 2104f6bc909eSSimon Trimmer reg = alg_region->base; 2105f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, reg); 2106f6bc909eSSimon Trimmer reg += offset; 2107f6bc909eSSimon Trimmer } else { 2108f6bc909eSSimon Trimmer cs_dsp_err(dsp, "No %x for algorithm %x\n", 2109f6bc909eSSimon Trimmer type, le32_to_cpu(blk->id)); 2110f6bc909eSSimon Trimmer } 2111f6bc909eSSimon Trimmer break; 2112f6bc909eSSimon Trimmer 2113f6bc909eSSimon Trimmer default: 2114f6bc909eSSimon Trimmer cs_dsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 2115f6bc909eSSimon Trimmer file, blocks, type, pos); 2116f6bc909eSSimon Trimmer break; 2117f6bc909eSSimon Trimmer } 2118f6bc909eSSimon Trimmer 211940a34ae7SCharles Keepax if (text) { 212040a34ae7SCharles Keepax memcpy(text, blk->data, le32_to_cpu(blk->len)); 212140a34ae7SCharles Keepax cs_dsp_info(dsp, "%s: %s\n", dsp->fw_name, text); 212240a34ae7SCharles Keepax kfree(text); 212340a34ae7SCharles Keepax text = NULL; 212440a34ae7SCharles Keepax } 212540a34ae7SCharles Keepax 2126f6bc909eSSimon Trimmer if (reg) { 2127f6bc909eSSimon Trimmer if (le32_to_cpu(blk->len) > 2128f6bc909eSSimon Trimmer firmware->size - pos - sizeof(*blk)) { 2129f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2130f6bc909eSSimon Trimmer "%s.%d: %s region len %d bytes exceeds file length %zu\n", 2131f6bc909eSSimon Trimmer file, blocks, region_name, 2132f6bc909eSSimon Trimmer le32_to_cpu(blk->len), 2133f6bc909eSSimon Trimmer firmware->size); 2134f6bc909eSSimon Trimmer ret = -EINVAL; 2135f6bc909eSSimon Trimmer goto out_fw; 2136f6bc909eSSimon Trimmer } 2137f6bc909eSSimon Trimmer 2138f6bc909eSSimon Trimmer buf = cs_dsp_buf_alloc(blk->data, 2139f6bc909eSSimon Trimmer le32_to_cpu(blk->len), 2140f6bc909eSSimon Trimmer &buf_list); 2141f6bc909eSSimon Trimmer if (!buf) { 2142f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Out of memory\n"); 2143f6bc909eSSimon Trimmer ret = -ENOMEM; 2144f6bc909eSSimon Trimmer goto out_fw; 2145f6bc909eSSimon Trimmer } 2146f6bc909eSSimon Trimmer 2147f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 2148f6bc909eSSimon Trimmer file, blocks, le32_to_cpu(blk->len), 2149f6bc909eSSimon Trimmer reg); 2150f6bc909eSSimon Trimmer ret = regmap_raw_write_async(regmap, reg, buf->buf, 2151f6bc909eSSimon Trimmer le32_to_cpu(blk->len)); 2152f6bc909eSSimon Trimmer if (ret != 0) { 2153f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2154f6bc909eSSimon Trimmer "%s.%d: Failed to write to %x in %s: %d\n", 2155f6bc909eSSimon Trimmer file, blocks, reg, region_name, ret); 2156f6bc909eSSimon Trimmer } 2157f6bc909eSSimon Trimmer } 2158f6bc909eSSimon Trimmer 2159f6bc909eSSimon Trimmer pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 2160f6bc909eSSimon Trimmer blocks++; 2161f6bc909eSSimon Trimmer } 2162f6bc909eSSimon Trimmer 2163f6bc909eSSimon Trimmer ret = regmap_async_complete(regmap); 2164f6bc909eSSimon Trimmer if (ret != 0) 2165f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret); 2166f6bc909eSSimon Trimmer 2167f6bc909eSSimon Trimmer if (pos > firmware->size) 2168f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 2169f6bc909eSSimon Trimmer file, blocks, pos - firmware->size); 2170f6bc909eSSimon Trimmer 2171f6bc909eSSimon Trimmer cs_dsp_debugfs_save_binname(dsp, file); 2172f6bc909eSSimon Trimmer 2173f6bc909eSSimon Trimmer out_fw: 2174f6bc909eSSimon Trimmer regmap_async_complete(regmap); 2175f6bc909eSSimon Trimmer cs_dsp_buf_free(&buf_list); 217640a34ae7SCharles Keepax kfree(text); 2177f6bc909eSSimon Trimmer return ret; 2178f6bc909eSSimon Trimmer } 2179f6bc909eSSimon Trimmer 2180f6bc909eSSimon Trimmer static int cs_dsp_create_name(struct cs_dsp *dsp) 2181f6bc909eSSimon Trimmer { 2182f6bc909eSSimon Trimmer if (!dsp->name) { 2183f6bc909eSSimon Trimmer dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2184f6bc909eSSimon Trimmer dsp->num); 2185f6bc909eSSimon Trimmer if (!dsp->name) 2186f6bc909eSSimon Trimmer return -ENOMEM; 2187f6bc909eSSimon Trimmer } 2188f6bc909eSSimon Trimmer 2189f6bc909eSSimon Trimmer return 0; 2190f6bc909eSSimon Trimmer } 2191f6bc909eSSimon Trimmer 2192f6bc909eSSimon Trimmer static int cs_dsp_common_init(struct cs_dsp *dsp) 2193f6bc909eSSimon Trimmer { 2194f6bc909eSSimon Trimmer int ret; 2195f6bc909eSSimon Trimmer 2196f6bc909eSSimon Trimmer ret = cs_dsp_create_name(dsp); 2197f6bc909eSSimon Trimmer if (ret) 2198f6bc909eSSimon Trimmer return ret; 2199f6bc909eSSimon Trimmer 2200f6bc909eSSimon Trimmer INIT_LIST_HEAD(&dsp->alg_regions); 2201f6bc909eSSimon Trimmer INIT_LIST_HEAD(&dsp->ctl_list); 2202f6bc909eSSimon Trimmer 2203f6bc909eSSimon Trimmer mutex_init(&dsp->pwr_lock); 2204f6bc909eSSimon Trimmer 2205f6bc909eSSimon Trimmer return 0; 2206f6bc909eSSimon Trimmer } 2207f6bc909eSSimon Trimmer 2208f6bc909eSSimon Trimmer /** 2209f6bc909eSSimon Trimmer * cs_dsp_adsp1_init() - Initialise a cs_dsp structure representing a ADSP1 device 2210f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2211f6bc909eSSimon Trimmer * 2212f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2213f6bc909eSSimon Trimmer */ 2214f6bc909eSSimon Trimmer int cs_dsp_adsp1_init(struct cs_dsp *dsp) 2215f6bc909eSSimon Trimmer { 2216f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp1_ops; 2217f6bc909eSSimon Trimmer 2218f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 2219f6bc909eSSimon Trimmer } 2220f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp1_init); 2221f6bc909eSSimon Trimmer 2222f6bc909eSSimon Trimmer /** 2223f6bc909eSSimon Trimmer * cs_dsp_adsp1_power_up() - Load and start the named firmware 2224f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2225f6bc909eSSimon Trimmer * @wmfw_firmware: the firmware to be sent 2226f6bc909eSSimon Trimmer * @wmfw_filename: file name of firmware to be sent 2227f6bc909eSSimon Trimmer * @coeff_firmware: the coefficient data to be sent 2228f6bc909eSSimon Trimmer * @coeff_filename: file name of coefficient to data be sent 2229f6bc909eSSimon Trimmer * @fw_name: the user-friendly firmware name 2230f6bc909eSSimon Trimmer * 2231f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2232f6bc909eSSimon Trimmer */ 2233f6bc909eSSimon Trimmer int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, 2234f6bc909eSSimon Trimmer const struct firmware *wmfw_firmware, char *wmfw_filename, 2235f6bc909eSSimon Trimmer const struct firmware *coeff_firmware, char *coeff_filename, 2236f6bc909eSSimon Trimmer const char *fw_name) 2237f6bc909eSSimon Trimmer { 2238f6bc909eSSimon Trimmer unsigned int val; 2239f6bc909eSSimon Trimmer int ret; 2240f6bc909eSSimon Trimmer 2241f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2242f6bc909eSSimon Trimmer 2243f6bc909eSSimon Trimmer dsp->fw_name = fw_name; 2244f6bc909eSSimon Trimmer 2245f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2246f6bc909eSSimon Trimmer ADSP1_SYS_ENA, ADSP1_SYS_ENA); 2247f6bc909eSSimon Trimmer 2248f6bc909eSSimon Trimmer /* 2249f6bc909eSSimon Trimmer * For simplicity set the DSP clock rate to be the 2250f6bc909eSSimon Trimmer * SYSCLK rate rather than making it configurable. 2251f6bc909eSSimon Trimmer */ 2252f6bc909eSSimon Trimmer if (dsp->sysclk_reg) { 2253f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 2254f6bc909eSSimon Trimmer if (ret != 0) { 2255f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 2256f6bc909eSSimon Trimmer goto err_mutex; 2257f6bc909eSSimon Trimmer } 2258f6bc909eSSimon Trimmer 2259f6bc909eSSimon Trimmer val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 2260f6bc909eSSimon Trimmer 2261f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, 2262f6bc909eSSimon Trimmer dsp->base + ADSP1_CONTROL_31, 2263f6bc909eSSimon Trimmer ADSP1_CLK_SEL_MASK, val); 2264f6bc909eSSimon Trimmer if (ret != 0) { 2265f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2266f6bc909eSSimon Trimmer goto err_mutex; 2267f6bc909eSSimon Trimmer } 2268f6bc909eSSimon Trimmer } 2269f6bc909eSSimon Trimmer 2270f6bc909eSSimon Trimmer ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename); 2271f6bc909eSSimon Trimmer if (ret != 0) 2272f6bc909eSSimon Trimmer goto err_ena; 2273f6bc909eSSimon Trimmer 2274f6bc909eSSimon Trimmer ret = cs_dsp_adsp1_setup_algs(dsp); 2275f6bc909eSSimon Trimmer if (ret != 0) 2276f6bc909eSSimon Trimmer goto err_ena; 2277f6bc909eSSimon Trimmer 2278f6bc909eSSimon Trimmer ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename); 2279f6bc909eSSimon Trimmer if (ret != 0) 2280f6bc909eSSimon Trimmer goto err_ena; 2281f6bc909eSSimon Trimmer 2282f6bc909eSSimon Trimmer /* Initialize caches for enabled and unset controls */ 2283f6bc909eSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 2284f6bc909eSSimon Trimmer if (ret != 0) 2285f6bc909eSSimon Trimmer goto err_ena; 2286f6bc909eSSimon Trimmer 2287f6bc909eSSimon Trimmer /* Sync set controls */ 2288f6bc909eSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 2289f6bc909eSSimon Trimmer if (ret != 0) 2290f6bc909eSSimon Trimmer goto err_ena; 2291f6bc909eSSimon Trimmer 2292f6bc909eSSimon Trimmer dsp->booted = true; 2293f6bc909eSSimon Trimmer 2294f6bc909eSSimon Trimmer /* Start the core running */ 2295f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2296f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START, 2297f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START); 2298f6bc909eSSimon Trimmer 2299f6bc909eSSimon Trimmer dsp->running = true; 2300f6bc909eSSimon Trimmer 2301f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2302f6bc909eSSimon Trimmer 2303f6bc909eSSimon Trimmer return 0; 2304f6bc909eSSimon Trimmer 2305f6bc909eSSimon Trimmer err_ena: 2306f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2307f6bc909eSSimon Trimmer ADSP1_SYS_ENA, 0); 2308f6bc909eSSimon Trimmer err_mutex: 2309f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2310f6bc909eSSimon Trimmer return ret; 2311f6bc909eSSimon Trimmer } 2312f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp1_power_up); 2313f6bc909eSSimon Trimmer 2314f6bc909eSSimon Trimmer /** 2315f6bc909eSSimon Trimmer * cs_dsp_adsp1_power_down() - Halts the DSP 2316f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2317f6bc909eSSimon Trimmer */ 2318f6bc909eSSimon Trimmer void cs_dsp_adsp1_power_down(struct cs_dsp *dsp) 2319f6bc909eSSimon Trimmer { 2320f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2321f6bc909eSSimon Trimmer 2322f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2323f6bc909eSSimon Trimmer 2324f6bc909eSSimon Trimmer dsp->running = false; 2325f6bc909eSSimon Trimmer dsp->booted = false; 2326f6bc909eSSimon Trimmer 2327f6bc909eSSimon Trimmer /* Halt the core */ 2328f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2329f6bc909eSSimon Trimmer ADSP1_CORE_ENA | ADSP1_START, 0); 2330f6bc909eSSimon Trimmer 2331f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 2332f6bc909eSSimon Trimmer ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 2333f6bc909eSSimon Trimmer 2334f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 2335f6bc909eSSimon Trimmer ADSP1_SYS_ENA, 0); 2336f6bc909eSSimon Trimmer 2337f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) 2338f6bc909eSSimon Trimmer ctl->enabled = 0; 2339f6bc909eSSimon Trimmer 2340f6bc909eSSimon Trimmer cs_dsp_free_alg_regions(dsp); 2341f6bc909eSSimon Trimmer 2342f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2343f6bc909eSSimon Trimmer } 2344f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp1_power_down); 2345f6bc909eSSimon Trimmer 2346f6bc909eSSimon Trimmer static int cs_dsp_adsp2v2_enable_core(struct cs_dsp *dsp) 2347f6bc909eSSimon Trimmer { 2348f6bc909eSSimon Trimmer unsigned int val; 2349f6bc909eSSimon Trimmer int ret, count; 2350f6bc909eSSimon Trimmer 2351f6bc909eSSimon Trimmer /* Wait for the RAM to start, should be near instantaneous */ 2352f6bc909eSSimon Trimmer for (count = 0; count < 10; ++count) { 2353f6bc909eSSimon Trimmer ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 2354f6bc909eSSimon Trimmer if (ret != 0) 2355f6bc909eSSimon Trimmer return ret; 2356f6bc909eSSimon Trimmer 2357f6bc909eSSimon Trimmer if (val & ADSP2_RAM_RDY) 2358f6bc909eSSimon Trimmer break; 2359f6bc909eSSimon Trimmer 2360f6bc909eSSimon Trimmer usleep_range(250, 500); 2361f6bc909eSSimon Trimmer } 2362f6bc909eSSimon Trimmer 2363f6bc909eSSimon Trimmer if (!(val & ADSP2_RAM_RDY)) { 2364f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to start DSP RAM\n"); 2365f6bc909eSSimon Trimmer return -EBUSY; 2366f6bc909eSSimon Trimmer } 2367f6bc909eSSimon Trimmer 2368f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "RAM ready after %d polls\n", count); 2369f6bc909eSSimon Trimmer 2370f6bc909eSSimon Trimmer return 0; 2371f6bc909eSSimon Trimmer } 2372f6bc909eSSimon Trimmer 2373f6bc909eSSimon Trimmer static int cs_dsp_adsp2_enable_core(struct cs_dsp *dsp) 2374f6bc909eSSimon Trimmer { 2375f6bc909eSSimon Trimmer int ret; 2376f6bc909eSSimon Trimmer 2377f6bc909eSSimon Trimmer ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 2378f6bc909eSSimon Trimmer ADSP2_SYS_ENA, ADSP2_SYS_ENA); 2379f6bc909eSSimon Trimmer if (ret != 0) 2380f6bc909eSSimon Trimmer return ret; 2381f6bc909eSSimon Trimmer 2382f6bc909eSSimon Trimmer return cs_dsp_adsp2v2_enable_core(dsp); 2383f6bc909eSSimon Trimmer } 2384f6bc909eSSimon Trimmer 2385f6bc909eSSimon Trimmer static int cs_dsp_adsp2_lock(struct cs_dsp *dsp, unsigned int lock_regions) 2386f6bc909eSSimon Trimmer { 2387f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 2388f6bc909eSSimon Trimmer unsigned int code0, code1, lock_reg; 2389f6bc909eSSimon Trimmer 2390f6bc909eSSimon Trimmer if (!(lock_regions & CS_ADSP2_REGION_ALL)) 2391f6bc909eSSimon Trimmer return 0; 2392f6bc909eSSimon Trimmer 2393f6bc909eSSimon Trimmer lock_regions &= CS_ADSP2_REGION_ALL; 2394f6bc909eSSimon Trimmer lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 2395f6bc909eSSimon Trimmer 2396f6bc909eSSimon Trimmer while (lock_regions) { 2397f6bc909eSSimon Trimmer code0 = code1 = 0; 2398f6bc909eSSimon Trimmer if (lock_regions & BIT(0)) { 2399f6bc909eSSimon Trimmer code0 = ADSP2_LOCK_CODE_0; 2400f6bc909eSSimon Trimmer code1 = ADSP2_LOCK_CODE_1; 2401f6bc909eSSimon Trimmer } 2402f6bc909eSSimon Trimmer if (lock_regions & BIT(1)) { 2403f6bc909eSSimon Trimmer code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 2404f6bc909eSSimon Trimmer code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 2405f6bc909eSSimon Trimmer } 2406f6bc909eSSimon Trimmer regmap_write(regmap, lock_reg, code0); 2407f6bc909eSSimon Trimmer regmap_write(regmap, lock_reg, code1); 2408f6bc909eSSimon Trimmer lock_regions >>= 2; 2409f6bc909eSSimon Trimmer lock_reg += 2; 2410f6bc909eSSimon Trimmer } 2411f6bc909eSSimon Trimmer 2412f6bc909eSSimon Trimmer return 0; 2413f6bc909eSSimon Trimmer } 2414f6bc909eSSimon Trimmer 2415f6bc909eSSimon Trimmer static int cs_dsp_adsp2_enable_memory(struct cs_dsp *dsp) 2416f6bc909eSSimon Trimmer { 2417f6bc909eSSimon Trimmer return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2418f6bc909eSSimon Trimmer ADSP2_MEM_ENA, ADSP2_MEM_ENA); 2419f6bc909eSSimon Trimmer } 2420f6bc909eSSimon Trimmer 2421f6bc909eSSimon Trimmer static void cs_dsp_adsp2_disable_memory(struct cs_dsp *dsp) 2422f6bc909eSSimon Trimmer { 2423f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2424f6bc909eSSimon Trimmer ADSP2_MEM_ENA, 0); 2425f6bc909eSSimon Trimmer } 2426f6bc909eSSimon Trimmer 2427f6bc909eSSimon Trimmer static void cs_dsp_adsp2_disable_core(struct cs_dsp *dsp) 2428f6bc909eSSimon Trimmer { 2429f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2430f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2431f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 2432f6bc909eSSimon Trimmer 2433f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2434f6bc909eSSimon Trimmer ADSP2_SYS_ENA, 0); 2435f6bc909eSSimon Trimmer } 2436f6bc909eSSimon Trimmer 2437f6bc909eSSimon Trimmer static void cs_dsp_adsp2v2_disable_core(struct cs_dsp *dsp) 2438f6bc909eSSimon Trimmer { 2439f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2440f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2441f6bc909eSSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 2442f6bc909eSSimon Trimmer } 2443f6bc909eSSimon Trimmer 2444f6bc909eSSimon Trimmer static int cs_dsp_halo_configure_mpu(struct cs_dsp *dsp, unsigned int lock_regions) 2445f6bc909eSSimon Trimmer { 2446f6bc909eSSimon Trimmer struct reg_sequence config[] = { 2447f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, 2448f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, 2449f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, 2450f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, 2451f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, 2452f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, 2453f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, 2454f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, 2455f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, 2456f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, 2457f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, 2458f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, 2459f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, 2460f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, 2461f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, 2462f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, 2463f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, 2464f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, 2465f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, 2466f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, 2467f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, 2468f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, 2469f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, 2470f6bc909eSSimon Trimmer }; 2471f6bc909eSSimon Trimmer 2472f6bc909eSSimon Trimmer return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); 2473f6bc909eSSimon Trimmer } 2474f6bc909eSSimon Trimmer 2475f6bc909eSSimon Trimmer /** 2476f6bc909eSSimon Trimmer * cs_dsp_set_dspclk() - Applies the given frequency to the given cs_dsp 2477f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2478f6bc909eSSimon Trimmer * @freq: clock rate to set 2479f6bc909eSSimon Trimmer * 2480f6bc909eSSimon Trimmer * This is only for use on ADSP2 cores. 2481f6bc909eSSimon Trimmer * 2482f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2483f6bc909eSSimon Trimmer */ 2484f6bc909eSSimon Trimmer int cs_dsp_set_dspclk(struct cs_dsp *dsp, unsigned int freq) 2485f6bc909eSSimon Trimmer { 2486f6bc909eSSimon Trimmer int ret; 2487f6bc909eSSimon Trimmer 2488f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 2489f6bc909eSSimon Trimmer ADSP2_CLK_SEL_MASK, 2490f6bc909eSSimon Trimmer freq << ADSP2_CLK_SEL_SHIFT); 2491f6bc909eSSimon Trimmer if (ret) 2492f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2493f6bc909eSSimon Trimmer 2494f6bc909eSSimon Trimmer return ret; 2495f6bc909eSSimon Trimmer } 2496f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_set_dspclk); 2497f6bc909eSSimon Trimmer 2498f6bc909eSSimon Trimmer static void cs_dsp_stop_watchdog(struct cs_dsp *dsp) 2499f6bc909eSSimon Trimmer { 2500f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 2501f6bc909eSSimon Trimmer ADSP2_WDT_ENA_MASK, 0); 2502f6bc909eSSimon Trimmer } 2503f6bc909eSSimon Trimmer 2504f6bc909eSSimon Trimmer static void cs_dsp_halo_stop_watchdog(struct cs_dsp *dsp) 2505f6bc909eSSimon Trimmer { 2506f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, 2507f6bc909eSSimon Trimmer HALO_WDT_EN_MASK, 0); 2508f6bc909eSSimon Trimmer } 2509f6bc909eSSimon Trimmer 2510f6bc909eSSimon Trimmer /** 2511f6bc909eSSimon Trimmer * cs_dsp_power_up() - Downloads firmware to the DSP 2512f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2513f6bc909eSSimon Trimmer * @wmfw_firmware: the firmware to be sent 2514f6bc909eSSimon Trimmer * @wmfw_filename: file name of firmware to be sent 2515f6bc909eSSimon Trimmer * @coeff_firmware: the coefficient data to be sent 2516f6bc909eSSimon Trimmer * @coeff_filename: file name of coefficient to data be sent 2517f6bc909eSSimon Trimmer * @fw_name: the user-friendly firmware name 2518f6bc909eSSimon Trimmer * 2519f6bc909eSSimon Trimmer * This function is used on ADSP2 and Halo DSP cores, it powers-up the DSP core 2520f6bc909eSSimon Trimmer * and downloads the firmware but does not start the firmware running. The 2521f6bc909eSSimon Trimmer * cs_dsp booted flag will be set once completed and if the core has a low-power 2522f6bc909eSSimon Trimmer * memory retention mode it will be put into this state after the firmware is 2523f6bc909eSSimon Trimmer * downloaded. 2524f6bc909eSSimon Trimmer * 2525f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2526f6bc909eSSimon Trimmer */ 2527f6bc909eSSimon Trimmer int cs_dsp_power_up(struct cs_dsp *dsp, 2528f6bc909eSSimon Trimmer const struct firmware *wmfw_firmware, char *wmfw_filename, 2529f6bc909eSSimon Trimmer const struct firmware *coeff_firmware, char *coeff_filename, 2530f6bc909eSSimon Trimmer const char *fw_name) 2531f6bc909eSSimon Trimmer { 2532f6bc909eSSimon Trimmer int ret; 2533f6bc909eSSimon Trimmer 2534f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2535f6bc909eSSimon Trimmer 2536f6bc909eSSimon Trimmer dsp->fw_name = fw_name; 2537f6bc909eSSimon Trimmer 2538f6bc909eSSimon Trimmer if (dsp->ops->enable_memory) { 2539f6bc909eSSimon Trimmer ret = dsp->ops->enable_memory(dsp); 2540f6bc909eSSimon Trimmer if (ret != 0) 2541f6bc909eSSimon Trimmer goto err_mutex; 2542f6bc909eSSimon Trimmer } 2543f6bc909eSSimon Trimmer 2544f6bc909eSSimon Trimmer if (dsp->ops->enable_core) { 2545f6bc909eSSimon Trimmer ret = dsp->ops->enable_core(dsp); 2546f6bc909eSSimon Trimmer if (ret != 0) 2547f6bc909eSSimon Trimmer goto err_mem; 2548f6bc909eSSimon Trimmer } 2549f6bc909eSSimon Trimmer 2550f6bc909eSSimon Trimmer ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename); 2551f6bc909eSSimon Trimmer if (ret != 0) 2552f6bc909eSSimon Trimmer goto err_ena; 2553f6bc909eSSimon Trimmer 2554f6bc909eSSimon Trimmer ret = dsp->ops->setup_algs(dsp); 2555f6bc909eSSimon Trimmer if (ret != 0) 2556f6bc909eSSimon Trimmer goto err_ena; 2557f6bc909eSSimon Trimmer 2558f6bc909eSSimon Trimmer ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename); 2559f6bc909eSSimon Trimmer if (ret != 0) 2560f6bc909eSSimon Trimmer goto err_ena; 2561f6bc909eSSimon Trimmer 2562f6bc909eSSimon Trimmer /* Initialize caches for enabled and unset controls */ 2563f6bc909eSSimon Trimmer ret = cs_dsp_coeff_init_control_caches(dsp); 2564f6bc909eSSimon Trimmer if (ret != 0) 2565f6bc909eSSimon Trimmer goto err_ena; 2566f6bc909eSSimon Trimmer 2567f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2568f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2569f6bc909eSSimon Trimmer 2570f6bc909eSSimon Trimmer dsp->booted = true; 2571f6bc909eSSimon Trimmer 2572f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2573f6bc909eSSimon Trimmer 2574f6bc909eSSimon Trimmer return 0; 2575f6bc909eSSimon Trimmer err_ena: 2576f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2577f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2578f6bc909eSSimon Trimmer err_mem: 2579f6bc909eSSimon Trimmer if (dsp->ops->disable_memory) 2580f6bc909eSSimon Trimmer dsp->ops->disable_memory(dsp); 2581f6bc909eSSimon Trimmer err_mutex: 2582f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2583f6bc909eSSimon Trimmer 2584f6bc909eSSimon Trimmer return ret; 2585f6bc909eSSimon Trimmer } 2586f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_power_up); 2587f6bc909eSSimon Trimmer 2588f6bc909eSSimon Trimmer /** 2589f6bc909eSSimon Trimmer * cs_dsp_power_down() - Powers-down the DSP 2590f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2591f6bc909eSSimon Trimmer * 2592f6bc909eSSimon Trimmer * cs_dsp_stop() must have been called before this function. The core will be 2593f6bc909eSSimon Trimmer * fully powered down and so the memory will not be retained. 2594f6bc909eSSimon Trimmer */ 2595f6bc909eSSimon Trimmer void cs_dsp_power_down(struct cs_dsp *dsp) 2596f6bc909eSSimon Trimmer { 2597f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2598f6bc909eSSimon Trimmer 2599f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2600f6bc909eSSimon Trimmer 2601f6bc909eSSimon Trimmer cs_dsp_debugfs_clear(dsp); 2602f6bc909eSSimon Trimmer 2603f6bc909eSSimon Trimmer dsp->fw_id = 0; 2604f6bc909eSSimon Trimmer dsp->fw_id_version = 0; 2605f6bc909eSSimon Trimmer 2606f6bc909eSSimon Trimmer dsp->booted = false; 2607f6bc909eSSimon Trimmer 2608f6bc909eSSimon Trimmer if (dsp->ops->disable_memory) 2609f6bc909eSSimon Trimmer dsp->ops->disable_memory(dsp); 2610f6bc909eSSimon Trimmer 2611f6bc909eSSimon Trimmer list_for_each_entry(ctl, &dsp->ctl_list, list) 2612f6bc909eSSimon Trimmer ctl->enabled = 0; 2613f6bc909eSSimon Trimmer 2614f6bc909eSSimon Trimmer cs_dsp_free_alg_regions(dsp); 2615f6bc909eSSimon Trimmer 2616f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2617f6bc909eSSimon Trimmer 2618f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Shutdown complete\n"); 2619f6bc909eSSimon Trimmer } 2620f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_power_down); 2621f6bc909eSSimon Trimmer 2622f6bc909eSSimon Trimmer static int cs_dsp_adsp2_start_core(struct cs_dsp *dsp) 2623f6bc909eSSimon Trimmer { 2624f6bc909eSSimon Trimmer return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2625f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START, 2626f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START); 2627f6bc909eSSimon Trimmer } 2628f6bc909eSSimon Trimmer 2629f6bc909eSSimon Trimmer static void cs_dsp_adsp2_stop_core(struct cs_dsp *dsp) 2630f6bc909eSSimon Trimmer { 2631f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2632f6bc909eSSimon Trimmer ADSP2_CORE_ENA | ADSP2_START, 0); 2633f6bc909eSSimon Trimmer } 2634f6bc909eSSimon Trimmer 2635f6bc909eSSimon Trimmer /** 2636f6bc909eSSimon Trimmer * cs_dsp_run() - Starts the firmware running 2637f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2638f6bc909eSSimon Trimmer * 2639f6bc909eSSimon Trimmer * cs_dsp_power_up() must have previously been called successfully. 2640f6bc909eSSimon Trimmer * 2641f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2642f6bc909eSSimon Trimmer */ 2643f6bc909eSSimon Trimmer int cs_dsp_run(struct cs_dsp *dsp) 2644f6bc909eSSimon Trimmer { 2645f6bc909eSSimon Trimmer int ret; 2646f6bc909eSSimon Trimmer 2647f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2648f6bc909eSSimon Trimmer 2649f6bc909eSSimon Trimmer if (!dsp->booted) { 2650f6bc909eSSimon Trimmer ret = -EIO; 2651f6bc909eSSimon Trimmer goto err; 2652f6bc909eSSimon Trimmer } 2653f6bc909eSSimon Trimmer 2654f6bc909eSSimon Trimmer if (dsp->ops->enable_core) { 2655f6bc909eSSimon Trimmer ret = dsp->ops->enable_core(dsp); 2656f6bc909eSSimon Trimmer if (ret != 0) 2657f6bc909eSSimon Trimmer goto err; 2658f6bc909eSSimon Trimmer } 2659f6bc909eSSimon Trimmer 266014055b5aSCharles Keepax if (dsp->client_ops->pre_run) { 266114055b5aSCharles Keepax ret = dsp->client_ops->pre_run(dsp); 266214055b5aSCharles Keepax if (ret) 266314055b5aSCharles Keepax goto err; 266414055b5aSCharles Keepax } 266514055b5aSCharles Keepax 2666f6bc909eSSimon Trimmer /* Sync set controls */ 2667f6bc909eSSimon Trimmer ret = cs_dsp_coeff_sync_controls(dsp); 2668f6bc909eSSimon Trimmer if (ret != 0) 2669f6bc909eSSimon Trimmer goto err; 2670f6bc909eSSimon Trimmer 2671f6bc909eSSimon Trimmer if (dsp->ops->lock_memory) { 2672f6bc909eSSimon Trimmer ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); 2673f6bc909eSSimon Trimmer if (ret != 0) { 2674f6bc909eSSimon Trimmer cs_dsp_err(dsp, "Error configuring MPU: %d\n", ret); 2675f6bc909eSSimon Trimmer goto err; 2676f6bc909eSSimon Trimmer } 2677f6bc909eSSimon Trimmer } 2678f6bc909eSSimon Trimmer 2679f6bc909eSSimon Trimmer if (dsp->ops->start_core) { 2680f6bc909eSSimon Trimmer ret = dsp->ops->start_core(dsp); 2681f6bc909eSSimon Trimmer if (ret != 0) 2682f6bc909eSSimon Trimmer goto err; 2683f6bc909eSSimon Trimmer } 2684f6bc909eSSimon Trimmer 2685f6bc909eSSimon Trimmer dsp->running = true; 2686f6bc909eSSimon Trimmer 2687f6bc909eSSimon Trimmer if (dsp->client_ops->post_run) { 2688f6bc909eSSimon Trimmer ret = dsp->client_ops->post_run(dsp); 2689f6bc909eSSimon Trimmer if (ret) 2690f6bc909eSSimon Trimmer goto err; 2691f6bc909eSSimon Trimmer } 2692f6bc909eSSimon Trimmer 2693f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2694f6bc909eSSimon Trimmer 2695f6bc909eSSimon Trimmer return 0; 2696f6bc909eSSimon Trimmer 2697f6bc909eSSimon Trimmer err: 2698f6bc909eSSimon Trimmer if (dsp->ops->stop_core) 2699f6bc909eSSimon Trimmer dsp->ops->stop_core(dsp); 2700f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2701f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2702f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2703f6bc909eSSimon Trimmer 2704f6bc909eSSimon Trimmer return ret; 2705f6bc909eSSimon Trimmer } 2706f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_run); 2707f6bc909eSSimon Trimmer 2708f6bc909eSSimon Trimmer /** 2709f6bc909eSSimon Trimmer * cs_dsp_stop() - Stops the firmware 2710f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2711f6bc909eSSimon Trimmer * 2712f6bc909eSSimon Trimmer * Memory will not be disabled so firmware will remain loaded. 2713f6bc909eSSimon Trimmer */ 2714f6bc909eSSimon Trimmer void cs_dsp_stop(struct cs_dsp *dsp) 2715f6bc909eSSimon Trimmer { 2716f6bc909eSSimon Trimmer /* Tell the firmware to cleanup */ 2717f6bc909eSSimon Trimmer cs_dsp_signal_event_controls(dsp, CS_DSP_FW_EVENT_SHUTDOWN); 2718f6bc909eSSimon Trimmer 2719f6bc909eSSimon Trimmer if (dsp->ops->stop_watchdog) 2720f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 2721f6bc909eSSimon Trimmer 2722f6bc909eSSimon Trimmer /* Log firmware state, it can be useful for analysis */ 2723f6bc909eSSimon Trimmer if (dsp->ops->show_fw_status) 2724f6bc909eSSimon Trimmer dsp->ops->show_fw_status(dsp); 2725f6bc909eSSimon Trimmer 2726f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2727f6bc909eSSimon Trimmer 2728f6bc909eSSimon Trimmer dsp->running = false; 2729f6bc909eSSimon Trimmer 2730f6bc909eSSimon Trimmer if (dsp->ops->stop_core) 2731f6bc909eSSimon Trimmer dsp->ops->stop_core(dsp); 2732f6bc909eSSimon Trimmer if (dsp->ops->disable_core) 2733f6bc909eSSimon Trimmer dsp->ops->disable_core(dsp); 2734f6bc909eSSimon Trimmer 2735f6bc909eSSimon Trimmer if (dsp->client_ops->post_stop) 2736f6bc909eSSimon Trimmer dsp->client_ops->post_stop(dsp); 2737f6bc909eSSimon Trimmer 2738f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 2739f6bc909eSSimon Trimmer 2740f6bc909eSSimon Trimmer cs_dsp_dbg(dsp, "Execution stopped\n"); 2741f6bc909eSSimon Trimmer } 2742f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_stop); 2743f6bc909eSSimon Trimmer 2744f6bc909eSSimon Trimmer static int cs_dsp_halo_start_core(struct cs_dsp *dsp) 2745f6bc909eSSimon Trimmer { 27467aa1cc10SCharles Keepax int ret; 27477aa1cc10SCharles Keepax 27487aa1cc10SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 2749f6bc909eSSimon Trimmer HALO_CORE_RESET | HALO_CORE_EN, 2750f6bc909eSSimon Trimmer HALO_CORE_RESET | HALO_CORE_EN); 27517aa1cc10SCharles Keepax if (ret) 27527aa1cc10SCharles Keepax return ret; 27537aa1cc10SCharles Keepax 27547aa1cc10SCharles Keepax return regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 27557aa1cc10SCharles Keepax HALO_CORE_RESET, 0); 2756f6bc909eSSimon Trimmer } 2757f6bc909eSSimon Trimmer 2758f6bc909eSSimon Trimmer static void cs_dsp_halo_stop_core(struct cs_dsp *dsp) 2759f6bc909eSSimon Trimmer { 2760f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, 2761f6bc909eSSimon Trimmer HALO_CORE_EN, 0); 2762f6bc909eSSimon Trimmer 2763f6bc909eSSimon Trimmer /* reset halo core with CORE_SOFT_RESET */ 2764f6bc909eSSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, 2765f6bc909eSSimon Trimmer HALO_CORE_SOFT_RESET_MASK, 1); 2766f6bc909eSSimon Trimmer } 2767f6bc909eSSimon Trimmer 2768f6bc909eSSimon Trimmer /** 2769f6bc909eSSimon Trimmer * cs_dsp_adsp2_init() - Initialise a cs_dsp structure representing a ADSP2 core 2770f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2771f6bc909eSSimon Trimmer * 2772f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2773f6bc909eSSimon Trimmer */ 2774f6bc909eSSimon Trimmer int cs_dsp_adsp2_init(struct cs_dsp *dsp) 2775f6bc909eSSimon Trimmer { 2776f6bc909eSSimon Trimmer int ret; 2777f6bc909eSSimon Trimmer 2778f6bc909eSSimon Trimmer switch (dsp->rev) { 2779f6bc909eSSimon Trimmer case 0: 2780f6bc909eSSimon Trimmer /* 2781f6bc909eSSimon Trimmer * Disable the DSP memory by default when in reset for a small 2782f6bc909eSSimon Trimmer * power saving. 2783f6bc909eSSimon Trimmer */ 2784f6bc909eSSimon Trimmer ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2785f6bc909eSSimon Trimmer ADSP2_MEM_ENA, 0); 2786f6bc909eSSimon Trimmer if (ret) { 2787f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2788f6bc909eSSimon Trimmer "Failed to clear memory retention: %d\n", ret); 2789f6bc909eSSimon Trimmer return ret; 2790f6bc909eSSimon Trimmer } 2791f6bc909eSSimon Trimmer 2792f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[0]; 2793f6bc909eSSimon Trimmer break; 2794f6bc909eSSimon Trimmer case 1: 2795f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[1]; 2796f6bc909eSSimon Trimmer break; 2797f6bc909eSSimon Trimmer default: 2798f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_adsp2_ops[2]; 2799f6bc909eSSimon Trimmer break; 2800f6bc909eSSimon Trimmer } 2801f6bc909eSSimon Trimmer 2802f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 2803f6bc909eSSimon Trimmer } 2804f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp2_init); 2805f6bc909eSSimon Trimmer 2806f6bc909eSSimon Trimmer /** 2807f6bc909eSSimon Trimmer * cs_dsp_halo_init() - Initialise a cs_dsp structure representing a HALO Core DSP 2808f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2809f6bc909eSSimon Trimmer * 2810f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2811f6bc909eSSimon Trimmer */ 2812f6bc909eSSimon Trimmer int cs_dsp_halo_init(struct cs_dsp *dsp) 2813f6bc909eSSimon Trimmer { 2814f6bc909eSSimon Trimmer dsp->ops = &cs_dsp_halo_ops; 2815f6bc909eSSimon Trimmer 2816f6bc909eSSimon Trimmer return cs_dsp_common_init(dsp); 2817f6bc909eSSimon Trimmer } 2818f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_halo_init); 2819f6bc909eSSimon Trimmer 2820f6bc909eSSimon Trimmer /** 2821f6bc909eSSimon Trimmer * cs_dsp_remove() - Clean a cs_dsp before deletion 2822f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2823f6bc909eSSimon Trimmer */ 2824f6bc909eSSimon Trimmer void cs_dsp_remove(struct cs_dsp *dsp) 2825f6bc909eSSimon Trimmer { 2826f6bc909eSSimon Trimmer struct cs_dsp_coeff_ctl *ctl; 2827f6bc909eSSimon Trimmer 2828f6bc909eSSimon Trimmer while (!list_empty(&dsp->ctl_list)) { 2829f6bc909eSSimon Trimmer ctl = list_first_entry(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list); 2830f6bc909eSSimon Trimmer 2831f6bc909eSSimon Trimmer if (dsp->client_ops->control_remove) 2832f6bc909eSSimon Trimmer dsp->client_ops->control_remove(ctl); 2833f6bc909eSSimon Trimmer 2834f6bc909eSSimon Trimmer list_del(&ctl->list); 2835f6bc909eSSimon Trimmer cs_dsp_free_ctl_blk(ctl); 2836f6bc909eSSimon Trimmer } 2837f6bc909eSSimon Trimmer } 2838f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_remove); 2839f6bc909eSSimon Trimmer 2840f6bc909eSSimon Trimmer /** 2841f6bc909eSSimon Trimmer * cs_dsp_read_raw_data_block() - Reads a block of data from DSP memory 2842f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2843f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be read 2844f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 2845f6bc909eSSimon Trimmer * @num_words: the length of the data to read 2846f6bc909eSSimon Trimmer * @data: a buffer to store the fetched data 2847f6bc909eSSimon Trimmer * 2848f6bc909eSSimon Trimmer * If this is used to read unpacked 24-bit memory, each 24-bit DSP word will 2849f6bc909eSSimon Trimmer * occupy 32-bits in data (MSbyte will be 0). This padding can be removed using 2850f6bc909eSSimon Trimmer * cs_dsp_remove_padding() 2851f6bc909eSSimon Trimmer * 2852f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2853f6bc909eSSimon Trimmer */ 2854f6bc909eSSimon Trimmer int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, 2855f6bc909eSSimon Trimmer unsigned int num_words, __be32 *data) 2856f6bc909eSSimon Trimmer { 2857f6bc909eSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 2858f6bc909eSSimon Trimmer unsigned int reg; 2859f6bc909eSSimon Trimmer int ret; 2860f6bc909eSSimon Trimmer 28615065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 28625065cfabSCharles Keepax 2863f6bc909eSSimon Trimmer if (!mem) 2864f6bc909eSSimon Trimmer return -EINVAL; 2865f6bc909eSSimon Trimmer 2866f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, mem_addr); 2867f6bc909eSSimon Trimmer 2868f6bc909eSSimon Trimmer ret = regmap_raw_read(dsp->regmap, reg, data, 2869f6bc909eSSimon Trimmer sizeof(*data) * num_words); 2870f6bc909eSSimon Trimmer if (ret < 0) 2871f6bc909eSSimon Trimmer return ret; 2872f6bc909eSSimon Trimmer 2873f6bc909eSSimon Trimmer return 0; 2874f6bc909eSSimon Trimmer } 2875f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_read_raw_data_block); 2876f6bc909eSSimon Trimmer 2877f6bc909eSSimon Trimmer /** 2878f6bc909eSSimon Trimmer * cs_dsp_read_data_word() - Reads a word from DSP memory 2879f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2880f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be read 2881f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 2882f6bc909eSSimon Trimmer * @data: a buffer to store the fetched data 2883f6bc909eSSimon Trimmer * 2884f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2885f6bc909eSSimon Trimmer */ 2886f6bc909eSSimon Trimmer int cs_dsp_read_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 *data) 2887f6bc909eSSimon Trimmer { 2888f6bc909eSSimon Trimmer __be32 raw; 2889f6bc909eSSimon Trimmer int ret; 2890f6bc909eSSimon Trimmer 2891f6bc909eSSimon Trimmer ret = cs_dsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw); 2892f6bc909eSSimon Trimmer if (ret < 0) 2893f6bc909eSSimon Trimmer return ret; 2894f6bc909eSSimon Trimmer 2895f6bc909eSSimon Trimmer *data = be32_to_cpu(raw) & 0x00ffffffu; 2896f6bc909eSSimon Trimmer 2897f6bc909eSSimon Trimmer return 0; 2898f6bc909eSSimon Trimmer } 2899f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_read_data_word); 2900f6bc909eSSimon Trimmer 2901f6bc909eSSimon Trimmer /** 2902f6bc909eSSimon Trimmer * cs_dsp_write_data_word() - Writes a word to DSP memory 2903f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2904f6bc909eSSimon Trimmer * @mem_type: the type of DSP memory containing the data to be written 2905f6bc909eSSimon Trimmer * @mem_addr: the address of the data within the memory region 2906f6bc909eSSimon Trimmer * @data: the data to be written 2907f6bc909eSSimon Trimmer * 2908f6bc909eSSimon Trimmer * Return: Zero for success, a negative number on error. 2909f6bc909eSSimon Trimmer */ 2910f6bc909eSSimon Trimmer int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 data) 2911f6bc909eSSimon Trimmer { 2912f6bc909eSSimon Trimmer struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type); 2913f6bc909eSSimon Trimmer __be32 val = cpu_to_be32(data & 0x00ffffffu); 2914f6bc909eSSimon Trimmer unsigned int reg; 2915f6bc909eSSimon Trimmer 29165065cfabSCharles Keepax lockdep_assert_held(&dsp->pwr_lock); 29175065cfabSCharles Keepax 2918f6bc909eSSimon Trimmer if (!mem) 2919f6bc909eSSimon Trimmer return -EINVAL; 2920f6bc909eSSimon Trimmer 2921f6bc909eSSimon Trimmer reg = dsp->ops->region_to_reg(mem, mem_addr); 2922f6bc909eSSimon Trimmer 2923f6bc909eSSimon Trimmer return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 2924f6bc909eSSimon Trimmer } 2925f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_write_data_word); 2926f6bc909eSSimon Trimmer 2927f6bc909eSSimon Trimmer /** 2928f6bc909eSSimon Trimmer * cs_dsp_remove_padding() - Convert unpacked words to packed bytes 2929f6bc909eSSimon Trimmer * @buf: buffer containing DSP words read from DSP memory 2930f6bc909eSSimon Trimmer * @nwords: number of words to convert 2931f6bc909eSSimon Trimmer * 2932f6bc909eSSimon Trimmer * DSP words from the register map have pad bytes and the data bytes 2933f6bc909eSSimon Trimmer * are in swapped order. This swaps to the native endian order and 2934f6bc909eSSimon Trimmer * strips the pad bytes. 2935f6bc909eSSimon Trimmer */ 2936f6bc909eSSimon Trimmer void cs_dsp_remove_padding(u32 *buf, int nwords) 2937f6bc909eSSimon Trimmer { 2938f6bc909eSSimon Trimmer const __be32 *pack_in = (__be32 *)buf; 2939f6bc909eSSimon Trimmer u8 *pack_out = (u8 *)buf; 2940f6bc909eSSimon Trimmer int i; 2941f6bc909eSSimon Trimmer 2942f6bc909eSSimon Trimmer for (i = 0; i < nwords; i++) { 2943f6bc909eSSimon Trimmer u32 word = be32_to_cpu(*pack_in++); 2944f6bc909eSSimon Trimmer *pack_out++ = (u8)word; 2945f6bc909eSSimon Trimmer *pack_out++ = (u8)(word >> 8); 2946f6bc909eSSimon Trimmer *pack_out++ = (u8)(word >> 16); 2947f6bc909eSSimon Trimmer } 2948f6bc909eSSimon Trimmer } 2949f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_remove_padding); 2950f6bc909eSSimon Trimmer 2951f6bc909eSSimon Trimmer /** 2952f6bc909eSSimon Trimmer * cs_dsp_adsp2_bus_error() - Handle a DSP bus error interrupt 2953f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 2954f6bc909eSSimon Trimmer * 2955f6bc909eSSimon Trimmer * The firmware and DSP state will be logged for future analysis. 2956f6bc909eSSimon Trimmer */ 2957f6bc909eSSimon Trimmer void cs_dsp_adsp2_bus_error(struct cs_dsp *dsp) 2958f6bc909eSSimon Trimmer { 2959f6bc909eSSimon Trimmer unsigned int val; 2960f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 2961f6bc909eSSimon Trimmer int ret = 0; 2962f6bc909eSSimon Trimmer 2963f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 2964f6bc909eSSimon Trimmer 2965f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 2966f6bc909eSSimon Trimmer if (ret) { 2967f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2968f6bc909eSSimon Trimmer "Failed to read Region Lock Ctrl register: %d\n", ret); 2969f6bc909eSSimon Trimmer goto error; 2970f6bc909eSSimon Trimmer } 2971f6bc909eSSimon Trimmer 2972f6bc909eSSimon Trimmer if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 2973f6bc909eSSimon Trimmer cs_dsp_err(dsp, "watchdog timeout error\n"); 2974f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 2975f6bc909eSSimon Trimmer if (dsp->client_ops->watchdog_expired) 2976f6bc909eSSimon Trimmer dsp->client_ops->watchdog_expired(dsp); 2977f6bc909eSSimon Trimmer } 2978f6bc909eSSimon Trimmer 2979f6bc909eSSimon Trimmer if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 2980f6bc909eSSimon Trimmer if (val & ADSP2_ADDR_ERR_MASK) 2981f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error: address error\n"); 2982f6bc909eSSimon Trimmer else 2983f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error: region lock error\n"); 2984f6bc909eSSimon Trimmer 2985f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 2986f6bc909eSSimon Trimmer if (ret) { 2987f6bc909eSSimon Trimmer cs_dsp_err(dsp, 2988f6bc909eSSimon Trimmer "Failed to read Bus Err Addr register: %d\n", 2989f6bc909eSSimon Trimmer ret); 2990f6bc909eSSimon Trimmer goto error; 2991f6bc909eSSimon Trimmer } 2992f6bc909eSSimon Trimmer 2993f6bc909eSSimon Trimmer cs_dsp_err(dsp, "bus error address = 0x%x\n", 2994f6bc909eSSimon Trimmer val & ADSP2_BUS_ERR_ADDR_MASK); 2995f6bc909eSSimon Trimmer 2996f6bc909eSSimon Trimmer ret = regmap_read(regmap, 2997f6bc909eSSimon Trimmer dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 2998f6bc909eSSimon Trimmer &val); 2999f6bc909eSSimon Trimmer if (ret) { 3000f6bc909eSSimon Trimmer cs_dsp_err(dsp, 3001f6bc909eSSimon Trimmer "Failed to read Pmem Xmem Err Addr register: %d\n", 3002f6bc909eSSimon Trimmer ret); 3003f6bc909eSSimon Trimmer goto error; 3004f6bc909eSSimon Trimmer } 3005f6bc909eSSimon Trimmer 3006f6bc909eSSimon Trimmer cs_dsp_err(dsp, "xmem error address = 0x%x\n", 3007f6bc909eSSimon Trimmer val & ADSP2_XMEM_ERR_ADDR_MASK); 3008f6bc909eSSimon Trimmer cs_dsp_err(dsp, "pmem error address = 0x%x\n", 3009f6bc909eSSimon Trimmer (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 3010f6bc909eSSimon Trimmer ADSP2_PMEM_ERR_ADDR_SHIFT); 3011f6bc909eSSimon Trimmer } 3012f6bc909eSSimon Trimmer 3013f6bc909eSSimon Trimmer regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 3014f6bc909eSSimon Trimmer ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 3015f6bc909eSSimon Trimmer 3016f6bc909eSSimon Trimmer error: 3017f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3018f6bc909eSSimon Trimmer } 3019f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_adsp2_bus_error); 3020f6bc909eSSimon Trimmer 3021f6bc909eSSimon Trimmer /** 3022f6bc909eSSimon Trimmer * cs_dsp_halo_bus_error() - Handle a DSP bus error interrupt 3023f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3024f6bc909eSSimon Trimmer * 3025f6bc909eSSimon Trimmer * The firmware and DSP state will be logged for future analysis. 3026f6bc909eSSimon Trimmer */ 3027f6bc909eSSimon Trimmer void cs_dsp_halo_bus_error(struct cs_dsp *dsp) 3028f6bc909eSSimon Trimmer { 3029f6bc909eSSimon Trimmer struct regmap *regmap = dsp->regmap; 3030f6bc909eSSimon Trimmer unsigned int fault[6]; 3031f6bc909eSSimon Trimmer struct reg_sequence clear[] = { 3032f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, 3033f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, 3034f6bc909eSSimon Trimmer { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, 3035f6bc909eSSimon Trimmer }; 3036f6bc909eSSimon Trimmer int ret; 3037f6bc909eSSimon Trimmer 3038f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 3039f6bc909eSSimon Trimmer 3040f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, 3041f6bc909eSSimon Trimmer fault); 3042f6bc909eSSimon Trimmer if (ret) { 3043f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); 3044f6bc909eSSimon Trimmer goto exit_unlock; 3045f6bc909eSSimon Trimmer } 3046f6bc909eSSimon Trimmer 3047f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", 3048f6bc909eSSimon Trimmer *fault & HALO_AHBM_FLAGS_ERR_MASK, 3049f6bc909eSSimon Trimmer (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> 3050f6bc909eSSimon Trimmer HALO_AHBM_CORE_ERR_ADDR_SHIFT); 3051f6bc909eSSimon Trimmer 3052f6bc909eSSimon Trimmer ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, 3053f6bc909eSSimon Trimmer fault); 3054f6bc909eSSimon Trimmer if (ret) { 3055f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); 3056f6bc909eSSimon Trimmer goto exit_unlock; 3057f6bc909eSSimon Trimmer } 3058f6bc909eSSimon Trimmer 3059f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); 3060f6bc909eSSimon Trimmer 3061f6bc909eSSimon Trimmer ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, 3062f6bc909eSSimon Trimmer fault, ARRAY_SIZE(fault)); 3063f6bc909eSSimon Trimmer if (ret) { 3064f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); 3065f6bc909eSSimon Trimmer goto exit_unlock; 3066f6bc909eSSimon Trimmer } 3067f6bc909eSSimon Trimmer 3068f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); 3069f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); 3070f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); 3071f6bc909eSSimon Trimmer 3072f6bc909eSSimon Trimmer ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); 3073f6bc909eSSimon Trimmer if (ret) 3074f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); 3075f6bc909eSSimon Trimmer 3076f6bc909eSSimon Trimmer exit_unlock: 3077f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3078f6bc909eSSimon Trimmer } 3079f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_halo_bus_error); 3080f6bc909eSSimon Trimmer 3081f6bc909eSSimon Trimmer /** 3082f6bc909eSSimon Trimmer * cs_dsp_halo_wdt_expire() - Handle DSP watchdog expiry 3083f6bc909eSSimon Trimmer * @dsp: pointer to DSP structure 3084f6bc909eSSimon Trimmer * 3085f6bc909eSSimon Trimmer * This is logged for future analysis. 3086f6bc909eSSimon Trimmer */ 3087f6bc909eSSimon Trimmer void cs_dsp_halo_wdt_expire(struct cs_dsp *dsp) 3088f6bc909eSSimon Trimmer { 3089f6bc909eSSimon Trimmer mutex_lock(&dsp->pwr_lock); 3090f6bc909eSSimon Trimmer 3091f6bc909eSSimon Trimmer cs_dsp_warn(dsp, "WDT Expiry Fault\n"); 3092f6bc909eSSimon Trimmer 3093f6bc909eSSimon Trimmer dsp->ops->stop_watchdog(dsp); 3094f6bc909eSSimon Trimmer if (dsp->client_ops->watchdog_expired) 3095f6bc909eSSimon Trimmer dsp->client_ops->watchdog_expired(dsp); 3096f6bc909eSSimon Trimmer 3097f6bc909eSSimon Trimmer mutex_unlock(&dsp->pwr_lock); 3098f6bc909eSSimon Trimmer } 3099f6bc909eSSimon Trimmer EXPORT_SYMBOL_GPL(cs_dsp_halo_wdt_expire); 3100f6bc909eSSimon Trimmer 3101f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp1_ops = { 3102f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3103f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp1_parse_sizes, 3104f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3105f6bc909eSSimon Trimmer }; 3106f6bc909eSSimon Trimmer 3107f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_adsp2_ops[] = { 3108f6bc909eSSimon Trimmer { 3109f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3110f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3111f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3112f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3113f6bc909eSSimon Trimmer 3114f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2_show_fw_status, 3115f6bc909eSSimon Trimmer 3116f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3117f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3118f6bc909eSSimon Trimmer 3119f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2_enable_core, 3120f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2_disable_core, 3121f6bc909eSSimon Trimmer 3122f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3123f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3124f6bc909eSSimon Trimmer 3125f6bc909eSSimon Trimmer }, 3126f6bc909eSSimon Trimmer { 3127f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3128f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3129f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3130f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3131f6bc909eSSimon Trimmer 3132f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 3133f6bc909eSSimon Trimmer 3134f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3135f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3136f6bc909eSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 3137f6bc909eSSimon Trimmer 3138f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 3139f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 3140f6bc909eSSimon Trimmer 3141f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3142f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3143f6bc909eSSimon Trimmer }, 3144f6bc909eSSimon Trimmer { 3145f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3146f6bc909eSSimon Trimmer .validate_version = cs_dsp_validate_version, 3147f6bc909eSSimon Trimmer .setup_algs = cs_dsp_adsp2_setup_algs, 3148f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_region_to_reg, 3149f6bc909eSSimon Trimmer 3150f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_adsp2v2_show_fw_status, 3151f6bc909eSSimon Trimmer .stop_watchdog = cs_dsp_stop_watchdog, 3152f6bc909eSSimon Trimmer 3153f6bc909eSSimon Trimmer .enable_memory = cs_dsp_adsp2_enable_memory, 3154f6bc909eSSimon Trimmer .disable_memory = cs_dsp_adsp2_disable_memory, 3155f6bc909eSSimon Trimmer .lock_memory = cs_dsp_adsp2_lock, 3156f6bc909eSSimon Trimmer 3157f6bc909eSSimon Trimmer .enable_core = cs_dsp_adsp2v2_enable_core, 3158f6bc909eSSimon Trimmer .disable_core = cs_dsp_adsp2v2_disable_core, 3159f6bc909eSSimon Trimmer 3160f6bc909eSSimon Trimmer .start_core = cs_dsp_adsp2_start_core, 3161f6bc909eSSimon Trimmer .stop_core = cs_dsp_adsp2_stop_core, 3162f6bc909eSSimon Trimmer }, 3163f6bc909eSSimon Trimmer }; 3164f6bc909eSSimon Trimmer 3165f6bc909eSSimon Trimmer static const struct cs_dsp_ops cs_dsp_halo_ops = { 3166f6bc909eSSimon Trimmer .parse_sizes = cs_dsp_adsp2_parse_sizes, 3167f6bc909eSSimon Trimmer .validate_version = cs_dsp_halo_validate_version, 3168f6bc909eSSimon Trimmer .setup_algs = cs_dsp_halo_setup_algs, 3169f6bc909eSSimon Trimmer .region_to_reg = cs_dsp_halo_region_to_reg, 3170f6bc909eSSimon Trimmer 3171f6bc909eSSimon Trimmer .show_fw_status = cs_dsp_halo_show_fw_status, 3172f6bc909eSSimon Trimmer .stop_watchdog = cs_dsp_halo_stop_watchdog, 3173f6bc909eSSimon Trimmer 3174f6bc909eSSimon Trimmer .lock_memory = cs_dsp_halo_configure_mpu, 3175f6bc909eSSimon Trimmer 3176f6bc909eSSimon Trimmer .start_core = cs_dsp_halo_start_core, 3177f6bc909eSSimon Trimmer .stop_core = cs_dsp_halo_stop_core, 3178f6bc909eSSimon Trimmer }; 3179f6bc909eSSimon Trimmer 3180f6bc909eSSimon Trimmer MODULE_DESCRIPTION("Cirrus Logic DSP Support"); 3181f6bc909eSSimon Trimmer MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); 3182f6bc909eSSimon Trimmer MODULE_LICENSE("GPL v2"); 3183