From fd80df352ba1884ce2b62dd8d9495582308101b7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 17 Feb 2025 14:01:56 +0000 Subject: [PATCH 01/16] regcache: Add support for sorting defaults arrays The defaults array in regcache must be sorted into ascending register address order, because binary search is used to locate values in the array. Add a helper to sort the register defaults array which can be useful for systems that dynamically create a defaults array based on external information. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250217140159.2288784-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 31 +++++++++++++++++++++++++++++++ include/linux/regmap.h | 7 +++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index b1f8508c3966..f7fcf2de1301 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -21,6 +21,37 @@ static const struct regcache_ops *cache_types[] = { ®cache_flat_ops, }; +static int regcache_defaults_cmp(const void *a, const void *b) +{ + const struct reg_default *x = a; + const struct reg_default *y = b; + + if (x->reg > y->reg) + return 1; + else if (x->reg < y->reg) + return -1; + else + return 0; +} + +static void regcache_defaults_swap(void *a, void *b, int size) +{ + struct reg_default *x = a; + struct reg_default *y = b; + struct reg_default tmp; + + tmp = *x; + *x = *y; + *y = tmp; +} + +void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults) +{ + sort(defaults, ndefaults, sizeof(*defaults), + regcache_defaults_cmp, regcache_defaults_swap); +} +EXPORT_SYMBOL_GPL(regcache_sort_defaults); + static int regcache_hw_init(struct regmap *map) { int i, j; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 3a96d068915f..d17c5ea3d55d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1352,6 +1352,7 @@ bool regmap_can_raw_write(struct regmap *map); size_t regmap_get_raw_read_max(struct regmap *map); size_t regmap_get_raw_write_max(struct regmap *map); +void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, unsigned int max); @@ -2043,6 +2044,12 @@ static inline bool regmap_might_sleep(struct regmap *map) return true; } +static inline void regcache_sort_defaults(struct reg_default *defaults, + unsigned int ndefaults) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + static inline int regcache_sync(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); -- 2.51.0 From e3f7caf74b795621252e3c25b4a9fb6888336ef1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 17 Feb 2025 14:01:57 +0000 Subject: [PATCH 02/16] ASoC: SDCA: Add generic regmap SDCA helpers Add helper functions that SDCA drivers can use to calculate the properties of SDCA Controls (registers) specified through DisCo. Most of these are fairly obvious from the SDCA Access Modes. DisCo Constants, values which are specified in the ACPI rather than on the device, are handled as unreadable and unwritable registers. The intention is these will be populated in the register defaults table allowing drivers to read them normally. This means the drivers can be agnostic as to which values are DisCo Constants. Finally, support for SDCA Dual Ranked Controls is currently limited here, at the moment the current value will be used directly. Writing the current value directly is valid as per the specification although the synchronicity of updates across multiple registers is lost. Support for this will probably need to be added later. But its a fairly hard problem and doesn't need to be solved immediately. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250217140159.2288784-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_regmap.h | 21 ++++ sound/soc/sdca/Makefile | 2 +- sound/soc/sdca/sdca_regmap.c | 192 +++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 include/sound/sdca_regmap.h create mode 100644 sound/soc/sdca/sdca_regmap.c diff --git a/include/sound/sdca_regmap.h b/include/sound/sdca_regmap.h new file mode 100644 index 000000000000..11826f4f0726 --- /dev/null +++ b/include/sound/sdca_regmap.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + * + * Copyright (C) 2025 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef __SDCA_REGMAP_H__ +#define __SDCA_REGMAP_H__ + +struct sdca_function_data; + +bool sdca_regmap_readable(struct sdca_function_data *function, unsigned int reg); +bool sdca_regmap_writeable(struct sdca_function_data *function, unsigned int reg); +bool sdca_regmap_volatile(struct sdca_function_data *function, unsigned int reg); +bool sdca_regmap_deferrable(struct sdca_function_data *function, unsigned int reg); +int sdca_regmap_mbq_size(struct sdca_function_data *function, unsigned int reg); + +#endif // __SDCA_REGMAP_H__ diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index 5d1ddbbfbf62..dddc3e694256 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-sdca-y := sdca_functions.o sdca_device.o +snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c new file mode 100644 index 000000000000..ae6358f0fc26 --- /dev/null +++ b/sound/soc/sdca/sdca_regmap.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct sdca_entity * +function_find_entity(struct sdca_function_data *function, unsigned int reg) +{ + int i; + + for (i = 0; i < function->num_entities; i++) + if (SDW_SDCA_CTL_ENT(reg) == function->entities[i].id) + return &function->entities[i]; + + return NULL; +} + +static struct sdca_control * +entity_find_control(struct sdca_entity *entity, unsigned int reg) +{ + int i; + + for (i = 0; i < entity->num_controls; i++) { + if (SDW_SDCA_CTL_CSEL(reg) == entity->controls[i].sel) + return &entity->controls[i]; + } + + return NULL; +} + +static struct sdca_control * +function_find_control(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_entity *entity; + + entity = function_find_entity(function, reg); + if (!entity) + return NULL; + + return entity_find_control(entity, reg); +} + +/** + * sdca_regmap_readable - return if a given SDCA Control is readable + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is readable. + */ +bool sdca_regmap_readable(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + switch (control->mode) { + case SDCA_ACCESS_MODE_RW: + case SDCA_ACCESS_MODE_RO: + case SDCA_ACCESS_MODE_DUAL: + case SDCA_ACCESS_MODE_RW1S: + case SDCA_ACCESS_MODE_RW1C: + /* No access to registers marked solely for device use */ + return control->layers & ~SDCA_ACCESS_LAYER_DEVICE; + default: + return false; + } +} +EXPORT_SYMBOL_NS(sdca_regmap_readable, "SND_SOC_SDCA"); + +/** + * sdca_regmap_writeable - return if a given SDCA Control is writeable + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is writeable. + */ +bool sdca_regmap_writeable(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + switch (control->mode) { + case SDCA_ACCESS_MODE_RW: + case SDCA_ACCESS_MODE_DUAL: + case SDCA_ACCESS_MODE_RW1S: + case SDCA_ACCESS_MODE_RW1C: + /* No access to registers marked solely for device use */ + return control->layers & ~SDCA_ACCESS_LAYER_DEVICE; + default: + return false; + } +} +EXPORT_SYMBOL_NS(sdca_regmap_writeable, "SND_SOC_SDCA"); + +/** + * sdca_regmap_volatile - return if a given SDCA Control is volatile + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is volatile. + */ +bool sdca_regmap_volatile(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + switch (control->mode) { + case SDCA_ACCESS_MODE_RO: + case SDCA_ACCESS_MODE_RW1S: + case SDCA_ACCESS_MODE_RW1C: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS(sdca_regmap_volatile, "SND_SOC_SDCA"); + +/** + * sdca_regmap_deferrable - return if a given SDCA Control is deferrable + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is deferrable. + */ +bool sdca_regmap_deferrable(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + return control->deferrable; +} +EXPORT_SYMBOL_NS(sdca_regmap_deferrable, "SND_SOC_SDCA"); + +/** + * sdca_regmap_mbq_size - return size in bytes of a given SDCA Control + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns the size in bytes of the Control. + */ +int sdca_regmap_mbq_size(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return -EINVAL; + + control = function_find_control(function, reg); + if (!control) + return false; + + return clamp_val(control->nbits / BITS_PER_BYTE, sizeof(u8), sizeof(u32)); +} +EXPORT_SYMBOL_NS(sdca_regmap_mbq_size, "SND_SOC_SDCA"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SDCA library"); -- 2.51.0 From 28c12866c22c2826ccbd8c82dc353f02ab2deea5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 17 Feb 2025 14:01:58 +0000 Subject: [PATCH 03/16] ASoC: SDCA: Add regmap helpers for parsing for DisCo Constant values Add helpers to parse the DisCo Constant values from ACPI and populate an array of reg_defaults with these. This will allow drivers to access these ACPI specified values through the same interface as other registers that are physically present on the device, using the regmap cache. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250217140159.2288784-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_regmap.h | 6 +++ sound/soc/sdca/sdca_regmap.c | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/include/sound/sdca_regmap.h b/include/sound/sdca_regmap.h index 11826f4f0726..850533e83f3b 100644 --- a/include/sound/sdca_regmap.h +++ b/include/sound/sdca_regmap.h @@ -10,7 +10,9 @@ #ifndef __SDCA_REGMAP_H__ #define __SDCA_REGMAP_H__ +struct device; struct sdca_function_data; +struct reg_default; bool sdca_regmap_readable(struct sdca_function_data *function, unsigned int reg); bool sdca_regmap_writeable(struct sdca_function_data *function, unsigned int reg); @@ -18,4 +20,8 @@ bool sdca_regmap_volatile(struct sdca_function_data *function, unsigned int reg) bool sdca_regmap_deferrable(struct sdca_function_data *function, unsigned int reg); int sdca_regmap_mbq_size(struct sdca_function_data *function, unsigned int reg); +int sdca_regmap_count_constants(struct device *dev, struct sdca_function_data *function); +int sdca_regmap_populate_constants(struct device *dev, struct sdca_function_data *function, + struct reg_default *consts); + #endif // __SDCA_REGMAP_H__ diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c index ae6358f0fc26..dba4188620f9 100644 --- a/sound/soc/sdca/sdca_regmap.c +++ b/sound/soc/sdca/sdca_regmap.c @@ -10,8 +10,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -188,5 +190,83 @@ int sdca_regmap_mbq_size(struct sdca_function_data *function, unsigned int reg) } EXPORT_SYMBOL_NS(sdca_regmap_mbq_size, "SND_SOC_SDCA"); +/** + * sdca_regmap_count_constants - count the number of DisCo constant Controls + * @dev: Pointer to the device. + * @function: Pointer to the Function information, to be parsed. + * + * This function returns the number of DisCo constant Controls present + * in a function. Typically this information will be used to populate + * the regmap defaults array, allowing drivers to access the values of + * DisCo constants as any other physical register. + * + * Return: Returns number of DisCo constant controls, or a negative error + * code on failure. + */ +int sdca_regmap_count_constants(struct device *dev, + struct sdca_function_data *function) +{ + int nconsts = 0; + int i, j; + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC) + nconsts += hweight64(entity->controls[j].cn_list); + } + } + + return nconsts; +} +EXPORT_SYMBOL_NS(sdca_regmap_count_constants, "SND_SOC_SDCA"); + +/** + * sdca_regmap_populate_constants - fill an array with DisCo constant values + * @dev: Pointer to the device. + * @function: Pointer to the Function information, to be parsed. + * @consts: Pointer to the array which should be filled with the DisCo + * constant values. + * + * This function will populate a regmap struct reg_default array with + * the values of the DisCo constants for a given Function. This + * allows to access the values of DisCo constants the same as any + * other physical register. + * + * Return: Returns the number of constants populated on success, a negative + * error code on failure. + */ +int sdca_regmap_populate_constants(struct device *dev, + struct sdca_function_data *function, + struct reg_default *consts) +{ + int i, j, k; + + for (i = 0, k = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + struct sdca_control *control = &entity->controls[j]; + int cn; + + if (control->mode != SDCA_ACCESS_MODE_DC) + continue; + + for_each_set_bit(cn, (unsigned long *)&control->cn_list, + BITS_PER_TYPE(control->cn_list)) { + consts[k].reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, + control->sel, cn); + consts[k].def = control->value; + k++; + } + } + } + + return k; +} +EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA"); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SDCA library"); -- 2.51.0 From c143755d8cce31e770234732ff23134993b0550f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 17 Feb 2025 14:01:59 +0000 Subject: [PATCH 04/16] ASoC: SDCA: Add helper to write out defaults and fixed values The concept of an SDCA default value differs slightly from the regmap usage of the term. An SDCA default is a value that is parsed from DisCo and then written out to the hardware if no user value has superceded it. Add a helper function that will iterate through all the SDCA Controls and write out any default values. After these have been written out once they will exist in the cache and that will take care of any user values superceeding them. The code here also writes out any Controls with a fixed value as there is only one available value for these Controls there is no point in allowing the user to select them, simply treat them similarly to a default. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250217140159.2288784-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_regmap.h | 4 +++ sound/soc/sdca/sdca_regmap.c | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/sound/sdca_regmap.h b/include/sound/sdca_regmap.h index 850533e83f3b..b2e3c2ad2bb8 100644 --- a/include/sound/sdca_regmap.h +++ b/include/sound/sdca_regmap.h @@ -12,6 +12,7 @@ struct device; struct sdca_function_data; +struct regmap; struct reg_default; bool sdca_regmap_readable(struct sdca_function_data *function, unsigned int reg); @@ -24,4 +25,7 @@ int sdca_regmap_count_constants(struct device *dev, struct sdca_function_data *f int sdca_regmap_populate_constants(struct device *dev, struct sdca_function_data *function, struct reg_default *consts); +int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function); + #endif // __SDCA_REGMAP_H__ diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c index dba4188620f9..4b78188cfceb 100644 --- a/sound/soc/sdca/sdca_regmap.c +++ b/sound/soc/sdca/sdca_regmap.c @@ -268,5 +268,54 @@ int sdca_regmap_populate_constants(struct device *dev, } EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA"); +/** + * sdca_regmap_write_defaults - write out DisCo defaults to device + * @dev: Pointer to the device. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information, to be parsed. + * + * This function will write out to the hardware all the DisCo default and + * fixed value controls. This will cause them to be populated into the cache, + * and subsequent handling can be done through a cache sync. + * + * Return: Returns zero on success, and a negative error code on failure. + */ +int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function) +{ + int i, j; + int ret; + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + struct sdca_control *control = &entity->controls[j]; + int cn; + + if (control->mode == SDCA_ACCESS_MODE_DC) + continue; + + if (!control->has_default && !control->has_fixed) + continue; + + for_each_set_bit(cn, (unsigned long *)&control->cn_list, + BITS_PER_TYPE(control->cn_list)) { + unsigned int reg; + + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, + control->sel, cn); + + ret = regmap_write(regmap, reg, control->value); + if (ret) + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL_NS(sdca_regmap_write_defaults, "SND_SOC_SDCA"); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SDCA library"); -- 2.51.0 From 79ed408b2402e8113aa5a298f3bb9088ede58f6c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 27 Feb 2025 14:19:01 +0100 Subject: [PATCH 05/16] ASoC: mediatek: mt8188: avoid uninitialized variable use MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The 'msk' variable has no initialization: sound/soc/mediatek/mt8188/mt8188-dai-dmic.c:311:4: error: variable 'msk' is uninitialized when used here [-Werror,-Wuninitialized] 311 | msk |= PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(i); | ^~~ Set it to zero before the loop. Fixes: c1e42ec04197 ("ASoC: mediatek: mt8188: Add support for DMIC") Signed-off-by: Arnd Bergmann Reviewed-by: Nícolas F. R. A. Prado Link: https://patch.msgid.link/20250227131939.1040168-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8188/mt8188-dai-dmic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c index 4cfbcb71d2d9..adcea7818be2 100644 --- a/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c +++ b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c @@ -307,6 +307,7 @@ static int mtk_dmic_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: /* request fifo soft rst */ + msk = 0; for (i = dmic_num; i >= DMIC0; i--) msk |= PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(i); -- 2.51.0 From 0bd862846e7f89910252cbef8718a757950f1683 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Fri, 28 Feb 2025 11:58:07 +0530 Subject: [PATCH 06/16] ASoC: Intel: avs: use devm_kmemdup_array() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Convert to use devm_kmemdup_array() and while at it, use source size instead of destination. Signed-off-by: Raag Jadav Acked-by: Mark Brown Reviewed-by: Amadeusz Sławiński Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20250228062812.150004-2-raag.jadav@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/boards/da7219.c | 3 ++- sound/soc/intel/avs/boards/es8336.c | 3 ++- sound/soc/intel/avs/boards/nau8825.c | 3 ++- sound/soc/intel/avs/boards/rt274.c | 3 ++- sound/soc/intel/avs/boards/rt286.c | 3 ++- sound/soc/intel/avs/boards/rt298.c | 3 ++- sound/soc/intel/avs/boards/rt5663.c | 3 ++- sound/soc/intel/avs/boards/rt5682.c | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 76078a7005b0..9507a96f26ac 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -113,7 +113,8 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) } num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 426ce37105ae..6f3c4f6c9302 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -109,7 +109,8 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime) data = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index bf902540744c..6833eebd82d6 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -88,7 +88,8 @@ static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index 4b6c02a40204..f5caafc21861 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -98,7 +98,8 @@ static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index e40563ca99fd..1eb0399c0fae 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -59,7 +59,8 @@ static int avs_rt286_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index 94fce07c83f9..85269a3be981 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -70,7 +70,8 @@ static int avs_rt298_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c index b456b9d14665..e3310b3268ba 100644 --- a/sound/soc/intel/avs/boards/rt5663.c +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -65,7 +65,8 @@ static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime) jack = &priv->jack; num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 335960cfd7ba..339df0b944c1 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -102,7 +102,8 @@ static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_jack_pins); - pins = devm_kmemdup(card->dev, card_jack_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_jack_pins, num_pins, + sizeof(card_jack_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; -- 2.51.0 From 3e706be02befae55b50b240d4360b5993f9879a8 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Fri, 28 Feb 2025 11:58:08 +0530 Subject: [PATCH 07/16] ASoC: hdac_hdmi: use devm_kmemdup_array() Convert to use devm_kmemdup_array() and while at it, make the size robust against type changes. Signed-off-by: Raag Jadav Link: https://patch.msgid.link/20250228062812.150004-3-raag.jadav@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e1a7f0b0c0f3..3bea5d09219a 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1017,8 +1017,7 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev, return -ENOMEM; } - se->texts = devm_kmemdup(&hdev->dev, items, - (num_items * sizeof(char *)), GFP_KERNEL); + se->texts = devm_kmemdup_array(&hdev->dev, items, num_items, sizeof(items[0]), GFP_KERNEL); if (!se->texts) return -ENOMEM; -- 2.51.0 From 69aaab0e65e9bd7601740c1e14cc6de86dafb621 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Fri, 28 Feb 2025 11:58:09 +0530 Subject: [PATCH 08/16] ASoC: tlv320dac33: use devm_kmemdup_array() Convert to use devm_kmemdup_array() and while at it, make the size robust against type changes. Signed-off-by: Raag Jadav Link: https://patch.msgid.link/20250228062812.150004-4-raag.jadav@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index fa46f51d4341..423b9264a205 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1477,10 +1477,8 @@ static int dac33_i2c_probe(struct i2c_client *client) if (dac33 == NULL) return -ENOMEM; - dac33->reg_cache = devm_kmemdup(&client->dev, - dac33_reg, - ARRAY_SIZE(dac33_reg) * sizeof(u8), - GFP_KERNEL); + dac33->reg_cache = devm_kmemdup_array(&client->dev, dac33_reg, ARRAY_SIZE(dac33_reg), + sizeof(dac33_reg[0]), GFP_KERNEL); if (!dac33->reg_cache) return -ENOMEM; -- 2.51.0 From d9d71a6e2d19a2f3ccebea0092b8ddc1e935886f Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Fri, 28 Feb 2025 11:58:10 +0530 Subject: [PATCH 09/16] ASoC: uda1380: use devm_kmemdup_array() Convert to use devm_kmemdup_array() and while at it, make the size robust against type changes. Signed-off-by: Raag Jadav Link: https://patch.msgid.link/20250228062812.150004-5-raag.jadav@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/uda1380.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 4f8fdd574585..c179d865b938 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -766,10 +766,8 @@ static int uda1380_i2c_probe(struct i2c_client *i2c) return ret; } - uda1380->reg_cache = devm_kmemdup(&i2c->dev, - uda1380_reg, - ARRAY_SIZE(uda1380_reg) * sizeof(u16), - GFP_KERNEL); + uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg), + sizeof(uda1380_reg[0]), GFP_KERNEL); if (!uda1380->reg_cache) return -ENOMEM; -- 2.51.0 From b26205e172ca035e327e49edb0c2611e5d2ede8d Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Fri, 28 Feb 2025 11:58:11 +0530 Subject: [PATCH 10/16] ASoC: meson: axg-tdm-interface: use devm_kmemdup_array() Convert to use devm_kmemdup_array() which is more robust. Signed-off-by: Raag Jadav Link: https://patch.msgid.link/20250228062812.150004-6-raag.jadav@intel.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-interface.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 09103eef2a97..421b5d719fb3 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -529,7 +529,6 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct snd_soc_dai_driver *dai_drv; struct axg_tdm_iface *iface; - int i; iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL); if (!iface) @@ -541,15 +540,11 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) * We'll change the number of channel provided by DAI stream, so dpcm * channel merge can be done properly */ - dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv), - sizeof(*dai_drv), GFP_KERNEL); + dai_drv = devm_kmemdup_array(dev, axg_tdm_iface_dai_drv, ARRAY_SIZE(axg_tdm_iface_dai_drv), + sizeof(axg_tdm_iface_dai_drv[0]), GFP_KERNEL); if (!dai_drv) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++) - memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i], - sizeof(*dai_drv)); - /* Bit clock provided on the pad */ iface->sclk = devm_clk_get(dev, "sclk"); if (IS_ERR(iface->sclk)) -- 2.51.0 From c173b5ee81a25e8aafb21ccdb7ab457da7783bf1 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Fri, 28 Feb 2025 11:58:12 +0530 Subject: [PATCH 11/16] ASoC: uniphier: use devm_kmemdup_array() Convert to use devm_kmemdup_array() and while at it, make the size robust against type changes. Signed-off-by: Raag Jadav Link: https://patch.msgid.link/20250228062812.150004-7-raag.jadav@intel.com Signed-off-by: Mark Brown --- sound/soc/uniphier/aio-cpu.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 470f129166a4..3224c11a527f 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -762,14 +762,10 @@ int uniphier_aio_probe(struct platform_device *pdev) return -ENOMEM; chip->num_plls = chip->chip_spec->num_plls; - chip->plls = devm_kcalloc(dev, - chip->num_plls, - sizeof(struct uniphier_aio_pll), - GFP_KERNEL); + chip->plls = devm_kmemdup_array(dev, chip->chip_spec->plls, chip->num_plls, + sizeof(*chip->chip_spec->plls), GFP_KERNEL); if (!chip->plls) return -ENOMEM; - memcpy(chip->plls, chip->chip_spec->plls, - sizeof(struct uniphier_aio_pll) * chip->num_plls); for (i = 0; i < chip->num_aios; i++) { struct uniphier_aio *aio = &chip->aios[i]; -- 2.51.0 From feb849404a8b677aa6760d1539acf597e4574337 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 23 Feb 2025 21:25:45 +0100 Subject: [PATCH 12/16] ASoC: SOF: Intel: hda-dai: Remove unnecessary bool conversion Remove the unnecessary bool conversion and simplify the code. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250223202547.1795-2-thorsten.blum@linux.dev Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index da12aabc1bb8..883d0d3bae9e 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -318,7 +318,7 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: ret = hda_link_dma_cleanup(substream, hext_stream, dai, - cmd == SNDRV_PCM_TRIGGER_STOP ? false : true); + cmd != SNDRV_PCM_TRIGGER_STOP); if (ret < 0) { dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); return ret; -- 2.51.0 From a02c42d41af7d66db71ca43c52531c3253ebe35e Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Fri, 21 Feb 2025 03:21:41 +0000 Subject: [PATCH 13/16] ASoC: codecs: wsa883x: Implement temperature reading and hwmon Read temperature of the amplifier and expose it via hwmon interface, which will be later used during calibration of speaker protection algorithms. The method is the same as for wsa884x and therefore this is based on Krzysztof Kozlowski's approach implemented in commit 6b99dc62d940 ("ASoC: codecs: wsa884x: Implement temperature reading and hwmon"). Cc: Krzysztof Kozlowski Cc: Srinivas Kandagatla Cc: Steev Klimaszewski Signed-off-by: Alexey Klimov Tested-by: Steev Klimaszewski #Thinkpad X13s Link: https://patch.msgid.link/20250221032141.1206902-1-alexey.klimov@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 194 +++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 47da5674d7c9..a5a6cb90bb43 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -156,8 +157,28 @@ #define WSA883X_PA_FSM_ERR_COND (WSA883X_DIG_CTRL_BASE + 0x0014) #define WSA883X_PA_FSM_MSK (WSA883X_DIG_CTRL_BASE + 0x0015) #define WSA883X_PA_FSM_BYP (WSA883X_DIG_CTRL_BASE + 0x0016) +#define WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK 0x01 +#define WSA883X_PA_FSM_BYP_DC_CAL_EN_SHIFT 0 +#define WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK 0x02 +#define WSA883X_PA_FSM_BYP_CLK_WD_EN_SHIFT 1 +#define WSA883X_PA_FSM_BYP_BG_EN_MASK 0x04 +#define WSA883X_PA_FSM_BYP_BG_EN_SHIFT 2 +#define WSA883X_PA_FSM_BYP_BOOST_EN_MASK 0x08 +#define WSA883X_PA_FSM_BYP_BOOST_EN_SHIFT 3 +#define WSA883X_PA_FSM_BYP_PA_EN_MASK 0x10 +#define WSA883X_PA_FSM_BYP_PA_EN_SHIFT 4 +#define WSA883X_PA_FSM_BYP_D_UNMUTE_MASK 0x20 +#define WSA883X_PA_FSM_BYP_D_UNMUTE_SHIFT 5 +#define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK 0x40 +#define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_SHIFT 6 +#define WSA883X_PA_FSM_BYP_TSADC_EN_MASK 0x80 +#define WSA883X_PA_FSM_BYP_TSADC_EN_SHIFT 7 #define WSA883X_PA_FSM_DBG (WSA883X_DIG_CTRL_BASE + 0x0017) #define WSA883X_TADC_VALUE_CTL (WSA883X_DIG_CTRL_BASE + 0x0020) +#define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK 0x01 +#define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_SHIFT 0 +#define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_MASK 0x02 +#define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_SHIFT 1 #define WSA883X_TEMP_DETECT_CTL (WSA883X_DIG_CTRL_BASE + 0x0021) #define WSA883X_TEMP_MSB (WSA883X_DIG_CTRL_BASE + 0x0022) #define WSA883X_TEMP_LSB (WSA883X_DIG_CTRL_BASE + 0x0023) @@ -427,6 +448,17 @@ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) +/* Two-point trimming for temperature calibration */ +#define WSA883X_T1_TEMP -10L +#define WSA883X_T2_TEMP 150L + +/* + * Device will report senseless data in many cases, so discard any measurements + * outside of valid range. + */ +#define WSA883X_LOW_TEMP_THRESHOLD 5 +#define WSA883X_HIGH_TEMP_THRESHOLD 45 + struct wsa883x_priv { struct regmap *regmap; struct device *dev; @@ -441,6 +473,13 @@ struct wsa883x_priv { int active_ports; int dev_mode; int comp_offset; + /* + * Protects temperature reading code (related to speaker protection) and + * fields: temperature and pa_on. + */ + struct mutex sp_lock; + unsigned int temperature; + bool pa_on; }; enum { @@ -1186,6 +1225,10 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: + mutex_lock(&wsa883x->sp_lock); + wsa883x->pa_on = true; + mutex_unlock(&wsa883x->sp_lock); + switch (wsa883x->dev_mode) { case RECEIVER: snd_soc_component_write_field(component, WSA883X_CDC_PATH_MODE, @@ -1235,6 +1278,9 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, WSA883X_GLOBAL_PA_EN_MASK, 0); snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, WSA883X_PDM_EN_MASK, 0); + mutex_lock(&wsa883x->sp_lock); + wsa883x->pa_on = false; + mutex_unlock(&wsa883x->sp_lock); break; } return 0; @@ -1367,6 +1413,140 @@ static struct snd_soc_dai_driver wsa883x_dais[] = { }, }; +static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) +{ + unsigned int d1_msb = 0, d1_lsb = 0, d2_msb = 0, d2_lsb = 0; + unsigned int dmeas_msb = 0, dmeas_lsb = 0; + int d1, d2, dmeas; + unsigned int mask; + int ret, range; + long val; + + guard(mutex)(&wsa883x->sp_lock); + + if (wsa883x->pa_on) { + /* + * Reading temperature is possible only when Power Amplifier is + * off. Report last cached data. + */ + *temp = wsa883x->temperature * 1000; + return 0; + } + + ret = pm_runtime_resume_and_get(wsa883x->dev); + if (ret < 0) + return ret; + + mask = WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK | + WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK | + WSA883X_PA_FSM_BYP_BG_EN_MASK | + WSA883X_PA_FSM_BYP_D_UNMUTE_MASK | + WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK | + WSA883X_PA_FSM_BYP_TSADC_EN_MASK; + + /* + * Here and further do not care about read or update failures. + * For example, before turning the amplifier on for the first + * time, reading WSA883X_TEMP_DIN_MSB will always return 0. + * Instead, check if returned value is within reasonable + * thresholds. + */ + regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, mask); + + regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL, + WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, + FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x0)); + + regmap_read(wsa883x->regmap, WSA883X_TEMP_MSB, &dmeas_msb); + regmap_read(wsa883x->regmap, WSA883X_TEMP_LSB, &dmeas_lsb); + + regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL, + WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, + FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x1)); + + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_1, &d1_msb); + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_2, &d1_lsb); + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_3, &d2_msb); + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_4, &d2_lsb); + + regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, 0x0); + + dmeas = (((dmeas_msb & 0xff) << 0x8) | (dmeas_lsb & 0xff)) >> 0x6; + d1 = (((d1_msb & 0xff) << 0x8) | (d1_lsb & 0xff)) >> 0x6; + d2 = (((d2_msb & 0xff) << 0x8) | (d2_lsb & 0xff)) >> 0x6; + + if (d1 == d2) { + /* Incorrect data in OTP? */ + ret = -EINVAL; + goto out; + } + + val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1)); + range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD; + if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) { + wsa883x->temperature = val; + *temp = val * 1000; + ret = 0; + } else { + ret = -EAGAIN; + } +out: + pm_runtime_mark_last_busy(wsa883x->dev); + pm_runtime_put_autosuspend(wsa883x->dev); + + return ret; +} + +static umode_t wsa883x_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + break; + } + + return 0; +} + +static int wsa883x_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int ret; + + switch (attr) { + case hwmon_temp_input: + ret = wsa883x_get_temp(dev_get_drvdata(dev), temp); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static const struct hwmon_channel_info *const wsa883x_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops wsa883x_hwmon_ops = { + .is_visible = wsa883x_hwmon_is_visible, + .read = wsa883x_hwmon_read, +}; + +static const struct hwmon_chip_info wsa883x_hwmon_chip_info = { + .ops = &wsa883x_hwmon_ops, + .info = wsa883x_hwmon_info, +}; + static int wsa883x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { @@ -1402,6 +1582,7 @@ static int wsa883x_probe(struct sdw_slave *pdev, wsa883x->sconfig.bps = 1; wsa883x->sconfig.direction = SDW_DATA_DIR_RX; wsa883x->sconfig.type = SDW_STREAM_PDM; + mutex_init(&wsa883x->sp_lock); /** * Port map index starts with 0, however the data port for this codec @@ -1424,6 +1605,19 @@ static int wsa883x_probe(struct sdw_slave *pdev, "regmap_init failed\n"); goto err; } + + if (IS_REACHABLE(CONFIG_HWMON)) { + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, "wsa883x", + wsa883x, + &wsa883x_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon)) + return dev_err_probe(dev, PTR_ERR(hwmon), + "Failed to register hwmon sensor\n"); + } + pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); pm_runtime_mark_last_busy(dev); -- 2.51.0 From 79c080c75cdd0a5ba38be039f6f9bb66ec53b0c4 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Fri, 28 Feb 2025 11:32:19 +0100 Subject: [PATCH 14/16] ASoC: mediatek: mt6359: Fix DT parse error due to wrong child node name MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A recent dtbs_check error fix in mt6359.dtsi file changed a node name (from "mt6359codec" to "audio-codec") without modifying the mt6539 codec code that uses it. It leads to a probe failure after devicetree parsing returns in error: ``` [ 1.354025] mt6359-sound mt6359-sound: mt6359_platform_driver_probe() failed to parse dts [ 1.355066] mt6359-sound mt6359-sound: probe with driver mt6359-sound failed with error -22 ``` So, add the child node retrieval with the new name and if not found, try with the older one for backward compatibility. Fixes: 76b35f59bbe6 ("arm64: dts: mediatek: mt6359: fix dtbs_check error for audio-codec") Signed-off-by: Louis-Alexis Eyraud Reviewed-by: Nícolas F. R. A. Prado Link: https://patch.msgid.link/20250228-mt6359-fix-probe-failed-v1-1-64941d387b2c@collabora.com Signed-off-by: Mark Brown --- sound/soc/codecs/mt6359.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index 0b76a55664b0..f73120c6a6ce 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -2867,9 +2867,12 @@ static int mt6359_parse_dt(struct mt6359_priv *priv) struct device *dev = priv->dev; struct device_node *np; - np = of_get_child_by_name(dev->parent->of_node, "mt6359codec"); - if (!np) - return -EINVAL; + np = of_get_child_by_name(dev->parent->of_node, "audio-codec"); + if (!np) { + np = of_get_child_by_name(dev->parent->of_node, "mt6359codec"); + if (!np) + return -EINVAL; + } ret = of_property_read_u32(np, "mediatek,dmic-mode", &priv->dmic_one_wire_mode); -- 2.51.0 From 1d251a7adc5b720a71641c758a45b8a119971d80 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 4 Mar 2025 11:38:07 +0100 Subject: [PATCH 15/16] ASoC: dt-bindings: everest,es8328: Mark ES8388 compatible with ES8328 Based on Linux driver, the ES8388 looks fully compatible with ES8328. One upstream DTS (ARM rk3288-rock2-square.dts) already uses ES8328 fallback, so mark the devices as compatible in the binding. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250304103808.75236-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/everest,es8328.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/everest,es8328.yaml b/Documentation/devicetree/bindings/sound/everest,es8328.yaml index ed18e40dcaac..309c4d01db76 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8328.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8328.yaml @@ -24,9 +24,13 @@ maintainers: properties: compatible: - enum: - - everest,es8328 - - everest,es8388 + oneOf: + - enum: + - everest,es8328 + - items: + - enum: + - everest,es8388 + - const: everest,es8328 reg: maxItems: 1 -- 2.51.0 From 8243a49145e59f19032b86b20d8906f05e31bdcc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 4 Mar 2025 11:38:08 +0100 Subject: [PATCH 16/16] ASoC: dt-bindings: everest,es8328: Require reg property ES8328 and ES8388 codecs are I2C or SPI devices, thus they are addressable on their bus and 'reg' property should be always provided. Requiring 'reg' is pretty close to redundant, because the I2C and SPI controller/bus bindings require it already, but the convention is to mention 'reg' also in the device schemas. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250304103808.75236-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/everest,es8328.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/everest,es8328.yaml b/Documentation/devicetree/bindings/sound/everest,es8328.yaml index 309c4d01db76..ddddd7b143ab 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8328.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8328.yaml @@ -60,6 +60,7 @@ properties: required: - compatible + - reg - clocks - DVDD-supply - AVDD-supply -- 2.51.0