diff options
| -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) \ |
