Unverified Commit 8ae7fdc3 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: Add Multi CPU DAI support

Merge series from Bard Liao <yung-chuan.liao@linux.intel.com>:

As discussed in [1], ASoC core supports multi codec DAIs
on a DAI link. However it does not do so for CPU DAIs.

So, add support for multi CPU DAIs on a DAI Link by adding
multi CPU DAI in Card instantiation, suspend and resume
functions, PCM ops, stream handling functions and DAPM.

[1]: https://www.spinics.net/lists/alsa-devel/msg71369.html

changes in v5:
- rebase to latest kernel base

Bard Liao (2):
  ASoC: Return error if the function does not support multi-cpu
  ASoC: pcm: check if cpu-dai supports a given stream

Shreyas NC (4):
  ASoC: Add initial support for multiple CPU DAIs
  ASoC: Add multiple CPU DAI support for PCM ops
  ASoC: Add dapm_add_valid_dai_widget helper
  ASoC: Add multiple CPU DAI support in DAPM

 include/sound/soc.h                   |  15 +
 sound/soc/soc-compress.c              |   5 +-
 sound/soc/soc-core.c                  | 168 +++++-----
 sound/soc/soc-dapm.c                  | 133 ++++----
 sound/soc/soc-generic-dmaengine-pcm.c |  18 +
 sound/soc/soc-pcm.c                   | 463 ++++++++++++++++++--------
 6 files changed, 531 insertions(+), 271 deletions(-)

--
2.17.1
parents 870dede0 0e9cf4c4
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -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
@@ -1159,6 +1167,13 @@ struct snd_soc_pcm_runtime {
#define for_each_rtd_codec_dai_rollback(rtd, i, dai)		\
	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);

/* mixer control */
+3 −2
Original line number Diff line number Diff line
@@ -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;
	}

+91 −77
Original line number Diff line number Diff line
@@ -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,20 +890,13 @@ 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;
	}

	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 (link->cpus->name && link->cpus->of_node) {
		if (cpu->name && cpu->of_node) {
			dev_err(card->dev,
				"ASoC: Neither/both cpu name/of_node are set for %s\n",
				link->name);
@@ -906,21 +907,22 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card,
		 * 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))
		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)) {
		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) {
	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",
			 dai_link->cpus->dai_name);
				 cpu->dai_name);
			goto _err_defer;
		}
	snd_soc_rtd_add_component(rtd, rtd->cpu_dai->component);
		snd_soc_rtd_add_component(rtd, rtd->cpu_dais[i]->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);
			/* 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,9 +1499,6 @@ 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:
@@ -1503,16 +1514,19 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
		inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
		break;
	}
	for_each_rtd_cpu_dai(rtd, i, cpu_dai) {
		unsigned int fmt = dai_fmt;

		dai_fmt = inv_dai_fmt;
	}
		if (cpu_dai->component->driver->non_legacy_dai_naming)
			fmt = inv_dai_fmt;

	ret = snd_soc_dai_set_fmt(cpu_dai, 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;
}
+76 −57
Original line number Diff line number Diff line
@@ -4277,16 +4277,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;
@@ -4298,7 +4297,6 @@ 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;

@@ -4312,7 +4310,7 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
					"ASoC: Failed to create DAI %s: %ld\n",
					codec_dai->name,
					PTR_ERR(playback));
					continue;
				goto capture;
			}

			snd_soc_dapm_add_path(&card->dapm, playback_cpu,
@@ -4326,9 +4324,8 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
		snd_soc_dapm_add_path(&card->dapm, playback, codec,
				      NULL, NULL);
	}
	}

	for_each_rtd_codec_dai(rtd, i, codec_dai) {
capture:
	/* connect BE DAI capture if widgets are valid */
	codec = codec_dai->capture_widget;

@@ -4342,7 +4339,7 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
					"ASoC: Failed to create DAI %s: %ld\n",
					codec_dai->name,
					PTR_ERR(capture));
					continue;
				return;
			}

			snd_soc_dapm_add_path(&card->dapm, capture,
@@ -4357,6 +4354,26 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
				      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,
@@ -4417,8 +4434,10 @@ 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;

	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);
+18 −0
Original line number Diff line number Diff line
@@ -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])
Loading