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