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