Unverified Commit e2de6808 authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown
Browse files

ASoC: audio-graph-card2: add CPU:Codec = N:M support



Now ASoC is supporting CPU:Codec = N:M support.
This patch enables it on Audio Graph Card2.

Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Link: https://lore.kernel.org/r/87r0ku4f0t.wl-kuninori.morimoto.gx@renesas.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 912eb415
Loading
Loading
Loading
Loading
+202 −23
Original line number Diff line number Diff line
@@ -504,40 +504,203 @@ static int __graph_parse_node(struct simple_util_priv *priv,
	return 0;
}

static int graph_parse_node(struct simple_util_priv *priv,
static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link,
				     int *nm_idx, int cpu_idx,
				     struct device_node *mcpu_port)
{
	/*
	 *		+---+		+---+
	 *		|  X|<-@------->|x  |
	 *		|   |		|   |
	 *	cpu0 <--|A 1|<--------->|4 a|-> codec0
	 *	cpu1 <--|B 2|<-----+--->|5 b|-> codec1
	 *	cpu2 <--|C 3|<----/	+---+
	 *		+---+
	 *
	 * multi {
	 *	ports {
	 *		port@0 { mcpu_top_ep	{...  = mcodec_ep;	}; };	// (X) to pair
	 * <mcpu_port>	port@1 { mcpu0_ep	{ ... = cpu0_ep;	};	// (A) Multi Element
	 *			 mcpu0_ep_0	{ ... = mcodec0_ep_0;	}; };	// (1) connected Codec
	 *		port@2 { mcpu1_ep	{ ... = cpu1_ep;	};	// (B) Multi Element
	 *			 mcpu1_ep_0	{ ... = mcodec1_ep_0;	}; };	// (2) connected Codec
	 *		port@3 { mcpu2_ep	{ ... = cpu2_ep;	};	// (C) Multi Element
	 *			 mcpu2_ep_0	{ ... = mcodec1_ep_1;	}; };	// (3) connected Codec
	 *	};
	 *
	 *	ports {
	 *		port@0 { mcodec_top_ep	{...  = mcpu_ep;	}; };	// (x) to pair
	 * <mcodec_port>port@1 { mcodec0_ep	{ ... = codec0_ep;	};	// (a) Multi Element
	 *			 mcodec0_ep_0	{ ... = mcpu0_ep_0;	}; };	// (4) connected CPU
	 *		port@2 { mcodec1_ep	{ ... = codec1_ep;	};	// (b) Multi Element
	 *			 mcodec1_ep_0	{ ... = mcpu1_ep_0;	};	// (5) connected CPU
	 *			 mcodec1_ep_1	{ ... = mcpu2_ep_0;	}; };	// (5) connected CPU
	 *	};
	 * };
	 */
	struct device_node *mcpu_ep		= port_to_endpoint(mcpu_port);
	struct device_node *mcpu_ep_n		= mcpu_ep;
	struct device_node *mcpu_port_top	= of_get_next_child(of_get_parent(mcpu_port), NULL);
	struct device_node *mcpu_ep_top		= port_to_endpoint(mcpu_port_top);
	struct device_node *mcodec_ep_top	= of_graph_get_remote_endpoint(mcpu_ep_top);
	struct device_node *mcodec_port_top	= of_get_parent(mcodec_ep_top);
	struct device_node *mcodec_ports	= of_get_parent(mcodec_port_top);
	int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
	int ret = -EINVAL;

	if (cpu_idx > dai_link->num_cpus)
		goto mcpu_err;

	while (1) {
		struct device_node *mcodec_ep_n;
		struct device_node *mcodec_port_i;
		struct device_node *mcodec_port;
		int codec_idx;

		if (*nm_idx > nm_max)
			break;

		mcpu_ep_n = of_get_next_child(mcpu_port, mcpu_ep_n);
		if (!mcpu_ep_n) {
			ret = 0;
			break;
		}

		mcodec_ep_n	= of_graph_get_remote_endpoint(mcpu_ep_n);
		mcodec_port	= of_get_parent(mcodec_ep_n);

		if (mcodec_ports != of_get_parent(mcodec_port))
			goto mcpu_err;

		codec_idx = 0;
		mcodec_port_i = of_get_next_child(mcodec_ports, NULL);
		while (1) {
			if (codec_idx > dai_link->num_codecs)
				goto mcodec_err;

			mcodec_port_i = of_get_next_child(mcodec_ports, mcodec_port_i);

			if (!mcodec_port_i)
				goto mcodec_err;

			if (mcodec_port_i == mcodec_port)
				break;

			codec_idx++;
		}

		dai_link->ch_maps[*nm_idx].cpu		= cpu_idx;
		dai_link->ch_maps[*nm_idx].codec	= codec_idx;

		(*nm_idx)++;

		of_node_put(mcodec_port_i);
mcodec_err:
		of_node_put(mcodec_port);
		of_node_put(mcpu_ep_n);
		of_node_put(mcodec_ep_n);
	}
mcpu_err:
	of_node_put(mcpu_ep);
	of_node_put(mcpu_port_top);
	of_node_put(mcpu_ep_top);
	of_node_put(mcodec_ep_top);
	of_node_put(mcodec_port_top);
	of_node_put(mcodec_ports);

	return ret;
}

static int graph_parse_node_multi(struct simple_util_priv *priv,
				  enum graph_type gtype,
				  struct device_node *port,
				  struct link_info *li, int is_cpu)
{
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
	struct device *dev = simple_priv_to_dev(priv);
	struct device_node *ep;
	int ret = 0;
	int ret = -ENOMEM;
	int nm_idx = 0;
	int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);

	if (graph_lnk_is_multi(port)) {
		int idx;

		of_node_get(port);
	/*
	 * create ch_maps if CPU:Codec = N:M
	 * DPCM is out of scope
	 */
	if (gtype != GRAPH_DPCM && !dai_link->ch_maps &&
	    dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
	    dai_link->num_cpus != dai_link->num_codecs) {

		dai_link->ch_maps = devm_kcalloc(dev, nm_max,
					sizeof(struct snd_soc_dai_link_ch_map), GFP_KERNEL);
		if (!dai_link->ch_maps)
			goto multi_err;
	}

		for (idx = 0;; idx++) {
	for (int idx = 0;; idx++) {
		/*
		 * multi {
		 *	ports {
		 * <port>	port@0 { ... 			    }; // to pair
		 *		port@1 { mcpu1_ep { ... = cpu1_ep };}; // Multi Element
		 *		port@2 { mcpu2_ep { ... = cpu2_ep };}; // Multi Element
		 *	};
		 * };
		 *
		 * cpu {
		 *	ports {
		 * <ep>		port@0 { cpu1_ep   { ... = mcpu1_ep };};
		 *	};
		 * };
		 */
		ep = graph_get_next_multi_ep(&port);
		if (!ep)
			break;

			ret = __graph_parse_node(priv, gtype, ep,
						 li, is_cpu, idx);
		ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, idx);
		of_node_put(ep);
		if (ret < 0)
				break;
			goto multi_err;

		/* CPU:Codec = N:M */
		if (is_cpu && dai_link->ch_maps) {
			ret = graph_parse_node_multi_nm(dai_link, &nm_idx, idx, port);
			if (ret < 0)
				goto multi_err;
		}
	} else {
		/* Single CPU / Codec */
		ep = port_to_endpoint(port);
		ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
		of_node_put(ep);
	}

	if (is_cpu && dai_link->ch_maps && (nm_idx != nm_max))
		ret = -EINVAL;

multi_err:
	return ret;
}

static int graph_parse_node_single(struct simple_util_priv *priv,
				   enum graph_type gtype,
				   struct device_node *port,
				   struct link_info *li, int is_cpu)
{
	struct device_node *ep = port_to_endpoint(port);
	int ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);

	of_node_put(ep);

	return ret;
}

static int graph_parse_node(struct simple_util_priv *priv,
			    enum graph_type gtype,
			    struct device_node *port,
			    struct link_info *li, int is_cpu)
{
	if (graph_lnk_is_multi(port))
		return graph_parse_node_multi(priv, gtype, port, li, is_cpu);
	else
		return graph_parse_node_single(priv, gtype, port, li, is_cpu);
}

static void graph_parse_daifmt(struct device_node *node,
			       unsigned int *daifmt, unsigned int *bit_frame)
{
@@ -929,8 +1092,24 @@ static int graph_counter(struct device_node *lnk)
	 *
	 * ignore first lnk part
	 */
	if (graph_lnk_is_multi(lnk))
		return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1;
	if (graph_lnk_is_multi(lnk)) {
		struct device_node *ports = of_get_parent(lnk);
		struct device_node *port = NULL;
		int cnt = 0;

		/*
		 * CPU/Codec = N:M case has many endpoints.
		 * We can't use of_graph_get_endpoint_count() here
		 */
		while(1) {
			port = of_get_next_child(ports, port);
			if (!port)
				break;
			cnt++;
		}

		return cnt - 1;
	}
	/*
	 * Single CPU / Codec
	 */