| // 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 <linux/bitops.h> | |
| #include <linux/minmax.h> | |
| #include <linux/module.h> | |
| #include <linux/regmap.h> | |
| #include <linux/soundwire/sdw_registers.h> | |
| #include <linux/types.h> | |
| #include <sound/sdca.h> | |
| #include <sound/sdca_function.h> | |
| #include <sound/sdca_regmap.h> | |
| 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; | |
| if (!(BIT(SDW_SDCA_CTL_CNUM(reg)) & control->cn_list)) | |
| return false; | |
| switch (control->mode) { | |
| case SDCA_ACCESS_MODE_RW: | |
| case SDCA_ACCESS_MODE_RO: | |
| case SDCA_ACCESS_MODE_RW1S: | |
| case SDCA_ACCESS_MODE_RW1C: | |
| if (SDW_SDCA_NEXT_CTL(0) & reg) | |
| return false; | |
| fallthrough; | |
| case SDCA_ACCESS_MODE_DUAL: | |
| /* 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; | |
| if (!(BIT(SDW_SDCA_CTL_CNUM(reg)) & control->cn_list)) | |
| return false; | |
| switch (control->mode) { | |
| case SDCA_ACCESS_MODE_RW: | |
| case SDCA_ACCESS_MODE_RW1S: | |
| case SDCA_ACCESS_MODE_RW1C: | |
| if (SDW_SDCA_NEXT_CTL(0) & reg) | |
| return false; | |
| fallthrough; | |
| case SDCA_ACCESS_MODE_DUAL: | |
| /* 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; | |
| return control->is_volatile; | |
| } | |
| 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 -EINVAL; | |
| 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 || | |
| entity->controls[j].has_reset) | |
| 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, l; | |
| 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 && | |
| !control->has_reset) | |
| continue; | |
| l = 0; | |
| 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); | |
| if (control->mode == SDCA_ACCESS_MODE_DC) | |
| consts[k].def = control->values[l]; | |
| else | |
| consts[k].def = control->reset; | |
| k++; | |
| l++; | |
| } | |
| } | |
| } | |
| return k; | |
| } | |
| EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA"); | |
| static int populate_control_defaults(struct device *dev, struct regmap *regmap, | |
| struct sdca_function_data *function, | |
| struct sdca_entity *entity, | |
| struct sdca_control *control) | |
| { | |
| int i, ret; | |
| int cn; | |
| if (control->mode == SDCA_ACCESS_MODE_DC) | |
| return 0; | |
| if (control->layers & SDCA_ACCESS_LAYER_DEVICE) | |
| return 0; | |
| i = 0; | |
| for_each_set_bit(cn, (unsigned long *)&control->cn_list, | |
| BITS_PER_TYPE(control->cn_list)) { | |
| unsigned int reg, val; | |
| reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, cn); | |
| if (control->has_default || control->has_fixed) { | |
| ret = regmap_write(regmap, reg, control->values[i]); | |
| if (ret) { | |
| dev_err(dev, "Failed to write default %#x: %d\n", | |
| reg, ret); | |
| return ret; | |
| } | |
| i++; | |
| } else if (!control->is_volatile) { | |
| if (control->has_reset) | |
| regcache_drop_region(regmap, reg, reg); | |
| ret = regmap_read(regmap, reg, &val); | |
| if (ret) { | |
| dev_err(dev, "Failed to read initial %#x: %d\n", | |
| reg, ret); | |
| return ret; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| /** | |
| * 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. It will also | |
| * read any non-volatile registers that don't have defaults/fixed values to | |
| * populate those into the cache, this ensures they are available for reads | |
| * even when the device is runtime suspended. | |
| * | |
| * 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]; | |
| ret = populate_control_defaults(dev, regmap, function, | |
| entity, control); | |
| if (ret) | |
| return ret; | |
| } | |
| } | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_NS(sdca_regmap_write_defaults, "SND_SOC_SDCA"); | |
| int sdca_regmap_write_init(struct device *dev, struct regmap *regmap, | |
| struct sdca_function_data *function) | |
| { | |
| struct sdca_init_write *init = function->init_table; | |
| int ret, i; | |
| for (i = 0; i < function->num_init_table; i++) { | |
| ret = regmap_write(regmap, init[i].addr, init[i].val); | |
| if (ret) | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| EXPORT_SYMBOL_NS(sdca_regmap_write_init, "SND_SOC_SDCA"); |