]> git.openfabrics.org - ~shefty/rdma-dev.git/blob - sound/soc/codecs/wm_adsp.c
c72d3fa8572740f9c317a73fc5ed8bbbb7c5a767
[~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/slab.h>
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/pcm_params.h>
25 #include <sound/soc.h>
26 #include <sound/jack.h>
27 #include <sound/initval.h>
28 #include <sound/tlv.h>
29
30 #include <linux/mfd/arizona/registers.h>
31
32 #include "wm_adsp.h"
33
34 #define adsp_crit(_dsp, fmt, ...) \
35         dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
36 #define adsp_err(_dsp, fmt, ...) \
37         dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
38 #define adsp_warn(_dsp, fmt, ...) \
39         dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
40 #define adsp_info(_dsp, fmt, ...) \
41         dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
42 #define adsp_dbg(_dsp, fmt, ...) \
43         dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
44
45 #define ADSP1_CONTROL_1                   0x00
46 #define ADSP1_CONTROL_2                   0x02
47 #define ADSP1_CONTROL_3                   0x03
48 #define ADSP1_CONTROL_4                   0x04
49 #define ADSP1_CONTROL_5                   0x06
50 #define ADSP1_CONTROL_6                   0x07
51 #define ADSP1_CONTROL_7                   0x08
52 #define ADSP1_CONTROL_8                   0x09
53 #define ADSP1_CONTROL_9                   0x0A
54 #define ADSP1_CONTROL_10                  0x0B
55 #define ADSP1_CONTROL_11                  0x0C
56 #define ADSP1_CONTROL_12                  0x0D
57 #define ADSP1_CONTROL_13                  0x0F
58 #define ADSP1_CONTROL_14                  0x10
59 #define ADSP1_CONTROL_15                  0x11
60 #define ADSP1_CONTROL_16                  0x12
61 #define ADSP1_CONTROL_17                  0x13
62 #define ADSP1_CONTROL_18                  0x14
63 #define ADSP1_CONTROL_19                  0x16
64 #define ADSP1_CONTROL_20                  0x17
65 #define ADSP1_CONTROL_21                  0x18
66 #define ADSP1_CONTROL_22                  0x1A
67 #define ADSP1_CONTROL_23                  0x1B
68 #define ADSP1_CONTROL_24                  0x1C
69 #define ADSP1_CONTROL_25                  0x1E
70 #define ADSP1_CONTROL_26                  0x20
71 #define ADSP1_CONTROL_27                  0x21
72 #define ADSP1_CONTROL_28                  0x22
73 #define ADSP1_CONTROL_29                  0x23
74 #define ADSP1_CONTROL_30                  0x24
75 #define ADSP1_CONTROL_31                  0x26
76
77 /*
78  * ADSP1 Control 19
79  */
80 #define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
81 #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
82 #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
83
84
85 /*
86  * ADSP1 Control 30
87  */
88 #define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
89 #define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
90 #define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
91 #define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
92 #define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
93 #define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
94 #define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
95 #define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
96 #define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
97 #define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
98 #define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
99 #define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
100 #define ADSP1_START                       0x0001  /* DSP1_START */
101 #define ADSP1_START_MASK                  0x0001  /* DSP1_START */
102 #define ADSP1_START_SHIFT                      0  /* DSP1_START */
103 #define ADSP1_START_WIDTH                      1  /* DSP1_START */
104
105 #define ADSP2_CONTROL 0
106 #define ADSP2_STATUS1 4
107
108 /*
109  * ADSP2 Control
110  */
111
112 #define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
113 #define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
114 #define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
115 #define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
116 #define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
117 #define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
118 #define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
119 #define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
120 #define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
121 #define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
122 #define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
123 #define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
124 #define ADSP2_START                       0x0001  /* DSP1_START */
125 #define ADSP2_START_MASK                  0x0001  /* DSP1_START */
126 #define ADSP2_START_SHIFT                      0  /* DSP1_START */
127 #define ADSP2_START_WIDTH                      1  /* DSP1_START */
128
129 /*
130  * ADSP2 Status 1
131  */
132 #define ADSP2_RAM_RDY                     0x0001
133 #define ADSP2_RAM_RDY_MASK                0x0001
134 #define ADSP2_RAM_RDY_SHIFT                    0
135 #define ADSP2_RAM_RDY_WIDTH                    1
136
137
138 static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
139                                                         int type)
140 {
141         int i;
142
143         for (i = 0; i < dsp->num_mems; i++)
144                 if (dsp->mem[i].type == type)
145                         return &dsp->mem[i];
146
147         return NULL;
148 }
149
150 static int wm_adsp_load(struct wm_adsp *dsp)
151 {
152         const struct firmware *firmware;
153         struct regmap *regmap = dsp->regmap;
154         unsigned int pos = 0;
155         const struct wmfw_header *header;
156         const struct wmfw_adsp1_sizes *adsp1_sizes;
157         const struct wmfw_adsp2_sizes *adsp2_sizes;
158         const struct wmfw_footer *footer;
159         const struct wmfw_region *region;
160         const struct wm_adsp_region *mem;
161         const char *region_name;
162         char *file, *text;
163         unsigned int reg;
164         int regions = 0;
165         int ret, offset, type, sizes;
166
167         file = kzalloc(PAGE_SIZE, GFP_KERNEL);
168         if (file == NULL)
169                 return -ENOMEM;
170
171         snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
172         file[PAGE_SIZE - 1] = '\0';
173
174         ret = request_firmware(&firmware, file, dsp->dev);
175         if (ret != 0) {
176                 adsp_err(dsp, "Failed to request '%s'\n", file);
177                 goto out;
178         }
179         ret = -EINVAL;
180
181         pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
182         if (pos >= firmware->size) {
183                 adsp_err(dsp, "%s: file too short, %zu bytes\n",
184                          file, firmware->size);
185                 goto out_fw;
186         }
187
188         header = (void*)&firmware->data[0];
189
190         if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
191                 adsp_err(dsp, "%s: invalid magic\n", file);
192                 goto out_fw;
193         }
194
195         if (header->ver != 0) {
196                 adsp_err(dsp, "%s: unknown file format %d\n",
197                          file, header->ver);
198                 goto out_fw;
199         }
200
201         if (header->core != dsp->type) {
202                 adsp_err(dsp, "%s: invalid core %d != %d\n",
203                          file, header->core, dsp->type);
204                 goto out_fw;
205         }
206
207         switch (dsp->type) {
208         case WMFW_ADSP1:
209                 pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
210                 adsp1_sizes = (void *)&(header[1]);
211                 footer = (void *)&(adsp1_sizes[1]);
212                 sizes = sizeof(*adsp1_sizes);
213
214                 adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
215                          file, le32_to_cpu(adsp1_sizes->dm),
216                          le32_to_cpu(adsp1_sizes->pm),
217                          le32_to_cpu(adsp1_sizes->zm));
218                 break;
219
220         case WMFW_ADSP2:
221                 pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
222                 adsp2_sizes = (void *)&(header[1]);
223                 footer = (void *)&(adsp2_sizes[1]);
224                 sizes = sizeof(*adsp2_sizes);
225
226                 adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
227                          file, le32_to_cpu(adsp2_sizes->xm),
228                          le32_to_cpu(adsp2_sizes->ym),
229                          le32_to_cpu(adsp2_sizes->pm),
230                          le32_to_cpu(adsp2_sizes->zm));
231                 break;
232
233         default:
234                 BUG_ON(NULL == "Unknown DSP type");
235                 goto out_fw;
236         }
237
238         if (le32_to_cpu(header->len) != sizeof(*header) +
239             sizes + sizeof(*footer)) {
240                 adsp_err(dsp, "%s: unexpected header length %d\n",
241                          file, le32_to_cpu(header->len));
242                 goto out_fw;
243         }
244
245         adsp_dbg(dsp, "%s: timestamp %llu\n", file,
246                  le64_to_cpu(footer->timestamp));
247
248         while (pos < firmware->size &&
249                pos - firmware->size > sizeof(*region)) {
250                 region = (void *)&(firmware->data[pos]);
251                 region_name = "Unknown";
252                 reg = 0;
253                 text = NULL;
254                 offset = le32_to_cpu(region->offset) & 0xffffff;
255                 type = be32_to_cpu(region->type) & 0xff;
256                 mem = wm_adsp_find_region(dsp, type);
257                 
258                 switch (type) {
259                 case WMFW_NAME_TEXT:
260                         region_name = "Firmware name";
261                         text = kzalloc(le32_to_cpu(region->len) + 1,
262                                        GFP_KERNEL);
263                         break;
264                 case WMFW_INFO_TEXT:
265                         region_name = "Information";
266                         text = kzalloc(le32_to_cpu(region->len) + 1,
267                                        GFP_KERNEL);
268                         break;
269                 case WMFW_ABSOLUTE:
270                         region_name = "Absolute";
271                         reg = offset;
272                         break;
273                 case WMFW_ADSP1_PM:
274                         BUG_ON(!mem);
275                         region_name = "PM";
276                         reg = mem->base + (offset * 3);
277                         break;
278                 case WMFW_ADSP1_DM:
279                         BUG_ON(!mem);
280                         region_name = "DM";
281                         reg = mem->base + (offset * 2);
282                         break;
283                 case WMFW_ADSP2_XM:
284                         BUG_ON(!mem);
285                         region_name = "XM";
286                         reg = mem->base + (offset * 2);
287                         break;
288                 case WMFW_ADSP2_YM:
289                         BUG_ON(!mem);
290                         region_name = "YM";
291                         reg = mem->base + (offset * 2);
292                         break;
293                 case WMFW_ADSP1_ZM:
294                         BUG_ON(!mem);
295                         region_name = "ZM";
296                         reg = mem->base + (offset * 2);
297                         break;
298                 default:
299                         adsp_warn(dsp,
300                                   "%s.%d: Unknown region type %x at %d(%x)\n",
301                                   file, regions, type, pos, pos);
302                         break;
303                 }
304
305                 adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
306                          regions, le32_to_cpu(region->len), offset,
307                          region_name);
308
309                 if (text) {
310                         memcpy(text, region->data, le32_to_cpu(region->len));
311                         adsp_info(dsp, "%s: %s\n", file, text);
312                         kfree(text);
313                 }
314
315                 if (reg) {
316                         ret = regmap_raw_write(regmap, reg, region->data,
317                                                le32_to_cpu(region->len));
318                         if (ret != 0) {
319                                 adsp_err(dsp,
320                                         "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
321                                         file, regions,
322                                         le32_to_cpu(region->len), offset,
323                                         region_name, ret);
324                                 goto out_fw;
325                         }
326                 }
327
328                 pos += le32_to_cpu(region->len) + sizeof(*region);
329                 regions++;
330         }
331         
332         if (pos > firmware->size)
333                 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
334                           file, regions, pos - firmware->size);
335
336 out_fw:
337         release_firmware(firmware);
338 out:
339         kfree(file);
340
341         return ret;
342 }
343
344 static int wm_adsp_load_coeff(struct wm_adsp *dsp)
345 {
346         struct regmap *regmap = dsp->regmap;
347         struct wmfw_coeff_hdr *hdr;
348         struct wmfw_coeff_item *blk;
349         const struct firmware *firmware;
350         const char *region_name;
351         int ret, pos, blocks, type, offset, reg;
352         char *file;
353
354         file = kzalloc(PAGE_SIZE, GFP_KERNEL);
355         if (file == NULL)
356                 return -ENOMEM;
357
358         snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
359         file[PAGE_SIZE - 1] = '\0';
360
361         ret = request_firmware(&firmware, file, dsp->dev);
362         if (ret != 0) {
363                 adsp_warn(dsp, "Failed to request '%s'\n", file);
364                 ret = 0;
365                 goto out;
366         }
367         ret = -EINVAL;
368
369         if (sizeof(*hdr) >= firmware->size) {
370                 adsp_err(dsp, "%s: file too short, %zu bytes\n",
371                         file, firmware->size);
372                 goto out_fw;
373         }
374
375         hdr = (void*)&firmware->data[0];
376         if (memcmp(hdr->magic, "WMDR", 4) != 0) {
377                 adsp_err(dsp, "%s: invalid magic\n", file);
378                 return -EINVAL;
379         }
380
381         adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
382                 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
383                 (le32_to_cpu(hdr->ver) >>  8) & 0xff,
384                 le32_to_cpu(hdr->ver) & 0xff);
385
386         pos = le32_to_cpu(hdr->len);
387
388         blocks = 0;
389         while (pos < firmware->size &&
390                pos - firmware->size > sizeof(*blk)) {
391                 blk = (void*)(&firmware->data[pos]);
392
393                 type = be32_to_cpu(blk->type) & 0xff;
394                 offset = le32_to_cpu(blk->offset) & 0xffffff;
395
396                 adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
397                          file, blocks, le32_to_cpu(blk->id),
398                          (le32_to_cpu(blk->ver) >> 16) & 0xff,
399                          (le32_to_cpu(blk->ver) >>  8) & 0xff,
400                          le32_to_cpu(blk->ver) & 0xff);
401                 adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
402                          file, blocks, le32_to_cpu(blk->len), offset, type);
403
404                 reg = 0;
405                 region_name = "Unknown";
406                 switch (type) {
407                 case WMFW_NAME_TEXT:
408                 case WMFW_INFO_TEXT:
409                         break;
410                 case WMFW_ABSOLUTE:
411                         region_name = "register";
412                         reg = offset;
413                         break;
414                 default:
415                         adsp_err(dsp, "Unknown region type %x\n", type);
416                         break;
417                 }
418
419                 if (reg) {
420                         ret = regmap_raw_write(regmap, reg, blk->data,
421                                                le32_to_cpu(blk->len));
422                         if (ret != 0) {
423                                 adsp_err(dsp,
424                                         "%s.%d: Failed to write to %x in %s\n",
425                                         file, blocks, reg, region_name);
426                         }
427                 }
428
429                 pos += le32_to_cpu(blk->len) + sizeof(*blk);
430                 blocks++;
431         }
432
433         if (pos > firmware->size)
434                 adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
435                           file, blocks, pos - firmware->size);
436
437 out_fw:
438         release_firmware(firmware);
439 out:
440         kfree(file);
441         return 0;
442 }
443
444 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
445                    struct snd_kcontrol *kcontrol,
446                    int event)
447 {
448         struct snd_soc_codec *codec = w->codec;
449         struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
450         struct wm_adsp *dsp = &dsps[w->shift];
451         int ret;
452
453         switch (event) {
454         case SND_SOC_DAPM_POST_PMU:
455                 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
456                                    ADSP1_SYS_ENA, ADSP1_SYS_ENA);
457
458                 ret = wm_adsp_load(dsp);
459                 if (ret != 0)
460                         goto err;
461
462                 ret = wm_adsp_load_coeff(dsp);
463                 if (ret != 0)
464                         goto err;
465
466                 /* Start the core running */
467                 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
468                                    ADSP1_CORE_ENA | ADSP1_START,
469                                    ADSP1_CORE_ENA | ADSP1_START);
470                 break;
471
472         case SND_SOC_DAPM_PRE_PMD:
473                 /* Halt the core */
474                 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
475                                    ADSP1_CORE_ENA | ADSP1_START, 0);
476
477                 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
478                                    ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
479
480                 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
481                                    ADSP1_SYS_ENA, 0);
482                 break;
483
484         default:
485                 break;
486         }
487
488         return 0;
489
490 err:
491         regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
492                            ADSP1_SYS_ENA, 0);
493         return ret;
494 }
495 EXPORT_SYMBOL_GPL(wm_adsp1_event);
496
497 static int wm_adsp2_ena(struct wm_adsp *dsp)
498 {
499         unsigned int val;
500         int ret, count;
501
502         ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
503                                  ADSP2_SYS_ENA, ADSP2_SYS_ENA);
504         if (ret != 0)
505                 return ret;
506
507         /* Wait for the RAM to start, should be near instantaneous */
508         count = 0;
509         do {
510                 ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
511                                   &val);
512                 if (ret != 0)
513                         return ret;
514         } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
515
516         if (!(val & ADSP2_RAM_RDY)) {
517                 adsp_err(dsp, "Failed to start DSP RAM\n");
518                 return -EBUSY;
519         }
520
521         adsp_dbg(dsp, "RAM ready after %d polls\n", count);
522         adsp_info(dsp, "RAM ready after %d polls\n", count);
523
524         return 0;
525 }
526
527 int wm_adsp2_event(struct snd_soc_dapm_widget *w,
528                    struct snd_kcontrol *kcontrol, int event)
529 {
530         struct snd_soc_codec *codec = w->codec;
531         struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
532         struct wm_adsp *dsp = &dsps[w->shift];
533         int ret;
534
535         switch (event) {
536         case SND_SOC_DAPM_POST_PMU:
537                 ret = wm_adsp2_ena(dsp);
538                 if (ret != 0)
539                         return ret;
540
541                 ret = wm_adsp_load(dsp);
542                 if (ret != 0)
543                         goto err;
544
545                 ret = wm_adsp_load_coeff(dsp);
546                 if (ret != 0)
547                         goto err;
548
549                 ret = regmap_update_bits(dsp->regmap,
550                                          dsp->base + ADSP2_CONTROL,
551                                          ADSP2_SYS_ENA | ADSP2_START, 0);
552                 if (ret != 0)
553                         goto err;
554                 break;
555
556         case SND_SOC_DAPM_PRE_PMD:
557                 regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
558                                    ADSP2_SYS_ENA | ADSP2_START, 0);
559                 break;
560
561         default:
562                 break;
563         }
564
565         return 0;
566 err:
567         regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
568                            ADSP2_SYS_ENA | ADSP2_START, 0);
569         return ret;
570 }
571 EXPORT_SYMBOL_GPL(wm_adsp2_event);