From db40517c75e1a33a886c8cadfa2d95f0fe5f5f4c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 26 Oct 2012 19:30:40 +0100 Subject: ASoC: wm_adsp: Add support for parsing algorithms ADSP devices report information on the algorithms loaded on them. Parse this data and use it to allow coefficients to be configured for specific algorithms. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 143 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ffc89fab96fb..990403b162fe 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -350,6 +350,141 @@ out: return ret; } +static int wm_adsp_setup_algs(struct wm_adsp *dsp) +{ + struct regmap *regmap = dsp->regmap; + struct wmfw_adsp1_id_hdr adsp1_id; + struct wmfw_adsp2_id_hdr adsp2_id; + struct wmfw_adsp1_alg_hdr *adsp1_alg; + struct wmfw_adsp2_alg_hdr *adsp2_alg; + void *alg; + const struct wm_adsp_region *mem; + unsigned int pos, term; + size_t algs; + __be32 val; + int i, ret; + + switch (dsp->type) { + case WMFW_ADSP1: + mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); + break; + case WMFW_ADSP2: + mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); + break; + default: + mem = NULL; + break; + } + + if (mem == NULL) { + BUG_ON(mem != NULL); + return -EINVAL; + } + + switch (dsp->type) { + case WMFW_ADSP1: + ret = regmap_raw_read(regmap, mem->base, &adsp1_id, + sizeof(adsp1_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + algs = be32_to_cpu(adsp1_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp1_id.fw.id), + (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp1_id) / 2; + term = pos + ((sizeof(*adsp1_alg) * algs) / 2); + break; + + case WMFW_ADSP2: + ret = regmap_raw_read(regmap, mem->base, &adsp2_id, + sizeof(adsp2_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + algs = be32_to_cpu(adsp2_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp2_id.fw.id), + (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp2_id) / 2; + term = pos + ((sizeof(*adsp2_alg) * algs) / 2); + break; + + default: + BUG_ON(NULL == "Unknown DSP type"); + return -EINVAL; + } + + if (algs == 0) { + adsp_err(dsp, "No algorithms\n"); + return -EINVAL; + } + + /* Read the terminator first to validate the length */ + ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list end: %d\n", + ret); + return ret; + } + + if (be32_to_cpu(val) != 0xbedead) + adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", + term, be32_to_cpu(val)); + + alg = kzalloc((term - pos) * 2, GFP_KERNEL); + if (!alg) + return -ENOMEM; + + ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list: %d\n", + ret); + goto out; + } + + adsp1_alg = alg; + adsp2_alg = alg; + + for (i = 0; i < algs; i++) { + switch (dsp->type) { + case WMFW_ADSP1: + adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + i, be32_to_cpu(adsp1_alg[i].alg.id), + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff); + break; + + case WMFW_ADSP2: + adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + i, be32_to_cpu(adsp2_alg[i].alg.id), + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff); + break; + } + } + +out: + kfree(alg); + return ret; +} + static int wm_adsp_load_coeff(struct wm_adsp *dsp) { struct regmap *regmap = dsp->regmap; @@ -468,6 +603,10 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -604,6 +743,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; -- cgit v1.2.3 From 45b9ee72d0e70c11a01152ef4bec92718b55906b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 16:02:06 +0000 Subject: ASoC: wm_adsp: Factor out calculation of memory base addresses Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 990403b162fe..bcc0d0ffe72e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -156,6 +156,26 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, return NULL; } +static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, + unsigned int offset) +{ + switch (region->type) { + case WMFW_ADSP1_PM: + return region->base + (offset * 3); + case WMFW_ADSP1_DM: + return region->base + (offset * 2); + case WMFW_ADSP2_XM: + return region->base + (offset * 2); + case WMFW_ADSP2_YM: + return region->base + (offset * 2); + case WMFW_ADSP1_ZM: + return region->base + (offset * 2); + default: + WARN_ON(NULL != "Unknown memory region type"); + return offset; + } +} + static int wm_adsp_load(struct wm_adsp *dsp) { const struct firmware *firmware; @@ -282,27 +302,27 @@ static int wm_adsp_load(struct wm_adsp *dsp) case WMFW_ADSP1_PM: BUG_ON(!mem); region_name = "PM"; - reg = mem->base + (offset * 3); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_DM: BUG_ON(!mem); region_name = "DM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_XM: BUG_ON(!mem); region_name = "XM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_YM: BUG_ON(!mem); region_name = "YM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_ZM: BUG_ON(!mem); region_name = "ZM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; default: adsp_warn(dsp, -- cgit v1.2.3 From d62f4bc665f3efd2a3d20134f874608b8afce7a1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 19 Dec 2012 14:00:30 +0000 Subject: ASoC: wm_asdp: Validate sanity of algorithm count If we run into I/O problems the algorithm count may be crazy, validate it before we proceed and dump the read data for diagnostic purposes. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index bcc0d0ffe72e..017d5101e789 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -377,10 +377,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) struct wmfw_adsp2_id_hdr adsp2_id; struct wmfw_adsp1_alg_hdr *adsp1_alg; struct wmfw_adsp2_alg_hdr *adsp2_alg; - void *alg; + void *alg, *buf; const struct wm_adsp_region *mem; unsigned int pos, term; - size_t algs; + size_t algs, buf_size; __be32 val; int i, ret; @@ -411,6 +411,9 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) return ret; } + buf = &adsp1_id; + buf_size = sizeof(adsp1_id); + algs = be32_to_cpu(adsp1_id.algs); adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", be32_to_cpu(adsp1_id.fw.id), @@ -432,6 +435,9 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) return ret; } + buf = &adsp2_id; + buf_size = sizeof(adsp2_id); + algs = be32_to_cpu(adsp2_id.algs); adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", be32_to_cpu(adsp2_id.fw.id), @@ -454,6 +460,13 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) return -EINVAL; } + if (algs > 1024) { + adsp_err(dsp, "Algorithm count %zx excessive\n", algs); + print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, + buf, buf_size); + return -EINVAL; + } + /* Read the terminator first to validate the length */ ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); if (ret != 0) { -- cgit v1.2.3 From 471f488583c62e4daca7d24fc7c937a39de7d95f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 16:09:31 +0000 Subject: ASoC: wm_adsp: Implement support for algorithm-specific coefficient blocks WMDR coefficient files can specify coefficients in terms of algorithm specific data regions. Record the start addresses of these regions while parsing the algorithms and then use them to handle coefficients with these formats. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 114 +++++++++++++++++++++++++++++++++++++++++++-- sound/soc/codecs/wm_adsp.h | 9 ++++ 2 files changed, 119 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 017d5101e789..5015ff287c39 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -378,6 +378,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) struct wmfw_adsp1_alg_hdr *adsp1_alg; struct wmfw_adsp2_alg_hdr *adsp2_alg; void *alg, *buf; + struct wm_adsp_alg_region *region; const struct wm_adsp_region *mem; unsigned int pos, term; size_t algs, buf_size; @@ -496,19 +497,80 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) for (i = 0; i < algs; i++) { switch (dsp->type) { case WMFW_ADSP1: - adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", i, be32_to_cpu(adsp1_alg[i].alg.id), (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff); + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp1_alg[i].dm), + be32_to_cpu(adsp1_alg[i].zm)); + + if (adsp1_alg[i].dm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_DM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].dm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } + + if (adsp1_alg[i].zm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_ZM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].zm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } break; case WMFW_ADSP2: - adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + adsp_info(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", i, be32_to_cpu(adsp2_alg[i].alg.id), (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff); + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp2_alg[i].xm), + be32_to_cpu(adsp2_alg[i].ym), + be32_to_cpu(adsp2_alg[i].zm)); + + if (adsp2_alg[i].xm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_XM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].xm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } + + if (adsp2_alg[i].ym) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_YM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].ym); + list_add_tail(®ion->list, + &dsp->alg_regions); + } + + if (adsp2_alg[i].zm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_ZM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].zm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } break; } } @@ -524,6 +586,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) struct wmfw_coeff_hdr *hdr; struct wmfw_coeff_item *blk; const struct firmware *firmware; + const struct wm_adsp_region *mem; + struct wm_adsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg; char *file; @@ -588,6 +652,37 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) region_name = "register"; reg = offset; break; + + case WMFW_ADSP1_DM: + case WMFW_ADSP1_ZM: + case WMFW_ADSP2_XM: + case WMFW_ADSP2_YM: + adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", + file, blocks, le32_to_cpu(blk->len), + type, le32_to_cpu(blk->id)); + + mem = wm_adsp_find_region(dsp, type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", type); + break; + } + + reg = 0; + list_for_each_entry(alg_region, + &dsp->alg_regions, list) { + if (le32_to_cpu(blk->id) == alg_region->alg && + type == alg_region->type) { + reg = alg_region->base + offset; + reg = wm_adsp_region_to_reg(mem, + reg); + } + } + + if (reg == 0) + adsp_err(dsp, "No %x for algorithm %x\n", + type, le32_to_cpu(blk->id)); + break; + default: adsp_err(dsp, "Unknown region type %x\n", type); break; @@ -711,6 +806,7 @@ int wm_adsp2_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_adsp_alg_region *alg_region; unsigned int val; int ret; @@ -811,6 +907,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, "Failed to enable supply: %d\n", ret); } + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } break; default: @@ -840,6 +944,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) return ret; } + INIT_LIST_HEAD(&adsp->alg_regions); + if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index ffd29a4609e2..48814198e24e 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -25,6 +25,13 @@ struct wm_adsp_region { unsigned int base; }; +struct wm_adsp_alg_region { + struct list_head list; + unsigned int alg; + int type; + unsigned int base; +}; + struct wm_adsp { const char *part; int num; @@ -34,6 +41,8 @@ struct wm_adsp { int base; + struct list_head alg_regions; + const struct wm_adsp_region *mem; int num_mems; -- cgit v1.2.3 From 1023dbd90c1e3e87921198939917c1f50b4b6af7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 22:58:28 +0000 Subject: ASoC: wm_adsp: Add basic firmware selection support There are many firmwares available for ADSP devices. Add basic support for selecting between them, including a couple of feature sets in the set of available firmware to start off with. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-- sound/soc/codecs/wm_adsp.h | 5 ++++ 2 files changed, 78 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5015ff287c39..1f8e8e2a1a6a 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -143,6 +143,71 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +#define WM_ADSP_NUM_FW 3 + +static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { + "MBC/VSS", "Tx", "Rx ANC" +}; + +static struct { + const char *file; +} wm_adsp_fw[WM_ADSP_NUM_FW] = { + { .file = "mbc-vss" }, + { .file = "tx" }, + { .file = "rx-anc" }, +}; + +static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; + + return 0; +} + +static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) + return 0; + + if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + + if (adsp[e->shift_l].running) + return -EBUSY; + + adsp->fw = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct soc_enum wm_adsp_fw_enum[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), +}; + +const struct snd_kcontrol_new wm_adsp_fw_controls[] = { + SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], + wm_adsp_fw_get, wm_adsp_fw_put), +}; +EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -197,7 +262,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -596,7 +662,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -886,9 +953,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; + + dsp->running = true; break; case SND_SOC_DAPM_PRE_PMD: + dsp->running = false; + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 48814198e24e..5e71410f8b05 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -46,6 +46,9 @@ struct wm_adsp { const struct wm_adsp_region *mem; int num_mems; + int fw; + bool running; + struct regulator *dvfs; }; @@ -59,6 +62,8 @@ struct wm_adsp { .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } +extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; + int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -- cgit v1.2.3 From 5e7a7a221fbae313a8635411b557e736ba044c98 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Jan 2013 10:03:56 +0900 Subject: ASoC: wm_adsp: Add initialisation function for ADSP1 Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 8 ++++++++ sound/soc/codecs/wm_adsp.h | 1 + 2 files changed, 9 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 1f8e8e2a1a6a..58cac071456b 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -780,6 +780,14 @@ out: return 0; } +int wm_adsp1_init(struct wm_adsp *adsp) +{ + INIT_LIST_HEAD(&adsp->alg_regions); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp1_init); + int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 5e71410f8b05..41206d79e038 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -64,6 +64,7 @@ struct wm_adsp { extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; +int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -- cgit v1.2.3 From c712326d6c1e74678791d5864cd2ed283e1cc572 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Jan 2013 16:59:04 +0900 Subject: ASoC: wm_adsp: Implement support for coefficeint file format 1 Implement support for a new revision of the coefficeint file format for ADSP cores. Since coefficient file format 0 has not been widely deployed and is very unlikely to ever be used with this driver code support for it has been removed. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 20 +++++++++++++++----- sound/soc/codecs/wmfw.h | 15 ++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 58cac071456b..5841285c213f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -686,6 +686,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) return -EINVAL; } + switch (be32_to_cpu(hdr->rev) & 0xff) { + case 1: + break; + default: + adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", + file, be32_to_cpu(hdr->rev) & 0xff); + ret = -EINVAL; + goto out_fw; + } + adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, (le32_to_cpu(hdr->ver) >> 16) & 0xff, (le32_to_cpu(hdr->ver) >> 8) & 0xff, @@ -698,8 +708,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) pos - firmware->size > sizeof(*blk)) { blk = (void*)(&firmware->data[pos]); - type = be32_to_cpu(blk->type) & 0xff; - offset = le32_to_cpu(blk->offset) & 0xffffff; + type = le16_to_cpu(blk->type); + offset = le16_to_cpu(blk->offset); adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", file, blocks, le32_to_cpu(blk->id), @@ -712,10 +722,10 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) reg = 0; region_name = "Unknown"; switch (type) { - case WMFW_NAME_TEXT: - case WMFW_INFO_TEXT: + case (WMFW_NAME_TEXT << 8): + case (WMFW_INFO_TEXT << 8): break; - case WMFW_ABSOLUTE: + case (WMFW_ABSOLUTE << 8): region_name = "register"; reg = offset; break; diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 5632ded67fdd..ef163360a745 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr { struct wmfw_coeff_hdr { u8 magic[4]; __le32 len; - __le32 ver; + union { + __be32 rev; + __le32 ver; + }; + union { + __be32 core; + __le32 core_ver; + }; u8 data[]; } __packed; struct wmfw_coeff_item { - union { - __be32 type; - __le32 offset; - }; + __le16 offset; + __le16 type; __le32 id; __le32 ver; __le32 sr; -- cgit v1.2.3 From 94e205bfb73b6d19028dbd40404219fdeb27175e Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Fri, 18 Jan 2013 08:43:09 +0000 Subject: ASoC: wm_adsp: Set ADSP1 clock rate to match sys clock Sets the ADSP1 clock rate to match the system clock rate. To support this the codec driver provides details of register containing the system clock control bits. Signed-off-by: Chris Rattray Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 33 +++++++++++++++++++++++++++++++++ sound/soc/codecs/wm_adsp.h | 3 +++ 2 files changed, 36 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5841285c213f..9e311622190e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -103,6 +103,13 @@ #define ADSP1_START_SHIFT 0 /* DSP1_START */ #define ADSP1_START_WIDTH 1 /* DSP1_START */ +/* + * ADSP1 Control 31 + */ +#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ + #define ADSP2_CONTROL 0 #define ADSP2_CLOCKING 1 #define ADSP2_STATUS1 4 @@ -806,12 +813,38 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; int ret; + int val; switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, ADSP1_SYS_ENA); + /* + * For simplicity set the DSP clock rate to be the + * SYSCLK rate rather than making it configurable. + */ + if(dsp->sysclk_reg) { + ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); + if (ret != 0) { + adsp_err(dsp, "Failed to read SYSCLK state: %d\n", + ret); + return ret; + } + + val = (val & dsp->sysclk_mask) + >> dsp->sysclk_shift; + + ret = regmap_update_bits(dsp->regmap, + dsp->base + ADSP1_CONTROL_31, + ADSP1_CLK_SEL_MASK, val); + if (ret != 0) { + adsp_err(dsp, "Failed to set clock rate: %d\n", + ret); + return ret; + } + } + ret = wm_adsp_load(dsp); if (ret != 0) goto err; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 41206d79e038..cb8871a3ec00 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -40,6 +40,9 @@ struct wm_adsp { struct regmap *regmap; int base; + int sysclk_reg; + int sysclk_mask; + int sysclk_shift; struct list_head alg_regions; -- cgit v1.2.3 From 25c62f7e7014d64c0308d0dedb99282ed8ade2ce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Jan 2013 19:02:19 +0900 Subject: ASoC: wm_adsp: Make region identification errors more informative Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 9e311622190e..084ea5f4aedd 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -768,7 +768,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) break; default: - adsp_err(dsp, "Unknown region type %x\n", type); + adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", + file, blocks, type, pos); break; } -- cgit v1.2.3 From f2a93e2a4c048dfb5c7ee3f159a4a1d1cb61b4b5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Jan 2013 22:17:30 +0900 Subject: ASoC: wm_adsp: Use GFP_DMA for algorithm readback Normally kmalloc() returns things that are DMA safe so not visible on all platforms but we do need to explicitly request DMA safe memory. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 084ea5f4aedd..edb67138d548 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -553,7 +553,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", term, be32_to_cpu(val)); - alg = kzalloc((term - pos) * 2, GFP_KERNEL); + alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); if (!alg) return -ENOMEM; -- cgit v1.2.3 From 20da6d5ac05905833db4b339b57b9dcbd21b6152 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 12 Jan 2013 19:58:17 +0000 Subject: ASoC: wm_adsp: Provide explicit trace of coefficient writes Helpful for debugging. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 76ca176eac07..bc5e38385996 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -793,6 +793,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) return -ENOMEM; } + adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", + file, blocks, le32_to_cpu(blk->len), + reg); ret = regmap_raw_write(regmap, reg, blk->data, le32_to_cpu(blk->len)); if (ret != 0) { -- cgit v1.2.3 From 338c5188f6fbccf3ef34a6ae46ada8f64565e1d0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 24 Jan 2013 00:35:48 +0800 Subject: ASoC: wm_adsp: Correct handling of some coefficeint blocks Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index bc5e38385996..55a0089d775e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -768,9 +768,10 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) &dsp->alg_regions, list) { if (le32_to_cpu(blk->id) == alg_region->alg && type == alg_region->type) { - reg = alg_region->base + offset; + reg = alg_region->base; reg = wm_adsp_region_to_reg(mem, reg); + reg += offset; } } -- cgit v1.2.3 From 36e8fe990153d015d9bb1d36f5bd70ec5b205204 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 25 Jan 2013 17:47:48 +0800 Subject: ASoC: wm_adsp: Add speaker Tx as a firmware option Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 55a0089d775e..99f00f879c5e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -150,10 +150,10 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 -#define WM_ADSP_NUM_FW 3 +#define WM_ADSP_NUM_FW 4 static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { - "MBC/VSS", "Tx", "Rx ANC" + "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC" }; static struct { @@ -161,6 +161,7 @@ static struct { } wm_adsp_fw[WM_ADSP_NUM_FW] = { { .file = "mbc-vss" }, { .file = "tx" }, + { .file = "tx-spk" }, { .file = "rx-anc" }, }; -- cgit v1.2.3 From 7480800ea682b70b0a15cda00eed7eedc90ceb9c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 26 Jan 2013 00:29:51 +0800 Subject: ASoC: wm_adsp: Accept 0 as a parameter block address Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 85 +++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 50 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 99f00f879c5e..888316484541 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -590,27 +590,21 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp1_alg[i].dm), be32_to_cpu(adsp1_alg[i].zm)); - if (adsp1_alg[i].dm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_DM; - region->alg = be32_to_cpu(adsp1_alg[i].alg.id); - region->base = be32_to_cpu(adsp1_alg[i].dm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_DM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].dm); + list_add_tail(®ion->list, &dsp->alg_regions); - if (adsp1_alg[i].zm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_ZM; - region->alg = be32_to_cpu(adsp1_alg[i].alg.id); - region->base = be32_to_cpu(adsp1_alg[i].zm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_ZM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); break; case WMFW_ADSP2: @@ -624,38 +618,29 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp2_alg[i].ym), be32_to_cpu(adsp2_alg[i].zm)); - if (adsp2_alg[i].xm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_XM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].xm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_XM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].xm); + list_add_tail(®ion->list, &dsp->alg_regions); - if (adsp2_alg[i].ym) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_YM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].ym); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_YM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].ym); + list_add_tail(®ion->list, &dsp->alg_regions); - if (adsp2_alg[i].zm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_ZM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].zm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_ZM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); break; } } -- cgit v1.2.3 From 31522764c6b57e41b79220156efc5d208f2f841a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Jan 2013 20:11:01 +0800 Subject: ASoC: wm_adsp: Fix support for firmware switching on DSP2 and higher Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index f3a9b559e0fe..5487a94f4595 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -196,7 +196,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, if (adsp[e->shift_l].running) return -EBUSY; - adsp->fw = ucontrol->value.integer.value[0]; + adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; return 0; } -- cgit v1.2.3 From cf17c83c4ac2de13a7b158c1c27fffb30ce109c3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Jan 2013 14:37:23 +0800 Subject: ASoC: wm_adsp: Use asynchronous I/O to write firmware and coefficients Allow the regmap API to use asynchronous I/O where supported to minimise the delay between transfers, reducing firmware download times. Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 82 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 16 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5487a94f4595..be45e2b8b59e 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,43 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +struct wm_adsp_buf { + struct list_head list; + void *buf; +}; + +static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, + struct list_head *list) +{ + struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); + + if (buf == NULL) + return NULL; + + buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); + if (!buf->buf) { + kfree(buf); + return NULL; + } + + if (list) + list_add_tail(&buf->list, list); + + return buf; +} + +static void wm_adsp_buf_free(struct list_head *list) +{ + while (!list_empty(list)) { + struct wm_adsp_buf *buf = list_first_entry(list, + struct wm_adsp_buf, + list); + list_del(&buf->list); + kfree(buf->buf); + kfree(buf); + } +} + #define WM_ADSP_NUM_FW 4 static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { @@ -254,6 +292,7 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, static int wm_adsp_load(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); const struct firmware *firmware; struct regmap *regmap = dsp->regmap; unsigned int pos = 0; @@ -265,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) const struct wm_adsp_region *mem; const char *region_name; char *file, *text; - void *buf; + struct wm_adsp_buf *buf; unsigned int reg; int regions = 0; int ret, offset, type, sizes; @@ -420,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - buf = kmemdup(region->data, le32_to_cpu(region->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(region->data, + le32_to_cpu(region->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; } - ret = regmap_raw_write(regmap, reg, buf, - le32_to_cpu(region->len)); - - kfree(buf); - + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(region->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write %d bytes at %d in %s: %d\n", @@ -445,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp) pos += le32_to_cpu(region->len) + sizeof(*region); regions++; } - + + ret = regmap_async_complete(regmap); + if (ret != 0) { + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + goto out_fw; + } + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, regions, pos - firmware->size); out_fw: + regmap_async_complete(regmap); + wm_adsp_buf_free(&buf_list); release_firmware(firmware); out: kfree(file); @@ -655,6 +700,7 @@ out: static int wm_adsp_load_coeff(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); struct regmap *regmap = dsp->regmap; struct wmfw_coeff_hdr *hdr; struct wmfw_coeff_item *blk; @@ -664,7 +710,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) const char *region_name; int ret, pos, blocks, type, offset, reg; char *file; - void *buf; + struct wm_adsp_buf *buf; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) @@ -776,8 +822,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } if (reg) { - buf = kmemdup(blk->data, le32_to_cpu(blk->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(blk->data, + le32_to_cpu(blk->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; @@ -786,27 +833,30 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", file, blocks, le32_to_cpu(blk->len), reg); - ret = regmap_raw_write(regmap, reg, blk->data, - le32_to_cpu(blk->len)); + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(blk->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write to %x in %s\n", file, blocks, reg, region_name); } - - kfree(buf); } pos += le32_to_cpu(blk->len) + sizeof(*blk); blocks++; } + ret = regmap_async_complete(regmap); + if (ret != 0) + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, blocks, pos - firmware->size); out_fw: release_firmware(firmware); + wm_adsp_buf_free(&buf_list); out: kfree(file); return 0; -- cgit v1.2.3 From bdaacea35960ae72e84bd481e69324da9f4d4de5 Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Fri, 8 Feb 2013 14:32:15 +0000 Subject: ASoC: wm_adsp: round to 4-byte boundary for coeff file blocks Signed-off-by: Chris Rattray Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index be45e2b8b59e..f3f7e75f8628 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -711,6 +711,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) int ret, pos, blocks, type, offset, reg; char *file; struct wm_adsp_buf *buf; + int tmp; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) @@ -842,7 +843,12 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } } - pos += le32_to_cpu(blk->len) + sizeof(*blk); + tmp = le32_to_cpu(blk->len) % 4; + if (tmp) + pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); + else + pos += le32_to_cpu(blk->len) + sizeof(*blk); + blocks++; } -- cgit v1.2.3