diff options
| author | Shengjiu Wang <shengjiu.wang@nxp.com> | 2026-03-10 13:42:33 +0300 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-03-10 16:55:57 +0300 |
| commit | 819cf1dc01ce66b6906d403a6925c4bd754a7a1d (patch) | |
| tree | 008bec4872638bc77356f915b2067963c5129d1a | |
| parent | 1f318b96cc84d7c2ab792fcc0bfd42a7ca890681 (diff) | |
| download | linux-819cf1dc01ce66b6906d403a6925c4bd754a7a1d.tar.xz | |
ASoC: fsl_utils: Add snd_kcontrol functions for specific cases
There are some registers which are volatile, at pm runtime suspend state,
the regmap cache only is enabled, regmap will return -EBUSY when trying to
access these registers.
static int _regmap_read(struct regmap *map, unsigned int reg,
unsigned int *val)
{
int ret;
void *context = _regmap_map_get_context(map);
if (!map->cache_bypass) {
ret = regcache_read(map, reg, val);
if (ret == 0)
return 0;
}
if (map->cache_only)
return -EBUSY;
if (!regmap_readable(map, reg))
return -EIO;
When exporting these registers by amixer interface to user space, there
will be -EBUSY errors in mixer-test when the cpu dai is in idle. In order
to avoid such error, needs to define FSL own functions to take a pm
runtime reference before calling snd_soc_get_xr_sx(),
snd_soc_get_enum_double(), snd_soc_get_volsw(), and so on.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Link: https://patch.msgid.link/20260310104235.1234569-2-shengjiu.wang@nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | sound/soc/fsl/fsl_utils.c | 131 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_utils.h | 48 |
2 files changed, 179 insertions, 0 deletions
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index d69a6b9795bf..7651c64bc837 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -10,6 +10,7 @@ #include <linux/clk-provider.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/pm_runtime.h> #include <sound/soc.h> #include "fsl_utils.h" @@ -197,6 +198,136 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, } EXPORT_SYMBOL(fsl_asoc_constrain_rates); +/* + * Below functions are used by mixer interface to avoid accessing registers + * which are volatile at pm runtime suspend state (cache_only is enabled). + */ +int fsl_asoc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_get_xr_sx(kcontrol, ucontrol); + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_get_xr_sx); + +int fsl_asoc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_put_xr_sx(kcontrol, ucontrol); + /* + * As this function only used by the SNDRV_CTL_ELEM_ACCESS_VOLATILE + * case. return 0 to avoid control event notification. + */ + if (ret > 0) + ret = 0; + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_put_xr_sx); + +int fsl_asoc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_get_enum_double); + +int fsl_asoc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + /* + * As this function only used by the SNDRV_CTL_ELEM_ACCESS_VOLATILE + * case. return 0 to avoid control event notification. + */ + if (ret > 0) + ret = 0; + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_put_enum_double); + +int fsl_asoc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_get_volsw(kcontrol, ucontrol); + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_get_volsw); + +int fsl_asoc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret = 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret) + return ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + /* + * As this function only used by the SNDRV_CTL_ELEM_ACCESS_VOLATILE + * case. return 0 to avoid control event notification. + */ + if (ret > 0) + ret = 0; + + pm_runtime_put_autosuspend(component->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asoc_put_volsw); + MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); MODULE_DESCRIPTION("Freescale ASoC utility code"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h index 21b25a11ecda..1aab0c1cee62 100644 --- a/sound/soc/fsl/fsl_utils.h +++ b/sound/soc/fsl/fsl_utils.h @@ -31,4 +31,52 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, const struct snd_pcm_hw_constraint_list *original_constr, struct clk *pll8k_clk, struct clk *pll11k_clk, struct clk *ext_clk, int *target_rates); + +/* Similar to SOC_SINGLE_XR_SX, but it is for read only registers. */ +#define FSL_ASOC_SINGLE_XR_SX_EXT_RO(xname, xregbase, xregcount, xnbits, \ + xmin, xmax, xinvert, xhandler_get) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_soc_info_xr_sx, .get = xhandler_get, \ + .private_value = (unsigned long)&(struct soc_mreg_control) \ + {.regbase = xregbase, .regcount = xregcount, .nbits = xnbits, \ + .invert = xinvert, .min = xmin, .max = xmax} } + +/* Similar to SOC_SINGLE_EXT, but it is for volatile register. */ +#define FSL_ASOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_VOLATILE | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_SINGLE_VALUE(xreg, xshift, 0, xmax, xinvert, 0) } + +#define FSL_ASOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_VOLATILE | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_enum_double, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&xenum } + +int fsl_asoc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int fsl_asoc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + #endif /* _FSL_UTILS_H */ |
