Loading sound/soc/codecs/wm_adsp.c +452 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> Loading Loading @@ -215,6 +216,36 @@ static struct { [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, }; struct wm_coeff_ctl_ops { int (*xget)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int (*xput)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int (*xinfo)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); }; struct wm_coeff { struct device *dev; struct list_head ctl_list; struct regmap *regmap; }; struct wm_coeff_ctl { const char *name; struct snd_card *card; struct wm_adsp_alg_region region; struct wm_coeff_ctl_ops ops; struct wm_adsp *adsp; void *private; unsigned int enabled:1; struct list_head list; void *cache; size_t len; unsigned int set:1; struct snd_kcontrol *kcontrol; }; static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { Loading Loading @@ -334,6 +365,181 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, } } static int wm_coeff_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; uinfo->count = ctl->len; return 0; } static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, const void *buf, size_t len) { struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; struct wm_adsp *adsp = ctl->adsp; void *scratch; int ret; unsigned int reg; mem = wm_adsp_find_region(adsp, region->type); if (!mem) { adsp_err(adsp, "No base for region %x\n", region->type); return -EINVAL; } reg = ctl->region.base; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; ret = regmap_raw_write(wm_coeff->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to write %zu bytes to %x\n", ctl->len, reg); kfree(scratch); return ret; } kfree(scratch); return 0; } static int wm_coeff_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; char *p = ucontrol->value.bytes.data; memcpy(ctl->cache, p, ctl->len); if (!ctl->enabled) { ctl->set = 1; return 0; } return wm_coeff_write_control(kcontrol, p, ctl->len); } static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, void *buf, size_t len) { struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; struct wm_adsp *adsp = ctl->adsp; void *scratch; int ret; unsigned int reg; mem = wm_adsp_find_region(adsp, region->type); if (!mem) { adsp_err(adsp, "No base for region %x\n", region->type); return -EINVAL; } reg = ctl->region.base; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to read %zu bytes from %x\n", ctl->len, reg); kfree(scratch); return ret; } memcpy(buf, scratch, ctl->len); kfree(scratch); return 0; } static int wm_coeff_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; char *p = ucontrol->value.bytes.data; memcpy(p, ctl->cache, ctl->len); return 0; } static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff, struct wm_coeff_ctl *ctl, const struct snd_kcontrol_new *kctl) { int ret; struct snd_kcontrol *kcontrol; kcontrol = snd_ctl_new1(kctl, wm_coeff); ret = snd_ctl_add(ctl->card, kcontrol); if (ret < 0) { dev_err(wm_coeff->dev, "Failed to add %s: %d\n", kctl->name, ret); return ret; } ctl->kcontrol = kcontrol; return 0; } struct wmfw_ctl_work { struct wm_coeff *wm_coeff; struct wm_coeff_ctl *ctl; struct work_struct work; }; static int wmfw_add_ctl(struct wm_coeff *wm_coeff, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; int ret; if (!wm_coeff || !ctl || !ctl->name || !ctl->card) return -EINVAL; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); if (!kcontrol) return -ENOMEM; kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; kcontrol->get = wm_coeff_get; kcontrol->put = wm_coeff_put; kcontrol->private_value = (unsigned long)ctl; ret = wm_coeff_add_kcontrol(wm_coeff, ctl, kcontrol); if (ret < 0) goto err_kcontrol; kfree(kcontrol); list_add(&ctl->list, &wm_coeff->ctl_list); return 0; err_kcontrol: kfree(kcontrol); return ret; } static int wm_adsp_load(struct wm_adsp *dsp) { LIST_HEAD(buf_list); Loading Loading @@ -547,7 +753,157 @@ static int wm_adsp_load(struct wm_adsp *dsp) return ret; } static int wm_adsp_setup_algs(struct wm_adsp *dsp) static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) { struct wm_coeff_ctl *ctl; int ret; list_for_each_entry(ctl, &wm_coeff->ctl_list, list) { if (!ctl->enabled || ctl->set) continue; ret = wm_coeff_read_control(ctl->kcontrol, ctl->cache, ctl->len); if (ret < 0) return ret; } return 0; } static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff) { struct wm_coeff_ctl *ctl; int ret; list_for_each_entry(ctl, &wm_coeff->ctl_list, list) { if (!ctl->enabled) continue; if (ctl->set) { ret = wm_coeff_write_control(ctl->kcontrol, ctl->cache, ctl->len); if (ret < 0) return ret; } } return 0; } static void wm_adsp_ctl_work(struct work_struct *work) { struct wmfw_ctl_work *ctl_work = container_of(work, struct wmfw_ctl_work, work); wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl); kfree(ctl_work); } static int wm_adsp_create_control(struct snd_soc_codec *codec, const struct wm_adsp_alg_region *region) { struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; char *name; char *region_name; int ret; name = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!name) return -ENOMEM; switch (region->type) { case WMFW_ADSP1_PM: region_name = "PM"; break; case WMFW_ADSP1_DM: region_name = "DM"; break; case WMFW_ADSP2_XM: region_name = "XM"; break; case WMFW_ADSP2_YM: region_name = "YM"; break; case WMFW_ADSP1_ZM: region_name = "ZM"; break; default: ret = -EINVAL; goto err_name; } snprintf(name, PAGE_SIZE, "DSP%d %s %x", dsp->num, region_name, region->alg); list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, list) { if (!strcmp(ctl->name, name)) { if (!ctl->enabled) ctl->enabled = 1; goto found; } } ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); if (!ctl) { ret = -ENOMEM; goto err_name; } ctl->region = *region; ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); if (!ctl->name) { ret = -ENOMEM; goto err_ctl; } ctl->enabled = 1; ctl->set = 0; ctl->ops.xget = wm_coeff_get; ctl->ops.xput = wm_coeff_put; ctl->card = codec->card->snd_card; ctl->adsp = dsp; ctl->len = region->len; ctl->cache = kzalloc(ctl->len, GFP_KERNEL); if (!ctl->cache) { ret = -ENOMEM; goto err_ctl_name; } ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); if (!ctl_work) { ret = -ENOMEM; goto err_ctl_cache; } ctl_work->wm_coeff = dsp->wm_coeff; ctl_work->ctl = ctl; INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); schedule_work(&ctl_work->work); found: kfree(name); return 0; err_ctl_cache: kfree(ctl->cache); err_ctl_name: kfree(ctl->name); err_ctl: kfree(ctl); err_name: kfree(name); return ret; } static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) { struct regmap *regmap = dsp->regmap; struct wmfw_adsp1_id_hdr adsp1_id; Loading Loading @@ -730,7 +1086,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP1_DM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].dm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].dm); region->len -= be32_to_cpu(adsp1_alg[i].dm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) Loading @@ -738,7 +1103,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP1_ZM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].zm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].zm); region->len -= be32_to_cpu(adsp1_alg[i].zm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } break; case WMFW_ADSP2: Loading @@ -758,7 +1132,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_XM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].xm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].xm); region->len -= be32_to_cpu(adsp2_alg[i].xm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) Loading @@ -766,7 +1149,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_YM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].ym); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].ym); region->len -= be32_to_cpu(adsp2_alg[i].ym); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) Loading @@ -774,7 +1166,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_ZM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].zm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].zm); region->len -= be32_to_cpu(adsp2_alg[i].zm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } break; } } Loading Loading @@ -986,6 +1387,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; struct wm_coeff_ctl *ctl; int ret; int val; Loading Loading @@ -1023,7 +1425,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; ret = wm_adsp_setup_algs(dsp); ret = wm_adsp_setup_algs(dsp, codec); if (ret != 0) goto err; Loading @@ -1031,6 +1433,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp->wm_coeff); if (ret != 0) goto err; /* Sync set controls */ ret = wm_coeff_sync_controls(dsp->wm_coeff); if (ret != 0) goto err; /* Start the core running */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_CORE_ENA | ADSP1_START, Loading @@ -1047,6 +1459,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, 0); list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, list) { ctl->enabled = 0; } break; default: Loading Loading @@ -1099,6 +1516,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; unsigned int val; int ret; Loading Loading @@ -1164,7 +1582,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; ret = wm_adsp_setup_algs(dsp); ret = wm_adsp_setup_algs(dsp, codec); if (ret != 0) goto err; Loading @@ -1172,6 +1590,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp->wm_coeff); if (ret != 0) goto err; /* Sync set controls */ ret = wm_coeff_sync_controls(dsp->wm_coeff); if (ret != 0) goto err; ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_CORE_ENA | ADSP2_START, Loading Loading @@ -1209,6 +1637,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ret); } list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, list) { ctl->enabled = 0; } while (!list_empty(&dsp->alg_regions)) { alg_region = list_first_entry(&dsp->alg_regions, struct wm_adsp_alg_region, Loading Loading @@ -1247,36 +1680,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) INIT_LIST_HEAD(&adsp->alg_regions); adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff), GFP_KERNEL); if (!adsp->wm_coeff) return -ENOMEM; adsp->wm_coeff->regmap = adsp->regmap; adsp->wm_coeff->dev = adsp->dev; INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list); if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { ret = PTR_ERR(adsp->dvfs); dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); return ret; goto out_coeff; } ret = regulator_enable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", ret); return ret; goto out_coeff; } ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); if (ret != 0) { dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", ret); return ret; goto out_coeff; } ret = regulator_disable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", ret); return ret; goto out_coeff; } } return 0; out_coeff: kfree(adsp->wm_coeff); return ret; } EXPORT_SYMBOL_GPL(wm_adsp2_init); sound/soc/codecs/wm_adsp.h +3 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ struct wm_adsp_alg_region { unsigned int alg; int type; unsigned int base; size_t len; }; struct wm_adsp { Loading @@ -55,6 +56,8 @@ struct wm_adsp { bool running; struct regulator *dvfs; struct wm_coeff *wm_coeff; }; #define WM_ADSP1(wname, num) \ Loading Loading
sound/soc/codecs/wm_adsp.c +452 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> Loading Loading @@ -215,6 +216,36 @@ static struct { [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, }; struct wm_coeff_ctl_ops { int (*xget)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int (*xput)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int (*xinfo)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); }; struct wm_coeff { struct device *dev; struct list_head ctl_list; struct regmap *regmap; }; struct wm_coeff_ctl { const char *name; struct snd_card *card; struct wm_adsp_alg_region region; struct wm_coeff_ctl_ops ops; struct wm_adsp *adsp; void *private; unsigned int enabled:1; struct list_head list; void *cache; size_t len; unsigned int set:1; struct snd_kcontrol *kcontrol; }; static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { Loading Loading @@ -334,6 +365,181 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, } } static int wm_coeff_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; uinfo->count = ctl->len; return 0; } static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, const void *buf, size_t len) { struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; struct wm_adsp *adsp = ctl->adsp; void *scratch; int ret; unsigned int reg; mem = wm_adsp_find_region(adsp, region->type); if (!mem) { adsp_err(adsp, "No base for region %x\n", region->type); return -EINVAL; } reg = ctl->region.base; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; ret = regmap_raw_write(wm_coeff->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to write %zu bytes to %x\n", ctl->len, reg); kfree(scratch); return ret; } kfree(scratch); return 0; } static int wm_coeff_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; char *p = ucontrol->value.bytes.data; memcpy(ctl->cache, p, ctl->len); if (!ctl->enabled) { ctl->set = 1; return 0; } return wm_coeff_write_control(kcontrol, p, ctl->len); } static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, void *buf, size_t len) { struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; struct wm_adsp *adsp = ctl->adsp; void *scratch; int ret; unsigned int reg; mem = wm_adsp_find_region(adsp, region->type); if (!mem) { adsp_err(adsp, "No base for region %x\n", region->type); return -EINVAL; } reg = ctl->region.base; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to read %zu bytes from %x\n", ctl->len, reg); kfree(scratch); return ret; } memcpy(buf, scratch, ctl->len); kfree(scratch); return 0; } static int wm_coeff_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; char *p = ucontrol->value.bytes.data; memcpy(p, ctl->cache, ctl->len); return 0; } static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff, struct wm_coeff_ctl *ctl, const struct snd_kcontrol_new *kctl) { int ret; struct snd_kcontrol *kcontrol; kcontrol = snd_ctl_new1(kctl, wm_coeff); ret = snd_ctl_add(ctl->card, kcontrol); if (ret < 0) { dev_err(wm_coeff->dev, "Failed to add %s: %d\n", kctl->name, ret); return ret; } ctl->kcontrol = kcontrol; return 0; } struct wmfw_ctl_work { struct wm_coeff *wm_coeff; struct wm_coeff_ctl *ctl; struct work_struct work; }; static int wmfw_add_ctl(struct wm_coeff *wm_coeff, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; int ret; if (!wm_coeff || !ctl || !ctl->name || !ctl->card) return -EINVAL; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); if (!kcontrol) return -ENOMEM; kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; kcontrol->get = wm_coeff_get; kcontrol->put = wm_coeff_put; kcontrol->private_value = (unsigned long)ctl; ret = wm_coeff_add_kcontrol(wm_coeff, ctl, kcontrol); if (ret < 0) goto err_kcontrol; kfree(kcontrol); list_add(&ctl->list, &wm_coeff->ctl_list); return 0; err_kcontrol: kfree(kcontrol); return ret; } static int wm_adsp_load(struct wm_adsp *dsp) { LIST_HEAD(buf_list); Loading Loading @@ -547,7 +753,157 @@ static int wm_adsp_load(struct wm_adsp *dsp) return ret; } static int wm_adsp_setup_algs(struct wm_adsp *dsp) static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) { struct wm_coeff_ctl *ctl; int ret; list_for_each_entry(ctl, &wm_coeff->ctl_list, list) { if (!ctl->enabled || ctl->set) continue; ret = wm_coeff_read_control(ctl->kcontrol, ctl->cache, ctl->len); if (ret < 0) return ret; } return 0; } static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff) { struct wm_coeff_ctl *ctl; int ret; list_for_each_entry(ctl, &wm_coeff->ctl_list, list) { if (!ctl->enabled) continue; if (ctl->set) { ret = wm_coeff_write_control(ctl->kcontrol, ctl->cache, ctl->len); if (ret < 0) return ret; } } return 0; } static void wm_adsp_ctl_work(struct work_struct *work) { struct wmfw_ctl_work *ctl_work = container_of(work, struct wmfw_ctl_work, work); wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl); kfree(ctl_work); } static int wm_adsp_create_control(struct snd_soc_codec *codec, const struct wm_adsp_alg_region *region) { struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; char *name; char *region_name; int ret; name = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!name) return -ENOMEM; switch (region->type) { case WMFW_ADSP1_PM: region_name = "PM"; break; case WMFW_ADSP1_DM: region_name = "DM"; break; case WMFW_ADSP2_XM: region_name = "XM"; break; case WMFW_ADSP2_YM: region_name = "YM"; break; case WMFW_ADSP1_ZM: region_name = "ZM"; break; default: ret = -EINVAL; goto err_name; } snprintf(name, PAGE_SIZE, "DSP%d %s %x", dsp->num, region_name, region->alg); list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, list) { if (!strcmp(ctl->name, name)) { if (!ctl->enabled) ctl->enabled = 1; goto found; } } ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); if (!ctl) { ret = -ENOMEM; goto err_name; } ctl->region = *region; ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); if (!ctl->name) { ret = -ENOMEM; goto err_ctl; } ctl->enabled = 1; ctl->set = 0; ctl->ops.xget = wm_coeff_get; ctl->ops.xput = wm_coeff_put; ctl->card = codec->card->snd_card; ctl->adsp = dsp; ctl->len = region->len; ctl->cache = kzalloc(ctl->len, GFP_KERNEL); if (!ctl->cache) { ret = -ENOMEM; goto err_ctl_name; } ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); if (!ctl_work) { ret = -ENOMEM; goto err_ctl_cache; } ctl_work->wm_coeff = dsp->wm_coeff; ctl_work->ctl = ctl; INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); schedule_work(&ctl_work->work); found: kfree(name); return 0; err_ctl_cache: kfree(ctl->cache); err_ctl_name: kfree(ctl->name); err_ctl: kfree(ctl); err_name: kfree(name); return ret; } static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) { struct regmap *regmap = dsp->regmap; struct wmfw_adsp1_id_hdr adsp1_id; Loading Loading @@ -730,7 +1086,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP1_DM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].dm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].dm); region->len -= be32_to_cpu(adsp1_alg[i].dm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) Loading @@ -738,7 +1103,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP1_ZM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].zm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].zm); region->len -= be32_to_cpu(adsp1_alg[i].zm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } break; case WMFW_ADSP2: Loading @@ -758,7 +1132,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_XM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].xm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].xm); region->len -= be32_to_cpu(adsp2_alg[i].xm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) Loading @@ -766,7 +1149,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_YM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].ym); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].ym); region->len -= be32_to_cpu(adsp2_alg[i].ym); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) Loading @@ -774,7 +1166,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_ZM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].zm); region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].zm); region->len -= be32_to_cpu(adsp2_alg[i].zm); wm_adsp_create_control(codec, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } break; } } Loading Loading @@ -986,6 +1387,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; struct wm_coeff_ctl *ctl; int ret; int val; Loading Loading @@ -1023,7 +1425,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; ret = wm_adsp_setup_algs(dsp); ret = wm_adsp_setup_algs(dsp, codec); if (ret != 0) goto err; Loading @@ -1031,6 +1433,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp->wm_coeff); if (ret != 0) goto err; /* Sync set controls */ ret = wm_coeff_sync_controls(dsp->wm_coeff); if (ret != 0) goto err; /* Start the core running */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_CORE_ENA | ADSP1_START, Loading @@ -1047,6 +1459,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, 0); list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, list) { ctl->enabled = 0; } break; default: Loading Loading @@ -1099,6 +1516,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; unsigned int val; int ret; Loading Loading @@ -1164,7 +1582,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; ret = wm_adsp_setup_algs(dsp); ret = wm_adsp_setup_algs(dsp, codec); if (ret != 0) goto err; Loading @@ -1172,6 +1590,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp->wm_coeff); if (ret != 0) goto err; /* Sync set controls */ ret = wm_coeff_sync_controls(dsp->wm_coeff); if (ret != 0) goto err; ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_CORE_ENA | ADSP2_START, Loading Loading @@ -1209,6 +1637,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ret); } list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, list) { ctl->enabled = 0; } while (!list_empty(&dsp->alg_regions)) { alg_region = list_first_entry(&dsp->alg_regions, struct wm_adsp_alg_region, Loading Loading @@ -1247,36 +1680,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) INIT_LIST_HEAD(&adsp->alg_regions); adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff), GFP_KERNEL); if (!adsp->wm_coeff) return -ENOMEM; adsp->wm_coeff->regmap = adsp->regmap; adsp->wm_coeff->dev = adsp->dev; INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list); if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { ret = PTR_ERR(adsp->dvfs); dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); return ret; goto out_coeff; } ret = regulator_enable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", ret); return ret; goto out_coeff; } ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); if (ret != 0) { dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", ret); return ret; goto out_coeff; } ret = regulator_disable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", ret); return ret; goto out_coeff; } } return 0; out_coeff: kfree(adsp->wm_coeff); return ret; } EXPORT_SYMBOL_GPL(wm_adsp2_init);
sound/soc/codecs/wm_adsp.h +3 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ struct wm_adsp_alg_region { unsigned int alg; int type; unsigned int base; size_t len; }; struct wm_adsp { Loading @@ -55,6 +56,8 @@ struct wm_adsp { bool running; struct regulator *dvfs; struct wm_coeff *wm_coeff; }; #define WM_ADSP1(wname, num) \ Loading