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