diff options
author | Mark Brown <broonie@linaro.org> | 2013-06-30 07:42:24 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-06-30 07:42:24 -0400 |
commit | fbc6c4ee659da5bdd49ab85c5d7e50c51db1b4fa (patch) | |
tree | 22da5a7ae0c2f038ff404c37220f5471cc44e214 /sound | |
parent | 4494783793459dc601aad574802e81592cf3173f (diff) | |
parent | 0c2e3f3420bb790a4e5bc14d3d50a722964ad73e (diff) |
Merge remote-tracking branch 'asoc/topic/adsp' into asoc-next
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm_adsp.c | 459 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.h | 3 |
2 files changed, 455 insertions, 7 deletions
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 3470b649c0b2..ddba3fea39eb 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 | ||
219 | struct 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 | |||
228 | struct wm_coeff { | ||
229 | struct device *dev; | ||
230 | struct list_head ctl_list; | ||
231 | struct regmap *regmap; | ||
232 | }; | ||
233 | |||
234 | struct 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 set:1; | ||
246 | struct snd_kcontrol *kcontrol; | ||
247 | }; | ||
248 | |||
218 | static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, | 249 | static 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 | ||
368 | static 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 | |||
378 | static 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 | |||
418 | static 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->set = 1; | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | return wm_coeff_write_control(kcontrol, p, ctl->len); | ||
432 | } | ||
433 | |||
434 | static 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 | |||
474 | static 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 | |||
484 | static 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 | |||
502 | struct wmfw_ctl_work { | ||
503 | struct wm_coeff *wm_coeff; | ||
504 | struct wm_coeff_ctl *ctl; | ||
505 | struct work_struct work; | ||
506 | }; | ||
507 | |||
508 | static 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 | |||
538 | err_kcontrol: | ||
539 | kfree(kcontrol); | ||
540 | return ret; | ||
541 | } | ||
542 | |||
337 | static int wm_adsp_load(struct wm_adsp *dsp) | 543 | static int wm_adsp_load(struct wm_adsp *dsp) |
338 | { | 544 | { |
339 | LIST_HEAD(buf_list); | 545 | LIST_HEAD(buf_list); |
@@ -547,7 +753,157 @@ out: | |||
547 | return ret; | 753 | return ret; |
548 | } | 754 | } |
549 | 755 | ||
550 | static int wm_adsp_setup_algs(struct wm_adsp *dsp) | 756 | static 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->set) | ||
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 | |||
775 | static 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->set) { | ||
785 | ret = wm_coeff_write_control(ctl->kcontrol, | ||
786 | ctl->cache, | ||
787 | ctl->len); | ||
788 | if (ret < 0) | ||
789 | return ret; | ||
790 | } | ||
791 | } | ||
792 | |||
793 | return 0; | ||
794 | } | ||
795 | |||
796 | static void wm_adsp_ctl_work(struct work_struct *work) | ||
797 | { | ||
798 | struct wmfw_ctl_work *ctl_work = container_of(work, | ||
799 | struct wmfw_ctl_work, | ||
800 | work); | ||
801 | |||
802 | wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl); | ||
803 | kfree(ctl_work); | ||
804 | } | ||
805 | |||
806 | static int wm_adsp_create_control(struct snd_soc_codec *codec, | ||
807 | const struct wm_adsp_alg_region *region) | ||
808 | |||
809 | { | ||
810 | struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); | ||
811 | struct wm_coeff_ctl *ctl; | ||
812 | struct wmfw_ctl_work *ctl_work; | ||
813 | char *name; | ||
814 | char *region_name; | ||
815 | int ret; | ||
816 | |||
817 | name = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
818 | if (!name) | ||
819 | return -ENOMEM; | ||
820 | |||
821 | switch (region->type) { | ||
822 | case WMFW_ADSP1_PM: | ||
823 | region_name = "PM"; | ||
824 | break; | ||
825 | case WMFW_ADSP1_DM: | ||
826 | region_name = "DM"; | ||
827 | break; | ||
828 | case WMFW_ADSP2_XM: | ||
829 | region_name = "XM"; | ||
830 | break; | ||
831 | case WMFW_ADSP2_YM: | ||
832 | region_name = "YM"; | ||
833 | break; | ||
834 | case WMFW_ADSP1_ZM: | ||
835 | region_name = "ZM"; | ||
836 | break; | ||
837 | default: | ||
838 | ret = -EINVAL; | ||
839 | goto err_name; | ||
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 | goto found; | ||
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->set = 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 | found: | ||
891 | kfree(name); | ||
892 | |||
893 | return 0; | ||
894 | |||
895 | err_ctl_cache: | ||
896 | kfree(ctl->cache); | ||
897 | err_ctl_name: | ||
898 | kfree(ctl->name); | ||
899 | err_ctl: | ||
900 | kfree(ctl); | ||
901 | err_name: | ||
902 | kfree(name); | ||
903 | return ret; | ||
904 | } | ||
905 | |||
906 | static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) | ||
551 | { | 907 | { |
552 | struct regmap *regmap = dsp->regmap; | 908 | struct regmap *regmap = dsp->regmap; |
553 | struct wmfw_adsp1_id_hdr adsp1_id; | 909 | struct wmfw_adsp1_id_hdr adsp1_id; |
@@ -730,7 +1086,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) | |||
730 | region->type = WMFW_ADSP1_DM; | 1086 | region->type = WMFW_ADSP1_DM; |
731 | region->alg = be32_to_cpu(adsp1_alg[i].alg.id); | 1087 | region->alg = be32_to_cpu(adsp1_alg[i].alg.id); |
732 | region->base = be32_to_cpu(adsp1_alg[i].dm); | 1088 | region->base = be32_to_cpu(adsp1_alg[i].dm); |
1089 | region->len = 0; | ||
733 | list_add_tail(®ion->list, &dsp->alg_regions); | 1090 | list_add_tail(®ion->list, &dsp->alg_regions); |
1091 | if (i + 1 < algs) { | ||
1092 | region->len = be32_to_cpu(adsp1_alg[i + 1].dm); | ||
1093 | region->len -= be32_to_cpu(adsp1_alg[i].dm); | ||
1094 | wm_adsp_create_control(codec, region); | ||
1095 | } else { | ||
1096 | adsp_warn(dsp, "Missing length info for region DM with ID %x\n", | ||
1097 | be32_to_cpu(adsp1_alg[i].alg.id)); | ||
1098 | } | ||
734 | 1099 | ||
735 | region = kzalloc(sizeof(*region), GFP_KERNEL); | 1100 | region = kzalloc(sizeof(*region), GFP_KERNEL); |
736 | if (!region) | 1101 | if (!region) |
@@ -738,7 +1103,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) | |||
738 | region->type = WMFW_ADSP1_ZM; | 1103 | region->type = WMFW_ADSP1_ZM; |
739 | region->alg = be32_to_cpu(adsp1_alg[i].alg.id); | 1104 | region->alg = be32_to_cpu(adsp1_alg[i].alg.id); |
740 | region->base = be32_to_cpu(adsp1_alg[i].zm); | 1105 | region->base = be32_to_cpu(adsp1_alg[i].zm); |
1106 | region->len = 0; | ||
741 | list_add_tail(®ion->list, &dsp->alg_regions); | 1107 | list_add_tail(®ion->list, &dsp->alg_regions); |
1108 | if (i + 1 < algs) { | ||
1109 | region->len = be32_to_cpu(adsp1_alg[i + 1].zm); | ||
1110 | region->len -= be32_to_cpu(adsp1_alg[i].zm); | ||
1111 | wm_adsp_create_control(codec, region); | ||
1112 | } else { | ||
1113 | adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", | ||
1114 | be32_to_cpu(adsp1_alg[i].alg.id)); | ||
1115 | } | ||
742 | break; | 1116 | break; |
743 | 1117 | ||
744 | case WMFW_ADSP2: | 1118 | case WMFW_ADSP2: |
@@ -758,7 +1132,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) | |||
758 | region->type = WMFW_ADSP2_XM; | 1132 | region->type = WMFW_ADSP2_XM; |
759 | region->alg = be32_to_cpu(adsp2_alg[i].alg.id); | 1133 | region->alg = be32_to_cpu(adsp2_alg[i].alg.id); |
760 | region->base = be32_to_cpu(adsp2_alg[i].xm); | 1134 | region->base = be32_to_cpu(adsp2_alg[i].xm); |
1135 | region->len = 0; | ||
761 | list_add_tail(®ion->list, &dsp->alg_regions); | 1136 | list_add_tail(®ion->list, &dsp->alg_regions); |
1137 | if (i + 1 < algs) { | ||
1138 | region->len = be32_to_cpu(adsp2_alg[i + 1].xm); | ||
1139 | region->len -= be32_to_cpu(adsp2_alg[i].xm); | ||
1140 | wm_adsp_create_control(codec, region); | ||
1141 | } else { | ||
1142 | adsp_warn(dsp, "Missing length info for region XM with ID %x\n", | ||
1143 | be32_to_cpu(adsp2_alg[i].alg.id)); | ||
1144 | } | ||
762 | 1145 | ||
763 | region = kzalloc(sizeof(*region), GFP_KERNEL); | 1146 | region = kzalloc(sizeof(*region), GFP_KERNEL); |
764 | if (!region) | 1147 | if (!region) |
@@ -766,7 +1149,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) | |||
766 | region->type = WMFW_ADSP2_YM; | 1149 | region->type = WMFW_ADSP2_YM; |
767 | region->alg = be32_to_cpu(adsp2_alg[i].alg.id); | 1150 | region->alg = be32_to_cpu(adsp2_alg[i].alg.id); |
768 | region->base = be32_to_cpu(adsp2_alg[i].ym); | 1151 | region->base = be32_to_cpu(adsp2_alg[i].ym); |
1152 | region->len = 0; | ||
769 | list_add_tail(®ion->list, &dsp->alg_regions); | 1153 | list_add_tail(®ion->list, &dsp->alg_regions); |
1154 | if (i + 1 < algs) { | ||
1155 | region->len = be32_to_cpu(adsp2_alg[i + 1].ym); | ||
1156 | region->len -= be32_to_cpu(adsp2_alg[i].ym); | ||
1157 | wm_adsp_create_control(codec, region); | ||
1158 | } else { | ||
1159 | adsp_warn(dsp, "Missing length info for region YM with ID %x\n", | ||
1160 | be32_to_cpu(adsp2_alg[i].alg.id)); | ||
1161 | } | ||
770 | 1162 | ||
771 | region = kzalloc(sizeof(*region), GFP_KERNEL); | 1163 | region = kzalloc(sizeof(*region), GFP_KERNEL); |
772 | if (!region) | 1164 | if (!region) |
@@ -774,7 +1166,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) | |||
774 | region->type = WMFW_ADSP2_ZM; | 1166 | region->type = WMFW_ADSP2_ZM; |
775 | region->alg = be32_to_cpu(adsp2_alg[i].alg.id); | 1167 | region->alg = be32_to_cpu(adsp2_alg[i].alg.id); |
776 | region->base = be32_to_cpu(adsp2_alg[i].zm); | 1168 | region->base = be32_to_cpu(adsp2_alg[i].zm); |
1169 | region->len = 0; | ||
777 | list_add_tail(®ion->list, &dsp->alg_regions); | 1170 | list_add_tail(®ion->list, &dsp->alg_regions); |
1171 | if (i + 1 < algs) { | ||
1172 | region->len = be32_to_cpu(adsp2_alg[i + 1].zm); | ||
1173 | region->len -= be32_to_cpu(adsp2_alg[i].zm); | ||
1174 | wm_adsp_create_control(codec, region); | ||
1175 | } else { | ||
1176 | adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", | ||
1177 | be32_to_cpu(adsp2_alg[i].alg.id)); | ||
1178 | } | ||
778 | break; | 1179 | break; |
779 | } | 1180 | } |
780 | } | 1181 | } |
@@ -986,6 +1387,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, | |||
986 | struct snd_soc_codec *codec = w->codec; | 1387 | struct snd_soc_codec *codec = w->codec; |
987 | struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); | 1388 | struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); |
988 | struct wm_adsp *dsp = &dsps[w->shift]; | 1389 | struct wm_adsp *dsp = &dsps[w->shift]; |
1390 | struct wm_coeff_ctl *ctl; | ||
989 | int ret; | 1391 | int ret; |
990 | int val; | 1392 | int val; |
991 | 1393 | ||
@@ -1023,7 +1425,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, | |||
1023 | if (ret != 0) | 1425 | if (ret != 0) |
1024 | goto err; | 1426 | goto err; |
1025 | 1427 | ||
1026 | ret = wm_adsp_setup_algs(dsp); | 1428 | ret = wm_adsp_setup_algs(dsp, codec); |
1027 | if (ret != 0) | 1429 | if (ret != 0) |
1028 | goto err; | 1430 | goto err; |
1029 | 1431 | ||
@@ -1031,6 +1433,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, | |||
1031 | if (ret != 0) | 1433 | if (ret != 0) |
1032 | goto err; | 1434 | goto err; |
1033 | 1435 | ||
1436 | /* Initialize caches for enabled and unset controls */ | ||
1437 | ret = wm_coeff_init_control_caches(dsp->wm_coeff); | ||
1438 | if (ret != 0) | ||
1439 | goto err; | ||
1440 | |||
1441 | /* Sync set controls */ | ||
1442 | ret = wm_coeff_sync_controls(dsp->wm_coeff); | ||
1443 | if (ret != 0) | ||
1444 | goto err; | ||
1445 | |||
1034 | /* Start the core running */ | 1446 | /* Start the core running */ |
1035 | regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, | 1447 | regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, |
1036 | ADSP1_CORE_ENA | ADSP1_START, | 1448 | ADSP1_CORE_ENA | ADSP1_START, |
@@ -1047,6 +1459,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, | |||
1047 | 1459 | ||
1048 | regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, | 1460 | regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, |
1049 | ADSP1_SYS_ENA, 0); | 1461 | ADSP1_SYS_ENA, 0); |
1462 | |||
1463 | list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, | ||
1464 | list) { | ||
1465 | ctl->enabled = 0; | ||
1466 | } | ||
1050 | break; | 1467 | break; |
1051 | 1468 | ||
1052 | default: | 1469 | default: |
@@ -1099,6 +1516,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, | |||
1099 | struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); | 1516 | struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); |
1100 | struct wm_adsp *dsp = &dsps[w->shift]; | 1517 | struct wm_adsp *dsp = &dsps[w->shift]; |
1101 | struct wm_adsp_alg_region *alg_region; | 1518 | struct wm_adsp_alg_region *alg_region; |
1519 | struct wm_coeff_ctl *ctl; | ||
1102 | unsigned int val; | 1520 | unsigned int val; |
1103 | int ret; | 1521 | int ret; |
1104 | 1522 | ||
@@ -1164,7 +1582,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, | |||
1164 | if (ret != 0) | 1582 | if (ret != 0) |
1165 | goto err; | 1583 | goto err; |
1166 | 1584 | ||
1167 | ret = wm_adsp_setup_algs(dsp); | 1585 | ret = wm_adsp_setup_algs(dsp, codec); |
1168 | if (ret != 0) | 1586 | if (ret != 0) |
1169 | goto err; | 1587 | goto err; |
1170 | 1588 | ||
@@ -1172,6 +1590,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, | |||
1172 | if (ret != 0) | 1590 | if (ret != 0) |
1173 | goto err; | 1591 | goto err; |
1174 | 1592 | ||
1593 | /* Initialize caches for enabled and unset controls */ | ||
1594 | ret = wm_coeff_init_control_caches(dsp->wm_coeff); | ||
1595 | if (ret != 0) | ||
1596 | goto err; | ||
1597 | |||
1598 | /* Sync set controls */ | ||
1599 | ret = wm_coeff_sync_controls(dsp->wm_coeff); | ||
1600 | if (ret != 0) | ||
1601 | goto err; | ||
1602 | |||
1175 | ret = regmap_update_bits(dsp->regmap, | 1603 | ret = regmap_update_bits(dsp->regmap, |
1176 | dsp->base + ADSP2_CONTROL, | 1604 | dsp->base + ADSP2_CONTROL, |
1177 | ADSP2_CORE_ENA | ADSP2_START, | 1605 | ADSP2_CORE_ENA | ADSP2_START, |
@@ -1209,6 +1637,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, | |||
1209 | ret); | 1637 | ret); |
1210 | } | 1638 | } |
1211 | 1639 | ||
1640 | list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, | ||
1641 | list) { | ||
1642 | ctl->enabled = 0; | ||
1643 | } | ||
1644 | |||
1212 | while (!list_empty(&dsp->alg_regions)) { | 1645 | while (!list_empty(&dsp->alg_regions)) { |
1213 | alg_region = list_first_entry(&dsp->alg_regions, | 1646 | alg_region = list_first_entry(&dsp->alg_regions, |
1214 | struct wm_adsp_alg_region, | 1647 | struct wm_adsp_alg_region, |
@@ -1247,36 +1680,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) | |||
1247 | 1680 | ||
1248 | INIT_LIST_HEAD(&adsp->alg_regions); | 1681 | INIT_LIST_HEAD(&adsp->alg_regions); |
1249 | 1682 | ||
1683 | adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff), | ||
1684 | GFP_KERNEL); | ||
1685 | if (!adsp->wm_coeff) | ||
1686 | return -ENOMEM; | ||
1687 | adsp->wm_coeff->regmap = adsp->regmap; | ||
1688 | adsp->wm_coeff->dev = adsp->dev; | ||
1689 | INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list); | ||
1690 | |||
1250 | if (dvfs) { | 1691 | if (dvfs) { |
1251 | adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); | 1692 | adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); |
1252 | if (IS_ERR(adsp->dvfs)) { | 1693 | if (IS_ERR(adsp->dvfs)) { |
1253 | ret = PTR_ERR(adsp->dvfs); | 1694 | ret = PTR_ERR(adsp->dvfs); |
1254 | dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); | 1695 | dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); |
1255 | return ret; | 1696 | goto out_coeff; |
1256 | } | 1697 | } |
1257 | 1698 | ||
1258 | ret = regulator_enable(adsp->dvfs); | 1699 | ret = regulator_enable(adsp->dvfs); |
1259 | if (ret != 0) { | 1700 | if (ret != 0) { |
1260 | dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", | 1701 | dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", |
1261 | ret); | 1702 | ret); |
1262 | return ret; | 1703 | goto out_coeff; |
1263 | } | 1704 | } |
1264 | 1705 | ||
1265 | ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); | 1706 | ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); |
1266 | if (ret != 0) { | 1707 | if (ret != 0) { |
1267 | dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", | 1708 | dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", |
1268 | ret); | 1709 | ret); |
1269 | return ret; | 1710 | goto out_coeff; |
1270 | } | 1711 | } |
1271 | 1712 | ||
1272 | ret = regulator_disable(adsp->dvfs); | 1713 | ret = regulator_disable(adsp->dvfs); |
1273 | if (ret != 0) { | 1714 | if (ret != 0) { |
1274 | dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", | 1715 | dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", |
1275 | ret); | 1716 | ret); |
1276 | return ret; | 1717 | goto out_coeff; |
1277 | } | 1718 | } |
1278 | } | 1719 | } |
1279 | 1720 | ||
1280 | return 0; | 1721 | return 0; |
1722 | |||
1723 | out_coeff: | ||
1724 | kfree(adsp->wm_coeff); | ||
1725 | return ret; | ||
1281 | } | 1726 | } |
1282 | EXPORT_SYMBOL_GPL(wm_adsp2_init); | 1727 | EXPORT_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 | ||
35 | struct wm_adsp { | 36 | struct 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) \ |