diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index a514f9cf5c9a1b..d9d354cf10e8c0 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -158,6 +158,7 @@ static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, struct snd_sof_dai *sof_dai; struct sof_ipc_reply reply; int ret = 0; + int i; list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) { if (!sof_dai->cpu_dai_name) @@ -165,30 +166,34 @@ static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, if (!strcmp(dai_name, sof_dai->cpu_dai_name) && dir == sof_dai->comp_dai.direction) { - config = sof_dai->dai_config; + for (i = 0; i < sof_dai->num_configs; i++) { + config = sof_dai->dai_config[i]; + + if (!config) { + dev_err(hda_stream->sdev->dev, + "error: no config for DAI %s\n", + sof_dai->name); + return -EINVAL; + } - if (!config) { - dev_err(hda_stream->sdev->dev, - "error: no config for DAI %s\n", - sof_dai->name); - return -EINVAL; + /* update config with stream tag */ + config->hda.link_dma_ch = channel; + + /* send IPC */ + ret = sof_ipc_tx_message(hda_stream->sdev->ipc, + config->hdr.cmd, + config, + config->hdr.size, + &reply, sizeof(reply)); + + if (ret < 0) { + dev_err(hda_stream->sdev->dev, + "error: failed to set dai config for %s\n", + sof_dai->name); + return ret; + } } - - /* update config with stream tag */ - config->hda.link_dma_ch = channel; - - /* send IPC */ - ret = sof_ipc_tx_message(hda_stream->sdev->ipc, - config->hdr.cmd, - config, - config->hdr.size, - &reply, sizeof(reply)); - - if (ret < 0) - dev_err(hda_stream->sdev->dev, - "error: failed to set dai config for %s\n", - sof_dai->name); - return ret; + return 0; } } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 334e9d59b1bafa..3088e870f7f0b1 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -608,6 +608,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_dai *dai = snd_sof_find_dai(sdev, (char *)rtd->dai_link->name); + int i; /* no topology exists for this BE, try a common configuration */ if (!dai) { @@ -646,12 +647,15 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, } /* read rate and channels from topology */ - switch (dai->dai_config->type) { + + i = dai->cur_config; + + switch (dai->dai_config[i]->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[i]->ssp.fsync_rate; + rate->max = dai->dai_config[i]->ssp.fsync_rate; + channels->min = dai->dai_config[i]->ssp.tdm_slots; + channels->max = dai->dai_config[i]->ssp.tdm_slots; dev_dbg(sdev->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); @@ -666,7 +670,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_err(sdev->dev, "error: invalid fmt %d for DAI type %d\n", dai->comp_dai.config.frame_fmt, - dai->dai_config->type); + dai->dai_config[i]->type); } break; case SOF_DAI_INTEL_HDA: @@ -674,7 +678,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, break; default: dev_err(sdev->dev, "error: invalid DAI type %d\n", - dai->dai_config->type); + dai->dai_config[i]->type); break; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8eeb3a1029f24d..e0ebf3e870ff53 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -145,34 +145,38 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) /* restore dai links */ list_for_each_entry_reverse(dai, &sdev->dai_list, list) { struct sof_ipc_reply reply; - struct sof_ipc_dai_config *config = dai->dai_config; + int i; - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", - dai->name); - continue; - } + for (i = 0; i < dai->num_configs; i++) { + struct sof_ipc_dai_config *config = dai->dai_config[i]; - /* - * The link DMA channel would be invalidated for running - * streams but not for streams that were in the PAUSED - * state during suspend. So invalidate it here before setting - * the dai config in the DSP. - */ - if (config->type == SOF_DAI_INTEL_HDA) - config->hda.link_dma_ch = DMA_CHAN_INVALID; + if (!config) { + dev_err(sdev->dev, "error: no config %d for DAI %s\n", + i, dai->name); + continue; + } - ret = sof_ipc_tx_message(sdev->ipc, - config->hdr.cmd, config, - config->hdr.size, - &reply, sizeof(reply)); + /* + * The link DMA channel would be invalidated for running + * streams but not for streams that were in the PAUSED + * state during suspend. So invalidate it here before + * setting the dai config in the DSP. + */ + if (config->type == SOF_DAI_INTEL_HDA) + config->hda.link_dma_ch = DMA_CHAN_INVALID; - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to set dai config for %s\n", - dai->name); + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, + config->hdr.size, + &reply, sizeof(reply)); - return ret; + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set dai config %d for %s\n", + i, dai->name); + + return ret; + } } } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e0441861512926..c5519e5650d689 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -62,6 +62,8 @@ #define DMA_CHAN_INVALID 0xFFFFFFFF +#define SOF_DAI_HW_CONFIG_MAX SND_SOC_TPLG_HW_CONFIG_MAX + struct snd_sof_dev; struct snd_sof_ipc_msg; struct snd_sof_ipc; @@ -346,7 +348,9 @@ struct snd_sof_dai { const char *cpu_dai_name; struct sof_ipc_comp_dai comp_dai; - struct sof_ipc_dai_config *dai_config; + struct sof_ipc_dai_config *dai_config[SOF_DAI_HW_CONFIG_MAX]; + int num_configs; + int cur_config; struct list_head list; /* list in sdev dai list */ }; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c9053ded8c5f50..7b3c9cc3ec6d01 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2140,8 +2140,9 @@ static int sof_widget_unload(struct snd_soc_component *scomp, dai = swidget->private; if (dai) { - /* free dai config */ - kfree(dai->dai_config); + /* free dai configs */ + for (i = 0; i < dai->num_configs; i++) + kfree(dai->dai_config[i]); list_del(&dai->list); } break; @@ -2343,26 +2344,42 @@ static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, } } -/* set config for all DAI's with name matching the link name */ -static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size, +/* add config for all DAI's with name matching the link name */ +static int sof_dai_add_config(struct snd_sof_dev *sdev, u32 size, struct snd_soc_dai_link *link, struct sof_ipc_dai_config *config) { struct snd_sof_dai *dai; int found = 0; + int ret = 0; + int i; list_for_each_entry(dai, &sdev->dai_list, list) { if (!dai->name) continue; if (strcmp(link->name, dai->name) == 0) { - dai->dai_config = kmemdup(config, size, GFP_KERNEL); - if (!dai->dai_config) - return -ENOMEM; + i = dai->num_configs; + + if (i >= SOF_DAI_HW_CONFIG_MAX) { + dev_err(sdev->dev, + "error: dai: %s no more hw_configs allowed\n", + link->name); + return -EINVAL; + } + + dai->dai_config[i] = kmemdup(config, size, GFP_KERNEL); + if (!dai->dai_config[i]) { + ret = -ENOMEM; + goto err; + } /* set cpu_dai_name */ dai->cpu_dai_name = link->cpu_dai_name; + dai->cur_config = i; + dai->num_configs++; + found = 1; } } @@ -2378,6 +2395,12 @@ static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size, } return 0; + +err: + for (i = 0; i < dai->num_configs; i++) + kfree(dai->dai_config[i]); + + return ret; } static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, @@ -2448,8 +2471,8 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, return ret; } - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); + /* add config for all DAI's with name matching the link name */ + ret = sof_dai_add_config(sdev, size, link, config); if (ret < 0) dev_err(sdev->dev, "error: failed to save DAI config for SSP%d\n", config->dai_index); @@ -2571,8 +2594,8 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, goto err; } - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, ipc_config); + /* add config for all DAI's with name matching the link name */ + ret = sof_dai_add_config(sdev, size, link, ipc_config); if (ret < 0) dev_err(sdev->dev, "error: failed to save DAI config for DMIC%d\n", config->dai_index); @@ -2598,6 +2621,7 @@ static int sof_link_hda_process(struct snd_sof_dev *sdev, struct snd_sof_dai *sof_dai; int found = 0; int ret; + int i; list_for_each_entry(sof_dai, &sdev->dai_list, list) { if (!sof_dai->name) @@ -2610,12 +2634,28 @@ static int sof_link_hda_process(struct snd_sof_dev *sdev, config->hda.link_dma_ch = DMA_CHAN_INVALID; /* save config in dai component */ - sof_dai->dai_config = kmemdup(config, size, GFP_KERNEL); - if (!sof_dai->dai_config) - return -ENOMEM; + i = sof_dai->num_configs; + + if (i >= SOF_DAI_HW_CONFIG_MAX) { + dev_err(sdev->dev, + "error: HDA dai: %d no more hw_configs allowed\n", + config->dai_index); + ret = -EINVAL; + goto err; + } + + sof_dai->dai_config[i] = kmemdup(config, + size, GFP_KERNEL); + if (!sof_dai->dai_config[i]) { + ret = -ENOMEM; + goto err; + } sof_dai->cpu_dai_name = link->cpu_dai_name; + sof_dai->num_configs++; + sof_dai->cur_config = i; + /* send message to DSP */ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, size, @@ -2626,7 +2666,7 @@ static int sof_link_hda_process(struct snd_sof_dev *sdev, sof_dai->comp_dai.direction, config->dai_index); - return ret; + goto err; } } } @@ -2642,6 +2682,12 @@ static int sof_link_hda_process(struct snd_sof_dev *sdev, } return 0; + +err: + for (i = 0; i < sof_dai->num_configs; i++) + kfree(sof_dai->dai_config[i]); + + return ret; } static int sof_link_hda_load(struct snd_soc_component *scomp, int index, @@ -2749,45 +2795,51 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, 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) + /* configure dai IPC message */ + hw_config = &cfg->hw_config[i]; + + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | + SOF_IPC_DAI_CONFIG; + config.format = le32_to_cpu(hw_config->fmt); + + /* + * TODO: if this is the default configuration, + * let the firmware know about it + */ + + /* + * now load DAI specific data and send IPC - + * type comes from token + */ + switch (config.type) { + case SOF_DAI_INTEL_SSP: + ret = sof_link_ssp_load(scomp, index, link, + cfg, hw_config, + &config); break; - } + case SOF_DAI_INTEL_DMIC: + ret = sof_link_dmic_load(scomp, index, link, + cfg, hw_config, + &config); + break; + case SOF_DAI_INTEL_HDA: + ret = sof_link_hda_load(scomp, index, link, + cfg, hw_config, + &config); + break; + default: + dev_err(sdev->dev, + "error: invalid DAI type %d\n", + config.type); + ret = -EINVAL; + break; + } - if (i == num_hw_configs) { - dev_err(sdev->dev, "error: default hw_config id: %d not found!\n", - le32_to_cpu(cfg->default_hw_config_id)); - return -EINVAL; + if (ret < 0) + return ret; } } - /* configure dai IPC message */ - hw_config = &cfg->hw_config[i]; - - config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; - config.format = le32_to_cpu(hw_config->fmt); - - /* now load DAI specific data and send IPC - type comes from token */ - switch (config.type) { - case SOF_DAI_INTEL_SSP: - ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, - &config); - break; - case SOF_DAI_INTEL_DMIC: - ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config, - &config); - break; - case SOF_DAI_INTEL_HDA: - ret = sof_link_hda_load(scomp, index, link, cfg, hw_config, - &config); - break; - default: - dev_err(sdev->dev, "error: invalid DAI type %d\n", config.type); - ret = -EINVAL; - break; - } - if (ret < 0) - return ret; - return 0; } @@ -2819,6 +2871,7 @@ static int sof_link_unload(struct snd_soc_component *scomp, struct snd_sof_dai *sof_dai; int ret = 0; + int i; /* only BE link is loaded by sof */ if (!link->no_pcm) @@ -2837,19 +2890,21 @@ static int sof_link_unload(struct snd_soc_component *scomp, return -EINVAL; found: - switch (sof_dai->dai_config->type) { - case SOF_DAI_INTEL_SSP: - case SOF_DAI_INTEL_DMIC: - /* no resource needs to be released for SSP and DMIC */ - break; - case SOF_DAI_INTEL_HDA: - ret = sof_link_hda_unload(sdev, link); - break; - default: - dev_err(sdev->dev, "error: invalid DAI type %d\n", - sof_dai->dai_config->type); - ret = -EINVAL; - break; + for (i = 0; i < sof_dai->num_configs; i++) { + switch (sof_dai->dai_config[i]->type) { + case SOF_DAI_INTEL_SSP: + case SOF_DAI_INTEL_DMIC: + /* no resource needs to be released for SSP and DMIC */ + break; + case SOF_DAI_INTEL_HDA: + ret = sof_link_hda_unload(sdev, link); + break; + default: + dev_err(sdev->dev, "error: config: %d invalid DAI type %d\n", + i, sof_dai->dai_config[i]->type); + ret = -EINVAL; + break; + } } return ret;