[alsa-devel] [PATCH 1/5] ASoC: wm_adsp: factor out getting base register for a control

The lookup of the base register corresponding to a control is duplicated in read and write so factor it out into a separate function.
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com --- sound/soc/codecs/wm_adsp.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 7320fca..049ff20 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -727,6 +727,24 @@ static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) return container_of(ext, struct wm_coeff_ctl, bytes_ext); }
+static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) +{ + const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; + struct wm_adsp *dsp = ctl->dsp; + const struct wm_adsp_region *mem; + + mem = wm_adsp_find_region(dsp, alg_region->type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", + alg_region->type); + return -EINVAL; + } + + *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); + + return 0; +} + static int wm_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -742,22 +760,14 @@ static int wm_coeff_info(struct snd_kcontrol *kctl, static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, const void *buf, size_t len) { - struct wm_adsp_alg_region *alg_region = &ctl->alg_region; - const struct wm_adsp_region *mem; struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", - alg_region->type); - return -EINVAL; - } - - reg = ctl->alg_region.base + ctl->offset; - reg = wm_adsp_region_to_reg(mem, reg); + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret;
scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); if (!scratch) @@ -826,22 +836,14 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { - struct wm_adsp_alg_region *alg_region = &ctl->alg_region; - const struct wm_adsp_region *mem; struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", - alg_region->type); - return -EINVAL; - } - - reg = ctl->alg_region.base + ctl->offset; - reg = wm_adsp_region_to_reg(mem, reg); + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret;
scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); if (!scratch)

This patch factors out converting a memory region type into a name string, mainly so that it can be used in log commands.
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com --- sound/soc/codecs/wm_adsp.c | 53 +++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 31 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 049ff20..eb2ca91 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -446,6 +446,24 @@ struct wm_coeff_ctl { unsigned int flags; };
+static const char *wm_adsp_mem_region_name(unsigned int type) +{ + switch (type) { + case WMFW_ADSP1_PM: + return "PM"; + case WMFW_ADSP1_DM: + return "DM"; + case WMFW_ADSP2_XM: + return "XM"; + case WMFW_ADSP2_YM: + return "YM"; + case WMFW_ADSP1_ZM: + return "ZM"; + default: + return NULL; + } +} + #ifdef CONFIG_DEBUG_FS static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) { @@ -1062,29 +1080,14 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char *region_name; + const char *region_name; int ret;
if (flags & WMFW_CTL_FLAG_SYS) return 0;
- switch (alg_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: + region_name = wm_adsp_mem_region_name(alg_region->type); + if (!region_name) { adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); return -EINVAL; } @@ -1492,23 +1495,11 @@ static int wm_adsp_load(struct wm_adsp *dsp) reg = offset; break; case WMFW_ADSP1_PM: - region_name = "PM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP1_DM: - region_name = "DM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP2_XM: - region_name = "XM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP2_YM: - region_name = "YM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP1_ZM: - region_name = "ZM"; + region_name = wm_adsp_mem_region_name(type); reg = wm_adsp_region_to_reg(mem, offset); break; default:

The patch
ASoC: wm_adsp: factor out getting region name from type
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 9ce5e6e61122cdd6556d4e61657aa47b207042a1 Mon Sep 17 00:00:00 2001
From: Richard Fitzgerald rf@opensource.wolfsonmicro.com Date: Wed, 9 Nov 2016 17:14:15 +0000 Subject: [PATCH] ASoC: wm_adsp: factor out getting region name from type
This patch factors out converting a memory region type into a name string, mainly so that it can be used in log commands.
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/wm_adsp.c | 53 +++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 31 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 07cf7cb93301..ef33de651028 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -454,6 +454,24 @@ struct wm_coeff_ctl { unsigned int flags; };
+static const char *wm_adsp_mem_region_name(unsigned int type) +{ + switch (type) { + case WMFW_ADSP1_PM: + return "PM"; + case WMFW_ADSP1_DM: + return "DM"; + case WMFW_ADSP2_XM: + return "XM"; + case WMFW_ADSP2_YM: + return "YM"; + case WMFW_ADSP1_ZM: + return "ZM"; + default: + return NULL; + } +} + #ifdef CONFIG_DEBUG_FS static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) { @@ -1146,29 +1164,14 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char *region_name; + const char *region_name; int ret;
if (flags & WMFW_CTL_FLAG_SYS) return 0;
- switch (alg_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: + region_name = wm_adsp_mem_region_name(alg_region->type); + if (!region_name) { adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); return -EINVAL; } @@ -1601,23 +1604,11 @@ static int wm_adsp_load(struct wm_adsp *dsp) reg = offset; break; case WMFW_ADSP1_PM: - region_name = "PM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP1_DM: - region_name = "DM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP2_XM: - region_name = "XM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP2_YM: - region_name = "YM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP1_ZM: - region_name = "ZM"; + region_name = wm_adsp_mem_region_name(type); reg = wm_adsp_region_to_reg(mem, offset); break; default:

From: Stuart Henderson stuarth@opensource.wolfsonmicro.com
Add support for firmware controls marked SYSTEM. These are internal to the driver-firmware interface and do not have a user-accessible ALSA control.
Signed-off-by: Stuart Henderson stuarth@opensource.wolfsonmicro.com Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com --- sound/soc/codecs/wm_adsp.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index eb2ca91..82bb3dc 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -444,6 +444,7 @@ struct wm_coeff_ctl { struct snd_kcontrol *kcontrol; struct soc_bytes_ext bytes_ext; unsigned int flags; + unsigned int type; };
static const char *wm_adsp_mem_region_name(unsigned int type) @@ -1075,7 +1076,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, const struct wm_adsp_alg_region *alg_region, unsigned int offset, unsigned int len, const char *subname, unsigned int subname_len, - unsigned int flags) + unsigned int flags, unsigned int type) { struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; @@ -1083,9 +1084,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, const char *region_name; int ret;
- if (flags & WMFW_CTL_FLAG_SYS) - return 0; - region_name = wm_adsp_mem_region_name(alg_region->type); if (!region_name) { adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); @@ -1143,6 +1141,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ctl->dsp = dsp;
ctl->flags = flags; + ctl->type = type; ctl->offset = offset; ctl->len = len; ctl->cache = kzalloc(ctl->len, GFP_KERNEL); @@ -1153,6 +1152,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
list_add(&ctl->list, &dsp->ctl_list);
+ if (flags & WMFW_CTL_FLAG_SYS) + return 0; + ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); if (!ctl_work) { ret = -ENOMEM; @@ -1342,7 +1344,8 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, coeff_blk.len, coeff_blk.name, coeff_blk.name_len, - coeff_blk.flags); + coeff_blk.flags, + coeff_blk.ctl_type); if (ret < 0) adsp_err(dsp, "Failed to create control: %.*s, %d\n", coeff_blk.name_len, coeff_blk.name, ret); @@ -1742,7 +1745,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp1_alg[i].dm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1762,7 +1766,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp1_alg[i].zm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1853,7 +1858,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].xm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1873,7 +1879,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].ym); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1893,7 +1900,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].zm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id));

The patch
ASoC: wm_adsp: Add support for SYSTEM firmware controls
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 8eb084d066baeca2b2911632bb4edddbc2b762c1 Mon Sep 17 00:00:00 2001
From: Stuart Henderson stuarth@opensource.wolfsonmicro.com Date: Wed, 9 Nov 2016 17:14:16 +0000 Subject: [PATCH] ASoC: wm_adsp: Add support for SYSTEM firmware controls
Add support for firmware controls marked SYSTEM. These are internal to the driver-firmware interface and do not have a user-accessible ALSA control.
Signed-off-by: Stuart Henderson stuarth@opensource.wolfsonmicro.com Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/wm_adsp.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ef33de651028..d13dd9a9c817 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -452,6 +452,7 @@ struct wm_coeff_ctl { struct snd_kcontrol *kcontrol; struct soc_bytes_ext bytes_ext; unsigned int flags; + unsigned int type; };
static const char *wm_adsp_mem_region_name(unsigned int type) @@ -1159,7 +1160,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, const struct wm_adsp_alg_region *alg_region, unsigned int offset, unsigned int len, const char *subname, unsigned int subname_len, - unsigned int flags) + unsigned int flags, unsigned int type) { struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; @@ -1167,9 +1168,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, const char *region_name; int ret;
- if (flags & WMFW_CTL_FLAG_SYS) - return 0; - region_name = wm_adsp_mem_region_name(alg_region->type); if (!region_name) { adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); @@ -1227,6 +1225,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ctl->dsp = dsp;
ctl->flags = flags; + ctl->type = type; ctl->offset = offset; ctl->len = len; ctl->cache = kzalloc(ctl->len, GFP_KERNEL); @@ -1237,6 +1236,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
list_add(&ctl->list, &dsp->ctl_list);
+ if (flags & WMFW_CTL_FLAG_SYS) + return 0; + ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); if (!ctl_work) { ret = -ENOMEM; @@ -1451,7 +1453,8 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, coeff_blk.len, coeff_blk.name, coeff_blk.name_len, - coeff_blk.flags); + coeff_blk.flags, + coeff_blk.ctl_type); if (ret < 0) adsp_err(dsp, "Failed to create control: %.*s, %d\n", coeff_blk.name_len, coeff_blk.name, ret); @@ -1851,7 +1854,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp1_alg[i].dm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1871,7 +1875,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp1_alg[i].zm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1962,7 +1967,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].xm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1982,7 +1988,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].ym); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -2002,7 +2009,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].zm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id));

If the firmware has any system event signalling controls, signal them during DSP PRE_PMD to tell the firmware it is about to be stopped.
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com --- sound/soc/codecs/wm_adsp.c | 114 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wmfw.h | 3 ++ 2 files changed, 117 insertions(+)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 82bb3dc..bdbcc44 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -162,6 +162,14 @@
#define ADSP_MAX_STD_CTRL_SIZE 512
+#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 +#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 + +/* + * Event control messages + */ +#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 + struct wm_adsp_buf { struct list_head list; void *buf; @@ -776,6 +784,66 @@ static int wm_coeff_info(struct snd_kcontrol *kctl, return 0; }
+static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, + unsigned int event_id) +{ + struct wm_adsp *dsp = ctl->dsp; + u32 val = cpu_to_be32(event_id); + unsigned int reg; + int i, ret; + + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret; + + adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", + event_id, ctl->alg_region.alg, + wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); + + ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); + if (ret) { + adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); + return ret; + } + + /* + * Poll for ack, we initially poll at ~1ms intervals for firmwares + * that respond quickly, then go to ~10ms polls. A firmware is unlikely + * to ack instantly so we do the first 1ms delay before reading the + * control to avoid a pointless bus transaction + */ + for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { + switch (i) { + case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: + usleep_range(1000, 2000); + i++; + break; + default: + usleep_range(10000, 20000); + i += 10; + break; + } + + ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + if (ret) { + adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); + return ret; + } + + if (val == 0) { + adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); + return 0; + } + } + + adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", + reg, ctl->alg_region.alg, + wm_adsp_mem_region_name(ctl->alg_region.type), + ctl->offset); + + return -ETIMEDOUT; +} + static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, const void *buf, size_t len) { @@ -1055,6 +1123,24 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp) return 0; }
+static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, + unsigned int event) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) + continue; + + ret = wm_coeff_write_acked_control(ctl, event); + if (ret) + adsp_warn(dsp, + "Failed to send 0x%x event to alg 0x%x (%d)\n", + event, ctl->alg_region.alg, ret); + } +} + static void wm_adsp_ctl_work(struct work_struct *work) { struct wmfw_ctl_work *ctl_work = container_of(work, @@ -1314,6 +1400,21 @@ static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); }
+static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, + const struct wm_coeff_parsed_coeff *coeff_blk, + unsigned int f_required, + unsigned int f_illegal) +{ + if ((coeff_blk->flags & f_illegal) || + ((coeff_blk->flags & f_required) != f_required)) { + adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", + coeff_blk->flags, coeff_blk->ctl_type); + return -EINVAL; + } + + return 0; +} + static int wm_adsp_parse_coeff(struct wm_adsp *dsp, const struct wmfw_region *region) { @@ -1330,6 +1431,16 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, switch (coeff_blk.ctl_type) { case SNDRV_CTL_ELEM_TYPE_BYTES: break; + case WMFW_CTL_TYPE_HOSTEVENT: + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_SYS | + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_WRITEABLE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; default: adsp_err(dsp, "Unknown control type: %d\n", coeff_blk.ctl_type); @@ -2401,6 +2512,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, break;
case SND_SOC_DAPM_PRE_PMD: + /* Tell the firmware to cleanup */ + wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); + /* Log firmware state, it can be useful for analysis */ wm_adsp2_show_fw_status(dsp);
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 7613d60..892fc74 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -26,6 +26,9 @@ #define WMFW_CTL_FLAG_WRITEABLE 0x0002 #define WMFW_CTL_FLAG_READABLE 0x0001
+/* Non-ALSA coefficient types start at 0x1000 */ +#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */ + struct wmfw_header { char magic[4]; __le32 len;

The patch
ASoC: wm_adsp: Signal firmware shutdown through event control
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From f4f0c4c60c3959a8f3650850b799049161732e91 Mon Sep 17 00:00:00 2001
From: Richard Fitzgerald rf@opensource.wolfsonmicro.com Date: Wed, 9 Nov 2016 17:14:17 +0000 Subject: [PATCH] ASoC: wm_adsp: Signal firmware shutdown through event control
If the firmware has any system event signalling controls, signal them during DSP PRE_PMD to tell the firmware it is about to be stopped.
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/wm_adsp.c | 114 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wmfw.h | 3 ++ 2 files changed, 117 insertions(+)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 7320fcacd86a..07cf7cb93301 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -162,6 +162,14 @@
#define ADSP_MAX_STD_CTRL_SIZE 512
+#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 +#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 + +/* + * Event control messages + */ +#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 + struct wm_adsp_buf { struct list_head list; void *buf; @@ -739,6 +747,66 @@ static int wm_coeff_info(struct snd_kcontrol *kctl, return 0; }
+static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, + unsigned int event_id) +{ + struct wm_adsp *dsp = ctl->dsp; + u32 val = cpu_to_be32(event_id); + unsigned int reg; + int i, ret; + + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret; + + adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", + event_id, ctl->alg_region.alg, + wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); + + ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); + if (ret) { + adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); + return ret; + } + + /* + * Poll for ack, we initially poll at ~1ms intervals for firmwares + * that respond quickly, then go to ~10ms polls. A firmware is unlikely + * to ack instantly so we do the first 1ms delay before reading the + * control to avoid a pointless bus transaction + */ + for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { + switch (i) { + case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: + usleep_range(1000, 2000); + i++; + break; + default: + usleep_range(10000, 20000); + i += 10; + break; + } + + ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + if (ret) { + adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); + return ret; + } + + if (val == 0) { + adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); + return 0; + } + } + + adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", + reg, ctl->alg_region.alg, + wm_adsp_mem_region_name(ctl->alg_region.type), + ctl->offset); + + return -ETIMEDOUT; +} + static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, const void *buf, size_t len) { @@ -1034,6 +1102,24 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp) return 0; }
+static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, + unsigned int event) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) + continue; + + ret = wm_coeff_write_acked_control(ctl, event); + if (ret) + adsp_warn(dsp, + "Failed to send 0x%x event to alg 0x%x (%d)\n", + event, ctl->alg_region.alg, ret); + } +} + static void wm_adsp_ctl_work(struct work_struct *work) { struct wmfw_ctl_work *ctl_work = container_of(work, @@ -1307,6 +1393,21 @@ static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); }
+static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, + const struct wm_coeff_parsed_coeff *coeff_blk, + unsigned int f_required, + unsigned int f_illegal) +{ + if ((coeff_blk->flags & f_illegal) || + ((coeff_blk->flags & f_required) != f_required)) { + adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", + coeff_blk->flags, coeff_blk->ctl_type); + return -EINVAL; + } + + return 0; +} + static int wm_adsp_parse_coeff(struct wm_adsp *dsp, const struct wmfw_region *region) { @@ -1323,6 +1424,16 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, switch (coeff_blk.ctl_type) { case SNDRV_CTL_ELEM_TYPE_BYTES: break; + case WMFW_CTL_TYPE_HOSTEVENT: + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_SYS | + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_WRITEABLE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; default: adsp_err(dsp, "Unknown control type: %d\n", coeff_blk.ctl_type); @@ -2400,6 +2511,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, break;
case SND_SOC_DAPM_PRE_PMD: + /* Tell the firmware to cleanup */ + wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); + /* Log firmware state, it can be useful for analysis */ wm_adsp2_show_fw_status(dsp);
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 7613d60d62ea..892fc7490f3b 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -26,6 +26,9 @@ #define WMFW_CTL_FLAG_WRITEABLE 0x0002 #define WMFW_CTL_FLAG_READABLE 0x0001
+/* Non-ALSA coefficient types start at 0x1000 */ +#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */ + struct wmfw_header { char magic[4]; __le32 len;

This patch handles publishing acknowledged controls through ALSA. These controls allow user-side to send events to the firmware and wait for the firmware to acknowledge it.
Note that although acked controls only operate in the direction host->firmware, and therefore they are write-only as seen from user- side code, we have to make them readable to account for all the code out there that assumes that ALSA controls are always readable (amixer for example.)
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com --- sound/soc/codecs/wm_adsp.c | 89 +++++++++++++++++++++++++++++++++++++++++----- sound/soc/codecs/wmfw.h | 1 + 2 files changed, 82 insertions(+), 8 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index bdbcc44..4fb6e2f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -164,6 +164,8 @@
#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 +#define WM_ADSP_ACKED_CTL_MIN_VALUE 0 +#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
/* * Event control messages @@ -779,8 +781,20 @@ static int wm_coeff_info(struct snd_kcontrol *kctl, (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = ctl->len; + switch (ctl->type) { + case WMFW_CTL_TYPE_ACKED: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; + uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; + uinfo->value.integer.step = 1; + uinfo->count = 1; + break; + default: + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = ctl->len; + break; + } + return 0; }
@@ -920,6 +934,30 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, return ret; }
+static int wm_coeff_put_acked(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + unsigned int val = ucontrol->value.integer.value[0]; + int ret; + + if (val == 0) + return 0; /* 0 means no event */ + + mutex_lock(&ctl->dsp->pwr_lock); + + if (ctl->enabled) + ret = wm_coeff_write_acked_control(ctl, val); + else + ret = -EPERM; + + mutex_unlock(&ctl->dsp->pwr_lock); + + return ret; +} + static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { @@ -1007,6 +1045,21 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, return ret; }
+static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Although it's not useful to read an acked control, we must satisfy + * user-side assumptions that all controls are readable and that a + * write of the same value should be filtered out (it's valid to send + * the same event number again to the firmware). We therefore return 0, + * meaning "no event" so valid event numbers will always be a change + */ + ucontrol->value.integer.value[0] = 0; + + return 0; +} + struct wmfw_ctl_work { struct wm_adsp *dsp; struct wm_coeff_ctl *ctl; @@ -1059,17 +1112,25 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; - kcontrol->get = wm_coeff_get; - kcontrol->put = wm_coeff_put; kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kcontrol->tlv.c = snd_soc_bytes_tlv_callback; kcontrol->private_value = (unsigned long)&ctl->bytes_ext; + kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
- ctl->bytes_ext.max = ctl->len; - ctl->bytes_ext.get = wm_coeff_tlv_get; - ctl->bytes_ext.put = wm_coeff_tlv_put; + switch (ctl->type) { + case WMFW_CTL_TYPE_ACKED: + kcontrol->get = wm_coeff_get_acked; + kcontrol->put = wm_coeff_put_acked; + break; + default: + kcontrol->get = wm_coeff_get; + kcontrol->put = wm_coeff_put;
- kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); + ctl->bytes_ext.max = ctl->len; + ctl->bytes_ext.get = wm_coeff_tlv_get; + ctl->bytes_ext.put = wm_coeff_tlv_put; + break; + }
ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) @@ -1431,6 +1492,18 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, switch (coeff_blk.ctl_type) { case SNDRV_CTL_ELEM_TYPE_BYTES: break; + case WMFW_CTL_TYPE_ACKED: + if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) + continue; /* ignore */ + + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_WRITEABLE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; case WMFW_CTL_TYPE_HOSTEVENT: ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, WMFW_CTL_FLAG_SYS | diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 892fc74..ec78b9d 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -27,6 +27,7 @@ #define WMFW_CTL_FLAG_READABLE 0x0001
/* Non-ALSA coefficient types start at 0x1000 */ +#define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */ #define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */
struct wmfw_header {

The patch
ASoC: wm_adsp: Support acknowledged controls
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From a23ebba845fee07cce00659c06844845b24da290 Mon Sep 17 00:00:00 2001
From: Richard Fitzgerald rf@opensource.wolfsonmicro.com Date: Wed, 9 Nov 2016 17:14:18 +0000 Subject: [PATCH] ASoC: wm_adsp: Support acknowledged controls
This patch handles publishing acknowledged controls through ALSA. These controls allow user-side to send events to the firmware and wait for the firmware to acknowledge it.
Note that although acked controls only operate in the direction host->firmware, and therefore they are write-only as seen from user- side code, we have to make them readable to account for all the code out there that assumes that ALSA controls are always readable (amixer for example.)
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/wm_adsp.c | 89 +++++++++++++++++++++++++++++++++++++++++----- sound/soc/codecs/wmfw.h | 1 + 2 files changed, 82 insertions(+), 8 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d13dd9a9c817..0f705bfd76b4 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -164,6 +164,8 @@
#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 +#define WM_ADSP_ACKED_CTL_MIN_VALUE 0 +#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
/* * Event control messages @@ -761,8 +763,20 @@ static int wm_coeff_info(struct snd_kcontrol *kctl, (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = ctl->len; + switch (ctl->type) { + case WMFW_CTL_TYPE_ACKED: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; + uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; + uinfo->value.integer.step = 1; + uinfo->count = 1; + break; + default: + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = ctl->len; + break; + } + return 0; }
@@ -910,6 +924,30 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, return ret; }
+static int wm_coeff_put_acked(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + unsigned int val = ucontrol->value.integer.value[0]; + int ret; + + if (val == 0) + return 0; /* 0 means no event */ + + mutex_lock(&ctl->dsp->pwr_lock); + + if (ctl->enabled) + ret = wm_coeff_write_acked_control(ctl, val); + else + ret = -EPERM; + + mutex_unlock(&ctl->dsp->pwr_lock); + + return ret; +} + static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { @@ -1005,6 +1043,21 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, return ret; }
+static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Although it's not useful to read an acked control, we must satisfy + * user-side assumptions that all controls are readable and that a + * write of the same value should be filtered out (it's valid to send + * the same event number again to the firmware). We therefore return 0, + * meaning "no event" so valid event numbers will always be a change + */ + ucontrol->value.integer.value[0] = 0; + + return 0; +} + struct wmfw_ctl_work { struct wm_adsp *dsp; struct wm_coeff_ctl *ctl; @@ -1057,17 +1110,25 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; - kcontrol->get = wm_coeff_get; - kcontrol->put = wm_coeff_put; kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kcontrol->tlv.c = snd_soc_bytes_tlv_callback; kcontrol->private_value = (unsigned long)&ctl->bytes_ext; + kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
- ctl->bytes_ext.max = ctl->len; - ctl->bytes_ext.get = wm_coeff_tlv_get; - ctl->bytes_ext.put = wm_coeff_tlv_put; + switch (ctl->type) { + case WMFW_CTL_TYPE_ACKED: + kcontrol->get = wm_coeff_get_acked; + kcontrol->put = wm_coeff_put_acked; + break; + default: + kcontrol->get = wm_coeff_get; + kcontrol->put = wm_coeff_put;
- kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); + ctl->bytes_ext.max = ctl->len; + ctl->bytes_ext.get = wm_coeff_tlv_get; + ctl->bytes_ext.put = wm_coeff_tlv_put; + break; + }
ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) @@ -1429,6 +1490,18 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, switch (coeff_blk.ctl_type) { case SNDRV_CTL_ELEM_TYPE_BYTES: break; + case WMFW_CTL_TYPE_ACKED: + if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) + continue; /* ignore */ + + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_WRITEABLE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; case WMFW_CTL_TYPE_HOSTEVENT: ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, WMFW_CTL_FLAG_SYS | diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 892fc7490f3b..ec78b9da020f 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -27,6 +27,7 @@ #define WMFW_CTL_FLAG_READABLE 0x0001
/* Non-ALSA coefficient types start at 0x1000 */ +#define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */ #define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */
struct wmfw_header {

The patch
ASoC: wm_adsp: factor out getting base register for a control
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From b396ebca736f94a1a18bdc9a518ad0ca58fbd842 Mon Sep 17 00:00:00 2001
From: Richard Fitzgerald rf@opensource.wolfsonmicro.com Date: Wed, 9 Nov 2016 17:14:14 +0000 Subject: [PATCH] ASoC: wm_adsp: factor out getting base register for a control
The lookup of the base register corresponding to a control is duplicated in read and write so factor it out into a separate function.
Signed-off-by: Richard Fitzgerald rf@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/wm_adsp.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 0f705bfd76b4..4fb6e2f035e0 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -756,6 +756,24 @@ static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) return container_of(ext, struct wm_coeff_ctl, bytes_ext); }
+static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) +{ + const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; + struct wm_adsp *dsp = ctl->dsp; + const struct wm_adsp_region *mem; + + mem = wm_adsp_find_region(dsp, alg_region->type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", + alg_region->type); + return -EINVAL; + } + + *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); + + return 0; +} + static int wm_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -843,22 +861,14 @@ static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, const void *buf, size_t len) { - struct wm_adsp_alg_region *alg_region = &ctl->alg_region; - const struct wm_adsp_region *mem; struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", - alg_region->type); - return -EINVAL; - } - - reg = ctl->alg_region.base + ctl->offset; - reg = wm_adsp_region_to_reg(mem, reg); + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret;
scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); if (!scratch) @@ -951,22 +961,14 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { - struct wm_adsp_alg_region *alg_region = &ctl->alg_region; - const struct wm_adsp_region *mem; struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", - alg_region->type); - return -EINVAL; - } - - reg = ctl->alg_region.base + ctl->offset; - reg = wm_adsp_region_to_reg(mem, reg); + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret;
scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); if (!scratch)
participants (2)
-
Mark Brown
-
Richard Fitzgerald