diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 6592327469b344..0d737079782c98 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1445,25 +1445,49 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai, struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dma_data *dma; - dma = kzalloc(sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; + if (stream) { + /* first paranoia check */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dma = dai->playback_dma_data; + else + dma = dai->capture_dma_data; + + if (dma) { + dev_err(dai->dev, + "dma_data already allocated for dai %s\n", + dai->name); + return -EINVAL; + } - if (pcm) - dma->stream_type = SDW_STREAM_PCM; - else - dma->stream_type = SDW_STREAM_PDM; + /* allocate and set dma info */ + dma = kzalloc(sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; - dma->bus = &cdns->bus; - dma->link_id = cdns->instance; + if (pcm) + dma->stream_type = SDW_STREAM_PCM; + else + dma->stream_type = SDW_STREAM_PDM; - dma->stream = stream; + dma->bus = &cdns->bus; + dma->link_id = cdns->instance; - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = dma; - else - dai->capture_dma_data = dma; + dma->stream = stream; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = dma; + else + dai->capture_dma_data = dma; + } else { + /* for NULL stream we release allocated dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + kfree(dai->playback_dma_data); + dai->playback_dma_data = NULL; + } else { + kfree(dai->capture_dma_data); + dai->capture_dma_data = NULL; + } + } return 0; } EXPORT_SYMBOL(cdns_set_sdw_stream); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 6be92badcb5af8..fdec62b912d3b1 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -163,20 +163,12 @@ int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset); void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); #endif -int sdw_cdns_get_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - u32 ch, u32 dir); struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir, int dai_id); void sdw_cdns_config_stream(struct sdw_cdns *cdns, u32 ch, u32 dir, struct sdw_cdns_pdi *pdi); -int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai, - void *stream, int direction); -int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai, - void *stream, int direction); - enum sdw_command_response cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 5154e89525c5a3..2a78356418cb01 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -869,57 +869,6 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * DAI routines */ -static int sdw_stream_setup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct sdw_stream_runtime *sdw_stream = NULL; - char *name; - int i, ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - name = kasprintf(GFP_KERNEL, "%s-Playback", dai->name); - else - name = kasprintf(GFP_KERNEL, "%s-Capture", dai->name); - - if (!name) - return -ENOMEM; - - sdw_stream = sdw_alloc_stream(name); - if (!sdw_stream) { - dev_err(dai->dev, "alloc stream failed for DAI %s", dai->name); - ret = -ENOMEM; - goto error; - } - - /* Set stream pointer on CPU DAI */ - ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); - if (ret < 0) { - dev_err(dai->dev, "failed to set stream pointer on cpu dai %s", - dai->name); - goto release_stream; - } - - /* Set stream pointer on all CODEC DAIs */ - for (i = 0; i < rtd->num_codecs; i++) { - ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sdw_stream, - substream->stream); - if (ret < 0) { - dev_err(dai->dev, "failed to set stream pointer on codec dai %s", - rtd->codec_dais[i]->name); - goto release_stream; - } - } - - return 0; - -release_stream: - sdw_release_stream(sdw_stream); -error: - kfree(name); - return ret; -} - static int intel_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -932,9 +881,9 @@ static int intel_startup(struct snd_pcm_substream *substream, "pm_runtime_get_sync failed in %s, ret %d\n", __func__, ret); pm_runtime_put_noidle(cdns->dev); + return ret; } - - return sdw_stream_setup(substream, dai); + return 0; } static int intel_hw_params(struct snd_pcm_substream *substream, @@ -1147,8 +1096,6 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) dma->hw_params = NULL; dma->pdi = NULL; - kfree(dma->stream->name); - sdw_release_stream(dma->stream); return 0; } @@ -1156,16 +1103,8 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) static void intel_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct sdw_cdns_dma_data *dma; struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); - dma = snd_soc_dai_get_dma_data(dai, substream); - if (!dma) - return; - - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(dma); - pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); } @@ -1206,6 +1145,22 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, return cdns_set_sdw_stream(dai, stream, false, direction); } +static void *intel_get_sdw_stream(struct snd_soc_dai *dai, + int direction) +{ + struct sdw_cdns_dma_data *dma; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dma = dai->playback_dma_data; + else + dma = dai->capture_dma_data; + + if (!dma) + return NULL; + + return dma->stream; +} + static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .startup = intel_startup, .hw_params = intel_hw_params, @@ -1214,6 +1169,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, + .get_sdw_stream = intel_get_sdw_stream, }; static const struct snd_soc_dai_ops intel_pdm_dai_ops = { @@ -1224,6 +1180,7 @@ static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, + .get_sdw_stream = intel_get_sdw_stream, }; static const struct snd_soc_component_driver dai_component = { diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e0e8e2985d7192..ce2c105c0ab4da 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include "bus.h" /* @@ -1578,6 +1581,7 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); if (stream->state == SDW_STREAM_PREPARED) { + /* nothing to do */ ret = 0; goto state_err; } @@ -1667,6 +1671,12 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_ENABLED) { + /* nothing to do */ + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { pr_err("%s: %s: inconsistent state state %d\n", @@ -1750,6 +1760,12 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_DISABLED) { + /* nothing to do */ + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_ENABLED) { pr_err("%s: %s: inconsistent state state %d\n", __func__, stream->name, stream->state); @@ -1825,6 +1841,12 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_DEPREPARED) { + /* nothing to do */ + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { pr_err("%s: %s: inconsistent state state %d\n", @@ -1840,3 +1862,102 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) return ret; } EXPORT_SYMBOL(sdw_deprepare_stream); + +static int set_stream(struct snd_pcm_substream *substream, + struct sdw_stream_runtime *sdw_stream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai; + int ret = 0; + int i; + + /* Set stream pointer on all CPU DAIs */ + for (i = 0; i < rtd->num_cpus; i++) { + dai = rtd->cpu_dais[i]; + ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, + substream->stream); + if (ret < 0) { + dev_err(rtd->dev, "failed to set stream pointer on cpu dai %s", + dai->name); + goto err_stream; + } + } + + /* Set stream pointer on all CODEC DAIs */ + for (i = 0; i < rtd->num_codecs; i++) { + dai = rtd->codec_dais[i]; + ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, + substream->stream); + if (ret < 0) { + dev_err(dai->dev, "failed to set stream pointer on codec dai %s", + dai->name); + goto err_stream; + } + } + +err_stream: + return ret; +} + +int sdw_startup_stream(void *sdw_substream) +{ + struct snd_pcm_substream *substream = sdw_substream; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream; + char *name; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + name = kasprintf(GFP_KERNEL, "%s-Playback", substream->name); + else + name = kasprintf(GFP_KERNEL, "%s-Capture", substream->name); + + if (!name) + return -ENOMEM; + + sdw_stream = sdw_alloc_stream(name); + if (!sdw_stream) { + dev_err(rtd->dev, "alloc stream failed for substream DAI %s", + substream->name); + ret = -ENOMEM; + goto error; + } + + ret = set_stream(substream, sdw_stream); + if (ret < 0) + goto release_stream; + return 0; + +release_stream: + sdw_release_stream(sdw_stream); + set_stream(substream, NULL); +error: + kfree(name); + return ret; +} +EXPORT_SYMBOL(sdw_startup_stream); + +void sdw_shutdown_stream(void *sdw_substream) +{ + struct snd_pcm_substream *substream = sdw_substream; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream; + struct snd_soc_dai *dai; + + /* Find stream from first CPU DAI */ + dai = rtd->cpu_dais[0]; + sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + + if (!sdw_stream) { + dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + return; + } + + /* release memory */ + kfree(sdw_stream->name); + sdw_release_stream(sdw_stream); + + /* clear DAI data */ + set_stream(substream, NULL); +} +EXPORT_SYMBOL(sdw_shutdown_stream); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index ead36ef08c4cf1..da5ff7a23c33af 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -941,10 +941,12 @@ int sdw_stream_remove_master(struct sdw_bus *bus, struct sdw_stream_runtime *stream); int sdw_stream_remove_slave(struct sdw_slave *slave, struct sdw_stream_runtime *stream); +int sdw_startup_stream(void *sdw_substream); int sdw_prepare_stream(struct sdw_stream_runtime *stream); int sdw_enable_stream(struct sdw_stream_runtime *stream); int sdw_disable_stream(struct sdw_stream_runtime *stream); int sdw_deprepare_stream(struct sdw_stream_runtime *stream); +void sdw_shutdown_stream(void *sdw_substream); int sdw_bus_prep_clk_stop(struct sdw_bus *bus); int sdw_bus_clk_stop(struct sdw_bus *bus); int sdw_bus_exit_clk_stop(struct sdw_bus *bus); diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 7481e468be3916..41f6668d282de1 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -202,6 +202,8 @@ struct snd_soc_dai_ops { int (*set_sdw_stream)(struct snd_soc_dai *dai, void *stream, int direction); + void *(*get_sdw_stream)(struct snd_soc_dai *dai, int direction); + /* * DAI digital mute - optional. * Called by soc-core to minimise any pops. @@ -408,4 +410,23 @@ static inline int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai, return -ENOTSUPP; } +/** + * snd_soc_dai_get_sdw_stream() - Retrieves SDW stream from DAI + * @dai: DAI + * @direction: Stream direction(Playback/Capture) + * + * This routine only retrieves that was previously configured + * with snd_soc_dai_get_sdw_stream() + * + * Returns pointer to stream or NULL; + */ +static inline void *snd_soc_dai_get_sdw_stream(struct snd_soc_dai *dai, + int direction) +{ + if (dai->driver->ops->get_sdw_stream) + return dai->driver->ops->get_sdw_stream(dai, direction); + else + return NULL; +} + #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index f0e4f36f83bfa7..81e5d17be9351d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -855,6 +855,11 @@ struct snd_soc_dai_link { ((platform) = &link->platforms[i]); \ (i)++) +#define for_each_link_cpus(link, i, cpu) \ + for ((i) = 0; \ + ((i) < link->num_cpus) && ((cpu) = &link->cpus[i]); \ + (i)++) + /* * Sample 1 : Single CPU/Codec/Platform * @@ -1132,6 +1137,9 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai **codec_dais; unsigned int num_codecs; + struct snd_soc_dai **cpu_dais; + unsigned int num_cpus; + struct delayed_work delayed_work; void (*close_delayed_work_func)(struct snd_soc_pcm_runtime *rtd); #ifdef CONFIG_DEBUG_FS @@ -1157,7 +1165,14 @@ struct snd_soc_pcm_runtime { ((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \ (i)++) #define for_each_rtd_codec_dai_rollback(rtd, i, dai) \ - for (; ((--i) >= 0) && ((dai) = rtd->codec_dais[i]);) + for (; (--(i) >= 0) && ((dai) = rtd->codec_dais[i]);) + +#define for_each_rtd_cpu_dai(rtd, i, dai)\ + for ((i) = 0; \ + ((i) < rtd->num_cpus) && ((dai) = rtd->cpu_dais[i]); \ + (i)++) +#define for_each_rtd_cpu_dai_rollback(rtd, i, dai) \ + for (; (--(i) >= 0) && ((dai) = rtd->cpu_dais[i]);) void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd); diff --git a/sound/soc/intel/boards/sdw_rt700.c b/sound/soc/intel/boards/sdw_rt700.c index be627ec10860e6..11a2f33afbb12a 100644 --- a/sound/soc/intel/boards/sdw_rt700.c +++ b/sound/soc/intel/boards/sdw_rt700.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "../../codecs/hdac_hdmi.h" #include "hda_dsp_common.h" @@ -152,6 +153,22 @@ static int headset_init(struct snd_soc_pcm_runtime *rtd) return ret; } +/* these wrappers are only needed to avoid typecast compilation errors */ +static int sdw_startup(struct snd_pcm_substream *substream) +{ + return sdw_startup_stream(substream); +} + +static void sdw_shutdown(struct snd_pcm_substream *substream) +{ + sdw_shutdown_stream(substream); +} + +static const struct snd_soc_ops sdw_ops = { + .startup = sdw_startup, + .shutdown = sdw_shutdown, +}; + static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), @@ -222,6 +239,7 @@ static struct snd_soc_dai_link dailink[] = { .no_pcm = 1, .dpcm_playback = 1, .nonatomic = true, + .ops = &sdw_ops, SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), }, { @@ -230,6 +248,7 @@ static struct snd_soc_dai_link dailink[] = { .no_pcm = 1, .dpcm_capture = 1, .nonatomic = true, + .ops = &sdw_ops, SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), }, { diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index cd3076a1aea8d5..e4f4c460cbccef 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "../../codecs/hdac_hdmi.h" #include "../../codecs/rt1308.h" #include "hda_dsp_common.h" @@ -456,6 +457,22 @@ static struct snd_soc_ops rt1308_i2s_ops = { .hw_params = rt1308_i2s_hw_params, }; +/* these wrappers are only needed to avoid typecast compilation errors */ +static int sdw_startup(struct snd_pcm_substream *substream) +{ + return sdw_startup_stream(substream); +} + +static void sdw_shutdown(struct snd_pcm_substream *substream) +{ + sdw_shutdown_stream(substream); +} + +static const struct snd_soc_ops sdw_ops = { + .startup = sdw_startup, + .shutdown = sdw_shutdown, +}; + static void rt711_init(const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct codec_info *info, @@ -759,7 +776,7 @@ static int create_sdw_codec_dai(struct device *dev, return -ENOMEM; init_dai_link(dai_links, *id, name, 1 - i, i, cpus, cpu_name, - codec, link->num_adr, *id, NULL, NULL); + codec, link->num_adr, *id, NULL, &sdw_ops); set_codec_init_func(link, dai_links + *id, 1 - i); (*id)++; j++; diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 223cd045719e8d..d05e78e963752c 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -810,9 +810,10 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) int playback = 0, capture = 0; int i; - if (rtd->num_codecs > 1) { + if (rtd->num_cpus > 1 || + rtd->num_codecs > 1) { dev_err(rtd->card->dev, - "Compress ASoC: Multicodec not supported\n"); + "Compress ASoC: Multi CPU/Codec not supported\n"); return -EINVAL; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 30c17fde14caf5..6038920c047b6e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -483,6 +483,14 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( if (!rtd->codec_dais) goto free_rtd; + /* + * for rtd->cpu_dais + */ + rtd->cpu_dais = devm_kcalloc(dev, dai_link->num_cpus, + sizeof(struct snd_soc_dai *), + GFP_KERNEL); + if (!rtd->cpu_dais) + goto free_rtd; /* * rtd remaining settings */ @@ -833,7 +841,7 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, struct snd_soc_dai_link *link) { int i; - struct snd_soc_dai_link_component *codec, *platform; + struct snd_soc_dai_link_component *cpu, *codec, *platform; for_each_link_codecs(link, i, codec) { /* @@ -882,44 +890,38 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, return -EPROBE_DEFER; } - /* FIXME */ - if (link->num_cpus > 1) { - dev_err(card->dev, - "ASoC: multi cpu is not yet supported %s\n", - link->name); - return -EINVAL; - } - - /* - * CPU device may be specified by either name or OF node, but - * can be left unspecified, and will be matched based on DAI - * name alone.. - */ - if (link->cpus->name && link->cpus->of_node) { - dev_err(card->dev, - "ASoC: Neither/both cpu name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + for_each_link_cpus(link, i, cpu) { + /* + * CPU device may be specified by either name or OF node, but + * can be left unspecified, and will be matched based on DAI + * name alone.. + */ + if (cpu->name && cpu->of_node) { + dev_err(card->dev, + "ASoC: Neither/both cpu name/of_node are set for %s\n", + link->name); + return -EINVAL; + } - /* - * Defer card registration if cpu dai component is not added to - * component list. - */ - if ((link->cpus->of_node || link->cpus->name) && - !soc_find_component(link->cpus)) - return -EPROBE_DEFER; + /* + * Defer card registration if cpu dai component is not added to + * component list. + */ + if ((cpu->of_node || cpu->name) && + !soc_find_component(cpu)) + return -EPROBE_DEFER; - /* - * At least one of CPU DAI name or CPU device name/node must be - * specified - */ - if (!link->cpus->dai_name && - !(link->cpus->name || link->cpus->of_node)) { - dev_err(card->dev, - "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", - link->name); - return -EINVAL; + /* + * At least one of CPU DAI name or CPU device name/node must be + * specified + */ + if (!cpu->dai_name && + !(cpu->name || cpu->of_node)) { + dev_err(card->dev, + "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", + link->name); + return -EINVAL; + } } return 0; @@ -962,7 +964,7 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai_link_component *codec, *platform; + struct snd_soc_dai_link_component *codec, *platform, *cpu; struct snd_soc_component *component; int i, ret; @@ -987,14 +989,19 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, if (!rtd) return -ENOMEM; - /* FIXME: we need multi CPU support in the future */ - rtd->cpu_dai = snd_soc_find_dai(dai_link->cpus); - if (!rtd->cpu_dai) { - dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", - dai_link->cpus->dai_name); - goto _err_defer; + rtd->num_cpus = dai_link->num_cpus; + for_each_link_cpus(dai_link, i, cpu) { + rtd->cpu_dais[i] = snd_soc_find_dai(cpu); + if (!rtd->cpu_dais[i]) { + dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", + cpu->dai_name); + goto _err_defer; + } + snd_soc_rtd_add_component(rtd, rtd->cpu_dais[i]->component); } - snd_soc_rtd_add_component(rtd, rtd->cpu_dai->component); + + /* Single cpu links expect cpu and cpu_dai in runtime data */ + rtd->cpu_dai = rtd->cpu_dais[0]; /* Find CODEC from registered CODECs */ rtd->num_codecs = dai_link->num_codecs; @@ -1114,7 +1121,8 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card, dai_link->stream_name, ret); return ret; } - ret = soc_dai_pcm_new(&cpu_dai, 1, rtd); + ret = soc_dai_pcm_new(rtd->cpu_dais, + rtd->num_cpus, rtd); if (ret < 0) return ret; ret = soc_dai_pcm_new(rtd->codec_dais, @@ -1306,6 +1314,7 @@ static void soc_remove_link_dais(struct snd_soc_card *card) { int i; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_pcm_runtime *rtd; int order; @@ -1315,14 +1324,15 @@ static void soc_remove_link_dais(struct snd_soc_card *card) for_each_rtd_codec_dai(rtd, i, codec_dai) soc_remove_dai(codec_dai, order); - soc_remove_dai(rtd->cpu_dai, order); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + soc_remove_dai(cpu_dai, order); } } } static int soc_probe_link_dais(struct snd_soc_card *card) { - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *codec_dai, *cpu_dai; struct snd_soc_pcm_runtime *rtd; int i, order, ret; @@ -1333,9 +1343,12 @@ static int soc_probe_link_dais(struct snd_soc_card *card) "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); - ret = soc_probe_dai(rtd->cpu_dai, order); - if (ret) - return ret; + /* probe the CPU DAI */ + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = soc_probe_dai(cpu_dai, order); + if (ret) + return ret; + } /* probe the CODEC DAI */ for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -1467,8 +1480,9 @@ static void soc_remove_aux_devices(struct snd_soc_card *card) int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, unsigned int dai_fmt) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; + unsigned int inv_dai_fmt; unsigned int i; int ret; @@ -1485,33 +1499,33 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, * Flip the polarity for the "CPU" end of a CODEC<->CODEC link * the component which has non_legacy_dai_naming is Codec */ - if (cpu_dai->component->driver->non_legacy_dai_naming) { - unsigned int inv_dai_fmt; - - inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; - switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; - break; - case SND_SOC_DAIFMT_CBM_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; - break; - case SND_SOC_DAIFMT_CBS_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; - break; - case SND_SOC_DAIFMT_CBS_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - break; - } - - dai_fmt = inv_dai_fmt; + inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; + switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + break; + case SND_SOC_DAIFMT_CBS_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + break; } + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + unsigned int fmt = dai_fmt; - ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) { - dev_warn(cpu_dai->dev, - "ASoC: Failed to set DAI format: %d\n", ret); - return ret; + if (cpu_dai->component->driver->non_legacy_dai_naming) + fmt = inv_dai_fmt; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret != 0 && ret != -ENOTSUPP) { + dev_warn(cpu_dai->dev, + "ASoC: Failed to set DAI format: %d\n", ret); + return ret; + } } return 0; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 34c83268663766..3a7dbbbd4a7666 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4267,16 +4267,15 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) return 0; } -static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, - struct snd_soc_pcm_runtime *rtd) +static void dapm_add_valid_dai_widget(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *codec_dai, + struct snd_soc_dai *cpu_dai) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; struct snd_soc_dapm_widget *playback = NULL, *capture = NULL; struct snd_soc_dapm_widget *codec, *playback_cpu, *capture_cpu; struct snd_pcm_substream *substream; struct snd_pcm_str *streams = rtd->pcm->streams; - int i; if (rtd->dai_link->params) { playback_cpu = cpu_dai->capture_widget; @@ -4288,67 +4287,85 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, capture_cpu = capture; } - for_each_rtd_codec_dai(rtd, i, codec_dai) { - /* connect BE DAI playback if widgets are valid */ - codec = codec_dai->playback_widget; - - if (playback_cpu && codec) { - if (!playback) { - substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - playback = snd_soc_dapm_new_dai(card, substream, - "playback"); - if (IS_ERR(playback)) { - dev_err(rtd->dev, - "ASoC: Failed to create DAI %s: %ld\n", - codec_dai->name, - PTR_ERR(playback)); - continue; - } - - snd_soc_dapm_add_path(&card->dapm, playback_cpu, - playback, NULL, NULL); + /* connect BE DAI playback if widgets are valid */ + codec = codec_dai->playback_widget; + + if (playback_cpu && codec) { + if (!playback) { + substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + playback = snd_soc_dapm_new_dai(card, substream, + "playback"); + if (IS_ERR(playback)) { + dev_err(rtd->dev, + "ASoC: Failed to create DAI %s: %ld\n", + codec_dai->name, + PTR_ERR(playback)); + goto capture; } - dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - cpu_dai->component->name, playback_cpu->name, - codec_dai->component->name, codec->name); - - snd_soc_dapm_add_path(&card->dapm, playback, codec, - NULL, NULL); + snd_soc_dapm_add_path(&card->dapm, playback_cpu, + playback, NULL, NULL); } - } - for_each_rtd_codec_dai(rtd, i, codec_dai) { - /* connect BE DAI capture if widgets are valid */ - codec = codec_dai->capture_widget; - - if (codec && capture_cpu) { - if (!capture) { - substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; - capture = snd_soc_dapm_new_dai(card, substream, - "capture"); - if (IS_ERR(capture)) { - dev_err(rtd->dev, - "ASoC: Failed to create DAI %s: %ld\n", - codec_dai->name, - PTR_ERR(capture)); - continue; - } - - snd_soc_dapm_add_path(&card->dapm, capture, - capture_cpu, NULL, NULL); + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + cpu_dai->component->name, playback_cpu->name, + codec_dai->component->name, codec->name); + + snd_soc_dapm_add_path(&card->dapm, playback, codec, + NULL, NULL); + } + +capture: + /* connect BE DAI capture if widgets are valid */ + codec = codec_dai->capture_widget; + + if (codec && capture_cpu) { + if (!capture) { + substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; + capture = snd_soc_dapm_new_dai(card, substream, + "capture"); + if (IS_ERR(capture)) { + dev_err(rtd->dev, + "ASoC: Failed to create DAI %s: %ld\n", + codec_dai->name, + PTR_ERR(capture)); + return; } - dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - codec_dai->component->name, codec->name, - cpu_dai->component->name, capture_cpu->name); - - snd_soc_dapm_add_path(&card->dapm, codec, capture, - NULL, NULL); + snd_soc_dapm_add_path(&card->dapm, capture, + capture_cpu, NULL, NULL); } + + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + codec_dai->component->name, codec->name, + cpu_dai->component->name, capture_cpu->name); + + snd_soc_dapm_add_path(&card->dapm, codec, capture, + NULL, NULL); } } +static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai; + int i; + + if (rtd->num_cpus == 1) { + for_each_rtd_codec_dai(rtd, i, codec_dai) + dapm_add_valid_dai_widget(card, rtd, codec_dai, + rtd->cpu_dais[0]); + } else if (rtd->num_codecs == rtd->num_cpus) { + for_each_rtd_codec_dai(rtd, i, codec_dai) + dapm_add_valid_dai_widget(card, rtd, codec_dai, + rtd->cpu_dais[i]); + } else { + dev_err(card->dev, + "N cpus to M codecs link is not supported yet\n"); + } + +} + static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, int event) { @@ -4410,9 +4427,11 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event) { struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; int i; - soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event); for_each_rtd_codec_dai(rtd, i, codec_dai) soc_dapm_dai_stream_event(codec_dai, stream, event); diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index d6b4831e8aec9e..facf1922a714d9 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -62,6 +62,12 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct snd_dmaengine_dai_dma_data *dma_data; int ret; + if (rtd->num_cpus > 1) { + dev_err(rtd->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); @@ -118,6 +124,12 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, struct snd_dmaengine_dai_dma_data *dma_data; struct snd_pcm_hardware hw; + if (rtd->num_cpus > 1) { + dev_err(rtd->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + if (pcm->config && pcm->config->pcm_hardware) return snd_soc_set_runtime_hwparams(substream, pcm->config->pcm_hardware); @@ -185,6 +197,12 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( struct snd_dmaengine_dai_dma_data *dma_data; dma_filter_fn fn = NULL; + if (rtd->num_cpus > 1) { + dev_err(rtd->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return NULL; + } + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0]) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 0c3a12d6290ab0..70402a058bf429 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -94,18 +94,22 @@ struct snd_soc_dapm_widget *dai_get_widget(struct snd_soc_dai *dai, int stream) static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, int stream, int action) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i; lockdep_assert_held(&rtd->card->pcm_mutex); - cpu_dai->stream_active[stream] += action; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + cpu_dai->stream_active[stream] += action; + for_each_rtd_codec_dai(rtd, i, codec_dai) codec_dai->stream_active[stream] += action; - cpu_dai->active += action; - cpu_dai->component->active += action; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_dai->active += action; + cpu_dai->component->active += action; + } for_each_rtd_codec_dai(rtd, i, codec_dai) { codec_dai->active += action; codec_dai->component->active += action; @@ -275,7 +279,7 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; unsigned int rate, channels, sample_bits, symmetry, i; @@ -284,40 +288,60 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, sample_bits = snd_pcm_format_physical_width(params_format(params)); /* reject unmatched parameters when applying symmetry */ - symmetry = cpu_dai->driver->symmetric_rates || - rtd->dai_link->symmetric_rates; + symmetry = rtd->dai_link->symmetric_rates; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry |= cpu_dai->driver->symmetric_rates; for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry |= codec_dai->driver->symmetric_rates; - if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { - dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", - cpu_dai->rate, rate); - return -EINVAL; + if (symmetry) { + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->rate && cpu_dai->rate != rate) { + dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", + cpu_dai->rate, rate); + return -EINVAL; + } + } } - symmetry = cpu_dai->driver->symmetric_channels || - rtd->dai_link->symmetric_channels; + symmetry = rtd->dai_link->symmetric_channels; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry |= cpu_dai->driver->symmetric_channels; for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry |= codec_dai->driver->symmetric_channels; - if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { - dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", - cpu_dai->channels, channels); - return -EINVAL; + if (symmetry) { + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->channels && + cpu_dai->channels != channels) { + dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", + cpu_dai->channels, channels); + return -EINVAL; + } + } } - symmetry = cpu_dai->driver->symmetric_samplebits || - rtd->dai_link->symmetric_samplebits; + symmetry = rtd->dai_link->symmetric_samplebits; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry |= cpu_dai->driver->symmetric_samplebits; for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry |= codec_dai->driver->symmetric_samplebits; - if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { - dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", - cpu_dai->sample_bits, sample_bits); - return -EINVAL; + if (symmetry) { + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->sample_bits && + cpu_dai->sample_bits != sample_bits) { + dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", + cpu_dai->sample_bits, sample_bits); + return -EINVAL; + } + } } return 0; @@ -326,14 +350,20 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; struct snd_soc_dai_link *link = rtd->dai_link; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; unsigned int symmetry, i; - symmetry = cpu_driver->symmetric_rates || link->symmetric_rates || - cpu_driver->symmetric_channels || link->symmetric_channels || - cpu_driver->symmetric_samplebits || link->symmetric_samplebits; + symmetry = link->symmetric_rates || + link->symmetric_channels || + link->symmetric_samplebits; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry = symmetry || + cpu_dai->driver->symmetric_rates || + cpu_dai->driver->symmetric_channels || + cpu_dai->driver->symmetric_samplebits; for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry = symmetry || @@ -361,10 +391,10 @@ static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i; - unsigned int bits = 0, cpu_bits; + unsigned int bits = 0, cpu_bits = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -374,7 +404,14 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } bits = max(codec_dai->driver->playback.sig_bits, bits); } - cpu_bits = cpu_dai->driver->playback.sig_bits; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->driver->playback.sig_bits == 0) { + cpu_bits = 0; + break; + } + cpu_bits = max(cpu_dai->driver->playback.sig_bits, + cpu_bits); + } } else { for_each_rtd_codec_dai(rtd, i, codec_dai) { if (codec_dai->driver->capture.sig_bits == 0) { @@ -383,7 +420,14 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } bits = max(codec_dai->driver->capture.sig_bits, bits); } - cpu_bits = cpu_dai->driver->capture.sig_bits; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->driver->capture.sig_bits == 0) { + cpu_bits = 0; + break; + } + cpu_bits = max(cpu_dai->driver->capture.sig_bits, + cpu_bits); + } } soc_pcm_set_msb(substream, bits); @@ -396,22 +440,36 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) struct snd_pcm_hardware *hw = &runtime->hw; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai; - struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver; + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai_driver *cpu_dai_drv; struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; unsigned int chan_min = 0, chan_max = UINT_MAX; + unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX; unsigned int rate_min = 0, rate_max = UINT_MAX; - unsigned int rates = UINT_MAX; + unsigned int cpu_rate_min = 0, cpu_rate_max = UINT_MAX; + unsigned int rates = UINT_MAX, cpu_rates = UINT_MAX; u64 formats = ULLONG_MAX; int i; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + /* first calculate min/max only for CPUs in the DAI link */ + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_dai_drv = cpu_dai->driver; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; + cpu_chan_min = max(cpu_chan_min, cpu_stream->channels_min); + cpu_chan_max = min(cpu_chan_max, cpu_stream->channels_max); + cpu_rate_min = max(cpu_rate_min, cpu_stream->rate_min); + cpu_rate_max = min_not_zero(cpu_rate_max, cpu_stream->rate_max); + formats &= cpu_stream->formats; + cpu_rates = snd_pcm_rate_mask_intersect(cpu_stream->rates, + cpu_rates); + } - /* first calculate min/max only for CODECs in the DAI link */ + /* second calculate min/max only for CODECs in the DAI link */ for_each_rtd_codec_dai(rtd, i, codec_dai) { /* @@ -442,27 +500,28 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) /* * chan min/max cannot be enforced if there are multiple CODEC DAIs - * connected to a single CPU DAI, use CPU DAI's directly and let + * connected to CPU DAI(s), use CPU DAI's directly and let * channel allocation be fixed up later */ if (rtd->num_codecs > 1) { - chan_min = cpu_stream->channels_min; - chan_max = cpu_stream->channels_max; + chan_min = cpu_chan_min; + chan_max = cpu_chan_max; } - hw->channels_min = max(chan_min, cpu_stream->channels_min); - hw->channels_max = min(chan_max, cpu_stream->channels_max); + /* finally find a intersection between CODECs and CPUs */ + hw->channels_min = max(chan_min, cpu_chan_min); + hw->channels_max = min(chan_max, cpu_chan_max); if (hw->formats) - hw->formats &= formats & cpu_stream->formats; + hw->formats &= formats; else - hw->formats = formats & cpu_stream->formats; - hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_stream->rates); + hw->formats = formats; + hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_rates); snd_pcm_limit_hw_rates(runtime); - hw->rate_min = max(hw->rate_min, cpu_stream->rate_min); + hw->rate_min = max(hw->rate_min, cpu_rate_min); hw->rate_min = max(hw->rate_min, rate_min); - hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max); + hw->rate_max = min_not_zero(hw->rate_max, cpu_rate_max); hw->rate_max = min_not_zero(hw->rate_max, rate_max); } @@ -526,7 +585,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i; @@ -534,9 +593,11 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) snd_soc_runtime_deactivate(rtd, substream->stream); - snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); - snd_soc_dai_shutdown(cpu_dai, substream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + snd_soc_dai_shutdown(cpu_dai, substream); for_each_rtd_codec_dai(rtd, i, codec_dai) snd_soc_dai_shutdown(codec_dai, substream); @@ -571,9 +632,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; + const char *cpu_dai_name = "multicpu"; int i, ret = 0; for_each_rtd_components(rtd, i, component) @@ -596,11 +658,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } /* startup the audio subsystem */ - ret = snd_soc_dai_startup(cpu_dai, substream); - if (ret < 0) { - dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n", - cpu_dai->name, ret); - goto cpu_dai_err; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_startup(cpu_dai, substream); + if (ret < 0) { + dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n", + cpu_dai->name, ret); + goto cpu_dai_err; + } } for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -628,34 +692,39 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (rtd->num_codecs == 1) codec_dai_name = rtd->codec_dai->name; + if (rtd->num_cpus == 1) + cpu_dai_name = rtd->cpu_dai->name; + if (soc_pcm_has_symmetry(substream)) runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; ret = -EINVAL; if (!runtime->hw.rates) { printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } if (!runtime->hw.formats) { printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } if (!runtime->hw.channels_min || !runtime->hw.channels_max || runtime->hw.channels_min > runtime->hw.channels_max) { printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } soc_pcm_apply_msb(substream); /* Symmetry only applies if we've already got an active stream. */ - if (cpu_dai->active) { - ret = soc_pcm_apply_symmetry(substream, cpu_dai); - if (ret != 0) - goto config_err; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->active) { + ret = soc_pcm_apply_symmetry(substream, cpu_dai); + if (ret != 0) + goto config_err; + } } for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -667,7 +736,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } pr_debug("ASoC: %s <-> %s info:\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates); pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min, runtime->hw.channels_max); @@ -684,8 +753,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) config_err: for_each_rtd_codec_dai(rtd, i, codec_dai) snd_soc_dai_shutdown(codec_dai, substream); + i = rtd->num_cpus; cpu_dai_err: - snd_soc_dai_shutdown(cpu_dai, substream); + for_each_rtd_cpu_dai_rollback(rtd, i, cpu_dai) + snd_soc_dai_shutdown(cpu_dai, substream); soc_rtd_shutdown(rtd, substream); component_err: @@ -724,7 +795,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; @@ -756,11 +827,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - ret = snd_soc_dai_prepare(cpu_dai, substream); - if (ret < 0) { - dev_err(cpu_dai->dev, - "ASoC: cpu DAI prepare error: %d\n", ret); - goto out; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_prepare(cpu_dai, substream); + if (ret < 0) { + dev_err(cpu_dai->dev, + "ASoC: cpu DAI prepare error: %d\n", ret); + goto out; + } } /* cancel any delayed stream shutdown that is pending */ @@ -776,7 +849,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) for_each_rtd_codec_dai(rtd, i, codec_dai) snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); - snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); out: mutex_unlock(&rtd->card->pcm_mutex); @@ -823,7 +897,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; @@ -887,17 +961,19 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_dapm_update_dai(substream, &codec_params, codec_dai); } - ret = snd_soc_dai_hw_params(cpu_dai, substream, params); - if (ret < 0) - goto interface_err; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_hw_params(cpu_dai, substream, params); + if (ret < 0) + goto interface_err; - /* store the parameters for each DAIs */ - cpu_dai->rate = params_rate(params); - cpu_dai->channels = params_channels(params); - cpu_dai->sample_bits = - snd_pcm_format_physical_width(params_format(params)); + /* store the parameters for each DAI */ + cpu_dai->rate = params_rate(params); + cpu_dai->channels = params_channels(params); + cpu_dai->sample_bits = + snd_pcm_format_physical_width(params_format(params)); - snd_soc_dapm_update_dai(substream, params, cpu_dai); + snd_soc_dapm_update_dai(substream, params, cpu_dai); + } for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_hw_params(component, substream, params); @@ -917,10 +993,14 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, component_err: soc_pcm_components_hw_free(substream, component); - snd_soc_dai_hw_free(cpu_dai, substream); - cpu_dai->rate = 0; + i = rtd->num_cpus; interface_err: + for_each_rtd_cpu_dai_rollback(rtd, i, cpu_dai) { + snd_soc_dai_hw_free(cpu_dai, substream); + cpu_dai->rate = 0; + } + i = rtd->num_codecs; codec_err: @@ -944,7 +1024,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i; @@ -952,10 +1032,12 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); /* clear the corresponding DAIs parameters when going to be inactive */ - if (cpu_dai->active == 1) { - cpu_dai->rate = 0; - cpu_dai->channels = 0; - cpu_dai->sample_bits = 0; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->active == 1) { + cpu_dai->rate = 0; + cpu_dai->channels = 0; + cpu_dai->sample_bits = 0; + } } for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -991,7 +1073,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) snd_soc_dai_hw_free(codec_dai, substream); } - snd_soc_dai_hw_free(cpu_dai, substream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + snd_soc_dai_hw_free(cpu_dai, substream); mutex_unlock(&rtd->card->pcm_mutex); return 0; @@ -1001,7 +1084,7 @@ static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret; @@ -1015,9 +1098,11 @@ static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) return ret; } - ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); + if (ret < 0) + return ret; + } for_each_rtd_codec_dai(rtd, i, codec_dai) { ret = snd_soc_dai_trigger(codec_dai, substream, cmd); @@ -1032,7 +1117,7 @@ static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret; @@ -1042,9 +1127,11 @@ static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) return ret; } - ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); + if (ret < 0) + return ret; + } for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_trigger(component, substream, cmd); @@ -1085,7 +1172,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret; @@ -1095,9 +1182,11 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd); + if (ret < 0) + return ret; + } return 0; } @@ -1109,12 +1198,13 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t offset = 0; snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t codec_delay = 0; + snd_pcm_sframes_t cpu_delay = 0; int i; /* clearing the previous total delay */ @@ -1125,7 +1215,11 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) /* base delay if assigned in pointer callback */ delay = runtime->delay; - delay += snd_soc_dai_delay(cpu_dai, substream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_delay = max(cpu_delay, + snd_soc_dai_delay(cpu_dai, substream)); + } + delay += cpu_delay; for_each_rtd_codec_dai(rtd, i, codec_dai) { codec_delay = max(codec_delay, @@ -1261,13 +1355,12 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, if (!be->dai_link->no_pcm) continue; - w = dai_get_widget(be->cpu_dai, stream); - - dev_dbg(card->dev, "ASoC: try BE : %s\n", - w ? w->name : "(not set)"); + for_each_rtd_cpu_dai(be, i, dai) { + w = dai_get_widget(dai, stream); - if (w == widget) - return be; + if (w == widget) + return be; + } for_each_rtd_codec_dai(be, i, dai) { w = dai_get_widget(dai, stream); @@ -1322,6 +1415,12 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, struct snd_soc_dai *cpu_dai = fe->cpu_dai; int paths; + if (fe->num_cpus > 1) { + dev_err(fe->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + /* get number of valid DAI paths and their widgets */ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list, dpcm_end_walk_at_be); @@ -1347,10 +1446,18 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, unsigned int i; /* is there a valid CPU DAI widget for this BE */ - widget = dai_get_widget(dpcm->be->cpu_dai, stream); + do_prune = 1; + for_each_rtd_cpu_dai(dpcm->be, i, dai) { + widget = dai_get_widget(dai, stream); - /* prune the BE if it's no longer in our active list */ - if (widget && widget_in_list(list, widget)) + /* + * The BE is pruned only if none of the cpu_dai + * widgets are in the active list. + */ + if (widget && widget_in_list(list, widget)) + do_prune = 0; + } + if (!do_prune) continue; /* is there a valid CODEC DAI widget for this BE */ @@ -1650,18 +1757,25 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; + struct snd_soc_dai_driver *cpu_dai_drv; struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; + struct snd_soc_dai *dai; + int i; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + for_each_rtd_cpu_dai(be, i, dai) { + cpu_dai_drv = dai->driver; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; - *channels_min = max(*channels_min, cpu_stream->channels_min); - *channels_max = min(*channels_max, cpu_stream->channels_max); + *channels_min = max(*channels_min, + cpu_stream->channels_min); + *channels_max = min(*channels_max, + cpu_stream->channels_max); + } /* * chan min/max cannot be enforced if there are multiple CODEC @@ -1702,21 +1816,26 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; + struct snd_soc_dai_driver *cpu_dai_drv; struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; struct snd_soc_dai *dai; int i; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + for_each_rtd_cpu_dai(be, i, dai) { + cpu_dai_drv = dai->driver; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; - *rate_min = max(*rate_min, cpu_stream->rate_min); - *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max); - *rates = snd_pcm_rate_mask_intersect(*rates, cpu_stream->rates); + *rate_min = max(*rate_min, cpu_stream->rate_min); + *rate_max = min_not_zero(*rate_max, + cpu_stream->rate_max); + *rates = snd_pcm_rate_mask_intersect(*rates, + cpu_stream->rates); + } for_each_rtd_codec_dai(be, i, dai) { /* @@ -1745,13 +1864,17 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai_driver *cpu_dai_drv; + int i; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); - else - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_dai_drv = cpu_dai->driver; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); + else + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + } dpcm_runtime_merge_format(substream, &runtime->hw.formats); dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min, @@ -1809,6 +1932,7 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, snd_soc_dpcm_get_substream(be, stream); struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; int i; /* A backend may not have the requested substream */ @@ -1823,11 +1947,13 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; /* Symmetry only applies if we've got an active stream. */ - if (rtd->cpu_dai->active) { - err = soc_pcm_apply_symmetry(fe_substream, - rtd->cpu_dai); - if (err < 0) - return err; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->active) { + err = soc_pcm_apply_symmetry(fe_substream, + cpu_dai); + if (err < 0) + return err; + } } for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -2622,6 +2748,12 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) int stream; int count, paths; + if (fe->num_cpus > 1) { + dev_err(fe->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + if (!fe->dai_link->dynamic) return 0; @@ -2763,7 +2895,7 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_dai *codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_component *component; struct snd_pcm *pcm; char new_name[64]; @@ -2781,6 +2913,16 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; for_each_rtd_codec_dai(rtd, i, codec_dai) { + if (rtd->num_cpus == 1) { + cpu_dai = rtd->cpu_dais[0]; + } else if (rtd->num_cpus == rtd->num_codecs) { + cpu_dai = rtd->cpu_dais[i]; + } else { + dev_err(rtd->card->dev, + "N cpus to M codecs link is not supported yet\n"); + return -EINVAL; + } + if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) playback = 1; @@ -2901,7 +3043,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) out: dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, - cpu_dai->name); + (rtd->num_cpus > 1) ? "multicpu" : rtd->cpu_dai->name); return ret; } @@ -3098,6 +3240,12 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, int stream; char *buf; + if (fe->num_cpus > 1) { + dev_err(fe->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + buf = kmalloc(out_count, GFP_KERNEL); if (!buf) return -ENOMEM; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 9f4f8868b3860b..7dfd5b2851dff5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1240,6 +1240,8 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, { struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *cpu_dai; + int i; list_for_each_entry(rtd, &card->rtd_list, list) { dev_vdbg(scomp->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", @@ -1254,13 +1256,15 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, switch (w->id) { case snd_soc_dapm_dai_out: - rtd->cpu_dai->capture_widget = w; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + cpu_dai->capture_widget = w; dai->name = rtd->dai_link->name; dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", w->name, rtd->dai_link->name); break; case snd_soc_dapm_dai_in: - rtd->cpu_dai->playback_widget = w; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + cpu_dai->playback_widget = w; dai->name = rtd->dai_link->name; dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", w->name, rtd->dai_link->name);