diff options
-rw-r--r-- | include/sound/sdca_asoc.h | 3 | ||||
-rw-r--r-- | include/sound/sdca_function.h | 8 | ||||
-rw-r--r-- | sound/soc/sdca/sdca_asoc.c | 75 |
3 files changed, 86 insertions, 0 deletions
diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index bbf146e4fcea..800a26adcd8e 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -48,5 +48,8 @@ int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, struct snd_soc_dai *dai); void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); +int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_soc_dai *dai); #endif // __SDCA_ASOC_H__ diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 3bde07409bf3..90d77fc46416 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -186,6 +186,14 @@ enum sdca_usage_range { }; /** + * enum sdca_dataport_selector_range - Column definitions for DataPort_Selector + */ +enum sdca_dataport_selector_range { + SDCA_DATAPORT_SELECTOR_NCOLS = 16, + SDCA_DATAPORT_SELECTOR_NROWS = 4, +}; + +/** * enum sdca_mu_controls - SDCA Controls for Mixer Unit * * Control Selectors for Mixer Unit from SDCA specification v1.0 diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 1a0149287584..03c663413cc9 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -19,6 +19,7 @@ #include <linux/regmap.h> #include <linux/soundwire/sdw_registers.h> #include <linux/string_helpers.h> +#include <linux/types.h> #include <sound/control.h> #include <sound/pcm.h> #include <sound/sdca.h> @@ -1368,3 +1369,77 @@ void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, kfree(constraint); } EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA"); + +/** + * sdca_asoc_get_port - return SoundWire port for a DAI + * @dev: Pointer to the device, used for error messages. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from hw_params(). + * + * Return: Returns a positive port number on success, and a negative error + * code on failure. + */ +int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_soc_dai *dai) +{ + struct sdca_entity *entity = &function->entities[dai->id]; + struct sdca_control_range *range; + unsigned int reg, val; + int sel = -EINVAL; + int i, ret; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + sel = SDCA_CTL_IT_DATAPORT_SELECTOR; + break; + case SDCA_ENTITY_TYPE_OT: + sel = SDCA_CTL_OT_DATAPORT_SELECTOR; + break; + default: + break; + } + + if (sel < 0 || !entity->iot.is_dataport) { + dev_err(dev, "%s: port number only available for dataports\n", + entity->label); + return -EINVAL; + } + + range = sdca_selector_find_range(dev, entity, sel, SDCA_DATAPORT_SELECTOR_NCOLS, + SDCA_DATAPORT_SELECTOR_NROWS); + if (!range) + return -EINVAL; + + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, sel, 0); + + ret = regmap_read(regmap, reg, &val); + if (ret) { + dev_err(dev, "%s: failed to read dataport selector: %d\n", + entity->label, ret); + return ret; + } + + for (i = 0; i < range->rows; i++) { + static const u8 port_mask = 0xF; + + sel = sdca_range(range, val & port_mask, i); + + /* + * FIXME: Currently only a single dataport is supported, so + * return the first one found, technically up to 4 dataports + * could be linked, but this is not yet supported. + */ + if (sel != 0xFF) + return sel; + + val >>= hweight8(port_mask); + } + + dev_err(dev, "%s: no dataport found\n", entity->label); + return -ENODEV; +} +EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA"); |