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, ®, 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, ®, 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, ®, 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, ®, 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 = ®ion->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 *)®ion->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 = ®ion->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