diff --git a/drivers/dai/intel/ssp/ssp.c b/drivers/dai/intel/ssp/ssp.c index 1c9488d3f0a5..45f796fdc4d3 100644 --- a/drivers/dai/intel/ssp/ssp.c +++ b/drivers/dai/intel/ssp/ssp.c @@ -1900,11 +1900,42 @@ static int dai_ssp_check_aux_data(struct ssp_intel_aux_tlv *aux_tlv, int aux_len return 0; } -static int dai_ssp_parse_aux_data(struct dai_intel_ssp *dp, const void *spec_config) +/** + * This function checks if the provided buffer contains valid DMA control + * TLV (Type-Length-Value) entries. It ensures that only specific types + * of DMA control settings are allowed to be modified at runtime. + */ +static int dai_ssp_check_dma_control(const uint8_t *aux_ptr, int aux_len) { - const struct dai_intel_ipc4_ssp_configuration_blob_ver_1_5 *blob = spec_config; - int aux_tlv_size = sizeof(struct ssp_intel_aux_tlv); - int hop, i, j, cfg_len, pre_aux_len, aux_len; + int hop; + struct ssp_intel_aux_tlv *aux_tlv; + + for (int i = 0; i < aux_len; i += hop) { + aux_tlv = (struct ssp_intel_aux_tlv *)(aux_ptr); + switch (aux_tlv->type) { + case SSP_DMA_CLK_CONTROLS: + case SSP_DMA_TRANSMISSION_START: + case SSP_DMA_TRANSMISSION_STOP: + case SSP_DMA_ALWAYS_RUNNING_MODE: + case SSP_DMA_SYNC_DATA: + case SSP_DMA_CLK_CONTROLS_EXT: + case SSP_LINK_CLK_SOURCE: + break; + default: + LOG_ERR("incorect config type %u", aux_tlv->type); + return -EINVAL; + } + + hop = aux_tlv->size + sizeof(struct ssp_intel_aux_tlv); + aux_ptr += hop; + } + + return 0; +} + +static int dai_ssp_parse_tlv(struct dai_intel_ssp *dp, const uint8_t *aux_ptr, size_t aux_len) +{ + int hop, i, j; struct ssp_intel_aux_tlv *aux_tlv; struct ssp_intel_mn_ctl *mn; struct ssp_intel_clk_ctl *clk; @@ -1916,15 +1947,6 @@ static int dai_ssp_parse_aux_data(struct dai_intel_ssp *dp, const void *spec_con #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE struct ssp_intel_link_ctl *link; #endif - uint8_t *aux_ptr; - - cfg_len = blob->size; - pre_aux_len = sizeof(*blob) + blob->i2s_mclk_control.mdivrcnt * sizeof(uint32_t); - aux_len = cfg_len - pre_aux_len; - aux_ptr = (uint8_t *)blob + pre_aux_len; - - if (aux_len <= 0) - return 0; for (i = 0; i < aux_len; i += hop) { aux_tlv = (struct ssp_intel_aux_tlv *)(aux_ptr); @@ -1989,13 +2011,30 @@ static int dai_ssp_parse_aux_data(struct dai_intel_ssp *dp, const void *spec_con return -EINVAL; } - hop = aux_tlv->size + aux_tlv_size; + hop = aux_tlv->size + sizeof(struct ssp_intel_aux_tlv); aux_ptr += hop; } return 0; } +static int dai_ssp_parse_aux_data(struct dai_intel_ssp *dp, const void *spec_config) +{ + const struct dai_intel_ipc4_ssp_configuration_blob_ver_1_5 *blob = spec_config; + int cfg_len, pre_aux_len, aux_len; + uint8_t *aux_ptr; + + cfg_len = blob->size; + pre_aux_len = sizeof(*blob) + blob->i2s_mclk_control.mdivrcnt * sizeof(uint32_t); + aux_len = cfg_len - pre_aux_len; + aux_ptr = (uint8_t *)blob + pre_aux_len; + + if (aux_len <= 0) + return 0; + + return dai_ssp_parse_tlv(dp, aux_ptr, aux_len); +} + static int dai_ssp_set_clock_control_ver_1_5(struct dai_intel_ssp *dp, const struct dai_intel_ipc4_ssp_mclk_config_2 *cc) { @@ -2579,6 +2618,30 @@ static int ssp_init(const struct device *dev) return pm_device_runtime_enable(dev); } +static int dai_ssp_dma_control_set(const struct device *dev, + const void *bespoke_cfg, + size_t size) +{ + struct dai_intel_ssp *dp = (struct dai_intel_ssp *)dev->data; + + LOG_INF("SSP%d: tlv addr = 0x%x, tlv size = %d", + dp->dai_index, (uint32_t)bespoke_cfg, size); + if (size < sizeof(struct ssp_intel_aux_tlv)) { + return -EINVAL; + } + + if (dp->state[DAI_DIR_PLAYBACK] != DAI_STATE_READY || + dp->state[DAI_DIR_CAPTURE] != DAI_STATE_READY) { + return -EIO; + } + + if (dai_ssp_check_dma_control(bespoke_cfg, size)) { + return -EINVAL; + } + + return dai_ssp_parse_tlv(dp, bespoke_cfg, size); +} + static struct dai_driver_api dai_intel_ssp_api_funcs = { .probe = pm_device_runtime_get, .remove = pm_device_runtime_put, @@ -2586,6 +2649,7 @@ static struct dai_driver_api dai_intel_ssp_api_funcs = { .config_get = dai_ssp_config_get, .trigger = dai_ssp_trigger, .get_properties = dai_ssp_get_properties, + .config_update = dai_ssp_dma_control_set, }; diff --git a/include/zephyr/drivers/dai.h b/include/zephyr/drivers/dai.h index fa78f3efb15e..fe22fffe1937 100644 --- a/include/zephyr/drivers/dai.h +++ b/include/zephyr/drivers/dai.h @@ -330,6 +330,8 @@ __subsystem struct dai_driver_api { int (*ts_stop)(const struct device *dev, struct dai_ts_cfg *cfg); int (*ts_get)(const struct device *dev, struct dai_ts_cfg *cfg, struct dai_ts_data *tsd); + int (*config_update)(const struct device *dev, const void *bespoke_cfg, + size_t size); }; /** @@ -540,6 +542,38 @@ static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg, return api->ts_get(dev, cfg, tsd); } +/** + * @brief Update DAI configuration at runtime. + * + * This function updates the configuration of a DAI interface at runtime. + * It allows setting bespoke configuration parameters that are specific to + * the DAI implementation, enabling updates outside of the regular flow with + * the full configuration blob. The details of the bespoke configuration are + * specific to each DAI implementation. This function should only be called + * when the DAI is in the READY state, ensuring that the configuration updates + * are applied before data transmission or reception begins. + * + * @param dev Pointer to the device structure for the driver instance. + * @param bespoke_cfg Pointer to the buffer containing bespoke configuration parameters. + * @param size Size of the bespoke_cfg buffer in bytes. + * + * @retval 0 If successful. + * @retval -ENOSYS If the configuration update operation is not implemented. + * @retval Negative errno code if failure. + */ +static inline int dai_config_update(const struct device *dev, + const void *bespoke_cfg, + size_t size) +{ + const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; + + if (!api->config_update) { + return -ENOSYS; + } + + return api->config_update(dev, bespoke_cfg, size); +} + /** * @} */