1 /* 2 * wm_adsp.c -- Wolfson ADSP support 3 * 4 * Copyright 2012 Wolfson Microelectronics plc 5 * 6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/moduleparam.h> 15 #include <linux/init.h> 16 #include <linux/delay.h> 17 #include <linux/firmware.h> 18 #include <linux/pm.h> 19 #include <linux/pm_runtime.h> 20 #include <linux/regmap.h> 21 #include <linux/regulator/consumer.h> 22 #include <linux/slab.h> 23 #include <sound/core.h> 24 #include <sound/pcm.h> 25 #include <sound/pcm_params.h> 26 #include <sound/soc.h> 27 #include <sound/jack.h> 28 #include <sound/initval.h> 29 #include <sound/tlv.h> 30 31 #include <linux/mfd/arizona/registers.h> 32 33 #include "wm_adsp.h" 34 35 #define adsp_crit(_dsp, fmt, ...) \ 36 dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 37 #define adsp_err(_dsp, fmt, ...) \ 38 dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 39 #define adsp_warn(_dsp, fmt, ...) \ 40 dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 41 #define adsp_info(_dsp, fmt, ...) \ 42 dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 43 #define adsp_dbg(_dsp, fmt, ...) \ 44 dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 45 46 #define ADSP1_CONTROL_1 0x00 47 #define ADSP1_CONTROL_2 0x02 48 #define ADSP1_CONTROL_3 0x03 49 #define ADSP1_CONTROL_4 0x04 50 #define ADSP1_CONTROL_5 0x06 51 #define ADSP1_CONTROL_6 0x07 52 #define ADSP1_CONTROL_7 0x08 53 #define ADSP1_CONTROL_8 0x09 54 #define ADSP1_CONTROL_9 0x0A 55 #define ADSP1_CONTROL_10 0x0B 56 #define ADSP1_CONTROL_11 0x0C 57 #define ADSP1_CONTROL_12 0x0D 58 #define ADSP1_CONTROL_13 0x0F 59 #define ADSP1_CONTROL_14 0x10 60 #define ADSP1_CONTROL_15 0x11 61 #define ADSP1_CONTROL_16 0x12 62 #define ADSP1_CONTROL_17 0x13 63 #define ADSP1_CONTROL_18 0x14 64 #define ADSP1_CONTROL_19 0x16 65 #define ADSP1_CONTROL_20 0x17 66 #define ADSP1_CONTROL_21 0x18 67 #define ADSP1_CONTROL_22 0x1A 68 #define ADSP1_CONTROL_23 0x1B 69 #define ADSP1_CONTROL_24 0x1C 70 #define ADSP1_CONTROL_25 0x1E 71 #define ADSP1_CONTROL_26 0x20 72 #define ADSP1_CONTROL_27 0x21 73 #define ADSP1_CONTROL_28 0x22 74 #define ADSP1_CONTROL_29 0x23 75 #define ADSP1_CONTROL_30 0x24 76 #define ADSP1_CONTROL_31 0x26 77 78 /* 79 * ADSP1 Control 19 80 */ 81 #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 82 #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 83 #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 84 85 86 /* 87 * ADSP1 Control 30 88 */ 89 #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 90 #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 91 #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 92 #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 93 #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 94 #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 95 #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 96 #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 97 #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 98 #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 99 #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 100 #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 101 #define ADSP1_START 0x0001 /* DSP1_START */ 102 #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 103 #define ADSP1_START_SHIFT 0 /* DSP1_START */ 104 #define ADSP1_START_WIDTH 1 /* DSP1_START */ 105 106 #define ADSP2_CONTROL 0 107 #define ADSP2_CLOCKING 1 108 #define ADSP2_STATUS1 4 109 110 /* 111 * ADSP2 Control 112 */ 113 114 #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 115 #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 116 #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 117 #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 118 #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 119 #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 120 #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 121 #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 122 #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 123 #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 124 #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 125 #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 126 #define ADSP2_START 0x0001 /* DSP1_START */ 127 #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 128 #define ADSP2_START_SHIFT 0 /* DSP1_START */ 129 #define ADSP2_START_WIDTH 1 /* DSP1_START */ 130 131 /* 132 * ADSP2 clocking 133 */ 134 #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 135 #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 136 #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 137 138 /* 139 * ADSP2 Status 1 140 */ 141 #define ADSP2_RAM_RDY 0x0001 142 #define ADSP2_RAM_RDY_MASK 0x0001 143 #define ADSP2_RAM_RDY_SHIFT 0 144 #define ADSP2_RAM_RDY_WIDTH 1 145 146 147 static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 148 int type) 149 { 150 int i; 151 152 for (i = 0; i < dsp->num_mems; i++) 153 if (dsp->mem[i].type == type) 154 return &dsp->mem[i]; 155 156 return NULL; 157 } 158 159 static int wm_adsp_load(struct wm_adsp *dsp) 160 { 161 const struct firmware *firmware; 162 struct regmap *regmap = dsp->regmap; 163 unsigned int pos = 0; 164 const struct wmfw_header *header; 165 const struct wmfw_adsp1_sizes *adsp1_sizes; 166 const struct wmfw_adsp2_sizes *adsp2_sizes; 167 const struct wmfw_footer *footer; 168 const struct wmfw_region *region; 169 const struct wm_adsp_region *mem; 170 const char *region_name; 171 char *file, *text; 172 unsigned int reg; 173 int regions = 0; 174 int ret, offset, type, sizes; 175 176 file = kzalloc(PAGE_SIZE, GFP_KERNEL); 177 if (file == NULL) 178 return -ENOMEM; 179 180 snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num); 181 file[PAGE_SIZE - 1] = '\0'; 182 183 ret = request_firmware(&firmware, file, dsp->dev); 184 if (ret != 0) { 185 adsp_err(dsp, "Failed to request '%s'\n", file); 186 goto out; 187 } 188 ret = -EINVAL; 189 190 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 191 if (pos >= firmware->size) { 192 adsp_err(dsp, "%s: file too short, %zu bytes\n", 193 file, firmware->size); 194 goto out_fw; 195 } 196 197 header = (void*)&firmware->data[0]; 198 199 if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 200 adsp_err(dsp, "%s: invalid magic\n", file); 201 goto out_fw; 202 } 203 204 if (header->ver != 0) { 205 adsp_err(dsp, "%s: unknown file format %d\n", 206 file, header->ver); 207 goto out_fw; 208 } 209 210 if (header->core != dsp->type) { 211 adsp_err(dsp, "%s: invalid core %d != %d\n", 212 file, header->core, dsp->type); 213 goto out_fw; 214 } 215 216 switch (dsp->type) { 217 case WMFW_ADSP1: 218 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 219 adsp1_sizes = (void *)&(header[1]); 220 footer = (void *)&(adsp1_sizes[1]); 221 sizes = sizeof(*adsp1_sizes); 222 223 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 224 file, le32_to_cpu(adsp1_sizes->dm), 225 le32_to_cpu(adsp1_sizes->pm), 226 le32_to_cpu(adsp1_sizes->zm)); 227 break; 228 229 case WMFW_ADSP2: 230 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 231 adsp2_sizes = (void *)&(header[1]); 232 footer = (void *)&(adsp2_sizes[1]); 233 sizes = sizeof(*adsp2_sizes); 234 235 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 236 file, le32_to_cpu(adsp2_sizes->xm), 237 le32_to_cpu(adsp2_sizes->ym), 238 le32_to_cpu(adsp2_sizes->pm), 239 le32_to_cpu(adsp2_sizes->zm)); 240 break; 241 242 default: 243 BUG_ON(NULL == "Unknown DSP type"); 244 goto out_fw; 245 } 246 247 if (le32_to_cpu(header->len) != sizeof(*header) + 248 sizes + sizeof(*footer)) { 249 adsp_err(dsp, "%s: unexpected header length %d\n", 250 file, le32_to_cpu(header->len)); 251 goto out_fw; 252 } 253 254 adsp_dbg(dsp, "%s: timestamp %llu\n", file, 255 le64_to_cpu(footer->timestamp)); 256 257 while (pos < firmware->size && 258 pos - firmware->size > sizeof(*region)) { 259 region = (void *)&(firmware->data[pos]); 260 region_name = "Unknown"; 261 reg = 0; 262 text = NULL; 263 offset = le32_to_cpu(region->offset) & 0xffffff; 264 type = be32_to_cpu(region->type) & 0xff; 265 mem = wm_adsp_find_region(dsp, type); 266 267 switch (type) { 268 case WMFW_NAME_TEXT: 269 region_name = "Firmware name"; 270 text = kzalloc(le32_to_cpu(region->len) + 1, 271 GFP_KERNEL); 272 break; 273 case WMFW_INFO_TEXT: 274 region_name = "Information"; 275 text = kzalloc(le32_to_cpu(region->len) + 1, 276 GFP_KERNEL); 277 break; 278 case WMFW_ABSOLUTE: 279 region_name = "Absolute"; 280 reg = offset; 281 break; 282 case WMFW_ADSP1_PM: 283 BUG_ON(!mem); 284 region_name = "PM"; 285 reg = mem->base + (offset * 3); 286 break; 287 case WMFW_ADSP1_DM: 288 BUG_ON(!mem); 289 region_name = "DM"; 290 reg = mem->base + (offset * 2); 291 break; 292 case WMFW_ADSP2_XM: 293 BUG_ON(!mem); 294 region_name = "XM"; 295 reg = mem->base + (offset * 2); 296 break; 297 case WMFW_ADSP2_YM: 298 BUG_ON(!mem); 299 region_name = "YM"; 300 reg = mem->base + (offset * 2); 301 break; 302 case WMFW_ADSP1_ZM: 303 BUG_ON(!mem); 304 region_name = "ZM"; 305 reg = mem->base + (offset * 2); 306 break; 307 default: 308 adsp_warn(dsp, 309 "%s.%d: Unknown region type %x at %d(%x)\n", 310 file, regions, type, pos, pos); 311 break; 312 } 313 314 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 315 regions, le32_to_cpu(region->len), offset, 316 region_name); 317 318 if (text) { 319 memcpy(text, region->data, le32_to_cpu(region->len)); 320 adsp_info(dsp, "%s: %s\n", file, text); 321 kfree(text); 322 } 323 324 if (reg) { 325 ret = regmap_raw_write(regmap, reg, region->data, 326 le32_to_cpu(region->len)); 327 if (ret != 0) { 328 adsp_err(dsp, 329 "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 330 file, regions, 331 le32_to_cpu(region->len), offset, 332 region_name, ret); 333 goto out_fw; 334 } 335 } 336 337 pos += le32_to_cpu(region->len) + sizeof(*region); 338 regions++; 339 } 340 341 if (pos > firmware->size) 342 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 343 file, regions, pos - firmware->size); 344 345 out_fw: 346 release_firmware(firmware); 347 out: 348 kfree(file); 349 350 return ret; 351 } 352 353 static int wm_adsp_load_coeff(struct wm_adsp *dsp) 354 { 355 struct regmap *regmap = dsp->regmap; 356 struct wmfw_coeff_hdr *hdr; 357 struct wmfw_coeff_item *blk; 358 const struct firmware *firmware; 359 const char *region_name; 360 int ret, pos, blocks, type, offset, reg; 361 char *file; 362 363 file = kzalloc(PAGE_SIZE, GFP_KERNEL); 364 if (file == NULL) 365 return -ENOMEM; 366 367 snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num); 368 file[PAGE_SIZE - 1] = '\0'; 369 370 ret = request_firmware(&firmware, file, dsp->dev); 371 if (ret != 0) { 372 adsp_warn(dsp, "Failed to request '%s'\n", file); 373 ret = 0; 374 goto out; 375 } 376 ret = -EINVAL; 377 378 if (sizeof(*hdr) >= firmware->size) { 379 adsp_err(dsp, "%s: file too short, %zu bytes\n", 380 file, firmware->size); 381 goto out_fw; 382 } 383 384 hdr = (void*)&firmware->data[0]; 385 if (memcmp(hdr->magic, "WMDR", 4) != 0) { 386 adsp_err(dsp, "%s: invalid magic\n", file); 387 return -EINVAL; 388 } 389 390 adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 391 (le32_to_cpu(hdr->ver) >> 16) & 0xff, 392 (le32_to_cpu(hdr->ver) >> 8) & 0xff, 393 le32_to_cpu(hdr->ver) & 0xff); 394 395 pos = le32_to_cpu(hdr->len); 396 397 blocks = 0; 398 while (pos < firmware->size && 399 pos - firmware->size > sizeof(*blk)) { 400 blk = (void*)(&firmware->data[pos]); 401 402 type = be32_to_cpu(blk->type) & 0xff; 403 offset = le32_to_cpu(blk->offset) & 0xffffff; 404 405 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 406 file, blocks, le32_to_cpu(blk->id), 407 (le32_to_cpu(blk->ver) >> 16) & 0xff, 408 (le32_to_cpu(blk->ver) >> 8) & 0xff, 409 le32_to_cpu(blk->ver) & 0xff); 410 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 411 file, blocks, le32_to_cpu(blk->len), offset, type); 412 413 reg = 0; 414 region_name = "Unknown"; 415 switch (type) { 416 case WMFW_NAME_TEXT: 417 case WMFW_INFO_TEXT: 418 break; 419 case WMFW_ABSOLUTE: 420 region_name = "register"; 421 reg = offset; 422 break; 423 default: 424 adsp_err(dsp, "Unknown region type %x\n", type); 425 break; 426 } 427 428 if (reg) { 429 ret = regmap_raw_write(regmap, reg, blk->data, 430 le32_to_cpu(blk->len)); 431 if (ret != 0) { 432 adsp_err(dsp, 433 "%s.%d: Failed to write to %x in %s\n", 434 file, blocks, reg, region_name); 435 } 436 } 437 438 pos += le32_to_cpu(blk->len) + sizeof(*blk); 439 blocks++; 440 } 441 442 if (pos > firmware->size) 443 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 444 file, blocks, pos - firmware->size); 445 446 out_fw: 447 release_firmware(firmware); 448 out: 449 kfree(file); 450 return 0; 451 } 452 453 int wm_adsp1_event(struct snd_soc_dapm_widget *w, 454 struct snd_kcontrol *kcontrol, 455 int event) 456 { 457 struct snd_soc_codec *codec = w->codec; 458 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 459 struct wm_adsp *dsp = &dsps[w->shift]; 460 int ret; 461 462 switch (event) { 463 case SND_SOC_DAPM_POST_PMU: 464 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 465 ADSP1_SYS_ENA, ADSP1_SYS_ENA); 466 467 ret = wm_adsp_load(dsp); 468 if (ret != 0) 469 goto err; 470 471 ret = wm_adsp_load_coeff(dsp); 472 if (ret != 0) 473 goto err; 474 475 /* Start the core running */ 476 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 477 ADSP1_CORE_ENA | ADSP1_START, 478 ADSP1_CORE_ENA | ADSP1_START); 479 break; 480 481 case SND_SOC_DAPM_PRE_PMD: 482 /* Halt the core */ 483 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 484 ADSP1_CORE_ENA | ADSP1_START, 0); 485 486 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 487 ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 488 489 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 490 ADSP1_SYS_ENA, 0); 491 break; 492 493 default: 494 break; 495 } 496 497 return 0; 498 499 err: 500 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 501 ADSP1_SYS_ENA, 0); 502 return ret; 503 } 504 EXPORT_SYMBOL_GPL(wm_adsp1_event); 505 506 static int wm_adsp2_ena(struct wm_adsp *dsp) 507 { 508 unsigned int val; 509 int ret, count; 510 511 ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 512 ADSP2_SYS_ENA, ADSP2_SYS_ENA); 513 if (ret != 0) 514 return ret; 515 516 /* Wait for the RAM to start, should be near instantaneous */ 517 count = 0; 518 do { 519 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, 520 &val); 521 if (ret != 0) 522 return ret; 523 } while (!(val & ADSP2_RAM_RDY) && ++count < 10); 524 525 if (!(val & ADSP2_RAM_RDY)) { 526 adsp_err(dsp, "Failed to start DSP RAM\n"); 527 return -EBUSY; 528 } 529 530 adsp_dbg(dsp, "RAM ready after %d polls\n", count); 531 adsp_info(dsp, "RAM ready after %d polls\n", count); 532 533 return 0; 534 } 535 536 int wm_adsp2_event(struct snd_soc_dapm_widget *w, 537 struct snd_kcontrol *kcontrol, int event) 538 { 539 struct snd_soc_codec *codec = w->codec; 540 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 541 struct wm_adsp *dsp = &dsps[w->shift]; 542 unsigned int val; 543 int ret; 544 545 switch (event) { 546 case SND_SOC_DAPM_POST_PMU: 547 /* 548 * For simplicity set the DSP clock rate to be the 549 * SYSCLK rate rather than making it configurable. 550 */ 551 ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); 552 if (ret != 0) { 553 adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 554 ret); 555 return ret; 556 } 557 val = (val & ARIZONA_SYSCLK_FREQ_MASK) 558 >> ARIZONA_SYSCLK_FREQ_SHIFT; 559 560 ret = regmap_update_bits(dsp->regmap, 561 dsp->base + ADSP2_CLOCKING, 562 ADSP2_CLK_SEL_MASK, val); 563 if (ret != 0) { 564 adsp_err(dsp, "Failed to set clock rate: %d\n", 565 ret); 566 return ret; 567 } 568 569 if (dsp->dvfs) { 570 ret = regmap_read(dsp->regmap, 571 dsp->base + ADSP2_CLOCKING, &val); 572 if (ret != 0) { 573 dev_err(dsp->dev, 574 "Failed to read clocking: %d\n", ret); 575 return ret; 576 } 577 578 if ((val & ADSP2_CLK_SEL_MASK) >= 3) { 579 ret = regulator_enable(dsp->dvfs); 580 if (ret != 0) { 581 dev_err(dsp->dev, 582 "Failed to enable supply: %d\n", 583 ret); 584 return ret; 585 } 586 587 ret = regulator_set_voltage(dsp->dvfs, 588 1800000, 589 1800000); 590 if (ret != 0) { 591 dev_err(dsp->dev, 592 "Failed to raise supply: %d\n", 593 ret); 594 return ret; 595 } 596 } 597 } 598 599 ret = wm_adsp2_ena(dsp); 600 if (ret != 0) 601 return ret; 602 603 ret = wm_adsp_load(dsp); 604 if (ret != 0) 605 goto err; 606 607 ret = wm_adsp_load_coeff(dsp); 608 if (ret != 0) 609 goto err; 610 611 ret = regmap_update_bits(dsp->regmap, 612 dsp->base + ADSP2_CONTROL, 613 ADSP2_CORE_ENA | ADSP2_START, 614 ADSP2_CORE_ENA | ADSP2_START); 615 if (ret != 0) 616 goto err; 617 break; 618 619 case SND_SOC_DAPM_PRE_PMD: 620 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 621 ADSP2_SYS_ENA | ADSP2_CORE_ENA | 622 ADSP2_START, 0); 623 624 if (dsp->dvfs) { 625 ret = regulator_set_voltage(dsp->dvfs, 1200000, 626 1800000); 627 if (ret != 0) 628 dev_warn(dsp->dev, 629 "Failed to lower supply: %d\n", 630 ret); 631 632 ret = regulator_disable(dsp->dvfs); 633 if (ret != 0) 634 dev_err(dsp->dev, 635 "Failed to enable supply: %d\n", 636 ret); 637 } 638 break; 639 640 default: 641 break; 642 } 643 644 return 0; 645 err: 646 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 647 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 648 return ret; 649 } 650 EXPORT_SYMBOL_GPL(wm_adsp2_event); 651 652 int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) 653 { 654 int ret; 655 656 /* 657 * Disable the DSP memory by default when in reset for a small 658 * power saving. 659 */ 660 ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL, 661 ADSP2_MEM_ENA, 0); 662 if (ret != 0) { 663 adsp_err(adsp, "Failed to clear memory retention: %d\n", ret); 664 return ret; 665 } 666 667 if (dvfs) { 668 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); 669 if (IS_ERR(adsp->dvfs)) { 670 ret = PTR_ERR(adsp->dvfs); 671 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); 672 return ret; 673 } 674 675 ret = regulator_enable(adsp->dvfs); 676 if (ret != 0) { 677 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", 678 ret); 679 return ret; 680 } 681 682 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); 683 if (ret != 0) { 684 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", 685 ret); 686 return ret; 687 } 688 689 ret = regulator_disable(adsp->dvfs); 690 if (ret != 0) { 691 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", 692 ret); 693 return ret; 694 } 695 } 696 697 return 0; 698 } 699 EXPORT_SYMBOL_GPL(wm_adsp2_init); 700