From 9f9ae6ef82a6a821ee1db464da3b63af0d0ba500 Mon Sep 17 00:00:00 2001 From: Dragos Tarcatu Date: Wed, 12 Jun 2019 10:17:57 +0300 Subject: [PATCH] ASoC: SOF: keep multiple configs for each dai If multiple hw_configs are currently being provided from the topology, the driver only keeps the default one and ignores the rest. This patch changes that by storing them all and also sending all of those to the firmware. Signed-off-by: Dragos Tarcatu --- sound/soc/sof/intel/hda-dai.c | 49 +++++----- sound/soc/sof/pcm.c | 18 ++-- sound/soc/sof/pm.c | 50 +++++----- sound/soc/sof/sof-priv.h | 6 +- sound/soc/sof/topology.c | 179 ++++++++++++++++++++++------------ 5 files changed, 187 insertions(+), 115 deletions(-) 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;