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"); diff --git a/include/sound/sdca_regmap.h b/include/sound/sdca_regmap.h new file mode 100644 index 000000000000..b2e3c2ad2bb8 --- /dev/null +++ b/include/sound/sdca_regmap.h @@ -0,0 +1,31 @@ +/* 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 device; +struct sdca_function_data; +struct regmap; +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); +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); + +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/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..4b78188cfceb --- /dev/null +++ b/sound/soc/sdca/sdca_regmap.c @@ -0,0 +1,321 @@ +// 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 +#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"); + +/** + * 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"); + +/** + * 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");