Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

intel_adsp: ssp: Add DMA Control Configuration API to Intel SSP DAI Driver #73158

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 78 additions & 14 deletions drivers/dai/intel/ssp/ssp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -2579,13 +2618,38 @@ 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,
.config_set = dai_ssp_config_set,
.config_get = dai_ssp_config_get,
.trigger = dai_ssp_trigger,
.get_properties = dai_ssp_get_properties,
.config_update = dai_ssp_dma_control_set,
};


Expand Down
34 changes: 34 additions & 0 deletions include/zephyr/drivers/dai.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

/**
Expand Down Expand Up @@ -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);
}

/**
* @}
*/
Expand Down
Loading