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