ASoC: arizona: Add parentheses
[~shefty/rdma-dev.git] / sound / soc / codecs / wm_adsp.c
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);