@@ -673,10 +673,10 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
/* read rate and channels from topology */
switch (dai->dai_config->type) {
case SOF_DAI_INTEL_SSP:
- rate->min = dai->dai_config->ssp.fsync_rate;
- rate->max = dai->dai_config->ssp.fsync_rate;
- channels->min = dai->dai_config->ssp.tdm_slots;
- channels->max = dai->dai_config->ssp.tdm_slots;
+ rate->min = dai->dai_config[dai->current_config].ssp.fsync_rate;
+ rate->max = dai->dai_config[dai->current_config].ssp.fsync_rate;
+ channels->min = dai->dai_config[dai->current_config].ssp.tdm_slots;
+ channels->max = dai->dai_config[dai->current_config].ssp.tdm_slots;
dev_dbg(component->dev,
"rate_min: %d rate_max: %d\n", rate->min, rate->max);
@@ -112,6 +112,8 @@ struct snd_sof_dai {
const char *cpu_dai_name;
struct sof_ipc_comp_dai comp_dai;
+ int number_configs;
+ int current_config;
struct sof_ipc_dai_config *dai_config;
struct list_head list; /* list in sdev dai list */
};
@@ -2811,12 +2811,14 @@ static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config,
* name. Note that the function can only be used for the case that all DAIs
* have a common DAI config for now.
*/
-static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
- struct snd_soc_dai_link *link,
- struct sof_ipc_dai_config *config)
+static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
+ struct snd_soc_dai_link *link,
+ struct sof_ipc_dai_config *config,
+ int num_conf, int curr_conf)
{
struct snd_sof_dai *dai;
int found = 0;
+ int i;
list_for_each_entry(dai, &sdev->dai_list, list) {
if (!dai->name)
@@ -2832,19 +2834,27 @@ static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
* dai config's dai_index match to the component's
* dai_index.
*/
- config->dai_index = dai->comp_dai.dai_index;
+ for (i = 0; i < num_conf; i++)
+ config[i].dai_index = dai->comp_dai.dai_index;
+ dev_dbg(sdev->dev, "set DAI config for %s index %d\n",
+ dai->name, config[curr_conf].dai_index);
/* send message to DSP */
ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config, size,
+ config[curr_conf].hdr.cmd,
+ &config[curr_conf], size,
&reply, sizeof(reply));
if (ret < 0) {
- dev_err(sdev->dev, "error: failed to set DAI config for %s index %d\n",
- dai->name, config->dai_index);
+ dev_err(sdev->dev,
+ "error: failed to set DAI config for %s index %d\n",
+ dai->name, config[curr_conf].dai_index);
return ret;
}
- dai->dai_config = kmemdup(config, size, GFP_KERNEL);
+
+ dai->number_configs = num_conf;
+ dai->current_config = curr_conf;
+ dai->dai_config = kmemdup(config, size * num_conf, GFP_KERNEL);
if (!dai->dai_config)
return -ENOMEM;
@@ -2868,64 +2878,81 @@ static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
return 0;
}
+static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
+ struct snd_soc_dai_link *link,
+ struct sof_ipc_dai_config *config)
+{
+ return sof_set_dai_config_multi(sdev, size, link, config, 1, 0);
+}
+
static int sof_link_ssp_load(struct snd_soc_component *scomp, int index,
struct snd_soc_dai_link *link,
struct snd_soc_tplg_link_config *cfg,
struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
+ struct sof_ipc_dai_config *config, int curr_conf)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &cfg->priv;
+ int num_conf = le32_to_cpu(cfg->num_hw_configs);
u32 size = sizeof(*config);
int ret;
+ int i;
- /* handle master/slave and inverted clocks */
- sof_dai_set_format(hw_config, config);
-
- /* init IPC */
- memset(&config->ssp, 0, sizeof(struct sof_ipc_dai_ssp_params));
- config->hdr.size = size;
+ /*
+ * Parse common data, we should have 1 common data per hw_config.
+ */
+ ret = sof_parse_token_sets(scomp, &config->ssp, ssp_tokens,
+ ARRAY_SIZE(ssp_tokens), private->array,
+ le32_to_cpu(private->size),
+ num_conf, size);
- ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens,
- ARRAY_SIZE(ssp_tokens), private->array,
- le32_to_cpu(private->size));
if (ret != 0) {
dev_err(scomp->dev, "error: parse ssp tokens failed %d\n",
le32_to_cpu(private->size));
return ret;
}
- config->ssp.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
- config->ssp.bclk_rate = le32_to_cpu(hw_config->bclk_rate);
- config->ssp.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
- config->ssp.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
- config->ssp.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
- config->ssp.mclk_direction = hw_config->mclk_direction;
- config->ssp.rx_slots = le32_to_cpu(hw_config->rx_slots);
- config->ssp.tx_slots = le32_to_cpu(hw_config->tx_slots);
+ /* process all possible hw configs */
+ for (i = 0; i < num_conf; i++) {
- dev_dbg(scomp->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n",
- config->dai_index, config->format,
- config->ssp.mclk_rate, config->ssp.bclk_rate,
- config->ssp.fsync_rate, config->ssp.sample_valid_bits,
- config->ssp.tdm_slot_width, config->ssp.tdm_slots,
- config->ssp.mclk_id, config->ssp.quirks);
-
- /* validate SSP fsync rate and channel count */
- if (config->ssp.fsync_rate < 8000 || config->ssp.fsync_rate > 192000) {
- dev_err(scomp->dev, "error: invalid fsync rate for SSP%d\n",
- config->dai_index);
- return -EINVAL;
- }
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(&hw_config[i], &config[i]);
- if (config->ssp.tdm_slots < 1 || config->ssp.tdm_slots > 8) {
- dev_err(scomp->dev, "error: invalid channel count for SSP%d\n",
- config->dai_index);
- return -EINVAL;
+ config[i].hdr.size = size;
+
+ /* copy differentiating hw configs to ipc structs */
+ config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate);
+ config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate);
+ config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate);
+ config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots);
+ config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width);
+ config[i].ssp.mclk_direction = hw_config[i].mclk_direction;
+ config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots);
+ config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots);
+
+ dev_dbg(scomp->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n",
+ config[i].dai_index, config[i].format,
+ config[i].ssp.mclk_rate, config[i].ssp.bclk_rate,
+ config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits,
+ config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots,
+ config[i].ssp.mclk_id, config[i].ssp.quirks);
+
+ /* validate SSP fsync rate and channel count */
+ if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) {
+ dev_err(scomp->dev, "error: invalid fsync rate for SSP%d\n",
+ config[i].dai_index);
+ return -EINVAL;
+ }
+
+ if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) {
+ dev_err(scomp->dev, "error: invalid channel count for SSP%d\n",
+ config[i].dai_index);
+ return -EINVAL;
+ }
}
/* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
+ ret = sof_set_dai_config_multi(sdev, size, link, config, num_conf, curr_conf);
if (ret < 0)
dev_err(scomp->dev, "error: failed to save DAI config for SSP%d\n",
config->dai_index);
@@ -3216,11 +3243,13 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_link_config *cfg)
{
struct snd_soc_tplg_private *private = &cfg->priv;
- struct sof_ipc_dai_config config;
struct snd_soc_tplg_hw_config *hw_config;
- int num_hw_configs;
+ struct sof_ipc_dai_config common_config;
+ struct sof_ipc_dai_config *config;
+ int curr_conf;
+ int num_conf;
int ret;
- int i = 0;
+ int i;
if (!link->platforms) {
dev_err(scomp->dev, "error: no platforms\n");
@@ -3257,13 +3286,11 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- /* Send BE DAI link configurations to DSP */
- memset(&config, 0, sizeof(config));
+ memset(&common_config, 0, sizeof(common_config));
/* get any common DAI tokens */
- ret = sof_parse_tokens(scomp, &config, dai_link_tokens,
- ARRAY_SIZE(dai_link_tokens), private->array,
- le32_to_cpu(private->size));
+ ret = sof_parse_tokens(scomp, &common_config, dai_link_tokens, ARRAY_SIZE(dai_link_tokens),
+ private->array, le32_to_cpu(private->size));
if (ret != 0) {
dev_err(scomp->dev, "error: parse link tokens failed %d\n",
le32_to_cpu(private->size));
@@ -3274,71 +3301,74 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
* DAI links are expected to have at least 1 hw_config.
* But some older topologies might have no hw_config for HDA dai links.
*/
- num_hw_configs = le32_to_cpu(cfg->num_hw_configs);
- if (!num_hw_configs) {
- if (config.type != SOF_DAI_INTEL_HDA) {
+ hw_config = cfg->hw_config;
+ num_conf = le32_to_cpu(cfg->num_hw_configs);
+ if (!num_conf) {
+ if (common_config.type != SOF_DAI_INTEL_HDA) {
dev_err(scomp->dev, "error: unexpected DAI config count %d!\n",
le32_to_cpu(cfg->num_hw_configs));
return -EINVAL;
}
+ num_conf = 1;
+ curr_conf = 0;
} else {
dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d!\n",
cfg->num_hw_configs, le32_to_cpu(cfg->default_hw_config_id));
- for (i = 0; i < num_hw_configs; i++) {
- if (cfg->hw_config[i].id == cfg->default_hw_config_id)
+ for (curr_conf = 0; curr_conf < num_conf; curr_conf++) {
+ if (hw_config[curr_conf].id == cfg->default_hw_config_id)
break;
}
- if (i == num_hw_configs) {
+ if (curr_conf == num_conf) {
dev_err(scomp->dev, "error: default hw_config id: %d not found!\n",
le32_to_cpu(cfg->default_hw_config_id));
return -EINVAL;
}
}
- /* configure dai IPC message */
- hw_config = &cfg->hw_config[i];
+ /* Reserve memory for all hw configs, eventually freed by widget */
+ config = kcalloc(num_conf, sizeof(*config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
- config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config.format = le32_to_cpu(hw_config->fmt);
+ /* Copy common data to all config ipc structs */
+ for (i = 0; i < num_conf; i++) {
+ config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
+ config[i].format = hw_config[i].fmt;
+ config[i].type = common_config.type;
+ config[i].dai_index = common_config.dai_index;
+ }
/* now load DAI specific data and send IPC - type comes from token */
- switch (config.type) {
+ switch (common_config.type) {
case SOF_DAI_INTEL_SSP:
- ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config,
- &config);
+ ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, config, curr_conf);
break;
case SOF_DAI_INTEL_DMIC:
- ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config,
- &config);
+ ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, config);
break;
case SOF_DAI_INTEL_HDA:
- ret = sof_link_hda_load(scomp, index, link, cfg, hw_config,
- &config);
+ ret = sof_link_hda_load(scomp, index, link, cfg, hw_config + curr_conf, config);
break;
case SOF_DAI_INTEL_ALH:
- ret = sof_link_alh_load(scomp, index, link, cfg, hw_config,
- &config);
+ ret = sof_link_alh_load(scomp, index, link, cfg, hw_config + curr_conf, config);
break;
case SOF_DAI_IMX_SAI:
- ret = sof_link_sai_load(scomp, index, link, cfg, hw_config,
- &config);
+ ret = sof_link_sai_load(scomp, index, link, cfg, hw_config + curr_conf, config);
break;
case SOF_DAI_IMX_ESAI:
- ret = sof_link_esai_load(scomp, index, link, cfg, hw_config,
- &config);
+ ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config);
break;
default:
- dev_err(scomp->dev, "error: invalid DAI type %d\n",
- config.type);
+ dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type);
ret = -EINVAL;
break;
}
- if (ret < 0)
- return ret;
- return 0;
+ kfree(config);
+
+ return ret;
}
static int sof_link_hda_unload(struct snd_sof_dev *sdev,