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 if (dsp->dvfs) { 548 ret = regmap_read(dsp->regmap, 549 dsp->base + ADSP2_CLOCKING, &val); 550 if (ret != 0) { 551 dev_err(dsp->dev, 552 "Failed to read clocking: %d\n", ret); 553 return ret; 554 } 555 556 if ((val & ADSP2_CLK_SEL_MASK) >= 3) { 557 ret = regulator_enable(dsp->dvfs); 558 if (ret != 0) { 559 dev_err(dsp->dev, 560 "Failed to enable supply: %d\n", 561 ret); 562 return ret; 563 } 564 565 ret = regulator_set_voltage(dsp->dvfs, 566 1800000, 567 1800000); 568 if (ret != 0) { 569 dev_err(dsp->dev, 570 "Failed to raise supply: %d\n", 571 ret); 572 return ret; 573 } 574 } 575 } 576 577 ret = wm_adsp2_ena(dsp); 578 if (ret != 0) 579 return ret; 580 581 ret = wm_adsp_load(dsp); 582 if (ret != 0) 583 goto err; 584 585 ret = wm_adsp_load_coeff(dsp); 586 if (ret != 0) 587 goto err; 588 589 ret = regmap_update_bits(dsp->regmap, 590 dsp->base + ADSP2_CONTROL, 591 ADSP2_CORE_ENA | ADSP2_START, 592 ADSP2_CORE_ENA | ADSP2_START); 593 if (ret != 0) 594 goto err; 595 break; 596 597 case SND_SOC_DAPM_PRE_PMD: 598 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 599 ADSP2_SYS_ENA | ADSP2_CORE_ENA | 600 ADSP2_START, 0); 601 602 if (dsp->dvfs) { 603 ret = regulator_set_voltage(dsp->dvfs, 1200000, 604 1800000); 605 if (ret != 0) 606 dev_warn(dsp->dev, 607 "Failed to lower supply: %d\n", 608 ret); 609 610 ret = regulator_disable(dsp->dvfs); 611 if (ret != 0) 612 dev_err(dsp->dev, 613 "Failed to enable supply: %d\n", 614 ret); 615 } 616 break; 617 618 default: 619 break; 620 } 621 622 return 0; 623 err: 624 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 625 ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 626 return ret; 627 } 628 EXPORT_SYMBOL_GPL(wm_adsp2_event); 629 630 int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) 631 { 632 int ret; 633 634 if (dvfs) { 635 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); 636 if (IS_ERR(adsp->dvfs)) { 637 ret = PTR_ERR(adsp->dvfs); 638 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); 639 return ret; 640 } 641 642 ret = regulator_enable(adsp->dvfs); 643 if (ret != 0) { 644 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", 645 ret); 646 return ret; 647 } 648 649 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); 650 if (ret != 0) { 651 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", 652 ret); 653 return ret; 654 } 655 656 ret = regulator_disable(adsp->dvfs); 657 if (ret != 0) { 658 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", 659 ret); 660 return ret; 661 } 662 } 663 664 return 0; 665 } 666 EXPORT_SYMBOL_GPL(wm_adsp2_init); 667