aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDimitris Papastamos <dp@opensource.wolfsonmicro.com>2013-05-08 09:15:35 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-05-12 12:30:36 -0400
commit6ab2b7b415441fa46357bef883e1ead086de1387 (patch)
tree8e306b0dfd309099825cee932362c086d0ac5c0f
parentf722406faae2d073cc1d01063d1123c35425939e (diff)
ASoC: wm_adsp: Expose coefficient blocks as ALSA binary controls
Add initial support for runtime tuning for the ADSP cores. This is achieved by exposing the coefficient configuration blocks as ALSA binary controls. The current code assumes that no controls on the DSP are volatile. Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--sound/soc/codecs/wm_adsp.c458
-rw-r--r--sound/soc/codecs/wm_adsp.h3
2 files changed, 454 insertions, 7 deletions
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 3470b649c0b2..137830611928 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -21,6 +21,7 @@
21#include <linux/regmap.h> 21#include <linux/regmap.h>
22#include <linux/regulator/consumer.h> 22#include <linux/regulator/consumer.h>
23#include <linux/slab.h> 23#include <linux/slab.h>
24#include <linux/workqueue.h>
24#include <sound/core.h> 25#include <sound/core.h>
25#include <sound/pcm.h> 26#include <sound/pcm.h>
26#include <sound/pcm_params.h> 27#include <sound/pcm_params.h>
@@ -215,6 +216,36 @@ static struct {
215 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 216 [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
216}; 217};
217 218
219struct wm_coeff_ctl_ops {
220 int (*xget)(struct snd_kcontrol *kcontrol,
221 struct snd_ctl_elem_value *ucontrol);
222 int (*xput)(struct snd_kcontrol *kcontrol,
223 struct snd_ctl_elem_value *ucontrol);
224 int (*xinfo)(struct snd_kcontrol *kcontrol,
225 struct snd_ctl_elem_info *uinfo);
226};
227
228struct wm_coeff {
229 struct device *dev;
230 struct list_head ctl_list;
231 struct regmap *regmap;
232};
233
234struct wm_coeff_ctl {
235 const char *name;
236 struct snd_card *card;
237 struct wm_adsp_alg_region region;
238 struct wm_coeff_ctl_ops ops;
239 struct wm_adsp *adsp;
240 void *private;
241 unsigned int enabled:1;
242 struct list_head list;
243 void *cache;
244 size_t len;
245 unsigned int dirty:1;
246 struct snd_kcontrol *kcontrol;
247};
248
218static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 249static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
219 struct snd_ctl_elem_value *ucontrol) 250 struct snd_ctl_elem_value *ucontrol)
220{ 251{
@@ -334,6 +365,181 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
334 } 365 }
335} 366}
336 367
368static int wm_coeff_info(struct snd_kcontrol *kcontrol,
369 struct snd_ctl_elem_info *uinfo)
370{
371 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
372
373 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
374 uinfo->count = ctl->len;
375 return 0;
376}
377
378static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
379 const void *buf, size_t len)
380{
381 struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
382 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
383 struct wm_adsp_alg_region *region = &ctl->region;
384 const struct wm_adsp_region *mem;
385 struct wm_adsp *adsp = ctl->adsp;
386 void *scratch;
387 int ret;
388 unsigned int reg;
389
390 mem = wm_adsp_find_region(adsp, region->type);
391 if (!mem) {
392 adsp_err(adsp, "No base for region %x\n",
393 region->type);
394 return -EINVAL;
395 }
396
397 reg = ctl->region.base;
398 reg = wm_adsp_region_to_reg(mem, reg);
399
400 scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
401 if (!scratch)
402 return -ENOMEM;
403
404 ret = regmap_raw_write(wm_coeff->regmap, reg, scratch,
405 ctl->len);
406 if (ret) {
407 adsp_err(adsp, "Failed to write %zu bytes to %x\n",
408 ctl->len, reg);
409 kfree(scratch);
410 return ret;
411 }
412
413 kfree(scratch);
414
415 return 0;
416}
417
418static int wm_coeff_put(struct snd_kcontrol *kcontrol,
419 struct snd_ctl_elem_value *ucontrol)
420{
421 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
422 char *p = ucontrol->value.bytes.data;
423
424 memcpy(ctl->cache, p, ctl->len);
425
426 if (!ctl->enabled) {
427 ctl->dirty = 1;
428 return 0;
429 }
430
431 return wm_coeff_write_control(kcontrol, p, ctl->len);
432}
433
434static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
435 void *buf, size_t len)
436{
437 struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
438 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
439 struct wm_adsp_alg_region *region = &ctl->region;
440 const struct wm_adsp_region *mem;
441 struct wm_adsp *adsp = ctl->adsp;
442 void *scratch;
443 int ret;
444 unsigned int reg;
445
446 mem = wm_adsp_find_region(adsp, region->type);
447 if (!mem) {
448 adsp_err(adsp, "No base for region %x\n",
449 region->type);
450 return -EINVAL;
451 }
452
453 reg = ctl->region.base;
454 reg = wm_adsp_region_to_reg(mem, reg);
455
456 scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
457 if (!scratch)
458 return -ENOMEM;
459
460 ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len);
461 if (ret) {
462 adsp_err(adsp, "Failed to read %zu bytes from %x\n",
463 ctl->len, reg);
464 kfree(scratch);
465 return ret;
466 }
467
468 memcpy(buf, scratch, ctl->len);
469 kfree(scratch);
470
471 return 0;
472}
473
474static int wm_coeff_get(struct snd_kcontrol *kcontrol,
475 struct snd_ctl_elem_value *ucontrol)
476{
477 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
478 char *p = ucontrol->value.bytes.data;
479
480 memcpy(p, ctl->cache, ctl->len);
481 return 0;
482}
483
484static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff,
485 struct wm_coeff_ctl *ctl,
486 const struct snd_kcontrol_new *kctl)
487{
488 int ret;
489 struct snd_kcontrol *kcontrol;
490
491 kcontrol = snd_ctl_new1(kctl, wm_coeff);
492 ret = snd_ctl_add(ctl->card, kcontrol);
493 if (ret < 0) {
494 dev_err(wm_coeff->dev, "Failed to add %s: %d\n",
495 kctl->name, ret);
496 return ret;
497 }
498 ctl->kcontrol = kcontrol;
499 return 0;
500}
501
502struct wmfw_ctl_work {
503 struct wm_coeff *wm_coeff;
504 struct wm_coeff_ctl *ctl;
505 struct work_struct work;
506};
507
508static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
509 struct wm_coeff_ctl *ctl)
510{
511 struct snd_kcontrol_new *kcontrol;
512 int ret;
513
514 if (!wm_coeff || !ctl || !ctl->name || !ctl->card)
515 return -EINVAL;
516
517 kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
518 if (!kcontrol)
519 return -ENOMEM;
520 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
521
522 kcontrol->name = ctl->name;
523 kcontrol->info = wm_coeff_info;
524 kcontrol->get = wm_coeff_get;
525 kcontrol->put = wm_coeff_put;
526 kcontrol->private_value = (unsigned long)ctl;
527
528 ret = wm_coeff_add_kcontrol(wm_coeff,
529 ctl, kcontrol);
530 if (ret < 0)
531 goto err_kcontrol;
532
533 kfree(kcontrol);
534
535 list_add(&ctl->list, &wm_coeff->ctl_list);
536 return 0;
537
538err_kcontrol:
539 kfree(kcontrol);
540 return ret;
541}
542
337static int wm_adsp_load(struct wm_adsp *dsp) 543static int wm_adsp_load(struct wm_adsp *dsp)
338{ 544{
339 LIST_HEAD(buf_list); 545 LIST_HEAD(buf_list);
@@ -547,7 +753,156 @@ out:
547 return ret; 753 return ret;
548} 754}
549 755
550static int wm_adsp_setup_algs(struct wm_adsp *dsp) 756static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
757{
758 struct wm_coeff_ctl *ctl;
759 int ret;
760
761 list_for_each_entry(ctl, &wm_coeff->ctl_list,
762 list) {
763 if (!ctl->enabled || ctl->dirty)
764 continue;
765 ret = wm_coeff_read_control(ctl->kcontrol,
766 ctl->cache,
767 ctl->len);
768 if (ret < 0)
769 return ret;
770 }
771
772 return 0;
773}
774
775static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff)
776{
777 struct wm_coeff_ctl *ctl;
778 int ret;
779
780 list_for_each_entry(ctl, &wm_coeff->ctl_list,
781 list) {
782 if (!ctl->enabled)
783 continue;
784 if (ctl->dirty) {
785 ret = wm_coeff_write_control(ctl->kcontrol,
786 ctl->cache,
787 ctl->len);
788 if (ret < 0)
789 return ret;
790 ctl->dirty = 0;
791 }
792 }
793
794 return 0;
795}
796
797static void wm_adsp_ctl_work(struct work_struct *work)
798{
799 struct wmfw_ctl_work *ctl_work = container_of(work,
800 struct wmfw_ctl_work,
801 work);
802
803 wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl);
804 kfree(ctl_work);
805}
806
807static int wm_adsp_create_control(struct snd_soc_codec *codec,
808 const struct wm_adsp_alg_region *region)
809
810{
811 struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
812 struct wm_coeff_ctl *ctl;
813 struct wmfw_ctl_work *ctl_work;
814 char *name;
815 char *region_name;
816 int ret;
817
818 name = kmalloc(PAGE_SIZE, GFP_KERNEL);
819 if (!name)
820 return -ENOMEM;
821
822 switch (region->type) {
823 case WMFW_ADSP1_PM:
824 region_name = "PM";
825 break;
826 case WMFW_ADSP1_DM:
827 region_name = "DM";
828 break;
829 case WMFW_ADSP2_XM:
830 region_name = "XM";
831 break;
832 case WMFW_ADSP2_YM:
833 region_name = "YM";
834 break;
835 case WMFW_ADSP1_ZM:
836 region_name = "ZM";
837 break;
838 default:
839 return -EINVAL;
840 }
841
842 snprintf(name, PAGE_SIZE, "DSP%d %s %x",
843 dsp->num, region_name, region->alg);
844
845 list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
846 list) {
847 if (!strcmp(ctl->name, name)) {
848 if (!ctl->enabled)
849 ctl->enabled = 1;
850 return 0;
851 }
852 }
853
854 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
855 if (!ctl) {
856 ret = -ENOMEM;
857 goto err_name;
858 }
859 ctl->region = *region;
860 ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
861 if (!ctl->name) {
862 ret = -ENOMEM;
863 goto err_ctl;
864 }
865 ctl->enabled = 1;
866 ctl->dirty = 0;
867 ctl->ops.xget = wm_coeff_get;
868 ctl->ops.xput = wm_coeff_put;
869 ctl->card = codec->card->snd_card;
870 ctl->adsp = dsp;
871
872 ctl->len = region->len;
873 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
874 if (!ctl->cache) {
875 ret = -ENOMEM;
876 goto err_ctl_name;
877 }
878
879 ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
880 if (!ctl_work) {
881 ret = -ENOMEM;
882 goto err_ctl_cache;
883 }
884
885 ctl_work->wm_coeff = dsp->wm_coeff;
886 ctl_work->ctl = ctl;
887 INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
888 schedule_work(&ctl_work->work);
889
890 kfree(name);
891
892 return 0;
893
894err_ctl_cache:
895 kfree(ctl->cache);
896err_ctl_name:
897 kfree(ctl->name);
898err_ctl:
899 kfree(ctl);
900err_name:
901 kfree(name);
902 return ret;
903}
904
905static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
551{ 906{
552 struct regmap *regmap = dsp->regmap; 907 struct regmap *regmap = dsp->regmap;
553 struct wmfw_adsp1_id_hdr adsp1_id; 908 struct wmfw_adsp1_id_hdr adsp1_id;
@@ -730,7 +1085,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
730 region->type = WMFW_ADSP1_DM; 1085 region->type = WMFW_ADSP1_DM;
731 region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 1086 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
732 region->base = be32_to_cpu(adsp1_alg[i].dm); 1087 region->base = be32_to_cpu(adsp1_alg[i].dm);
1088 region->len = 0;
733 list_add_tail(&region->list, &dsp->alg_regions); 1089 list_add_tail(&region->list, &dsp->alg_regions);
1090 if (i + 1 < algs) {
1091 region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
1092 region->len -= be32_to_cpu(adsp1_alg[i].dm);
1093 wm_adsp_create_control(codec, region);
1094 } else {
1095 adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
1096 be32_to_cpu(adsp1_alg[i].alg.id));
1097 }
734 1098
735 region = kzalloc(sizeof(*region), GFP_KERNEL); 1099 region = kzalloc(sizeof(*region), GFP_KERNEL);
736 if (!region) 1100 if (!region)
@@ -738,7 +1102,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
738 region->type = WMFW_ADSP1_ZM; 1102 region->type = WMFW_ADSP1_ZM;
739 region->alg = be32_to_cpu(adsp1_alg[i].alg.id); 1103 region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
740 region->base = be32_to_cpu(adsp1_alg[i].zm); 1104 region->base = be32_to_cpu(adsp1_alg[i].zm);
1105 region->len = 0;
741 list_add_tail(&region->list, &dsp->alg_regions); 1106 list_add_tail(&region->list, &dsp->alg_regions);
1107 if (i + 1 < algs) {
1108 region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
1109 region->len -= be32_to_cpu(adsp1_alg[i].zm);
1110 wm_adsp_create_control(codec, region);
1111 } else {
1112 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1113 be32_to_cpu(adsp1_alg[i].alg.id));
1114 }
742 break; 1115 break;
743 1116
744 case WMFW_ADSP2: 1117 case WMFW_ADSP2:
@@ -758,7 +1131,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
758 region->type = WMFW_ADSP2_XM; 1131 region->type = WMFW_ADSP2_XM;
759 region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 1132 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
760 region->base = be32_to_cpu(adsp2_alg[i].xm); 1133 region->base = be32_to_cpu(adsp2_alg[i].xm);
1134 region->len = 0;
761 list_add_tail(&region->list, &dsp->alg_regions); 1135 list_add_tail(&region->list, &dsp->alg_regions);
1136 if (i + 1 < algs) {
1137 region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
1138 region->len -= be32_to_cpu(adsp2_alg[i].xm);
1139 wm_adsp_create_control(codec, region);
1140 } else {
1141 adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
1142 be32_to_cpu(adsp2_alg[i].alg.id));
1143 }
762 1144
763 region = kzalloc(sizeof(*region), GFP_KERNEL); 1145 region = kzalloc(sizeof(*region), GFP_KERNEL);
764 if (!region) 1146 if (!region)
@@ -766,7 +1148,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
766 region->type = WMFW_ADSP2_YM; 1148 region->type = WMFW_ADSP2_YM;
767 region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 1149 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
768 region->base = be32_to_cpu(adsp2_alg[i].ym); 1150 region->base = be32_to_cpu(adsp2_alg[i].ym);
1151 region->len = 0;
769 list_add_tail(&region->list, &dsp->alg_regions); 1152 list_add_tail(&region->list, &dsp->alg_regions);
1153 if (i + 1 < algs) {
1154 region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
1155 region->len -= be32_to_cpu(adsp2_alg[i].ym);
1156 wm_adsp_create_control(codec, region);
1157 } else {
1158 adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
1159 be32_to_cpu(adsp2_alg[i].alg.id));
1160 }
770 1161
771 region = kzalloc(sizeof(*region), GFP_KERNEL); 1162 region = kzalloc(sizeof(*region), GFP_KERNEL);
772 if (!region) 1163 if (!region)
@@ -774,7 +1165,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
774 region->type = WMFW_ADSP2_ZM; 1165 region->type = WMFW_ADSP2_ZM;
775 region->alg = be32_to_cpu(adsp2_alg[i].alg.id); 1166 region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
776 region->base = be32_to_cpu(adsp2_alg[i].zm); 1167 region->base = be32_to_cpu(adsp2_alg[i].zm);
1168 region->len = 0;
777 list_add_tail(&region->list, &dsp->alg_regions); 1169 list_add_tail(&region->list, &dsp->alg_regions);
1170 if (i + 1 < algs) {
1171 region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
1172 region->len -= be32_to_cpu(adsp2_alg[i].zm);
1173 wm_adsp_create_control(codec, region);
1174 } else {
1175 adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
1176 be32_to_cpu(adsp2_alg[i].alg.id));
1177 }
778 break; 1178 break;
779 } 1179 }
780 } 1180 }
@@ -986,6 +1386,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
986 struct snd_soc_codec *codec = w->codec; 1386 struct snd_soc_codec *codec = w->codec;
987 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 1387 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
988 struct wm_adsp *dsp = &dsps[w->shift]; 1388 struct wm_adsp *dsp = &dsps[w->shift];
1389 struct wm_coeff_ctl *ctl;
989 int ret; 1390 int ret;
990 int val; 1391 int val;
991 1392
@@ -1023,7 +1424,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
1023 if (ret != 0) 1424 if (ret != 0)
1024 goto err; 1425 goto err;
1025 1426
1026 ret = wm_adsp_setup_algs(dsp); 1427 ret = wm_adsp_setup_algs(dsp, codec);
1027 if (ret != 0) 1428 if (ret != 0)
1028 goto err; 1429 goto err;
1029 1430
@@ -1031,6 +1432,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
1031 if (ret != 0) 1432 if (ret != 0)
1032 goto err; 1433 goto err;
1033 1434
1435 /* Initialize caches for enabled and non-dirty controls */
1436 ret = wm_coeff_init_control_caches(dsp->wm_coeff);
1437 if (ret != 0)
1438 goto err;
1439
1440 /* Sync dirty controls */
1441 ret = wm_coeff_sync_controls(dsp->wm_coeff);
1442 if (ret != 0)
1443 goto err;
1444
1034 /* Start the core running */ 1445 /* Start the core running */
1035 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 1446 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1036 ADSP1_CORE_ENA | ADSP1_START, 1447 ADSP1_CORE_ENA | ADSP1_START,
@@ -1047,6 +1458,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
1047 1458
1048 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 1459 regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
1049 ADSP1_SYS_ENA, 0); 1460 ADSP1_SYS_ENA, 0);
1461
1462 list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
1463 list) {
1464 ctl->enabled = 0;
1465 }
1050 break; 1466 break;
1051 1467
1052 default: 1468 default:
@@ -1099,6 +1515,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1099 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 1515 struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
1100 struct wm_adsp *dsp = &dsps[w->shift]; 1516 struct wm_adsp *dsp = &dsps[w->shift];
1101 struct wm_adsp_alg_region *alg_region; 1517 struct wm_adsp_alg_region *alg_region;
1518 struct wm_coeff_ctl *ctl;
1102 unsigned int val; 1519 unsigned int val;
1103 int ret; 1520 int ret;
1104 1521
@@ -1164,7 +1581,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1164 if (ret != 0) 1581 if (ret != 0)
1165 goto err; 1582 goto err;
1166 1583
1167 ret = wm_adsp_setup_algs(dsp); 1584 ret = wm_adsp_setup_algs(dsp, codec);
1168 if (ret != 0) 1585 if (ret != 0)
1169 goto err; 1586 goto err;
1170 1587
@@ -1172,6 +1589,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1172 if (ret != 0) 1589 if (ret != 0)
1173 goto err; 1590 goto err;
1174 1591
1592 /* Initialize caches for enabled and non-dirty controls */
1593 ret = wm_coeff_init_control_caches(dsp->wm_coeff);
1594 if (ret != 0)
1595 goto err;
1596
1597 /* Sync dirty controls */
1598 ret = wm_coeff_sync_controls(dsp->wm_coeff);
1599 if (ret != 0)
1600 goto err;
1601
1175 ret = regmap_update_bits(dsp->regmap, 1602 ret = regmap_update_bits(dsp->regmap,
1176 dsp->base + ADSP2_CONTROL, 1603 dsp->base + ADSP2_CONTROL,
1177 ADSP2_CORE_ENA | ADSP2_START, 1604 ADSP2_CORE_ENA | ADSP2_START,
@@ -1209,6 +1636,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
1209 ret); 1636 ret);
1210 } 1637 }
1211 1638
1639 list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
1640 list) {
1641 ctl->enabled = 0;
1642 }
1643
1212 while (!list_empty(&dsp->alg_regions)) { 1644 while (!list_empty(&dsp->alg_regions)) {
1213 alg_region = list_first_entry(&dsp->alg_regions, 1645 alg_region = list_first_entry(&dsp->alg_regions,
1214 struct wm_adsp_alg_region, 1646 struct wm_adsp_alg_region,
@@ -1247,36 +1679,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
1247 1679
1248 INIT_LIST_HEAD(&adsp->alg_regions); 1680 INIT_LIST_HEAD(&adsp->alg_regions);
1249 1681
1682 adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff),
1683 GFP_KERNEL);
1684 if (!adsp->wm_coeff)
1685 return -ENOMEM;
1686 adsp->wm_coeff->regmap = adsp->regmap;
1687 adsp->wm_coeff->dev = adsp->dev;
1688 INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list);
1689
1250 if (dvfs) { 1690 if (dvfs) {
1251 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); 1691 adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
1252 if (IS_ERR(adsp->dvfs)) { 1692 if (IS_ERR(adsp->dvfs)) {
1253 ret = PTR_ERR(adsp->dvfs); 1693 ret = PTR_ERR(adsp->dvfs);
1254 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); 1694 dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
1255 return ret; 1695 goto out_coeff;
1256 } 1696 }
1257 1697
1258 ret = regulator_enable(adsp->dvfs); 1698 ret = regulator_enable(adsp->dvfs);
1259 if (ret != 0) { 1699 if (ret != 0) {
1260 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", 1700 dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
1261 ret); 1701 ret);
1262 return ret; 1702 goto out_coeff;
1263 } 1703 }
1264 1704
1265 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); 1705 ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
1266 if (ret != 0) { 1706 if (ret != 0) {
1267 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", 1707 dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
1268 ret); 1708 ret);
1269 return ret; 1709 goto out_coeff;
1270 } 1710 }
1271 1711
1272 ret = regulator_disable(adsp->dvfs); 1712 ret = regulator_disable(adsp->dvfs);
1273 if (ret != 0) { 1713 if (ret != 0) {
1274 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", 1714 dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
1275 ret); 1715 ret);
1276 return ret; 1716 goto out_coeff;
1277 } 1717 }
1278 } 1718 }
1279 1719
1280 return 0; 1720 return 0;
1721
1722out_coeff:
1723 kfree(adsp->wm_coeff);
1724 return ret;
1281} 1725}
1282EXPORT_SYMBOL_GPL(wm_adsp2_init); 1726EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index fea514627526..6e890b916592 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -30,6 +30,7 @@ struct wm_adsp_alg_region {
30 unsigned int alg; 30 unsigned int alg;
31 int type; 31 int type;
32 unsigned int base; 32 unsigned int base;
33 size_t len;
33}; 34};
34 35
35struct wm_adsp { 36struct wm_adsp {
@@ -55,6 +56,8 @@ struct wm_adsp {
55 bool running; 56 bool running;
56 57
57 struct regulator *dvfs; 58 struct regulator *dvfs;
59
60 struct wm_coeff *wm_coeff;
58}; 61};
59 62
60#define WM_ADSP1(wname, num) \ 63#define WM_ADSP1(wname, num) \