diff mbox series

[RFC,09/15] ASoC: audio-graph-card2: add Codec2Codec support

Message ID 87o8by3djv.wl-kuninori.morimoto.gx@renesas.com
State Superseded
Headers show
Series ASoC: add Audio Graph Card2 driver | expand

Commit Message

Kuninori Morimoto June 22, 2021, 1:15 a.m. UTC
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

This patch adds Codec2Codec support to audio-graph-card2.
It can use Codec2Codec but very limited/simple case only for now.
It doesn't have "SWITCH" control yet, thus it start automatically
when probed, but can't stop, so far.
Thus it needs to be updated around widgets/routing handling,
and you need to understand that it is under experimental.

It is assuming 2channel, S32_LE format for now.
It needs to be updated, too.

Codec2Codec support needs to have extra node (= X) to indicate it.
Codec2Codec needs "routing" (= A) and "rate" (= C).
"links" (= B) needs to indicate Codec2Codec's CPU part node (= D).

	+--+
	|  |<-- Codec0
	|  |--> Codec1
	+--+

	sound {
		compatible = "audio-graph-card2";

(A)		routing = "OUT" ,"DAI1 Playback",
			  "DAI0 Capture", "IN";

(B)		links = <&codec2codec>;
	};

(X)	CODEC2CODEC {
		compatible = "audio-graph-card2-codec2codec";

(C)		rate = <48000>;
		ports {
(D)			codec2codec: port@0 { fe_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
				     port@1 { be_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
		};
	};

	Codec {
		ports {
			port@0 {
				 bitclock-master;
				 frame-master;
				 codec0_ep: endpoint { remote-endpoint = <&fe_ep>; }; };
			port@1 { codec1_ep: endpoint { remote-endpoint = <&be_ep>; }; };
		};
	};

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 include/sound/graph_card.h            |   3 +
 sound/soc/generic/audio-graph-card2.c | 164 ++++++++++++++++++++++++++
 2 files changed, 167 insertions(+)
diff mbox series

Patch

diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h
index d0ccb7afda78..e870ae133a15 100644
--- a/include/sound/graph_card.h
+++ b/include/sound/graph_card.h
@@ -19,6 +19,7 @@  struct graph_custom_hooks {
 	GRAPH_CUSTOM custom_normal;
 	GRAPH_CUSTOM custom_dpcm;
 	GRAPH_CUSTOM custom_multi;
+	GRAPH_CUSTOM custom_c2c;
 };
 
 int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev);
@@ -31,5 +32,7 @@  int audio_graph2_link_dpcm(struct asoc_simple_priv *priv,
 			   struct device_node *lnk, struct link_info *li);
 int audio_graph2_link_multi(struct asoc_simple_priv *priv,
 			    struct device_node *lnk, struct link_info *li);
+int audio_graph2_link_c2c(struct asoc_simple_priv *priv,
+			  struct device_node *lnk, struct link_info *li);
 
 #endif /* __GRAPH_CARD_H */
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index ece0f7d28437..2688b8d65e32 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -184,16 +184,58 @@  CPU2 <--> *    * <--> Codec2
 		port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
 	};
  };
+
+
+ ************************************
+	Codec to Codec
+ ************************************
+
+ +--+
+ |  |<-- Codec0
+ |  |--> Codec1
+ +--+
+
+ sound {
+	compatible = "audio-graph-card2";
+
+	routing = "OUT" ,"DAI1 Playback",
+		  "DAI0 Capture", "IN";
+
+	links = <&codec2codec>;
+ };
+
+ CODEC2CODEC {
+	compatible = "audio-graph-card2-codec2codec";
+
+	rate = <48000>;
+	ports {
+		codec2codec: port@0 { fe_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
+			     port@1 { be_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
+	};
+ };
+
+ Codec {
+	ports {
+		port@0 {
+			bitclock-master;
+			frame-master;
+			 codec0_ep: endpoint { remote-endpoint = <&fe_ep>; }; };
+		port@1 { codec1_ep: endpoint { remote-endpoint = <&be_ep>; }; };
+	};
+ };
+
 */
 
 enum graph_type {
 	GRAPH_NORMAL,
 	GRAPH_DPCM,
 	GRAPH_MULTI,
+	GRAPH_C2C,
 };
 
 #define GRAPH_COMPATIBLE_DPCM	"audio-graph-card2-dsp"
 #define GRAPH_COMPATIBLE_MULTI	"audio-graph-card2-multi"
+#define GRAPH_COMPATIBLE_C2C	"audio-graph-card2-codec2codec"
 
 #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
 
@@ -220,6 +262,8 @@  static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
 		type = GRAPH_DPCM;
 	else if (strcmp(string, GRAPH_COMPATIBLE_MULTI) == 0)
 		type = GRAPH_MULTI;
+	else if (strcmp(string, GRAPH_COMPATIBLE_C2C) == 0)
+		type = GRAPH_C2C;
 end:
 #ifdef DEBUG
 	{
@@ -236,6 +280,9 @@  static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
 		case GRAPH_MULTI:
 			str = "MULTI";
 			break;
+		case GRAPH_C2C:
+			str = "Codec2Codec";
+			break;
 		default:
 			break;
 		}
@@ -778,6 +825,93 @@  int audio_graph2_link_multi(struct asoc_simple_priv *priv,
 }
 EXPORT_SYMBOL_GPL(audio_graph2_link_multi);
 
+int audio_graph2_link_c2c(struct asoc_simple_priv *priv,
+			  struct device_node *lnk,
+			  struct link_info *li)
+{
+	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+	struct snd_soc_pcm_stream *c2c_conf = dai_props->c2c_conf;
+	struct device_node *port  = lnk;
+	struct device_node *ports = of_get_parent(port);
+	struct device_node *top = of_get_parent(ports);
+	struct device_node *ep;
+	struct device_node *rep;
+	struct device_node *first_rep = NULL;
+	char dai_name[64];
+	u32 val;
+	int is_cpu;
+	int ret = -EINVAL;
+
+	/*
+	 *	top: CODEC2CODEC {
+	 *		compatible = "audio-graph-card2-codec2codec";
+	 *
+	 *		rate = <48000>;
+	 *		ports {
+	 * =>			lnk: port@0 { ep: endpoint { remote-endpoint = <&rep>; }; };
+	 *			     port@1 { ... };
+	 *		};
+	 *	};
+	 */
+	if (!of_get_property(top, "rate", &val)) {
+		struct device *dev = simple_priv_to_dev(priv);
+
+		dev_err(dev, "unknown codec2codec rate\n");
+		goto err;
+	}
+
+	c2c_conf->formats	= SNDRV_PCM_FMTBIT_S32_LE; /* update ME */
+	c2c_conf->rate_min	=
+	c2c_conf->rate_max	= val;
+	c2c_conf->channels_min	=
+	c2c_conf->channels_max	= 2; /* update ME */
+	dai_link->params	= c2c_conf;
+
+	of_node_get(lnk);
+	for (is_cpu = 1; is_cpu >= 0; is_cpu--) {
+		int is_single_links = 0;
+
+		ep  = port_to_endpoint(port);
+		rep = of_graph_get_remote_endpoint(ep);
+
+		if (!first_rep)
+			first_rep = rep;
+
+		ret = graph_parse_node(priv, rep, li, 0,
+				       is_cpu ? &is_single_links : NULL);
+		if (ret < 0)
+			goto err;
+
+		of_node_put(ep);
+		of_node_put(rep);
+
+		/*
+		 * 1st turn was for CPU   part of Codec (is_cpu = 1)
+		 * 2nd turn  is for Codec part of Codec (is_cpu = 0)
+		 */
+		if (is_cpu) {
+			struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
+
+			asoc_simple_canonicalize_cpu(cpus, is_single_links);
+
+			/* next port = Codec part port */
+			port = of_get_next_child(ports, port);
+		}
+	}
+
+	snprintf(dai_name, sizeof(dai_name), "codec2codec-%pOFP", top);
+
+	ret = graph_link_init(priv, first_rep, li, 0, dai_name);  /* Codec base */
+err:
+	of_node_put(ports);
+	of_node_put(port);
+	of_node_put(top);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_c2c);
+
 static int graph_link(struct asoc_simple_priv *priv,
 		      struct graph_custom_hooks *hooks,
 		      enum graph_type gtype,
@@ -807,6 +941,12 @@  static int graph_link(struct asoc_simple_priv *priv,
 		else
 			func = audio_graph2_link_multi;
 		break;
+	case GRAPH_C2C:
+		if (hooks && hooks->custom_c2c)
+			func = hooks->custom_c2c;
+		else
+			func = audio_graph2_link_c2c;
+		break;
 	}
 
 	if (!func) {
@@ -904,6 +1044,27 @@  static int graph_count_multi(struct asoc_simple_priv *priv,
 	return 0;
 }
 
+static int graph_count_c2c(struct asoc_simple_priv *priv,
+			   struct device_node *lnk,
+			   struct link_info *li)
+{
+	/*
+	 *	CODEC2CODEC {
+	 *		compatible = "audio-graph-card2-codec2codec";
+	 *
+	 *		ports {
+	 * =>			lnk: port@0 { endpoint { ... }; };
+	 *			     port@1 { endpoint { ... }; };
+	 *		};
+	 *	};
+	 */
+	li->num[li->link].cpus		= 1;
+	li->num[li->link].codecs	= 1;
+	li->num[li->link].c2c		= 1;
+
+	return 0;
+}
+
 static int graph_count(struct asoc_simple_priv *priv,
 		       struct graph_custom_hooks *hooks,
 		       enum graph_type gtype,
@@ -929,6 +1090,9 @@  static int graph_count(struct asoc_simple_priv *priv,
 	case GRAPH_MULTI:
 		func = graph_count_multi;
 		break;
+	case GRAPH_C2C:
+		func = graph_count_c2c;
+		break;
 	}
 
 	if (!func) {