brintos

brintos / linux-shallow public Read only

0
0
Text · 97.3 KiB · 419220f Raw
3760 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * cs_dsp.c  --  Cirrus Logic DSP firmware support4 *5 * Based on sound/soc/codecs/wm_adsp.c6 *7 * Copyright 2012 Wolfson Microelectronics plc8 * Copyright (C) 2015-2021 Cirrus Logic, Inc. and9 *                         Cirrus Logic International Semiconductor Ltd.10 */11 12#include <linux/ctype.h>13#include <linux/debugfs.h>14#include <linux/delay.h>15#include <linux/minmax.h>16#include <linux/module.h>17#include <linux/moduleparam.h>18#include <linux/seq_file.h>19#include <linux/slab.h>20#include <linux/vmalloc.h>21 22#include <linux/firmware/cirrus/cs_dsp.h>23#include <linux/firmware/cirrus/wmfw.h>24 25#define cs_dsp_err(_dsp, fmt, ...) \26	dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)27#define cs_dsp_warn(_dsp, fmt, ...) \28	dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)29#define cs_dsp_info(_dsp, fmt, ...) \30	dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)31#define cs_dsp_dbg(_dsp, fmt, ...) \32	dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__)33 34#define ADSP1_CONTROL_1                   0x0035#define ADSP1_CONTROL_2                   0x0236#define ADSP1_CONTROL_3                   0x0337#define ADSP1_CONTROL_4                   0x0438#define ADSP1_CONTROL_5                   0x0639#define ADSP1_CONTROL_6                   0x0740#define ADSP1_CONTROL_7                   0x0841#define ADSP1_CONTROL_8                   0x0942#define ADSP1_CONTROL_9                   0x0A43#define ADSP1_CONTROL_10                  0x0B44#define ADSP1_CONTROL_11                  0x0C45#define ADSP1_CONTROL_12                  0x0D46#define ADSP1_CONTROL_13                  0x0F47#define ADSP1_CONTROL_14                  0x1048#define ADSP1_CONTROL_15                  0x1149#define ADSP1_CONTROL_16                  0x1250#define ADSP1_CONTROL_17                  0x1351#define ADSP1_CONTROL_18                  0x1452#define ADSP1_CONTROL_19                  0x1653#define ADSP1_CONTROL_20                  0x1754#define ADSP1_CONTROL_21                  0x1855#define ADSP1_CONTROL_22                  0x1A56#define ADSP1_CONTROL_23                  0x1B57#define ADSP1_CONTROL_24                  0x1C58#define ADSP1_CONTROL_25                  0x1E59#define ADSP1_CONTROL_26                  0x2060#define ADSP1_CONTROL_27                  0x2161#define ADSP1_CONTROL_28                  0x2262#define ADSP1_CONTROL_29                  0x2363#define ADSP1_CONTROL_30                  0x2464#define ADSP1_CONTROL_31                  0x2665 66/*67 * ADSP1 Control 1968 */69#define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */70#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */71#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */72 73/*74 * ADSP1 Control 3075 */76#define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */77#define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */78#define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */79#define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */80#define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */81#define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */82#define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */83#define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */84#define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */85#define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */86#define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */87#define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */88#define ADSP1_START                       0x0001  /* DSP1_START */89#define ADSP1_START_MASK                  0x0001  /* DSP1_START */90#define ADSP1_START_SHIFT                      0  /* DSP1_START */91#define ADSP1_START_WIDTH                      1  /* DSP1_START */92 93/*94 * ADSP1 Control 3195 */96#define ADSP1_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */97#define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */98#define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */99 100#define ADSP2_CONTROL                     0x0101#define ADSP2_CLOCKING                    0x1102#define ADSP2V2_CLOCKING                  0x2103#define ADSP2_STATUS1                     0x4104#define ADSP2_WDMA_CONFIG_1               0x30105#define ADSP2_WDMA_CONFIG_2               0x31106#define ADSP2V2_WDMA_CONFIG_2             0x32107#define ADSP2_RDMA_CONFIG_1               0x34108 109#define ADSP2_SCRATCH0                    0x40110#define ADSP2_SCRATCH1                    0x41111#define ADSP2_SCRATCH2                    0x42112#define ADSP2_SCRATCH3                    0x43113 114#define ADSP2V2_SCRATCH0_1                0x40115#define ADSP2V2_SCRATCH2_3                0x42116 117/*118 * ADSP2 Control119 */120#define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */121#define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */122#define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */123#define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */124#define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */125#define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */126#define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */127#define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */128#define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */129#define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */130#define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */131#define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */132#define ADSP2_START                       0x0001  /* DSP1_START */133#define ADSP2_START_MASK                  0x0001  /* DSP1_START */134#define ADSP2_START_SHIFT                      0  /* DSP1_START */135#define ADSP2_START_WIDTH                      1  /* DSP1_START */136 137/*138 * ADSP2 clocking139 */140#define ADSP2_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */141#define ADSP2_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */142#define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */143 144/*145 * ADSP2V2 clocking146 */147#define ADSP2V2_CLK_SEL_MASK             0x70000  /* CLK_SEL_ENA */148#define ADSP2V2_CLK_SEL_SHIFT                 16  /* CLK_SEL_ENA */149#define ADSP2V2_CLK_SEL_WIDTH                  3  /* CLK_SEL_ENA */150 151#define ADSP2V2_RATE_MASK                 0x7800  /* DSP_RATE */152#define ADSP2V2_RATE_SHIFT                    11  /* DSP_RATE */153#define ADSP2V2_RATE_WIDTH                     4  /* DSP_RATE */154 155/*156 * ADSP2 Status 1157 */158#define ADSP2_RAM_RDY                     0x0001159#define ADSP2_RAM_RDY_MASK                0x0001160#define ADSP2_RAM_RDY_SHIFT                    0161#define ADSP2_RAM_RDY_WIDTH                    1162 163/*164 * ADSP2 Lock support165 */166#define ADSP2_LOCK_CODE_0                    0x5555167#define ADSP2_LOCK_CODE_1                    0xAAAA168 169#define ADSP2_WATCHDOG                       0x0A170#define ADSP2_BUS_ERR_ADDR                   0x52171#define ADSP2_REGION_LOCK_STATUS             0x64172#define ADSP2_LOCK_REGION_1_LOCK_REGION_0    0x66173#define ADSP2_LOCK_REGION_3_LOCK_REGION_2    0x68174#define ADSP2_LOCK_REGION_5_LOCK_REGION_4    0x6A175#define ADSP2_LOCK_REGION_7_LOCK_REGION_6    0x6C176#define ADSP2_LOCK_REGION_9_LOCK_REGION_8    0x6E177#define ADSP2_LOCK_REGION_CTRL               0x7A178#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR    0x7C179 180#define ADSP2_REGION_LOCK_ERR_MASK           0x8000181#define ADSP2_ADDR_ERR_MASK                  0x4000182#define ADSP2_WDT_TIMEOUT_STS_MASK           0x2000183#define ADSP2_CTRL_ERR_PAUSE_ENA             0x0002184#define ADSP2_CTRL_ERR_EINT                  0x0001185 186#define ADSP2_BUS_ERR_ADDR_MASK              0x00FFFFFF187#define ADSP2_XMEM_ERR_ADDR_MASK             0x0000FFFF188#define ADSP2_PMEM_ERR_ADDR_MASK             0x7FFF0000189#define ADSP2_PMEM_ERR_ADDR_SHIFT            16190#define ADSP2_WDT_ENA_MASK                   0xFFFFFFFD191 192#define ADSP2_LOCK_REGION_SHIFT              16193 194/*195 * Event control messages196 */197#define CS_DSP_FW_EVENT_SHUTDOWN             0x000001198 199/*200 * HALO system info201 */202#define HALO_AHBM_WINDOW_DEBUG_0             0x02040203#define HALO_AHBM_WINDOW_DEBUG_1             0x02044204 205/*206 * HALO core207 */208#define HALO_SCRATCH1                        0x005c0209#define HALO_SCRATCH2                        0x005c8210#define HALO_SCRATCH3                        0x005d0211#define HALO_SCRATCH4                        0x005d8212#define HALO_CCM_CORE_CONTROL                0x41000213#define HALO_CORE_SOFT_RESET                 0x00010214#define HALO_WDT_CONTROL                     0x47000215 216/*217 * HALO MPU banks218 */219#define HALO_MPU_XMEM_ACCESS_0               0x43000220#define HALO_MPU_YMEM_ACCESS_0               0x43004221#define HALO_MPU_WINDOW_ACCESS_0             0x43008222#define HALO_MPU_XREG_ACCESS_0               0x4300C223#define HALO_MPU_YREG_ACCESS_0               0x43014224#define HALO_MPU_XMEM_ACCESS_1               0x43018225#define HALO_MPU_YMEM_ACCESS_1               0x4301C226#define HALO_MPU_WINDOW_ACCESS_1             0x43020227#define HALO_MPU_XREG_ACCESS_1               0x43024228#define HALO_MPU_YREG_ACCESS_1               0x4302C229#define HALO_MPU_XMEM_ACCESS_2               0x43030230#define HALO_MPU_YMEM_ACCESS_2               0x43034231#define HALO_MPU_WINDOW_ACCESS_2             0x43038232#define HALO_MPU_XREG_ACCESS_2               0x4303C233#define HALO_MPU_YREG_ACCESS_2               0x43044234#define HALO_MPU_XMEM_ACCESS_3               0x43048235#define HALO_MPU_YMEM_ACCESS_3               0x4304C236#define HALO_MPU_WINDOW_ACCESS_3             0x43050237#define HALO_MPU_XREG_ACCESS_3               0x43054238#define HALO_MPU_YREG_ACCESS_3               0x4305C239#define HALO_MPU_XM_VIO_ADDR                 0x43100240#define HALO_MPU_XM_VIO_STATUS               0x43104241#define HALO_MPU_YM_VIO_ADDR                 0x43108242#define HALO_MPU_YM_VIO_STATUS               0x4310C243#define HALO_MPU_PM_VIO_ADDR                 0x43110244#define HALO_MPU_PM_VIO_STATUS               0x43114245#define HALO_MPU_LOCK_CONFIG                 0x43140246 247/*248 * HALO_AHBM_WINDOW_DEBUG_1249 */250#define HALO_AHBM_CORE_ERR_ADDR_MASK         0x0fffff00251#define HALO_AHBM_CORE_ERR_ADDR_SHIFT                 8252#define HALO_AHBM_FLAGS_ERR_MASK             0x000000ff253 254/*255 * HALO_CCM_CORE_CONTROL256 */257#define HALO_CORE_RESET                     0x00000200258#define HALO_CORE_EN                        0x00000001259 260/*261 * HALO_CORE_SOFT_RESET262 */263#define HALO_CORE_SOFT_RESET_MASK           0x00000001264 265/*266 * HALO_WDT_CONTROL267 */268#define HALO_WDT_EN_MASK                    0x00000001269 270/*271 * HALO_MPU_?M_VIO_STATUS272 */273#define HALO_MPU_VIO_STS_MASK               0x007e0000274#define HALO_MPU_VIO_STS_SHIFT                      17275#define HALO_MPU_VIO_ERR_WR_MASK            0x00008000276#define HALO_MPU_VIO_ERR_SRC_MASK           0x00007fff277#define HALO_MPU_VIO_ERR_SRC_SHIFT                   0278 279/*280 * Write Sequence281 */282#define WSEQ_OP_MAX_WORDS	3283#define WSEQ_END_OF_SCRIPT	0xFFFFFF284 285struct cs_dsp_ops {286	bool (*validate_version)(struct cs_dsp *dsp, unsigned int version);287	unsigned int (*parse_sizes)(struct cs_dsp *dsp,288				    const char * const file,289				    unsigned int pos,290				    const struct firmware *firmware);291	int (*setup_algs)(struct cs_dsp *dsp);292	unsigned int (*region_to_reg)(struct cs_dsp_region const *mem,293				      unsigned int offset);294 295	void (*show_fw_status)(struct cs_dsp *dsp);296	void (*stop_watchdog)(struct cs_dsp *dsp);297 298	int (*enable_memory)(struct cs_dsp *dsp);299	void (*disable_memory)(struct cs_dsp *dsp);300	int (*lock_memory)(struct cs_dsp *dsp, unsigned int lock_regions);301 302	int (*enable_core)(struct cs_dsp *dsp);303	void (*disable_core)(struct cs_dsp *dsp);304 305	int (*start_core)(struct cs_dsp *dsp);306	void (*stop_core)(struct cs_dsp *dsp);307};308 309static const struct cs_dsp_ops cs_dsp_adsp1_ops;310static const struct cs_dsp_ops cs_dsp_adsp2_ops[];311static const struct cs_dsp_ops cs_dsp_halo_ops;312static const struct cs_dsp_ops cs_dsp_halo_ao_ops;313 314struct cs_dsp_buf {315	struct list_head list;316	void *buf;317};318 319static struct cs_dsp_buf *cs_dsp_buf_alloc(const void *src, size_t len,320					   struct list_head *list)321{322	struct cs_dsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);323 324	if (buf == NULL)325		return NULL;326 327	buf->buf = vmalloc(len);328	if (!buf->buf) {329		kfree(buf);330		return NULL;331	}332	memcpy(buf->buf, src, len);333 334	if (list)335		list_add_tail(&buf->list, list);336 337	return buf;338}339 340static void cs_dsp_buf_free(struct list_head *list)341{342	while (!list_empty(list)) {343		struct cs_dsp_buf *buf = list_first_entry(list,344							  struct cs_dsp_buf,345							  list);346		list_del(&buf->list);347		vfree(buf->buf);348		kfree(buf);349	}350}351 352/**353 * cs_dsp_mem_region_name() - Return a name string for a memory type354 * @type: the memory type to match355 *356 * Return: A const string identifying the memory region.357 */358const char *cs_dsp_mem_region_name(unsigned int type)359{360	switch (type) {361	case WMFW_ADSP1_PM:362		return "PM";363	case WMFW_HALO_PM_PACKED:364		return "PM_PACKED";365	case WMFW_ADSP1_DM:366		return "DM";367	case WMFW_ADSP2_XM:368		return "XM";369	case WMFW_HALO_XM_PACKED:370		return "XM_PACKED";371	case WMFW_ADSP2_YM:372		return "YM";373	case WMFW_HALO_YM_PACKED:374		return "YM_PACKED";375	case WMFW_ADSP1_ZM:376		return "ZM";377	default:378		return NULL;379	}380}381EXPORT_SYMBOL_NS_GPL(cs_dsp_mem_region_name, FW_CS_DSP);382 383#ifdef CONFIG_DEBUG_FS384static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s)385{386	char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);387 388	kfree(dsp->wmfw_file_name);389	dsp->wmfw_file_name = tmp;390}391 392static void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, const char *s)393{394	char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);395 396	kfree(dsp->bin_file_name);397	dsp->bin_file_name = tmp;398}399 400static void cs_dsp_debugfs_clear(struct cs_dsp *dsp)401{402	kfree(dsp->wmfw_file_name);403	kfree(dsp->bin_file_name);404	dsp->wmfw_file_name = NULL;405	dsp->bin_file_name = NULL;406}407 408static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file,409					char __user *user_buf,410					size_t count, loff_t *ppos)411{412	struct cs_dsp *dsp = file->private_data;413	ssize_t ret;414 415	mutex_lock(&dsp->pwr_lock);416 417	if (!dsp->wmfw_file_name || !dsp->booted)418		ret = 0;419	else420		ret = simple_read_from_buffer(user_buf, count, ppos,421					      dsp->wmfw_file_name,422					      strlen(dsp->wmfw_file_name));423 424	mutex_unlock(&dsp->pwr_lock);425	return ret;426}427 428static ssize_t cs_dsp_debugfs_bin_read(struct file *file,429				       char __user *user_buf,430				       size_t count, loff_t *ppos)431{432	struct cs_dsp *dsp = file->private_data;433	ssize_t ret;434 435	mutex_lock(&dsp->pwr_lock);436 437	if (!dsp->bin_file_name || !dsp->booted)438		ret = 0;439	else440		ret = simple_read_from_buffer(user_buf, count, ppos,441					      dsp->bin_file_name,442					      strlen(dsp->bin_file_name));443 444	mutex_unlock(&dsp->pwr_lock);445	return ret;446}447 448static const struct {449	const char *name;450	const struct file_operations fops;451} cs_dsp_debugfs_fops[] = {452	{453		.name = "wmfw_file_name",454		.fops = {455			.open = simple_open,456			.read = cs_dsp_debugfs_wmfw_read,457		},458	},459	{460		.name = "bin_file_name",461		.fops = {462			.open = simple_open,463			.read = cs_dsp_debugfs_bin_read,464		},465	},466};467 468static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg,469				 unsigned int off);470 471static int cs_dsp_debugfs_read_controls_show(struct seq_file *s, void *ignored)472{473	struct cs_dsp *dsp = s->private;474	struct cs_dsp_coeff_ctl *ctl;475	unsigned int reg;476 477	list_for_each_entry(ctl, &dsp->ctl_list, list) {478		cs_dsp_coeff_base_reg(ctl, &reg, 0);479		seq_printf(s, "%22.*s: %#8zx %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n",480			   ctl->subname_len, ctl->subname, ctl->len,481			   cs_dsp_mem_region_name(ctl->alg_region.type),482			   ctl->offset, reg, ctl->fw_name, ctl->alg_region.alg, ctl->type,483			   ctl->flags & WMFW_CTL_FLAG_VOLATILE ? 'V' : '-',484			   ctl->flags & WMFW_CTL_FLAG_SYS ? 'S' : '-',485			   ctl->flags & WMFW_CTL_FLAG_READABLE ? 'R' : '-',486			   ctl->flags & WMFW_CTL_FLAG_WRITEABLE ? 'W' : '-',487			   ctl->enabled ? "enabled" : "disabled",488			   ctl->set ? "dirty" : "clean");489	}490 491	return 0;492}493DEFINE_SHOW_ATTRIBUTE(cs_dsp_debugfs_read_controls);494 495/**496 * cs_dsp_init_debugfs() - Create and populate DSP representation in debugfs497 * @dsp: pointer to DSP structure498 * @debugfs_root: pointer to debugfs directory in which to create this DSP499 *                representation500 */501void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root)502{503	struct dentry *root = NULL;504	int i;505 506	root = debugfs_create_dir(dsp->name, debugfs_root);507 508	debugfs_create_bool("booted", 0444, root, &dsp->booted);509	debugfs_create_bool("running", 0444, root, &dsp->running);510	debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id);511	debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version);512 513	for (i = 0; i < ARRAY_SIZE(cs_dsp_debugfs_fops); ++i)514		debugfs_create_file(cs_dsp_debugfs_fops[i].name, 0444, root,515				    dsp, &cs_dsp_debugfs_fops[i].fops);516 517	debugfs_create_file("controls", 0444, root, dsp,518			    &cs_dsp_debugfs_read_controls_fops);519 520	dsp->debugfs_root = root;521}522EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP);523 524/**525 * cs_dsp_cleanup_debugfs() - Removes DSP representation from debugfs526 * @dsp: pointer to DSP structure527 */528void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp)529{530	cs_dsp_debugfs_clear(dsp);531	debugfs_remove_recursive(dsp->debugfs_root);532	dsp->debugfs_root = ERR_PTR(-ENODEV);533}534EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP);535#else536void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root)537{538}539EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP);540 541void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp)542{543}544EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP);545 546static inline void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp,547						const char *s)548{549}550 551static inline void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp,552					       const char *s)553{554}555 556static inline void cs_dsp_debugfs_clear(struct cs_dsp *dsp)557{558}559#endif560 561static const struct cs_dsp_region *cs_dsp_find_region(struct cs_dsp *dsp,562						      int type)563{564	int i;565 566	for (i = 0; i < dsp->num_mems; i++)567		if (dsp->mem[i].type == type)568			return &dsp->mem[i];569 570	return NULL;571}572 573static unsigned int cs_dsp_region_to_reg(struct cs_dsp_region const *mem,574					 unsigned int offset)575{576	switch (mem->type) {577	case WMFW_ADSP1_PM:578		return mem->base + (offset * 3);579	case WMFW_ADSP1_DM:580	case WMFW_ADSP2_XM:581	case WMFW_ADSP2_YM:582	case WMFW_ADSP1_ZM:583		return mem->base + (offset * 2);584	default:585		WARN(1, "Unknown memory region type");586		return offset;587	}588}589 590static unsigned int cs_dsp_halo_region_to_reg(struct cs_dsp_region const *mem,591					      unsigned int offset)592{593	switch (mem->type) {594	case WMFW_ADSP2_XM:595	case WMFW_ADSP2_YM:596		return mem->base + (offset * 4);597	case WMFW_HALO_XM_PACKED:598	case WMFW_HALO_YM_PACKED:599		return (mem->base + (offset * 3)) & ~0x3;600	case WMFW_HALO_PM_PACKED:601		return mem->base + (offset * 5);602	default:603		WARN(1, "Unknown memory region type");604		return offset;605	}606}607 608static void cs_dsp_read_fw_status(struct cs_dsp *dsp,609				  int noffs, unsigned int *offs)610{611	unsigned int i;612	int ret;613 614	for (i = 0; i < noffs; ++i) {615		ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]);616		if (ret) {617			cs_dsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret);618			return;619		}620	}621}622 623static void cs_dsp_adsp2_show_fw_status(struct cs_dsp *dsp)624{625	unsigned int offs[] = {626		ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3,627	};628 629	cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);630 631	cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",632		   offs[0], offs[1], offs[2], offs[3]);633}634 635static void cs_dsp_adsp2v2_show_fw_status(struct cs_dsp *dsp)636{637	unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 };638 639	cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);640 641	cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",642		   offs[0] & 0xFFFF, offs[0] >> 16,643		   offs[1] & 0xFFFF, offs[1] >> 16);644}645 646static void cs_dsp_halo_show_fw_status(struct cs_dsp *dsp)647{648	unsigned int offs[] = {649		HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4,650	};651 652	cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);653 654	cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",655		   offs[0], offs[1], offs[2], offs[3]);656}657 658static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg,659				 unsigned int off)660{661	const struct cs_dsp_alg_region *alg_region = &ctl->alg_region;662	struct cs_dsp *dsp = ctl->dsp;663	const struct cs_dsp_region *mem;664 665	mem = cs_dsp_find_region(dsp, alg_region->type);666	if (!mem) {667		cs_dsp_err(dsp, "No base for region %x\n",668			   alg_region->type);669		return -EINVAL;670	}671 672	*reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset + off);673 674	return 0;675}676 677/**678 * cs_dsp_coeff_write_acked_control() - Sends event_id to the acked control679 * @ctl: pointer to acked coefficient control680 * @event_id: the value to write to the given acked control681 *682 * Once the value has been written to the control the function shall block683 * until the running firmware acknowledges the write or timeout is exceeded.684 *685 * Must be called with pwr_lock held.686 *687 * Return: Zero for success, a negative number on error.688 */689int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int event_id)690{691	struct cs_dsp *dsp = ctl->dsp;692	__be32 val = cpu_to_be32(event_id);693	unsigned int reg;694	int i, ret;695 696	lockdep_assert_held(&dsp->pwr_lock);697 698	if (!dsp->running)699		return -EPERM;700 701	ret = cs_dsp_coeff_base_reg(ctl, &reg, 0);702	if (ret)703		return ret;704 705	cs_dsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",706		   event_id, ctl->alg_region.alg,707		   cs_dsp_mem_region_name(ctl->alg_region.type), ctl->offset);708 709	ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));710	if (ret) {711		cs_dsp_err(dsp, "Failed to write %x: %d\n", reg, ret);712		return ret;713	}714 715	/*716	 * Poll for ack, we initially poll at ~1ms intervals for firmwares717	 * that respond quickly, then go to ~10ms polls. A firmware is unlikely718	 * to ack instantly so we do the first 1ms delay before reading the719	 * control to avoid a pointless bus transaction720	 */721	for (i = 0; i < CS_DSP_ACKED_CTL_TIMEOUT_MS;) {722		switch (i) {723		case 0 ... CS_DSP_ACKED_CTL_N_QUICKPOLLS - 1:724			usleep_range(1000, 2000);725			i++;726			break;727		default:728			usleep_range(10000, 20000);729			i += 10;730			break;731		}732 733		ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));734		if (ret) {735			cs_dsp_err(dsp, "Failed to read %x: %d\n", reg, ret);736			return ret;737		}738 739		if (val == 0) {740			cs_dsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);741			return 0;742		}743	}744 745	cs_dsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",746		    reg, ctl->alg_region.alg,747		    cs_dsp_mem_region_name(ctl->alg_region.type),748		    ctl->offset);749 750	return -ETIMEDOUT;751}752EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_acked_control, FW_CS_DSP);753 754static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl,755				       unsigned int off, const void *buf, size_t len)756{757	struct cs_dsp *dsp = ctl->dsp;758	void *scratch;759	int ret;760	unsigned int reg;761 762	ret = cs_dsp_coeff_base_reg(ctl, &reg, off);763	if (ret)764		return ret;765 766	scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);767	if (!scratch)768		return -ENOMEM;769 770	ret = regmap_raw_write(dsp->regmap, reg, scratch,771			       len);772	if (ret) {773		cs_dsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",774			   len, reg, ret);775		kfree(scratch);776		return ret;777	}778	cs_dsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);779 780	kfree(scratch);781 782	return 0;783}784 785/**786 * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control787 * @ctl: pointer to coefficient control788 * @off: word offset at which data should be written789 * @buf: the buffer to write to the given control790 * @len: the length of the buffer in bytes791 *792 * Must be called with pwr_lock held.793 *794 * Return: < 0 on error, 1 when the control value changed and 0 when it has not.795 */796int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl,797			    unsigned int off, const void *buf, size_t len)798{799	int ret = 0;800 801	if (!ctl)802		return -ENOENT;803 804	lockdep_assert_held(&ctl->dsp->pwr_lock);805 806	if (ctl->flags && !(ctl->flags & WMFW_CTL_FLAG_WRITEABLE))807		return -EPERM;808 809	if (len + off * sizeof(u32) > ctl->len)810		return -EINVAL;811 812	if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {813		ret = -EPERM;814	} else if (buf != ctl->cache) {815		if (memcmp(ctl->cache + off * sizeof(u32), buf, len))816			memcpy(ctl->cache + off * sizeof(u32), buf, len);817		else818			return 0;819	}820 821	ctl->set = 1;822	if (ctl->enabled && ctl->dsp->running)823		ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len);824 825	if (ret < 0)826		return ret;827 828	return 1;829}830EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_ctrl, FW_CS_DSP);831 832/**833 * cs_dsp_coeff_lock_and_write_ctrl() - Writes the given buffer to the given coefficient control834 * @ctl: pointer to coefficient control835 * @off: word offset at which data should be written836 * @buf: the buffer to write to the given control837 * @len: the length of the buffer in bytes838 *839 * Same as cs_dsp_coeff_write_ctrl() but takes pwr_lock.840 *841 * Return: A negative number on error, 1 when the control value changed and 0 when it has not.842 */843int cs_dsp_coeff_lock_and_write_ctrl(struct cs_dsp_coeff_ctl *ctl,844				     unsigned int off, const void *buf, size_t len)845{846	struct cs_dsp *dsp = ctl->dsp;847	int ret;848 849	lockdep_assert_not_held(&dsp->pwr_lock);850 851	mutex_lock(&dsp->pwr_lock);852	ret = cs_dsp_coeff_write_ctrl(ctl, off, buf, len);853	mutex_unlock(&dsp->pwr_lock);854 855	return ret;856}857EXPORT_SYMBOL_GPL(cs_dsp_coeff_lock_and_write_ctrl);858 859static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl,860				      unsigned int off, void *buf, size_t len)861{862	struct cs_dsp *dsp = ctl->dsp;863	void *scratch;864	int ret;865	unsigned int reg;866 867	ret = cs_dsp_coeff_base_reg(ctl, &reg, off);868	if (ret)869		return ret;870 871	scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);872	if (!scratch)873		return -ENOMEM;874 875	ret = regmap_raw_read(dsp->regmap, reg, scratch, len);876	if (ret) {877		cs_dsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",878			   len, reg, ret);879		kfree(scratch);880		return ret;881	}882	cs_dsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);883 884	memcpy(buf, scratch, len);885	kfree(scratch);886 887	return 0;888}889 890/**891 * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer892 * @ctl: pointer to coefficient control893 * @off: word offset at which data should be read894 * @buf: the buffer to store to the given control895 * @len: the length of the buffer in bytes896 *897 * Must be called with pwr_lock held.898 *899 * Return: Zero for success, a negative number on error.900 */901int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl,902			   unsigned int off, void *buf, size_t len)903{904	int ret = 0;905 906	if (!ctl)907		return -ENOENT;908 909	lockdep_assert_held(&ctl->dsp->pwr_lock);910 911	if (len + off * sizeof(u32) > ctl->len)912		return -EINVAL;913 914	if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {915		if (ctl->enabled && ctl->dsp->running)916			return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len);917		else918			return -EPERM;919	} else {920		if (!ctl->flags && ctl->enabled && ctl->dsp->running)921			ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len);922 923		if (buf != ctl->cache)924			memcpy(buf, ctl->cache + off * sizeof(u32), len);925	}926 927	return ret;928}929EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_read_ctrl, FW_CS_DSP);930 931/**932 * cs_dsp_coeff_lock_and_read_ctrl() - Reads the given coefficient control into the given buffer933 * @ctl: pointer to coefficient control934 * @off: word offset at which data should be read935 * @buf: the buffer to store to the given control936 * @len: the length of the buffer in bytes937 *938 * Same as cs_dsp_coeff_read_ctrl() but takes pwr_lock.939 *940 * Return: Zero for success, a negative number on error.941 */942int cs_dsp_coeff_lock_and_read_ctrl(struct cs_dsp_coeff_ctl *ctl,943				    unsigned int off, void *buf, size_t len)944{945	struct cs_dsp *dsp = ctl->dsp;946	int ret;947 948	lockdep_assert_not_held(&dsp->pwr_lock);949 950	mutex_lock(&dsp->pwr_lock);951	ret = cs_dsp_coeff_read_ctrl(ctl, off, buf, len);952	mutex_unlock(&dsp->pwr_lock);953 954	return ret;955}956EXPORT_SYMBOL_GPL(cs_dsp_coeff_lock_and_read_ctrl);957 958static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp)959{960	struct cs_dsp_coeff_ctl *ctl;961	int ret;962 963	list_for_each_entry(ctl, &dsp->ctl_list, list) {964		if (!ctl->enabled || ctl->set)965			continue;966		if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)967			continue;968 969		/*970		 * For readable controls populate the cache from the DSP memory.971		 * For non-readable controls the cache was zero-filled when972		 * created so we don't need to do anything.973		 */974		if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) {975			ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len);976			if (ret < 0)977				return ret;978		}979	}980 981	return 0;982}983 984static int cs_dsp_coeff_sync_controls(struct cs_dsp *dsp)985{986	struct cs_dsp_coeff_ctl *ctl;987	int ret;988 989	list_for_each_entry(ctl, &dsp->ctl_list, list) {990		if (!ctl->enabled)991			continue;992		if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {993			ret = cs_dsp_coeff_write_ctrl_raw(ctl, 0, ctl->cache,994							  ctl->len);995			if (ret < 0)996				return ret;997		}998	}999 1000	return 0;1001}1002 1003static void cs_dsp_signal_event_controls(struct cs_dsp *dsp,1004					 unsigned int event)1005{1006	struct cs_dsp_coeff_ctl *ctl;1007	int ret;1008 1009	list_for_each_entry(ctl, &dsp->ctl_list, list) {1010		if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)1011			continue;1012 1013		if (!ctl->enabled)1014			continue;1015 1016		ret = cs_dsp_coeff_write_acked_control(ctl, event);1017		if (ret)1018			cs_dsp_warn(dsp,1019				    "Failed to send 0x%x event to alg 0x%x (%d)\n",1020				    event, ctl->alg_region.alg, ret);1021	}1022}1023 1024static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl)1025{1026	kfree(ctl->cache);1027	kfree(ctl->subname);1028	kfree(ctl);1029}1030 1031static int cs_dsp_create_control(struct cs_dsp *dsp,1032				 const struct cs_dsp_alg_region *alg_region,1033				 unsigned int offset, unsigned int len,1034				 const char *subname, unsigned int subname_len,1035				 unsigned int flags, unsigned int type)1036{1037	struct cs_dsp_coeff_ctl *ctl;1038	int ret;1039 1040	list_for_each_entry(ctl, &dsp->ctl_list, list) {1041		if (ctl->fw_name == dsp->fw_name &&1042		    ctl->alg_region.alg == alg_region->alg &&1043		    ctl->alg_region.type == alg_region->type) {1044			if ((!subname && !ctl->subname) ||1045			    (subname && (ctl->subname_len == subname_len) &&1046			     !strncmp(ctl->subname, subname, ctl->subname_len))) {1047				if (!ctl->enabled)1048					ctl->enabled = 1;1049				return 0;1050			}1051		}1052	}1053 1054	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);1055	if (!ctl)1056		return -ENOMEM;1057 1058	ctl->fw_name = dsp->fw_name;1059	ctl->alg_region = *alg_region;1060	if (subname && dsp->wmfw_ver >= 2) {1061		ctl->subname_len = subname_len;1062		ctl->subname = kasprintf(GFP_KERNEL, "%.*s", subname_len, subname);1063		if (!ctl->subname) {1064			ret = -ENOMEM;1065			goto err_ctl;1066		}1067	}1068	ctl->enabled = 1;1069	ctl->set = 0;1070	ctl->dsp = dsp;1071 1072	ctl->flags = flags;1073	ctl->type = type;1074	ctl->offset = offset;1075	ctl->len = len;1076	ctl->cache = kzalloc(ctl->len, GFP_KERNEL);1077	if (!ctl->cache) {1078		ret = -ENOMEM;1079		goto err_ctl_subname;1080	}1081 1082	list_add(&ctl->list, &dsp->ctl_list);1083 1084	if (dsp->client_ops->control_add) {1085		ret = dsp->client_ops->control_add(ctl);1086		if (ret)1087			goto err_list_del;1088	}1089 1090	return 0;1091 1092err_list_del:1093	list_del(&ctl->list);1094	kfree(ctl->cache);1095err_ctl_subname:1096	kfree(ctl->subname);1097err_ctl:1098	kfree(ctl);1099 1100	return ret;1101}1102 1103struct cs_dsp_coeff_parsed_alg {1104	int id;1105	const u8 *name;1106	int name_len;1107	int ncoeff;1108};1109 1110struct cs_dsp_coeff_parsed_coeff {1111	int offset;1112	int mem_type;1113	const u8 *name;1114	int name_len;1115	unsigned int ctl_type;1116	int flags;1117	int len;1118};1119 1120static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, unsigned int avail,1121				     const u8 **str)1122{1123	int length, total_field_len;1124 1125	/* String fields are at least one __le32 */1126	if (sizeof(__le32) > avail) {1127		*pos = NULL;1128		return 0;1129	}1130 1131	switch (bytes) {1132	case 1:1133		length = **pos;1134		break;1135	case 2:1136		length = le16_to_cpu(*((__le16 *)*pos));1137		break;1138	default:1139		return 0;1140	}1141 1142	total_field_len = ((length + bytes) + 3) & ~0x03;1143	if ((unsigned int)total_field_len > avail) {1144		*pos = NULL;1145		return 0;1146	}1147 1148	if (str)1149		*str = *pos + bytes;1150 1151	*pos += total_field_len;1152 1153	return length;1154}1155 1156static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos)1157{1158	int val = 0;1159 1160	switch (bytes) {1161	case 2:1162		val = le16_to_cpu(*((__le16 *)*pos));1163		break;1164	case 4:1165		val = le32_to_cpu(*((__le32 *)*pos));1166		break;1167	default:1168		break;1169	}1170 1171	*pos += bytes;1172 1173	return val;1174}1175 1176static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp,1177				  const struct wmfw_region *region,1178				  struct cs_dsp_coeff_parsed_alg *blk)1179{1180	const struct wmfw_adsp_alg_data *raw;1181	unsigned int data_len = le32_to_cpu(region->len);1182	unsigned int pos;1183	const u8 *tmp;1184 1185	raw = (const struct wmfw_adsp_alg_data *)region->data;1186 1187	switch (dsp->wmfw_ver) {1188	case 0:1189	case 1:1190		if (sizeof(*raw) > data_len)1191			return -EOVERFLOW;1192 1193		blk->id = le32_to_cpu(raw->id);1194		blk->name = raw->name;1195		blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));1196		blk->ncoeff = le32_to_cpu(raw->ncoeff);1197 1198		pos = sizeof(*raw);1199		break;1200	default:1201		if (sizeof(raw->id) > data_len)1202			return -EOVERFLOW;1203 1204		tmp = region->data;1205		blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), &tmp);1206		pos = tmp - region->data;1207 1208		tmp = &region->data[pos];1209		blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,1210							  &blk->name);1211		if (!tmp)1212			return -EOVERFLOW;1213 1214		pos = tmp - region->data;1215		cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);1216		if (!tmp)1217			return -EOVERFLOW;1218 1219		pos = tmp - region->data;1220		if (sizeof(raw->ncoeff) > (data_len - pos))1221			return -EOVERFLOW;1222 1223		blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), &tmp);1224		pos += sizeof(raw->ncoeff);1225		break;1226	}1227 1228	if ((int)blk->ncoeff < 0)1229		return -EOVERFLOW;1230 1231	cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);1232	cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);1233	cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);1234 1235	return pos;1236}1237 1238static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp,1239				    const struct wmfw_region *region,1240				    unsigned int pos,1241				    struct cs_dsp_coeff_parsed_coeff *blk)1242{1243	const struct wmfw_adsp_coeff_data *raw;1244	unsigned int data_len = le32_to_cpu(region->len);1245	unsigned int blk_len, blk_end_pos;1246	const u8 *tmp;1247 1248	raw = (const struct wmfw_adsp_coeff_data *)&region->data[pos];1249	if (sizeof(raw->hdr) > (data_len - pos))1250		return -EOVERFLOW;1251 1252	blk_len = le32_to_cpu(raw->hdr.size);1253	if (blk_len > S32_MAX)1254		return -EOVERFLOW;1255 1256	if (blk_len > (data_len - pos - sizeof(raw->hdr)))1257		return -EOVERFLOW;1258 1259	blk_end_pos = pos + sizeof(raw->hdr) + blk_len;1260 1261	blk->offset = le16_to_cpu(raw->hdr.offset);1262	blk->mem_type = le16_to_cpu(raw->hdr.type);1263 1264	switch (dsp->wmfw_ver) {1265	case 0:1266	case 1:1267		if (sizeof(*raw) > (data_len - pos))1268			return -EOVERFLOW;1269 1270		blk->name = raw->name;1271		blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));1272		blk->ctl_type = le16_to_cpu(raw->ctl_type);1273		blk->flags = le16_to_cpu(raw->flags);1274		blk->len = le32_to_cpu(raw->len);1275		break;1276	default:1277		pos += sizeof(raw->hdr);1278		tmp = &region->data[pos];1279		blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,1280							  &blk->name);1281		if (!tmp)1282			return -EOVERFLOW;1283 1284		pos = tmp - region->data;1285		cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, NULL);1286		if (!tmp)1287			return -EOVERFLOW;1288 1289		pos = tmp - region->data;1290		cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);1291		if (!tmp)1292			return -EOVERFLOW;1293 1294		pos = tmp - region->data;1295		if (sizeof(raw->ctl_type) + sizeof(raw->flags) + sizeof(raw->len) >1296		    (data_len - pos))1297			return -EOVERFLOW;1298 1299		blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp);1300		pos += sizeof(raw->ctl_type);1301		blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp);1302		pos += sizeof(raw->flags);1303		blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp);1304		break;1305	}1306 1307	cs_dsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);1308	cs_dsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);1309	cs_dsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);1310	cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);1311	cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);1312	cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);1313 1314	return blk_end_pos;1315}1316 1317static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp,1318				    const struct cs_dsp_coeff_parsed_coeff *coeff_blk,1319				    unsigned int f_required,1320				    unsigned int f_illegal)1321{1322	if ((coeff_blk->flags & f_illegal) ||1323	    ((coeff_blk->flags & f_required) != f_required)) {1324		cs_dsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",1325			   coeff_blk->flags, coeff_blk->ctl_type);1326		return -EINVAL;1327	}1328 1329	return 0;1330}1331 1332static int cs_dsp_parse_coeff(struct cs_dsp *dsp,1333			      const struct wmfw_region *region)1334{1335	struct cs_dsp_alg_region alg_region = {};1336	struct cs_dsp_coeff_parsed_alg alg_blk;1337	struct cs_dsp_coeff_parsed_coeff coeff_blk;1338	int i, pos, ret;1339 1340	pos = cs_dsp_coeff_parse_alg(dsp, region, &alg_blk);1341	if (pos < 0)1342		return pos;1343 1344	for (i = 0; i < alg_blk.ncoeff; i++) {1345		pos = cs_dsp_coeff_parse_coeff(dsp, region, pos, &coeff_blk);1346		if (pos < 0)1347			return pos;1348 1349		switch (coeff_blk.ctl_type) {1350		case WMFW_CTL_TYPE_BYTES:1351			break;1352		case WMFW_CTL_TYPE_ACKED:1353			if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)1354				continue;	/* ignore */1355 1356			ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk,1357						       WMFW_CTL_FLAG_VOLATILE |1358						       WMFW_CTL_FLAG_WRITEABLE |1359						       WMFW_CTL_FLAG_READABLE,1360						       0);1361			if (ret)1362				return -EINVAL;1363			break;1364		case WMFW_CTL_TYPE_HOSTEVENT:1365		case WMFW_CTL_TYPE_FWEVENT:1366			ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk,1367						       WMFW_CTL_FLAG_SYS |1368						       WMFW_CTL_FLAG_VOLATILE |1369						       WMFW_CTL_FLAG_WRITEABLE |1370						       WMFW_CTL_FLAG_READABLE,1371						       0);1372			if (ret)1373				return -EINVAL;1374			break;1375		case WMFW_CTL_TYPE_HOST_BUFFER:1376			ret = cs_dsp_check_coeff_flags(dsp, &coeff_blk,1377						       WMFW_CTL_FLAG_SYS |1378						       WMFW_CTL_FLAG_VOLATILE |1379						       WMFW_CTL_FLAG_READABLE,1380						       0);1381			if (ret)1382				return -EINVAL;1383			break;1384		default:1385			cs_dsp_err(dsp, "Unknown control type: %d\n",1386				   coeff_blk.ctl_type);1387			return -EINVAL;1388		}1389 1390		alg_region.type = coeff_blk.mem_type;1391		alg_region.alg = alg_blk.id;1392 1393		ret = cs_dsp_create_control(dsp, &alg_region,1394					    coeff_blk.offset,1395					    coeff_blk.len,1396					    coeff_blk.name,1397					    coeff_blk.name_len,1398					    coeff_blk.flags,1399					    coeff_blk.ctl_type);1400		if (ret < 0)1401			cs_dsp_err(dsp, "Failed to create control: %.*s, %d\n",1402				   coeff_blk.name_len, coeff_blk.name, ret);1403	}1404 1405	return 0;1406}1407 1408static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp,1409					     const char * const file,1410					     unsigned int pos,1411					     const struct firmware *firmware)1412{1413	const struct wmfw_adsp1_sizes *adsp1_sizes;1414 1415	adsp1_sizes = (void *)&firmware->data[pos];1416	if (sizeof(*adsp1_sizes) > firmware->size - pos) {1417		cs_dsp_err(dsp, "%s: file truncated\n", file);1418		return 0;1419	}1420 1421	cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file,1422		   le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm),1423		   le32_to_cpu(adsp1_sizes->zm));1424 1425	return pos + sizeof(*adsp1_sizes);1426}1427 1428static unsigned int cs_dsp_adsp2_parse_sizes(struct cs_dsp *dsp,1429					     const char * const file,1430					     unsigned int pos,1431					     const struct firmware *firmware)1432{1433	const struct wmfw_adsp2_sizes *adsp2_sizes;1434 1435	adsp2_sizes = (void *)&firmware->data[pos];1436	if (sizeof(*adsp2_sizes) > firmware->size - pos) {1437		cs_dsp_err(dsp, "%s: file truncated\n", file);1438		return 0;1439	}1440 1441	cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file,1442		   le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym),1443		   le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm));1444 1445	return pos + sizeof(*adsp2_sizes);1446}1447 1448static bool cs_dsp_validate_version(struct cs_dsp *dsp, unsigned int version)1449{1450	switch (version) {1451	case 0:1452		cs_dsp_warn(dsp, "Deprecated file format %d\n", version);1453		return true;1454	case 1:1455	case 2:1456		return true;1457	default:1458		return false;1459	}1460}1461 1462static bool cs_dsp_halo_validate_version(struct cs_dsp *dsp, unsigned int version)1463{1464	switch (version) {1465	case 3:1466		return true;1467	default:1468		return false;1469	}1470}1471 1472static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,1473		       const char *file)1474{1475	LIST_HEAD(buf_list);1476	struct regmap *regmap = dsp->regmap;1477	unsigned int pos = 0;1478	const struct wmfw_header *header;1479	const struct wmfw_footer *footer;1480	const struct wmfw_region *region;1481	const struct cs_dsp_region *mem;1482	const char *region_name;1483	struct cs_dsp_buf *buf;1484	unsigned int reg;1485	int regions = 0;1486	int ret, offset, type;1487 1488	if (!firmware)1489		return 0;1490 1491	ret = -EINVAL;1492 1493	if (sizeof(*header) >= firmware->size) {1494		ret = -EOVERFLOW;1495		goto out_fw;1496	}1497 1498	header = (void *)&firmware->data[0];1499 1500	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {1501		cs_dsp_err(dsp, "%s: invalid magic\n", file);1502		goto out_fw;1503	}1504 1505	if (!dsp->ops->validate_version(dsp, header->ver)) {1506		cs_dsp_err(dsp, "%s: unknown file format %d\n",1507			   file, header->ver);1508		goto out_fw;1509	}1510 1511	dsp->wmfw_ver = header->ver;1512 1513	if (header->core != dsp->type) {1514		cs_dsp_err(dsp, "%s: invalid core %d != %d\n",1515			   file, header->core, dsp->type);1516		goto out_fw;1517	}1518 1519	pos = sizeof(*header);1520	pos = dsp->ops->parse_sizes(dsp, file, pos, firmware);1521	if ((pos == 0) || (sizeof(*footer) > firmware->size - pos)) {1522		ret = -EOVERFLOW;1523		goto out_fw;1524	}1525 1526	footer = (void *)&firmware->data[pos];1527	pos += sizeof(*footer);1528 1529	if (le32_to_cpu(header->len) != pos) {1530		ret = -EOVERFLOW;1531		goto out_fw;1532	}1533 1534	cs_dsp_info(dsp, "%s: format %d timestamp %#llx\n", file, header->ver,1535		    le64_to_cpu(footer->timestamp));1536 1537	while (pos < firmware->size) {1538		/* Is there enough data for a complete block header? */1539		if (sizeof(*region) > firmware->size - pos) {1540			ret = -EOVERFLOW;1541			goto out_fw;1542		}1543 1544		region = (void *)&(firmware->data[pos]);1545 1546		if (le32_to_cpu(region->len) > firmware->size - pos - sizeof(*region)) {1547			ret = -EOVERFLOW;1548			goto out_fw;1549		}1550 1551		region_name = "Unknown";1552		reg = 0;1553		offset = le32_to_cpu(region->offset) & 0xffffff;1554		type = be32_to_cpu(region->type) & 0xff;1555 1556		switch (type) {1557		case WMFW_INFO_TEXT:1558		case WMFW_NAME_TEXT:1559			region_name = "Info/Name";1560			cs_dsp_info(dsp, "%s: %.*s\n", file,1561				    min(le32_to_cpu(region->len), 100), region->data);1562			break;1563		case WMFW_ALGORITHM_DATA:1564			region_name = "Algorithm";1565			ret = cs_dsp_parse_coeff(dsp, region);1566			if (ret != 0)1567				goto out_fw;1568			break;1569		case WMFW_ABSOLUTE:1570			region_name = "Absolute";1571			reg = offset;1572			break;1573		case WMFW_ADSP1_PM:1574		case WMFW_ADSP1_DM:1575		case WMFW_ADSP2_XM:1576		case WMFW_ADSP2_YM:1577		case WMFW_ADSP1_ZM:1578		case WMFW_HALO_PM_PACKED:1579		case WMFW_HALO_XM_PACKED:1580		case WMFW_HALO_YM_PACKED:1581			mem = cs_dsp_find_region(dsp, type);1582			if (!mem) {1583				cs_dsp_err(dsp, "No region of type: %x\n", type);1584				ret = -EINVAL;1585				goto out_fw;1586			}1587 1588			region_name = cs_dsp_mem_region_name(type);1589			reg = dsp->ops->region_to_reg(mem, offset);1590			break;1591		default:1592			cs_dsp_warn(dsp,1593				    "%s.%d: Unknown region type %x at %d(%x)\n",1594				    file, regions, type, pos, pos);1595			break;1596		}1597 1598		cs_dsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,1599			   regions, le32_to_cpu(region->len), offset,1600			   region_name);1601 1602		if (reg) {1603			buf = cs_dsp_buf_alloc(region->data,1604					       le32_to_cpu(region->len),1605					       &buf_list);1606			if (!buf) {1607				cs_dsp_err(dsp, "Out of memory\n");1608				ret = -ENOMEM;1609				goto out_fw;1610			}1611 1612			ret = regmap_raw_write_async(regmap, reg, buf->buf,1613						     le32_to_cpu(region->len));1614			if (ret != 0) {1615				cs_dsp_err(dsp,1616					   "%s.%d: Failed to write %d bytes at %d in %s: %d\n",1617					   file, regions,1618					   le32_to_cpu(region->len), offset,1619					   region_name, ret);1620				goto out_fw;1621			}1622		}1623 1624		pos += le32_to_cpu(region->len) + sizeof(*region);1625		regions++;1626	}1627 1628	ret = regmap_async_complete(regmap);1629	if (ret != 0) {1630		cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);1631		goto out_fw;1632	}1633 1634	if (pos > firmware->size)1635		cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",1636			    file, regions, pos - firmware->size);1637 1638	cs_dsp_debugfs_save_wmfwname(dsp, file);1639 1640out_fw:1641	regmap_async_complete(regmap);1642	cs_dsp_buf_free(&buf_list);1643 1644	if (ret == -EOVERFLOW)1645		cs_dsp_err(dsp, "%s: file content overflows file data\n", file);1646 1647	return ret;1648}1649 1650/**1651 * cs_dsp_get_ctl() - Finds a matching coefficient control1652 * @dsp: pointer to DSP structure1653 * @name: pointer to string to match with a control's subname1654 * @type: the algorithm type to match1655 * @alg: the algorithm id to match1656 *1657 * Find cs_dsp_coeff_ctl with input name as its subname1658 *1659 * Return: pointer to the control on success, NULL if not found1660 */1661struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, int type,1662					unsigned int alg)1663{1664	struct cs_dsp_coeff_ctl *pos, *rslt = NULL;1665 1666	lockdep_assert_held(&dsp->pwr_lock);1667 1668	list_for_each_entry(pos, &dsp->ctl_list, list) {1669		if (!pos->subname)1670			continue;1671		if (strncmp(pos->subname, name, pos->subname_len) == 0 &&1672		    pos->fw_name == dsp->fw_name &&1673		    pos->alg_region.alg == alg &&1674		    pos->alg_region.type == type) {1675			rslt = pos;1676			break;1677		}1678	}1679 1680	return rslt;1681}1682EXPORT_SYMBOL_NS_GPL(cs_dsp_get_ctl, FW_CS_DSP);1683 1684static void cs_dsp_ctl_fixup_base(struct cs_dsp *dsp,1685				  const struct cs_dsp_alg_region *alg_region)1686{1687	struct cs_dsp_coeff_ctl *ctl;1688 1689	list_for_each_entry(ctl, &dsp->ctl_list, list) {1690		if (ctl->fw_name == dsp->fw_name &&1691		    alg_region->alg == ctl->alg_region.alg &&1692		    alg_region->type == ctl->alg_region.type) {1693			ctl->alg_region.base = alg_region->base;1694		}1695	}1696}1697 1698static void *cs_dsp_read_algs(struct cs_dsp *dsp, size_t n_algs,1699			      const struct cs_dsp_region *mem,1700			      unsigned int pos, unsigned int len)1701{1702	void *alg;1703	unsigned int reg;1704	int ret;1705	__be32 val;1706 1707	if (n_algs == 0) {1708		cs_dsp_err(dsp, "No algorithms\n");1709		return ERR_PTR(-EINVAL);1710	}1711 1712	if (n_algs > 1024) {1713		cs_dsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);1714		return ERR_PTR(-EINVAL);1715	}1716 1717	/* Read the terminator first to validate the length */1718	reg = dsp->ops->region_to_reg(mem, pos + len);1719 1720	ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));1721	if (ret != 0) {1722		cs_dsp_err(dsp, "Failed to read algorithm list end: %d\n",1723			   ret);1724		return ERR_PTR(ret);1725	}1726 1727	if (be32_to_cpu(val) != 0xbedead)1728		cs_dsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n",1729			    reg, be32_to_cpu(val));1730 1731	/* Convert length from DSP words to bytes */1732	len *= sizeof(u32);1733 1734	alg = kzalloc(len, GFP_KERNEL | GFP_DMA);1735	if (!alg)1736		return ERR_PTR(-ENOMEM);1737 1738	reg = dsp->ops->region_to_reg(mem, pos);1739 1740	ret = regmap_raw_read(dsp->regmap, reg, alg, len);1741	if (ret != 0) {1742		cs_dsp_err(dsp, "Failed to read algorithm list: %d\n", ret);1743		kfree(alg);1744		return ERR_PTR(ret);1745	}1746 1747	return alg;1748}1749 1750/**1751 * cs_dsp_find_alg_region() - Finds a matching algorithm region1752 * @dsp: pointer to DSP structure1753 * @type: the algorithm type to match1754 * @id: the algorithm id to match1755 *1756 * Return: Pointer to matching algorithm region, or NULL if not found.1757 */1758struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp,1759						 int type, unsigned int id)1760{1761	struct cs_dsp_alg_region *alg_region;1762 1763	lockdep_assert_held(&dsp->pwr_lock);1764 1765	list_for_each_entry(alg_region, &dsp->alg_regions, list) {1766		if (id == alg_region->alg && type == alg_region->type)1767			return alg_region;1768	}1769 1770	return NULL;1771}1772EXPORT_SYMBOL_NS_GPL(cs_dsp_find_alg_region, FW_CS_DSP);1773 1774static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp,1775						      int type, __be32 id,1776						      __be32 ver, __be32 base)1777{1778	struct cs_dsp_alg_region *alg_region;1779 1780	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);1781	if (!alg_region)1782		return ERR_PTR(-ENOMEM);1783 1784	alg_region->type = type;1785	alg_region->alg = be32_to_cpu(id);1786	alg_region->ver = be32_to_cpu(ver);1787	alg_region->base = be32_to_cpu(base);1788 1789	list_add_tail(&alg_region->list, &dsp->alg_regions);1790 1791	if (dsp->wmfw_ver > 0)1792		cs_dsp_ctl_fixup_base(dsp, alg_region);1793 1794	return alg_region;1795}1796 1797static void cs_dsp_free_alg_regions(struct cs_dsp *dsp)1798{1799	struct cs_dsp_alg_region *alg_region;1800 1801	while (!list_empty(&dsp->alg_regions)) {1802		alg_region = list_first_entry(&dsp->alg_regions,1803					      struct cs_dsp_alg_region,1804					      list);1805		list_del(&alg_region->list);1806		kfree(alg_region);1807	}1808}1809 1810static void cs_dsp_parse_wmfw_id_header(struct cs_dsp *dsp,1811					struct wmfw_id_hdr *fw, int nalgs)1812{1813	dsp->fw_id = be32_to_cpu(fw->id);1814	dsp->fw_id_version = be32_to_cpu(fw->ver);1815 1816	cs_dsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n",1817		    dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16,1818		    (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff,1819		    nalgs);1820}1821 1822static void cs_dsp_parse_wmfw_v3_id_header(struct cs_dsp *dsp,1823					   struct wmfw_v3_id_hdr *fw, int nalgs)1824{1825	dsp->fw_id = be32_to_cpu(fw->id);1826	dsp->fw_id_version = be32_to_cpu(fw->ver);1827	dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id);1828 1829	cs_dsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n",1830		    dsp->fw_id, dsp->fw_vendor_id,1831		    (dsp->fw_id_version & 0xff0000) >> 16,1832		    (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff,1833		    nalgs);1834}1835 1836static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver,1837				 int nregions, const int *type, __be32 *base)1838{1839	struct cs_dsp_alg_region *alg_region;1840	int i;1841 1842	for (i = 0; i < nregions; i++) {1843		alg_region = cs_dsp_create_region(dsp, type[i], id, ver, base[i]);1844		if (IS_ERR(alg_region))1845			return PTR_ERR(alg_region);1846	}1847 1848	return 0;1849}1850 1851static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp)1852{1853	struct wmfw_adsp1_id_hdr adsp1_id;1854	struct wmfw_adsp1_alg_hdr *adsp1_alg;1855	struct cs_dsp_alg_region *alg_region;1856	const struct cs_dsp_region *mem;1857	unsigned int pos, len;1858	size_t n_algs;1859	int i, ret;1860 1861	mem = cs_dsp_find_region(dsp, WMFW_ADSP1_DM);1862	if (WARN_ON(!mem))1863		return -EINVAL;1864 1865	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,1866			      sizeof(adsp1_id));1867	if (ret != 0) {1868		cs_dsp_err(dsp, "Failed to read algorithm info: %d\n",1869			   ret);1870		return ret;1871	}1872 1873	n_algs = be32_to_cpu(adsp1_id.n_algs);1874 1875	cs_dsp_parse_wmfw_id_header(dsp, &adsp1_id.fw, n_algs);1876 1877	alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM,1878					  adsp1_id.fw.id, adsp1_id.fw.ver,1879					  adsp1_id.zm);1880	if (IS_ERR(alg_region))1881		return PTR_ERR(alg_region);1882 1883	alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM,1884					  adsp1_id.fw.id, adsp1_id.fw.ver,1885					  adsp1_id.dm);1886	if (IS_ERR(alg_region))1887		return PTR_ERR(alg_region);1888 1889	/* Calculate offset and length in DSP words */1890	pos = sizeof(adsp1_id) / sizeof(u32);1891	len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32);1892 1893	adsp1_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len);1894	if (IS_ERR(adsp1_alg))1895		return PTR_ERR(adsp1_alg);1896 1897	for (i = 0; i < n_algs; i++) {1898		cs_dsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",1899			    i, be32_to_cpu(adsp1_alg[i].alg.id),1900			    (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,1901			    (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,1902			    be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,1903			    be32_to_cpu(adsp1_alg[i].dm),1904			    be32_to_cpu(adsp1_alg[i].zm));1905 1906		alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM,1907						  adsp1_alg[i].alg.id,1908						  adsp1_alg[i].alg.ver,1909						  adsp1_alg[i].dm);1910		if (IS_ERR(alg_region)) {1911			ret = PTR_ERR(alg_region);1912			goto out;1913		}1914		if (dsp->wmfw_ver == 0) {1915			if (i + 1 < n_algs) {1916				len = be32_to_cpu(adsp1_alg[i + 1].dm);1917				len -= be32_to_cpu(adsp1_alg[i].dm);1918				len *= 4;1919				cs_dsp_create_control(dsp, alg_region, 0,1920						      len, NULL, 0, 0,1921						      WMFW_CTL_TYPE_BYTES);1922			} else {1923				cs_dsp_warn(dsp, "Missing length info for region DM with ID %x\n",1924					    be32_to_cpu(adsp1_alg[i].alg.id));1925			}1926		}1927 1928		alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM,1929						  adsp1_alg[i].alg.id,1930						  adsp1_alg[i].alg.ver,1931						  adsp1_alg[i].zm);1932		if (IS_ERR(alg_region)) {1933			ret = PTR_ERR(alg_region);1934			goto out;1935		}1936		if (dsp->wmfw_ver == 0) {1937			if (i + 1 < n_algs) {1938				len = be32_to_cpu(adsp1_alg[i + 1].zm);1939				len -= be32_to_cpu(adsp1_alg[i].zm);1940				len *= 4;1941				cs_dsp_create_control(dsp, alg_region, 0,1942						      len, NULL, 0, 0,1943						      WMFW_CTL_TYPE_BYTES);1944			} else {1945				cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n",1946					    be32_to_cpu(adsp1_alg[i].alg.id));1947			}1948		}1949	}1950 1951out:1952	kfree(adsp1_alg);1953	return ret;1954}1955 1956static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp)1957{1958	struct wmfw_adsp2_id_hdr adsp2_id;1959	struct wmfw_adsp2_alg_hdr *adsp2_alg;1960	struct cs_dsp_alg_region *alg_region;1961	const struct cs_dsp_region *mem;1962	unsigned int pos, len;1963	size_t n_algs;1964	int i, ret;1965 1966	mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM);1967	if (WARN_ON(!mem))1968		return -EINVAL;1969 1970	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,1971			      sizeof(adsp2_id));1972	if (ret != 0) {1973		cs_dsp_err(dsp, "Failed to read algorithm info: %d\n",1974			   ret);1975		return ret;1976	}1977 1978	n_algs = be32_to_cpu(adsp2_id.n_algs);1979 1980	cs_dsp_parse_wmfw_id_header(dsp, &adsp2_id.fw, n_algs);1981 1982	alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM,1983					  adsp2_id.fw.id, adsp2_id.fw.ver,1984					  adsp2_id.xm);1985	if (IS_ERR(alg_region))1986		return PTR_ERR(alg_region);1987 1988	alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM,1989					  adsp2_id.fw.id, adsp2_id.fw.ver,1990					  adsp2_id.ym);1991	if (IS_ERR(alg_region))1992		return PTR_ERR(alg_region);1993 1994	alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM,1995					  adsp2_id.fw.id, adsp2_id.fw.ver,1996					  adsp2_id.zm);1997	if (IS_ERR(alg_region))1998		return PTR_ERR(alg_region);1999 2000	/* Calculate offset and length in DSP words */2001	pos = sizeof(adsp2_id) / sizeof(u32);2002	len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32);2003 2004	adsp2_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len);2005	if (IS_ERR(adsp2_alg))2006		return PTR_ERR(adsp2_alg);2007 2008	for (i = 0; i < n_algs; i++) {2009		cs_dsp_dbg(dsp,2010			   "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",2011			   i, be32_to_cpu(adsp2_alg[i].alg.id),2012			   (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,2013			   (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,2014			   be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,2015			   be32_to_cpu(adsp2_alg[i].xm),2016			   be32_to_cpu(adsp2_alg[i].ym),2017			   be32_to_cpu(adsp2_alg[i].zm));2018 2019		alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM,2020						  adsp2_alg[i].alg.id,2021						  adsp2_alg[i].alg.ver,2022						  adsp2_alg[i].xm);2023		if (IS_ERR(alg_region)) {2024			ret = PTR_ERR(alg_region);2025			goto out;2026		}2027		if (dsp->wmfw_ver == 0) {2028			if (i + 1 < n_algs) {2029				len = be32_to_cpu(adsp2_alg[i + 1].xm);2030				len -= be32_to_cpu(adsp2_alg[i].xm);2031				len *= 4;2032				cs_dsp_create_control(dsp, alg_region, 0,2033						      len, NULL, 0, 0,2034						      WMFW_CTL_TYPE_BYTES);2035			} else {2036				cs_dsp_warn(dsp, "Missing length info for region XM with ID %x\n",2037					    be32_to_cpu(adsp2_alg[i].alg.id));2038			}2039		}2040 2041		alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM,2042						  adsp2_alg[i].alg.id,2043						  adsp2_alg[i].alg.ver,2044						  adsp2_alg[i].ym);2045		if (IS_ERR(alg_region)) {2046			ret = PTR_ERR(alg_region);2047			goto out;2048		}2049		if (dsp->wmfw_ver == 0) {2050			if (i + 1 < n_algs) {2051				len = be32_to_cpu(adsp2_alg[i + 1].ym);2052				len -= be32_to_cpu(adsp2_alg[i].ym);2053				len *= 4;2054				cs_dsp_create_control(dsp, alg_region, 0,2055						      len, NULL, 0, 0,2056						      WMFW_CTL_TYPE_BYTES);2057			} else {2058				cs_dsp_warn(dsp, "Missing length info for region YM with ID %x\n",2059					    be32_to_cpu(adsp2_alg[i].alg.id));2060			}2061		}2062 2063		alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM,2064						  adsp2_alg[i].alg.id,2065						  adsp2_alg[i].alg.ver,2066						  adsp2_alg[i].zm);2067		if (IS_ERR(alg_region)) {2068			ret = PTR_ERR(alg_region);2069			goto out;2070		}2071		if (dsp->wmfw_ver == 0) {2072			if (i + 1 < n_algs) {2073				len = be32_to_cpu(adsp2_alg[i + 1].zm);2074				len -= be32_to_cpu(adsp2_alg[i].zm);2075				len *= 4;2076				cs_dsp_create_control(dsp, alg_region, 0,2077						      len, NULL, 0, 0,2078						      WMFW_CTL_TYPE_BYTES);2079			} else {2080				cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n",2081					    be32_to_cpu(adsp2_alg[i].alg.id));2082			}2083		}2084	}2085 2086out:2087	kfree(adsp2_alg);2088	return ret;2089}2090 2091static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver,2092				      __be32 xm_base, __be32 ym_base)2093{2094	static const int types[] = {2095		WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED,2096		WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED2097	};2098	__be32 bases[] = { xm_base, xm_base, ym_base, ym_base };2099 2100	return cs_dsp_create_regions(dsp, id, ver, ARRAY_SIZE(types), types, bases);2101}2102 2103static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp)2104{2105	struct wmfw_halo_id_hdr halo_id;2106	struct wmfw_halo_alg_hdr *halo_alg;2107	const struct cs_dsp_region *mem;2108	unsigned int pos, len;2109	size_t n_algs;2110	int i, ret;2111 2112	mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM);2113	if (WARN_ON(!mem))2114		return -EINVAL;2115 2116	ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id,2117			      sizeof(halo_id));2118	if (ret != 0) {2119		cs_dsp_err(dsp, "Failed to read algorithm info: %d\n",2120			   ret);2121		return ret;2122	}2123 2124	n_algs = be32_to_cpu(halo_id.n_algs);2125 2126	cs_dsp_parse_wmfw_v3_id_header(dsp, &halo_id.fw, n_algs);2127 2128	ret = cs_dsp_halo_create_regions(dsp, halo_id.fw.id, halo_id.fw.ver,2129					 halo_id.xm_base, halo_id.ym_base);2130	if (ret)2131		return ret;2132 2133	/* Calculate offset and length in DSP words */2134	pos = sizeof(halo_id) / sizeof(u32);2135	len = (sizeof(*halo_alg) * n_algs) / sizeof(u32);2136 2137	halo_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len);2138	if (IS_ERR(halo_alg))2139		return PTR_ERR(halo_alg);2140 2141	for (i = 0; i < n_algs; i++) {2142		cs_dsp_dbg(dsp,2143			   "%d: ID %x v%d.%d.%d XM@%x YM@%x\n",2144			   i, be32_to_cpu(halo_alg[i].alg.id),2145			   (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16,2146			   (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8,2147			   be32_to_cpu(halo_alg[i].alg.ver) & 0xff,2148			   be32_to_cpu(halo_alg[i].xm_base),2149			   be32_to_cpu(halo_alg[i].ym_base));2150 2151		ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id,2152						 halo_alg[i].alg.ver,2153						 halo_alg[i].xm_base,2154						 halo_alg[i].ym_base);2155		if (ret)2156			goto out;2157	}2158 2159out:2160	kfree(halo_alg);2161	return ret;2162}2163 2164static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware,2165			     const char *file)2166{2167	LIST_HEAD(buf_list);2168	struct regmap *regmap = dsp->regmap;2169	struct wmfw_coeff_hdr *hdr;2170	struct wmfw_coeff_item *blk;2171	const struct cs_dsp_region *mem;2172	struct cs_dsp_alg_region *alg_region;2173	const char *region_name;2174	int ret, pos, blocks, type, offset, reg, version;2175	struct cs_dsp_buf *buf;2176 2177	if (!firmware)2178		return 0;2179 2180	ret = -EINVAL;2181 2182	if (sizeof(*hdr) >= firmware->size) {2183		cs_dsp_err(dsp, "%s: coefficient file too short, %zu bytes\n",2184			   file, firmware->size);2185		goto out_fw;2186	}2187 2188	hdr = (void *)&firmware->data[0];2189	if (memcmp(hdr->magic, "WMDR", 4) != 0) {2190		cs_dsp_err(dsp, "%s: invalid coefficient magic\n", file);2191		goto out_fw;2192	}2193 2194	switch (be32_to_cpu(hdr->rev) & 0xff) {2195	case 1:2196	case 2:2197		break;2198	default:2199		cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n",2200			   file, be32_to_cpu(hdr->rev) & 0xff);2201		ret = -EINVAL;2202		goto out_fw;2203	}2204 2205	cs_dsp_info(dsp, "%s: v%d.%d.%d\n", file,2206		    (le32_to_cpu(hdr->ver) >> 16) & 0xff,2207		    (le32_to_cpu(hdr->ver) >>  8) & 0xff,2208		    le32_to_cpu(hdr->ver) & 0xff);2209 2210	pos = le32_to_cpu(hdr->len);2211 2212	blocks = 0;2213	while (pos < firmware->size) {2214		/* Is there enough data for a complete block header? */2215		if (sizeof(*blk) > firmware->size - pos) {2216			ret = -EOVERFLOW;2217			goto out_fw;2218		}2219 2220		blk = (void *)(&firmware->data[pos]);2221 2222		if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) {2223			ret = -EOVERFLOW;2224			goto out_fw;2225		}2226 2227		type = le16_to_cpu(blk->type);2228		offset = le16_to_cpu(blk->offset);2229		version = le32_to_cpu(blk->ver) >> 8;2230 2231		cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",2232			   file, blocks, le32_to_cpu(blk->id),2233			   (le32_to_cpu(blk->ver) >> 16) & 0xff,2234			   (le32_to_cpu(blk->ver) >>  8) & 0xff,2235			   le32_to_cpu(blk->ver) & 0xff);2236		cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",2237			   file, blocks, le32_to_cpu(blk->len), offset, type);2238 2239		reg = 0;2240		region_name = "Unknown";2241		switch (type) {2242		case (WMFW_NAME_TEXT << 8):2243			cs_dsp_info(dsp, "%s: %.*s\n", dsp->fw_name,2244				    min(le32_to_cpu(blk->len), 100), blk->data);2245			break;2246		case (WMFW_INFO_TEXT << 8):2247		case (WMFW_METADATA << 8):2248			break;2249		case (WMFW_ABSOLUTE << 8):2250			/*2251			 * Old files may use this for global2252			 * coefficients.2253			 */2254			if (le32_to_cpu(blk->id) == dsp->fw_id &&2255			    offset == 0) {2256				region_name = "global coefficients";2257				mem = cs_dsp_find_region(dsp, type);2258				if (!mem) {2259					cs_dsp_err(dsp, "No ZM\n");2260					break;2261				}2262				reg = dsp->ops->region_to_reg(mem, 0);2263 2264			} else {2265				region_name = "register";2266				reg = offset;2267			}2268			break;2269 2270		case WMFW_ADSP1_DM:2271		case WMFW_ADSP1_ZM:2272		case WMFW_ADSP2_XM:2273		case WMFW_ADSP2_YM:2274		case WMFW_HALO_XM_PACKED:2275		case WMFW_HALO_YM_PACKED:2276		case WMFW_HALO_PM_PACKED:2277			cs_dsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",2278				   file, blocks, le32_to_cpu(blk->len),2279				   type, le32_to_cpu(blk->id));2280 2281			region_name = cs_dsp_mem_region_name(type);2282			mem = cs_dsp_find_region(dsp, type);2283			if (!mem) {2284				cs_dsp_err(dsp, "No base for region %x\n", type);2285				break;2286			}2287 2288			alg_region = cs_dsp_find_alg_region(dsp, type,2289							    le32_to_cpu(blk->id));2290			if (alg_region) {2291				if (version != alg_region->ver)2292					cs_dsp_warn(dsp,2293						    "Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n",2294						   (version >> 16) & 0xFF,2295						   (version >> 8) & 0xFF,2296						   version & 0xFF,2297						   (alg_region->ver >> 16) & 0xFF,2298						   (alg_region->ver >> 8) & 0xFF,2299						   alg_region->ver & 0xFF);2300 2301				reg = alg_region->base;2302				reg = dsp->ops->region_to_reg(mem, reg);2303				reg += offset;2304			} else {2305				cs_dsp_err(dsp, "No %s for algorithm %x\n",2306					   region_name, le32_to_cpu(blk->id));2307			}2308			break;2309 2310		default:2311			cs_dsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",2312				   file, blocks, type, pos);2313			break;2314		}2315 2316		if (reg) {2317			buf = cs_dsp_buf_alloc(blk->data,2318					       le32_to_cpu(blk->len),2319					       &buf_list);2320			if (!buf) {2321				cs_dsp_err(dsp, "Out of memory\n");2322				ret = -ENOMEM;2323				goto out_fw;2324			}2325 2326			cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",2327				   file, blocks, le32_to_cpu(blk->len),2328				   reg);2329			ret = regmap_raw_write_async(regmap, reg, buf->buf,2330						     le32_to_cpu(blk->len));2331			if (ret != 0) {2332				cs_dsp_err(dsp,2333					   "%s.%d: Failed to write to %x in %s: %d\n",2334					   file, blocks, reg, region_name, ret);2335			}2336		}2337 2338		pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;2339		blocks++;2340	}2341 2342	ret = regmap_async_complete(regmap);2343	if (ret != 0)2344		cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);2345 2346	if (pos > firmware->size)2347		cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",2348			    file, blocks, pos - firmware->size);2349 2350	cs_dsp_debugfs_save_binname(dsp, file);2351 2352out_fw:2353	regmap_async_complete(regmap);2354	cs_dsp_buf_free(&buf_list);2355 2356	if (ret == -EOVERFLOW)2357		cs_dsp_err(dsp, "%s: file content overflows file data\n", file);2358 2359	return ret;2360}2361 2362static int cs_dsp_create_name(struct cs_dsp *dsp)2363{2364	if (!dsp->name) {2365		dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d",2366					   dsp->num);2367		if (!dsp->name)2368			return -ENOMEM;2369	}2370 2371	return 0;2372}2373 2374static int cs_dsp_common_init(struct cs_dsp *dsp)2375{2376	int ret;2377 2378	ret = cs_dsp_create_name(dsp);2379	if (ret)2380		return ret;2381 2382	INIT_LIST_HEAD(&dsp->alg_regions);2383	INIT_LIST_HEAD(&dsp->ctl_list);2384 2385	mutex_init(&dsp->pwr_lock);2386 2387#ifdef CONFIG_DEBUG_FS2388	/* Ensure this is invalid if client never provides a debugfs root */2389	dsp->debugfs_root = ERR_PTR(-ENODEV);2390#endif2391 2392	return 0;2393}2394 2395/**2396 * cs_dsp_adsp1_init() - Initialise a cs_dsp structure representing a ADSP1 device2397 * @dsp: pointer to DSP structure2398 *2399 * Return: Zero for success, a negative number on error.2400 */2401int cs_dsp_adsp1_init(struct cs_dsp *dsp)2402{2403	dsp->ops = &cs_dsp_adsp1_ops;2404 2405	return cs_dsp_common_init(dsp);2406}2407EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_init, FW_CS_DSP);2408 2409/**2410 * cs_dsp_adsp1_power_up() - Load and start the named firmware2411 * @dsp: pointer to DSP structure2412 * @wmfw_firmware: the firmware to be sent2413 * @wmfw_filename: file name of firmware to be sent2414 * @coeff_firmware: the coefficient data to be sent2415 * @coeff_filename: file name of coefficient to data be sent2416 * @fw_name: the user-friendly firmware name2417 *2418 * Return: Zero for success, a negative number on error.2419 */2420int cs_dsp_adsp1_power_up(struct cs_dsp *dsp,2421			  const struct firmware *wmfw_firmware, const char *wmfw_filename,2422			  const struct firmware *coeff_firmware, const char *coeff_filename,2423			  const char *fw_name)2424{2425	unsigned int val;2426	int ret;2427 2428	mutex_lock(&dsp->pwr_lock);2429 2430	dsp->fw_name = fw_name;2431 2432	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,2433			   ADSP1_SYS_ENA, ADSP1_SYS_ENA);2434 2435	/*2436	 * For simplicity set the DSP clock rate to be the2437	 * SYSCLK rate rather than making it configurable.2438	 */2439	if (dsp->sysclk_reg) {2440		ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);2441		if (ret != 0) {2442			cs_dsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);2443			goto err_mutex;2444		}2445 2446		val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;2447 2448		ret = regmap_update_bits(dsp->regmap,2449					 dsp->base + ADSP1_CONTROL_31,2450					 ADSP1_CLK_SEL_MASK, val);2451		if (ret != 0) {2452			cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret);2453			goto err_mutex;2454		}2455	}2456 2457	ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename);2458	if (ret != 0)2459		goto err_ena;2460 2461	ret = cs_dsp_adsp1_setup_algs(dsp);2462	if (ret != 0)2463		goto err_ena;2464 2465	ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename);2466	if (ret != 0)2467		goto err_ena;2468 2469	/* Initialize caches for enabled and unset controls */2470	ret = cs_dsp_coeff_init_control_caches(dsp);2471	if (ret != 0)2472		goto err_ena;2473 2474	/* Sync set controls */2475	ret = cs_dsp_coeff_sync_controls(dsp);2476	if (ret != 0)2477		goto err_ena;2478 2479	dsp->booted = true;2480 2481	/* Start the core running */2482	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,2483			   ADSP1_CORE_ENA | ADSP1_START,2484			   ADSP1_CORE_ENA | ADSP1_START);2485 2486	dsp->running = true;2487 2488	mutex_unlock(&dsp->pwr_lock);2489 2490	return 0;2491 2492err_ena:2493	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,2494			   ADSP1_SYS_ENA, 0);2495err_mutex:2496	mutex_unlock(&dsp->pwr_lock);2497	return ret;2498}2499EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_up, FW_CS_DSP);2500 2501/**2502 * cs_dsp_adsp1_power_down() - Halts the DSP2503 * @dsp: pointer to DSP structure2504 */2505void cs_dsp_adsp1_power_down(struct cs_dsp *dsp)2506{2507	struct cs_dsp_coeff_ctl *ctl;2508 2509	mutex_lock(&dsp->pwr_lock);2510 2511	dsp->running = false;2512	dsp->booted = false;2513 2514	/* Halt the core */2515	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,2516			   ADSP1_CORE_ENA | ADSP1_START, 0);2517 2518	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,2519			   ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);2520 2521	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,2522			   ADSP1_SYS_ENA, 0);2523 2524	list_for_each_entry(ctl, &dsp->ctl_list, list)2525		ctl->enabled = 0;2526 2527	cs_dsp_free_alg_regions(dsp);2528 2529	mutex_unlock(&dsp->pwr_lock);2530}2531EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_down, FW_CS_DSP);2532 2533static int cs_dsp_adsp2v2_enable_core(struct cs_dsp *dsp)2534{2535	unsigned int val;2536	int ret, count;2537 2538	/* Wait for the RAM to start, should be near instantaneous */2539	for (count = 0; count < 10; ++count) {2540		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);2541		if (ret != 0)2542			return ret;2543 2544		if (val & ADSP2_RAM_RDY)2545			break;2546 2547		usleep_range(250, 500);2548	}2549 2550	if (!(val & ADSP2_RAM_RDY)) {2551		cs_dsp_err(dsp, "Failed to start DSP RAM\n");2552		return -EBUSY;2553	}2554 2555	cs_dsp_dbg(dsp, "RAM ready after %d polls\n", count);2556 2557	return 0;2558}2559 2560static int cs_dsp_adsp2_enable_core(struct cs_dsp *dsp)2561{2562	int ret;2563 2564	ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,2565				       ADSP2_SYS_ENA, ADSP2_SYS_ENA);2566	if (ret != 0)2567		return ret;2568 2569	return cs_dsp_adsp2v2_enable_core(dsp);2570}2571 2572static int cs_dsp_adsp2_lock(struct cs_dsp *dsp, unsigned int lock_regions)2573{2574	struct regmap *regmap = dsp->regmap;2575	unsigned int code0, code1, lock_reg;2576 2577	if (!(lock_regions & CS_ADSP2_REGION_ALL))2578		return 0;2579 2580	lock_regions &= CS_ADSP2_REGION_ALL;2581	lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;2582 2583	while (lock_regions) {2584		code0 = code1 = 0;2585		if (lock_regions & BIT(0)) {2586			code0 = ADSP2_LOCK_CODE_0;2587			code1 = ADSP2_LOCK_CODE_1;2588		}2589		if (lock_regions & BIT(1)) {2590			code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;2591			code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;2592		}2593		regmap_write(regmap, lock_reg, code0);2594		regmap_write(regmap, lock_reg, code1);2595		lock_regions >>= 2;2596		lock_reg += 2;2597	}2598 2599	return 0;2600}2601 2602static int cs_dsp_adsp2_enable_memory(struct cs_dsp *dsp)2603{2604	return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,2605				  ADSP2_MEM_ENA, ADSP2_MEM_ENA);2606}2607 2608static void cs_dsp_adsp2_disable_memory(struct cs_dsp *dsp)2609{2610	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,2611			   ADSP2_MEM_ENA, 0);2612}2613 2614static void cs_dsp_adsp2_disable_core(struct cs_dsp *dsp)2615{2616	regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);2617	regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);2618	regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);2619 2620	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,2621			   ADSP2_SYS_ENA, 0);2622}2623 2624static void cs_dsp_adsp2v2_disable_core(struct cs_dsp *dsp)2625{2626	regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);2627	regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);2628	regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);2629}2630 2631static int cs_dsp_halo_configure_mpu(struct cs_dsp *dsp, unsigned int lock_regions)2632{2633	struct reg_sequence config[] = {2634		{ dsp->base + HALO_MPU_LOCK_CONFIG,     0x5555 },2635		{ dsp->base + HALO_MPU_LOCK_CONFIG,     0xAAAA },2636		{ dsp->base + HALO_MPU_XMEM_ACCESS_0,   0xFFFFFFFF },2637		{ dsp->base + HALO_MPU_YMEM_ACCESS_0,   0xFFFFFFFF },2638		{ dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions },2639		{ dsp->base + HALO_MPU_XREG_ACCESS_0,   lock_regions },2640		{ dsp->base + HALO_MPU_YREG_ACCESS_0,   lock_regions },2641		{ dsp->base + HALO_MPU_XMEM_ACCESS_1,   0xFFFFFFFF },2642		{ dsp->base + HALO_MPU_YMEM_ACCESS_1,   0xFFFFFFFF },2643		{ dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions },2644		{ dsp->base + HALO_MPU_XREG_ACCESS_1,   lock_regions },2645		{ dsp->base + HALO_MPU_YREG_ACCESS_1,   lock_regions },2646		{ dsp->base + HALO_MPU_XMEM_ACCESS_2,   0xFFFFFFFF },2647		{ dsp->base + HALO_MPU_YMEM_ACCESS_2,   0xFFFFFFFF },2648		{ dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions },2649		{ dsp->base + HALO_MPU_XREG_ACCESS_2,   lock_regions },2650		{ dsp->base + HALO_MPU_YREG_ACCESS_2,   lock_regions },2651		{ dsp->base + HALO_MPU_XMEM_ACCESS_3,   0xFFFFFFFF },2652		{ dsp->base + HALO_MPU_YMEM_ACCESS_3,   0xFFFFFFFF },2653		{ dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions },2654		{ dsp->base + HALO_MPU_XREG_ACCESS_3,   lock_regions },2655		{ dsp->base + HALO_MPU_YREG_ACCESS_3,   lock_regions },2656		{ dsp->base + HALO_MPU_LOCK_CONFIG,     0 },2657	};2658 2659	return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config));2660}2661 2662/**2663 * cs_dsp_set_dspclk() - Applies the given frequency to the given cs_dsp2664 * @dsp: pointer to DSP structure2665 * @freq: clock rate to set2666 *2667 * This is only for use on ADSP2 cores.2668 *2669 * Return: Zero for success, a negative number on error.2670 */2671int cs_dsp_set_dspclk(struct cs_dsp *dsp, unsigned int freq)2672{2673	int ret;2674 2675	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING,2676				 ADSP2_CLK_SEL_MASK,2677				 freq << ADSP2_CLK_SEL_SHIFT);2678	if (ret)2679		cs_dsp_err(dsp, "Failed to set clock rate: %d\n", ret);2680 2681	return ret;2682}2683EXPORT_SYMBOL_NS_GPL(cs_dsp_set_dspclk, FW_CS_DSP);2684 2685static void cs_dsp_stop_watchdog(struct cs_dsp *dsp)2686{2687	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,2688			   ADSP2_WDT_ENA_MASK, 0);2689}2690 2691static void cs_dsp_halo_stop_watchdog(struct cs_dsp *dsp)2692{2693	regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL,2694			   HALO_WDT_EN_MASK, 0);2695}2696 2697/**2698 * cs_dsp_power_up() - Downloads firmware to the DSP2699 * @dsp: pointer to DSP structure2700 * @wmfw_firmware: the firmware to be sent2701 * @wmfw_filename: file name of firmware to be sent2702 * @coeff_firmware: the coefficient data to be sent2703 * @coeff_filename: file name of coefficient to data be sent2704 * @fw_name: the user-friendly firmware name2705 *2706 * This function is used on ADSP2 and Halo DSP cores, it powers-up the DSP core2707 * and downloads the firmware but does not start the firmware running. The2708 * cs_dsp booted flag will be set once completed and if the core has a low-power2709 * memory retention mode it will be put into this state after the firmware is2710 * downloaded.2711 *2712 * Return: Zero for success, a negative number on error.2713 */2714int cs_dsp_power_up(struct cs_dsp *dsp,2715		    const struct firmware *wmfw_firmware, const char *wmfw_filename,2716		    const struct firmware *coeff_firmware, const char *coeff_filename,2717		    const char *fw_name)2718{2719	int ret;2720 2721	mutex_lock(&dsp->pwr_lock);2722 2723	dsp->fw_name = fw_name;2724 2725	if (dsp->ops->enable_memory) {2726		ret = dsp->ops->enable_memory(dsp);2727		if (ret != 0)2728			goto err_mutex;2729	}2730 2731	if (dsp->ops->enable_core) {2732		ret = dsp->ops->enable_core(dsp);2733		if (ret != 0)2734			goto err_mem;2735	}2736 2737	ret = cs_dsp_load(dsp, wmfw_firmware, wmfw_filename);2738	if (ret != 0)2739		goto err_ena;2740 2741	ret = dsp->ops->setup_algs(dsp);2742	if (ret != 0)2743		goto err_ena;2744 2745	ret = cs_dsp_load_coeff(dsp, coeff_firmware, coeff_filename);2746	if (ret != 0)2747		goto err_ena;2748 2749	/* Initialize caches for enabled and unset controls */2750	ret = cs_dsp_coeff_init_control_caches(dsp);2751	if (ret != 0)2752		goto err_ena;2753 2754	if (dsp->ops->disable_core)2755		dsp->ops->disable_core(dsp);2756 2757	dsp->booted = true;2758 2759	mutex_unlock(&dsp->pwr_lock);2760 2761	return 0;2762err_ena:2763	if (dsp->ops->disable_core)2764		dsp->ops->disable_core(dsp);2765err_mem:2766	if (dsp->ops->disable_memory)2767		dsp->ops->disable_memory(dsp);2768err_mutex:2769	mutex_unlock(&dsp->pwr_lock);2770 2771	return ret;2772}2773EXPORT_SYMBOL_NS_GPL(cs_dsp_power_up, FW_CS_DSP);2774 2775/**2776 * cs_dsp_power_down() - Powers-down the DSP2777 * @dsp: pointer to DSP structure2778 *2779 * cs_dsp_stop() must have been called before this function. The core will be2780 * fully powered down and so the memory will not be retained.2781 */2782void cs_dsp_power_down(struct cs_dsp *dsp)2783{2784	struct cs_dsp_coeff_ctl *ctl;2785 2786	mutex_lock(&dsp->pwr_lock);2787 2788	cs_dsp_debugfs_clear(dsp);2789 2790	dsp->fw_id = 0;2791	dsp->fw_id_version = 0;2792 2793	dsp->booted = false;2794 2795	if (dsp->ops->disable_memory)2796		dsp->ops->disable_memory(dsp);2797 2798	list_for_each_entry(ctl, &dsp->ctl_list, list)2799		ctl->enabled = 0;2800 2801	cs_dsp_free_alg_regions(dsp);2802 2803	mutex_unlock(&dsp->pwr_lock);2804 2805	cs_dsp_dbg(dsp, "Shutdown complete\n");2806}2807EXPORT_SYMBOL_NS_GPL(cs_dsp_power_down, FW_CS_DSP);2808 2809static int cs_dsp_adsp2_start_core(struct cs_dsp *dsp)2810{2811	return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,2812				  ADSP2_CORE_ENA | ADSP2_START,2813				  ADSP2_CORE_ENA | ADSP2_START);2814}2815 2816static void cs_dsp_adsp2_stop_core(struct cs_dsp *dsp)2817{2818	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,2819			   ADSP2_CORE_ENA | ADSP2_START, 0);2820}2821 2822/**2823 * cs_dsp_run() - Starts the firmware running2824 * @dsp: pointer to DSP structure2825 *2826 * cs_dsp_power_up() must have previously been called successfully.2827 *2828 * Return: Zero for success, a negative number on error.2829 */2830int cs_dsp_run(struct cs_dsp *dsp)2831{2832	int ret;2833 2834	mutex_lock(&dsp->pwr_lock);2835 2836	if (!dsp->booted) {2837		ret = -EIO;2838		goto err;2839	}2840 2841	if (dsp->ops->enable_core) {2842		ret = dsp->ops->enable_core(dsp);2843		if (ret != 0)2844			goto err;2845	}2846 2847	if (dsp->client_ops->pre_run) {2848		ret = dsp->client_ops->pre_run(dsp);2849		if (ret)2850			goto err;2851	}2852 2853	/* Sync set controls */2854	ret = cs_dsp_coeff_sync_controls(dsp);2855	if (ret != 0)2856		goto err;2857 2858	if (dsp->ops->lock_memory) {2859		ret = dsp->ops->lock_memory(dsp, dsp->lock_regions);2860		if (ret != 0) {2861			cs_dsp_err(dsp, "Error configuring MPU: %d\n", ret);2862			goto err;2863		}2864	}2865 2866	if (dsp->ops->start_core) {2867		ret = dsp->ops->start_core(dsp);2868		if (ret != 0)2869			goto err;2870	}2871 2872	dsp->running = true;2873 2874	if (dsp->client_ops->post_run) {2875		ret = dsp->client_ops->post_run(dsp);2876		if (ret)2877			goto err;2878	}2879 2880	mutex_unlock(&dsp->pwr_lock);2881 2882	return 0;2883 2884err:2885	if (dsp->ops->stop_core)2886		dsp->ops->stop_core(dsp);2887	if (dsp->ops->disable_core)2888		dsp->ops->disable_core(dsp);2889	mutex_unlock(&dsp->pwr_lock);2890 2891	return ret;2892}2893EXPORT_SYMBOL_NS_GPL(cs_dsp_run, FW_CS_DSP);2894 2895/**2896 * cs_dsp_stop() - Stops the firmware2897 * @dsp: pointer to DSP structure2898 *2899 * Memory will not be disabled so firmware will remain loaded.2900 */2901void cs_dsp_stop(struct cs_dsp *dsp)2902{2903	/* Tell the firmware to cleanup */2904	cs_dsp_signal_event_controls(dsp, CS_DSP_FW_EVENT_SHUTDOWN);2905 2906	if (dsp->ops->stop_watchdog)2907		dsp->ops->stop_watchdog(dsp);2908 2909	/* Log firmware state, it can be useful for analysis */2910	if (dsp->ops->show_fw_status)2911		dsp->ops->show_fw_status(dsp);2912 2913	mutex_lock(&dsp->pwr_lock);2914 2915	if (dsp->client_ops->pre_stop)2916		dsp->client_ops->pre_stop(dsp);2917 2918	dsp->running = false;2919 2920	if (dsp->ops->stop_core)2921		dsp->ops->stop_core(dsp);2922	if (dsp->ops->disable_core)2923		dsp->ops->disable_core(dsp);2924 2925	if (dsp->client_ops->post_stop)2926		dsp->client_ops->post_stop(dsp);2927 2928	mutex_unlock(&dsp->pwr_lock);2929 2930	cs_dsp_dbg(dsp, "Execution stopped\n");2931}2932EXPORT_SYMBOL_NS_GPL(cs_dsp_stop, FW_CS_DSP);2933 2934static int cs_dsp_halo_start_core(struct cs_dsp *dsp)2935{2936	int ret;2937 2938	ret = regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL,2939				 HALO_CORE_RESET | HALO_CORE_EN,2940				 HALO_CORE_RESET | HALO_CORE_EN);2941	if (ret)2942		return ret;2943 2944	return regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL,2945				  HALO_CORE_RESET, 0);2946}2947 2948static void cs_dsp_halo_stop_core(struct cs_dsp *dsp)2949{2950	regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL,2951			   HALO_CORE_EN, 0);2952 2953	/* reset halo core with CORE_SOFT_RESET */2954	regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET,2955			   HALO_CORE_SOFT_RESET_MASK, 1);2956}2957 2958/**2959 * cs_dsp_adsp2_init() - Initialise a cs_dsp structure representing a ADSP2 core2960 * @dsp: pointer to DSP structure2961 *2962 * Return: Zero for success, a negative number on error.2963 */2964int cs_dsp_adsp2_init(struct cs_dsp *dsp)2965{2966	int ret;2967 2968	switch (dsp->rev) {2969	case 0:2970		/*2971		 * Disable the DSP memory by default when in reset for a small2972		 * power saving.2973		 */2974		ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,2975					 ADSP2_MEM_ENA, 0);2976		if (ret) {2977			cs_dsp_err(dsp,2978				   "Failed to clear memory retention: %d\n", ret);2979			return ret;2980		}2981 2982		dsp->ops = &cs_dsp_adsp2_ops[0];2983		break;2984	case 1:2985		dsp->ops = &cs_dsp_adsp2_ops[1];2986		break;2987	default:2988		dsp->ops = &cs_dsp_adsp2_ops[2];2989		break;2990	}2991 2992	return cs_dsp_common_init(dsp);2993}2994EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_init, FW_CS_DSP);2995 2996/**2997 * cs_dsp_halo_init() - Initialise a cs_dsp structure representing a HALO Core DSP2998 * @dsp: pointer to DSP structure2999 *3000 * Return: Zero for success, a negative number on error.3001 */3002int cs_dsp_halo_init(struct cs_dsp *dsp)3003{3004	if (dsp->no_core_startstop)3005		dsp->ops = &cs_dsp_halo_ao_ops;3006	else3007		dsp->ops = &cs_dsp_halo_ops;3008 3009	return cs_dsp_common_init(dsp);3010}3011EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_init, FW_CS_DSP);3012 3013/**3014 * cs_dsp_remove() - Clean a cs_dsp before deletion3015 * @dsp: pointer to DSP structure3016 */3017void cs_dsp_remove(struct cs_dsp *dsp)3018{3019	struct cs_dsp_coeff_ctl *ctl;3020 3021	while (!list_empty(&dsp->ctl_list)) {3022		ctl = list_first_entry(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);3023 3024		if (dsp->client_ops->control_remove)3025			dsp->client_ops->control_remove(ctl);3026 3027		list_del(&ctl->list);3028		cs_dsp_free_ctl_blk(ctl);3029	}3030}3031EXPORT_SYMBOL_NS_GPL(cs_dsp_remove, FW_CS_DSP);3032 3033/**3034 * cs_dsp_read_raw_data_block() - Reads a block of data from DSP memory3035 * @dsp: pointer to DSP structure3036 * @mem_type: the type of DSP memory containing the data to be read3037 * @mem_addr: the address of the data within the memory region3038 * @num_words: the length of the data to read3039 * @data: a buffer to store the fetched data3040 *3041 * If this is used to read unpacked 24-bit memory, each 24-bit DSP word will3042 * occupy 32-bits in data (MSbyte will be 0). This padding can be removed using3043 * cs_dsp_remove_padding()3044 *3045 * Return: Zero for success, a negative number on error.3046 */3047int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr,3048			       unsigned int num_words, __be32 *data)3049{3050	struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type);3051	unsigned int reg;3052	int ret;3053 3054	lockdep_assert_held(&dsp->pwr_lock);3055 3056	if (!mem)3057		return -EINVAL;3058 3059	reg = dsp->ops->region_to_reg(mem, mem_addr);3060 3061	ret = regmap_raw_read(dsp->regmap, reg, data,3062			      sizeof(*data) * num_words);3063	if (ret < 0)3064		return ret;3065 3066	return 0;3067}3068EXPORT_SYMBOL_NS_GPL(cs_dsp_read_raw_data_block, FW_CS_DSP);3069 3070/**3071 * cs_dsp_read_data_word() - Reads a word from DSP memory3072 * @dsp: pointer to DSP structure3073 * @mem_type: the type of DSP memory containing the data to be read3074 * @mem_addr: the address of the data within the memory region3075 * @data: a buffer to store the fetched data3076 *3077 * Return: Zero for success, a negative number on error.3078 */3079int cs_dsp_read_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 *data)3080{3081	__be32 raw;3082	int ret;3083 3084	ret = cs_dsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw);3085	if (ret < 0)3086		return ret;3087 3088	*data = be32_to_cpu(raw) & 0x00ffffffu;3089 3090	return 0;3091}3092EXPORT_SYMBOL_NS_GPL(cs_dsp_read_data_word, FW_CS_DSP);3093 3094/**3095 * cs_dsp_write_data_word() - Writes a word to DSP memory3096 * @dsp: pointer to DSP structure3097 * @mem_type: the type of DSP memory containing the data to be written3098 * @mem_addr: the address of the data within the memory region3099 * @data: the data to be written3100 *3101 * Return: Zero for success, a negative number on error.3102 */3103int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 data)3104{3105	struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, mem_type);3106	__be32 val = cpu_to_be32(data & 0x00ffffffu);3107	unsigned int reg;3108 3109	lockdep_assert_held(&dsp->pwr_lock);3110 3111	if (!mem)3112		return -EINVAL;3113 3114	reg = dsp->ops->region_to_reg(mem, mem_addr);3115 3116	return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));3117}3118EXPORT_SYMBOL_NS_GPL(cs_dsp_write_data_word, FW_CS_DSP);3119 3120/**3121 * cs_dsp_remove_padding() - Convert unpacked words to packed bytes3122 * @buf: buffer containing DSP words read from DSP memory3123 * @nwords: number of words to convert3124 *3125 * DSP words from the register map have pad bytes and the data bytes3126 * are in swapped order. This swaps to the native endian order and3127 * strips the pad bytes.3128 */3129void cs_dsp_remove_padding(u32 *buf, int nwords)3130{3131	const __be32 *pack_in = (__be32 *)buf;3132	u8 *pack_out = (u8 *)buf;3133	int i;3134 3135	for (i = 0; i < nwords; i++) {3136		u32 word = be32_to_cpu(*pack_in++);3137		*pack_out++ = (u8)word;3138		*pack_out++ = (u8)(word >> 8);3139		*pack_out++ = (u8)(word >> 16);3140	}3141}3142EXPORT_SYMBOL_NS_GPL(cs_dsp_remove_padding, FW_CS_DSP);3143 3144/**3145 * cs_dsp_adsp2_bus_error() - Handle a DSP bus error interrupt3146 * @dsp: pointer to DSP structure3147 *3148 * The firmware and DSP state will be logged for future analysis.3149 */3150void cs_dsp_adsp2_bus_error(struct cs_dsp *dsp)3151{3152	unsigned int val;3153	struct regmap *regmap = dsp->regmap;3154	int ret = 0;3155 3156	mutex_lock(&dsp->pwr_lock);3157 3158	ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);3159	if (ret) {3160		cs_dsp_err(dsp,3161			   "Failed to read Region Lock Ctrl register: %d\n", ret);3162		goto error;3163	}3164 3165	if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {3166		cs_dsp_err(dsp, "watchdog timeout error\n");3167		dsp->ops->stop_watchdog(dsp);3168		if (dsp->client_ops->watchdog_expired)3169			dsp->client_ops->watchdog_expired(dsp);3170	}3171 3172	if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {3173		if (val & ADSP2_ADDR_ERR_MASK)3174			cs_dsp_err(dsp, "bus error: address error\n");3175		else3176			cs_dsp_err(dsp, "bus error: region lock error\n");3177 3178		ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val);3179		if (ret) {3180			cs_dsp_err(dsp,3181				   "Failed to read Bus Err Addr register: %d\n",3182				   ret);3183			goto error;3184		}3185 3186		cs_dsp_err(dsp, "bus error address = 0x%x\n",3187			   val & ADSP2_BUS_ERR_ADDR_MASK);3188 3189		ret = regmap_read(regmap,3190				  dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR,3191				  &val);3192		if (ret) {3193			cs_dsp_err(dsp,3194				   "Failed to read Pmem Xmem Err Addr register: %d\n",3195				   ret);3196			goto error;3197		}3198 3199		cs_dsp_err(dsp, "xmem error address = 0x%x\n",3200			   val & ADSP2_XMEM_ERR_ADDR_MASK);3201		cs_dsp_err(dsp, "pmem error address = 0x%x\n",3202			   (val & ADSP2_PMEM_ERR_ADDR_MASK) >>3203			   ADSP2_PMEM_ERR_ADDR_SHIFT);3204	}3205 3206	regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,3207			   ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);3208 3209error:3210	mutex_unlock(&dsp->pwr_lock);3211}3212EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_bus_error, FW_CS_DSP);3213 3214/**3215 * cs_dsp_halo_bus_error() - Handle a DSP bus error interrupt3216 * @dsp: pointer to DSP structure3217 *3218 * The firmware and DSP state will be logged for future analysis.3219 */3220void cs_dsp_halo_bus_error(struct cs_dsp *dsp)3221{3222	struct regmap *regmap = dsp->regmap;3223	unsigned int fault[6];3224	struct reg_sequence clear[] = {3225		{ dsp->base + HALO_MPU_XM_VIO_STATUS,     0x0 },3226		{ dsp->base + HALO_MPU_YM_VIO_STATUS,     0x0 },3227		{ dsp->base + HALO_MPU_PM_VIO_STATUS,     0x0 },3228	};3229	int ret;3230 3231	mutex_lock(&dsp->pwr_lock);3232 3233	ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1,3234			  fault);3235	if (ret) {3236		cs_dsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret);3237		goto exit_unlock;3238	}3239 3240	cs_dsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n",3241		    *fault & HALO_AHBM_FLAGS_ERR_MASK,3242		    (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >>3243		    HALO_AHBM_CORE_ERR_ADDR_SHIFT);3244 3245	ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0,3246			  fault);3247	if (ret) {3248		cs_dsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret);3249		goto exit_unlock;3250	}3251 3252	cs_dsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault);3253 3254	ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR,3255			       fault, ARRAY_SIZE(fault));3256	if (ret) {3257		cs_dsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret);3258		goto exit_unlock;3259	}3260 3261	cs_dsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]);3262	cs_dsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]);3263	cs_dsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]);3264 3265	ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear));3266	if (ret)3267		cs_dsp_warn(dsp, "Failed to clear MPU status: %d\n", ret);3268 3269exit_unlock:3270	mutex_unlock(&dsp->pwr_lock);3271}3272EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_bus_error, FW_CS_DSP);3273 3274/**3275 * cs_dsp_halo_wdt_expire() - Handle DSP watchdog expiry3276 * @dsp: pointer to DSP structure3277 *3278 * This is logged for future analysis.3279 */3280void cs_dsp_halo_wdt_expire(struct cs_dsp *dsp)3281{3282	mutex_lock(&dsp->pwr_lock);3283 3284	cs_dsp_warn(dsp, "WDT Expiry Fault\n");3285 3286	dsp->ops->stop_watchdog(dsp);3287	if (dsp->client_ops->watchdog_expired)3288		dsp->client_ops->watchdog_expired(dsp);3289 3290	mutex_unlock(&dsp->pwr_lock);3291}3292EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_wdt_expire, FW_CS_DSP);3293 3294static const struct cs_dsp_ops cs_dsp_adsp1_ops = {3295	.validate_version = cs_dsp_validate_version,3296	.parse_sizes = cs_dsp_adsp1_parse_sizes,3297	.region_to_reg = cs_dsp_region_to_reg,3298};3299 3300static const struct cs_dsp_ops cs_dsp_adsp2_ops[] = {3301	{3302		.parse_sizes = cs_dsp_adsp2_parse_sizes,3303		.validate_version = cs_dsp_validate_version,3304		.setup_algs = cs_dsp_adsp2_setup_algs,3305		.region_to_reg = cs_dsp_region_to_reg,3306 3307		.show_fw_status = cs_dsp_adsp2_show_fw_status,3308 3309		.enable_memory = cs_dsp_adsp2_enable_memory,3310		.disable_memory = cs_dsp_adsp2_disable_memory,3311 3312		.enable_core = cs_dsp_adsp2_enable_core,3313		.disable_core = cs_dsp_adsp2_disable_core,3314 3315		.start_core = cs_dsp_adsp2_start_core,3316		.stop_core = cs_dsp_adsp2_stop_core,3317 3318	},3319	{3320		.parse_sizes = cs_dsp_adsp2_parse_sizes,3321		.validate_version = cs_dsp_validate_version,3322		.setup_algs = cs_dsp_adsp2_setup_algs,3323		.region_to_reg = cs_dsp_region_to_reg,3324 3325		.show_fw_status = cs_dsp_adsp2v2_show_fw_status,3326 3327		.enable_memory = cs_dsp_adsp2_enable_memory,3328		.disable_memory = cs_dsp_adsp2_disable_memory,3329		.lock_memory = cs_dsp_adsp2_lock,3330 3331		.enable_core = cs_dsp_adsp2v2_enable_core,3332		.disable_core = cs_dsp_adsp2v2_disable_core,3333 3334		.start_core = cs_dsp_adsp2_start_core,3335		.stop_core = cs_dsp_adsp2_stop_core,3336	},3337	{3338		.parse_sizes = cs_dsp_adsp2_parse_sizes,3339		.validate_version = cs_dsp_validate_version,3340		.setup_algs = cs_dsp_adsp2_setup_algs,3341		.region_to_reg = cs_dsp_region_to_reg,3342 3343		.show_fw_status = cs_dsp_adsp2v2_show_fw_status,3344		.stop_watchdog = cs_dsp_stop_watchdog,3345 3346		.enable_memory = cs_dsp_adsp2_enable_memory,3347		.disable_memory = cs_dsp_adsp2_disable_memory,3348		.lock_memory = cs_dsp_adsp2_lock,3349 3350		.enable_core = cs_dsp_adsp2v2_enable_core,3351		.disable_core = cs_dsp_adsp2v2_disable_core,3352 3353		.start_core = cs_dsp_adsp2_start_core,3354		.stop_core = cs_dsp_adsp2_stop_core,3355	},3356};3357 3358static const struct cs_dsp_ops cs_dsp_halo_ops = {3359	.parse_sizes = cs_dsp_adsp2_parse_sizes,3360	.validate_version = cs_dsp_halo_validate_version,3361	.setup_algs = cs_dsp_halo_setup_algs,3362	.region_to_reg = cs_dsp_halo_region_to_reg,3363 3364	.show_fw_status = cs_dsp_halo_show_fw_status,3365	.stop_watchdog = cs_dsp_halo_stop_watchdog,3366 3367	.lock_memory = cs_dsp_halo_configure_mpu,3368 3369	.start_core = cs_dsp_halo_start_core,3370	.stop_core = cs_dsp_halo_stop_core,3371};3372 3373static const struct cs_dsp_ops cs_dsp_halo_ao_ops = {3374	.parse_sizes = cs_dsp_adsp2_parse_sizes,3375	.validate_version = cs_dsp_halo_validate_version,3376	.setup_algs = cs_dsp_halo_setup_algs,3377	.region_to_reg = cs_dsp_halo_region_to_reg,3378	.show_fw_status = cs_dsp_halo_show_fw_status,3379};3380 3381/**3382 * cs_dsp_chunk_write() - Format data to a DSP memory chunk3383 * @ch: Pointer to the chunk structure3384 * @nbits: Number of bits to write3385 * @val: Value to write3386 *3387 * This function sequentially writes values into the format required for DSP3388 * memory, it handles both inserting of the padding bytes and converting to3389 * big endian. Note that data is only committed to the chunk when a whole DSP3390 * words worth of data is available.3391 *3392 * Return: Zero for success, a negative number on error.3393 */3394int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val)3395{3396	int nwrite, i;3397 3398	nwrite = min(CS_DSP_DATA_WORD_BITS - ch->cachebits, nbits);3399 3400	ch->cache <<= nwrite;3401	ch->cache |= val >> (nbits - nwrite);3402	ch->cachebits += nwrite;3403	nbits -= nwrite;3404 3405	if (ch->cachebits == CS_DSP_DATA_WORD_BITS) {3406		if (cs_dsp_chunk_end(ch))3407			return -ENOSPC;3408 3409		ch->cache &= 0xFFFFFF;3410		for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE)3411			*ch->data++ = (ch->cache & 0xFF000000) >> CS_DSP_DATA_WORD_BITS;3412 3413		ch->bytes += sizeof(ch->cache);3414		ch->cachebits = 0;3415	}3416 3417	if (nbits)3418		return cs_dsp_chunk_write(ch, nbits, val);3419 3420	return 0;3421}3422EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_write, FW_CS_DSP);3423 3424/**3425 * cs_dsp_chunk_flush() - Pad remaining data with zero and commit to chunk3426 * @ch: Pointer to the chunk structure3427 *3428 * As cs_dsp_chunk_write only writes data when a whole DSP word is ready to3429 * be written out it is possible that some data will remain in the cache, this3430 * function will pad that data with zeros upto a whole DSP word and write out.3431 *3432 * Return: Zero for success, a negative number on error.3433 */3434int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch)3435{3436	if (!ch->cachebits)3437		return 0;3438 3439	return cs_dsp_chunk_write(ch, CS_DSP_DATA_WORD_BITS - ch->cachebits, 0);3440}3441EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_flush, FW_CS_DSP);3442 3443/**3444 * cs_dsp_chunk_read() - Parse data from a DSP memory chunk3445 * @ch: Pointer to the chunk structure3446 * @nbits: Number of bits to read3447 *3448 * This function sequentially reads values from a DSP memory formatted buffer,3449 * it handles both removing of the padding bytes and converting from big endian.3450 *3451 * Return: A negative number is returned on error, otherwise the read value.3452 */3453int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits)3454{3455	int nread, i;3456	u32 result;3457 3458	if (!ch->cachebits) {3459		if (cs_dsp_chunk_end(ch))3460			return -ENOSPC;3461 3462		ch->cache = 0;3463		ch->cachebits = CS_DSP_DATA_WORD_BITS;3464 3465		for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE)3466			ch->cache |= *ch->data++;3467 3468		ch->bytes += sizeof(ch->cache);3469	}3470 3471	nread = min(ch->cachebits, nbits);3472	nbits -= nread;3473 3474	result = ch->cache >> ((sizeof(ch->cache) * BITS_PER_BYTE) - nread);3475	ch->cache <<= nread;3476	ch->cachebits -= nread;3477 3478	if (nbits)3479		result = (result << nbits) | cs_dsp_chunk_read(ch, nbits);3480 3481	return result;3482}3483EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP);3484 3485 3486struct cs_dsp_wseq_op {3487	struct list_head list;3488	u32 address;3489	u32 data;3490	u16 offset;3491	u8 operation;3492};3493 3494static void cs_dsp_wseq_clear(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq)3495{3496	struct cs_dsp_wseq_op *op, *op_tmp;3497 3498	list_for_each_entry_safe(op, op_tmp, &wseq->ops, list) {3499		list_del(&op->list);3500		devm_kfree(dsp->dev, op);3501	}3502}3503 3504static int cs_dsp_populate_wseq(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq)3505{3506	struct cs_dsp_wseq_op *op = NULL;3507	struct cs_dsp_chunk chunk;3508	u8 *words;3509	int ret;3510 3511	if (!wseq->ctl) {3512		cs_dsp_err(dsp, "No control for write sequence\n");3513		return -EINVAL;3514	}3515 3516	words = kzalloc(wseq->ctl->len, GFP_KERNEL);3517	if (!words)3518		return -ENOMEM;3519 3520	ret = cs_dsp_coeff_read_ctrl(wseq->ctl, 0, words, wseq->ctl->len);3521	if (ret) {3522		cs_dsp_err(dsp, "Failed to read %s: %d\n", wseq->ctl->subname, ret);3523		goto err_free;3524	}3525 3526	INIT_LIST_HEAD(&wseq->ops);3527 3528	chunk = cs_dsp_chunk(words, wseq->ctl->len);3529 3530	while (!cs_dsp_chunk_end(&chunk)) {3531		op = devm_kzalloc(dsp->dev, sizeof(*op), GFP_KERNEL);3532		if (!op) {3533			ret = -ENOMEM;3534			goto err_free;3535		}3536 3537		op->offset = cs_dsp_chunk_bytes(&chunk);3538		op->operation = cs_dsp_chunk_read(&chunk, 8);3539 3540		switch (op->operation) {3541		case CS_DSP_WSEQ_END:3542			op->data = WSEQ_END_OF_SCRIPT;3543			break;3544		case CS_DSP_WSEQ_UNLOCK:3545			op->data = cs_dsp_chunk_read(&chunk, 16);3546			break;3547		case CS_DSP_WSEQ_ADDR8:3548			op->address = cs_dsp_chunk_read(&chunk, 8);3549			op->data = cs_dsp_chunk_read(&chunk, 32);3550			break;3551		case CS_DSP_WSEQ_H16:3552		case CS_DSP_WSEQ_L16:3553			op->address = cs_dsp_chunk_read(&chunk, 24);3554			op->data = cs_dsp_chunk_read(&chunk, 16);3555			break;3556		case CS_DSP_WSEQ_FULL:3557			op->address = cs_dsp_chunk_read(&chunk, 32);3558			op->data = cs_dsp_chunk_read(&chunk, 32);3559			break;3560		default:3561			ret = -EINVAL;3562			cs_dsp_err(dsp, "Unsupported op: %X\n", op->operation);3563			devm_kfree(dsp->dev, op);3564			goto err_free;3565		}3566 3567		list_add_tail(&op->list, &wseq->ops);3568 3569		if (op->operation == CS_DSP_WSEQ_END)3570			break;3571	}3572 3573	if (op && op->operation != CS_DSP_WSEQ_END) {3574		cs_dsp_err(dsp, "%s missing end terminator\n", wseq->ctl->subname);3575		ret = -ENOENT;3576	}3577 3578err_free:3579	kfree(words);3580 3581	return ret;3582}3583 3584/**3585 * cs_dsp_wseq_init() - Initialize write sequences contained within the loaded DSP firmware3586 * @dsp: Pointer to DSP structure3587 * @wseqs: List of write sequences to initialize3588 * @num_wseqs: Number of write sequences to initialize3589 *3590 * Return: Zero for success, a negative number on error.3591 */3592int cs_dsp_wseq_init(struct cs_dsp *dsp, struct cs_dsp_wseq *wseqs, unsigned int num_wseqs)3593{3594	int i, ret;3595 3596	lockdep_assert_held(&dsp->pwr_lock);3597 3598	for (i = 0; i < num_wseqs; i++) {3599		ret = cs_dsp_populate_wseq(dsp, &wseqs[i]);3600		if (ret) {3601			cs_dsp_wseq_clear(dsp, &wseqs[i]);3602			return ret;3603		}3604	}3605 3606	return 0;3607}3608EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_init, FW_CS_DSP);3609 3610static struct cs_dsp_wseq_op *cs_dsp_wseq_find_op(u32 addr, u8 op_code,3611						  struct list_head *wseq_ops)3612{3613	struct cs_dsp_wseq_op *op;3614 3615	list_for_each_entry(op, wseq_ops, list) {3616		if (op->operation == op_code && op->address == addr)3617			return op;3618	}3619 3620	return NULL;3621}3622 3623/**3624 * cs_dsp_wseq_write() - Add or update an entry in a write sequence3625 * @dsp: Pointer to a DSP structure3626 * @wseq: Write sequence to write to3627 * @addr: Address of the register to be written to3628 * @data: Data to be written3629 * @op_code: The type of operation of the new entry3630 * @update: If true, searches for the first entry in the write sequence with3631 * the same address and op_code, and replaces it. If false, creates a new entry3632 * at the tail3633 *3634 * This function formats register address and value pairs into the format3635 * required for write sequence entries, and either updates or adds the3636 * new entry into the write sequence.3637 *3638 * If update is set to true and no matching entry is found, it will add a new entry.3639 *3640 * Return: Zero for success, a negative number on error.3641 */3642int cs_dsp_wseq_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq,3643		      u32 addr, u32 data, u8 op_code, bool update)3644{3645	struct cs_dsp_wseq_op *op_end, *op_new = NULL;3646	u32 words[WSEQ_OP_MAX_WORDS];3647	struct cs_dsp_chunk chunk;3648	int new_op_size, ret;3649 3650	if (update)3651		op_new = cs_dsp_wseq_find_op(addr, op_code, &wseq->ops);3652 3653	/* If entry to update is not found, treat it as a new operation */3654	if (!op_new) {3655		op_end = cs_dsp_wseq_find_op(0, CS_DSP_WSEQ_END, &wseq->ops);3656		if (!op_end) {3657			cs_dsp_err(dsp, "Missing terminator for %s\n", wseq->ctl->subname);3658			return -EINVAL;3659		}3660 3661		op_new = devm_kzalloc(dsp->dev, sizeof(*op_new), GFP_KERNEL);3662		if (!op_new)3663			return -ENOMEM;3664 3665		op_new->operation = op_code;3666		op_new->address = addr;3667		op_new->offset = op_end->offset;3668		update = false;3669	}3670 3671	op_new->data = data;3672 3673	chunk = cs_dsp_chunk(words, sizeof(words));3674	cs_dsp_chunk_write(&chunk, 8, op_new->operation);3675 3676	switch (op_code) {3677	case CS_DSP_WSEQ_FULL:3678		cs_dsp_chunk_write(&chunk, 32, op_new->address);3679		cs_dsp_chunk_write(&chunk, 32, op_new->data);3680		break;3681	case CS_DSP_WSEQ_L16:3682	case CS_DSP_WSEQ_H16:3683		cs_dsp_chunk_write(&chunk, 24, op_new->address);3684		cs_dsp_chunk_write(&chunk, 16, op_new->data);3685		break;3686	default:3687		ret = -EINVAL;3688		cs_dsp_err(dsp, "Operation %X not supported\n", op_code);3689		goto op_new_free;3690	}3691 3692	new_op_size = cs_dsp_chunk_bytes(&chunk);3693 3694	if (!update) {3695		if (wseq->ctl->len - op_end->offset < new_op_size) {3696			cs_dsp_err(dsp, "Not enough memory in %s for entry\n", wseq->ctl->subname);3697			ret = -E2BIG;3698			goto op_new_free;3699		}3700 3701		op_end->offset += new_op_size;3702 3703		ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_end->offset / sizeof(u32),3704					      &op_end->data, sizeof(u32));3705		if (ret)3706			goto op_new_free;3707 3708		list_add_tail(&op_new->list, &op_end->list);3709	}3710 3711	ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_new->offset / sizeof(u32),3712				      words, new_op_size);3713	if (ret)3714		goto op_new_free;3715 3716	return 0;3717 3718op_new_free:3719	devm_kfree(dsp->dev, op_new);3720 3721	return ret;3722}3723EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_write, FW_CS_DSP);3724 3725/**3726 * cs_dsp_wseq_multi_write() - Add or update multiple entries in a write sequence3727 * @dsp: Pointer to a DSP structure3728 * @wseq: Write sequence to write to3729 * @reg_seq: List of address-data pairs3730 * @num_regs: Number of address-data pairs3731 * @op_code: The types of operations of the new entries3732 * @update: If true, searches for the first entry in the write sequence with3733 * the same address and op_code, and replaces it. If false, creates a new entry3734 * at the tail3735 *3736 * This function calls cs_dsp_wseq_write() for multiple address-data pairs.3737 *3738 * Return: Zero for success, a negative number on error.3739 */3740int cs_dsp_wseq_multi_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq,3741			    const struct reg_sequence *reg_seq, int num_regs,3742			    u8 op_code, bool update)3743{3744	int i, ret;3745 3746	for (i = 0; i < num_regs; i++) {3747		ret = cs_dsp_wseq_write(dsp, wseq, reg_seq[i].reg,3748					reg_seq[i].def, op_code, update);3749		if (ret)3750			return ret;3751	}3752 3753	return 0;3754}3755EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_multi_write, FW_CS_DSP);3756 3757MODULE_DESCRIPTION("Cirrus Logic DSP Support");3758MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");3759MODULE_LICENSE("GPL v2");3760