[PATCH v4 08/16] ASoC: audio-graph-card2: add Codec2Codec support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Kuninori Morimoto <kuninori.morimoto.gx@xxxxxxxxxxx>

This patch adds Codec2Codec support to audio-graph-card2.
It can use Codec2Codec but very simple case only for now.
It doesn't have "SWITCH" control yet, thus it start automatically
when it was probed, and 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.

Codec has SND_SOC_DAPM_INPUT() (= IN) / SND_SOC_DAPM_OUTPUT(= OUT)
widgets in below case.

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

It needs "codec2codec" node (= C), needs to have routing (= A),
need to indicate both CPU side at links (= B).
ports@0 is for CPU side (= X), port@1 is Codec side (= Y).
It needs to have "rate" (= D)

	+--+
	|  |<-- Codec0 <-- IN
	|  |--> Codec1 --> OUT
	+--+

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

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

(B)		links = <&c2c>;

(C)		codec2codec {
			ports {
(D)				rate = <48000>;
(X)			c2c:	port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
(Y)				port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
		};
	};

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

Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@xxxxxxxxxxx
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@xxxxxxxxxxx>
---
 include/sound/graph_card.h            |   3 +
 sound/soc/generic/audio-graph-card2.c | 181 ++++++++++++++++++++++++++
 2 files changed, 184 insertions(+)

diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h
index ece78a84391c..4c8b94c77b8e 100644
--- a/include/sound/graph_card.h
+++ b/include/sound/graph_card.h
@@ -18,6 +18,7 @@ struct graph2_custom_hooks {
 	int (*hook_post)(struct asoc_simple_priv *priv);
 	GRAPH2_CUSTOM custom_normal;
 	GRAPH2_CUSTOM custom_dpcm;
+	GRAPH2_CUSTOM custom_c2c;
 };
 
 int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev);
@@ -28,5 +29,7 @@ int audio_graph2_link_normal(struct asoc_simple_priv *priv,
 			     struct device_node *lnk, struct link_info *li);
 int audio_graph2_link_dpcm(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 56e9e6c3b86e..b6049bcfb771 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -176,17 +176,54 @@ links indicates connection part of CPU side (= A).
 	};
  };
 
+ ************************************
+	Codec to Codec
+ ************************************
+
+ +--+
+ |  |<-- Codec0 <- IN
+ |  |--> Codec1 -> OUT
+ +--+
+
+ sound {
+	compatible = "audio-graph-card2";
+
+	routing = "OUT" ,"DAI1 Playback",
+		  "DAI0 Capture", "IN";
+
+	links = <&c2c>;
+
+	codec2codec {
+		ports {
+			rate = <48000>;
+		c2c:	port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
+			port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
+	};
+ };
+
+ Codec {
+	ports {
+		port@0 {
+			bitclock-master;
+			frame-master;
+			 codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
+		port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
+	};
+ };
+
 */
 
 enum graph_type {
 	GRAPH_NORMAL,
 	GRAPH_DPCM,
+	GRAPH_C2C,
 
 	GRAPH_MULTI,	/* don't use ! Use this only in __graph_get_type() */
 };
 
 #define GRAPH_NODENAME_MULTI	"multi"
 #define GRAPH_NODENAME_DPCM	"dpcm"
+#define GRAPH_NODENAME_C2C	"codec2codec"
 
 #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
 
@@ -212,6 +249,9 @@ static enum graph_type __graph_get_type(struct device_node *lnk)
 	if (of_node_name_eq(np, GRAPH_NODENAME_DPCM))
 		return GRAPH_DPCM;
 
+	if (of_node_name_eq(np, GRAPH_NODENAME_C2C))
+		return GRAPH_C2C;
+
 	return GRAPH_NORMAL;
 }
 
@@ -236,6 +276,9 @@ static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
 			else
 				str = "DPCM Back-End";
 			break;
+		case GRAPH_C2C:
+			str = "Codec2Codec";
+			break;
 		default:
 			break;
 		}
@@ -494,6 +537,13 @@ static int __graph_parse_node(struct asoc_simple_priv *priv,
 				asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s",
 						codecs->of_node, codecs->dai_name, codec_multi);
 			break;
+		case GRAPH_C2C:
+			/* run is_cpu only. see audio_graph2_link_c2c() */
+			if (is_cpu)
+				asoc_simple_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s",
+							     cpus->dai_name,   cpu_multi,
+							     codecs->dai_name, codec_multi);
+			break;
 		default:
 			break;
 		}
@@ -792,6 +842,91 @@ int audio_graph2_link_dpcm(struct asoc_simple_priv *priv,
 }
 EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm);
 
+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 *port0, *port1, *ports;
+	struct device_node *codec0_port, *codec1_port;
+	struct device_node *ep0, *ep1;
+	u32 val;
+	int ret = -EINVAL;
+
+	/*
+	 * codec2codec {
+	 *	ports {
+	 *		rate = <48000>;
+	 * =>	lnk:	port@0 { c2c0_ep: { ... = codec0_ep; }; };
+	 *		port@1 { c2c1_ep: { ... = codec1_ep; }; };
+	 *	};
+	 * };
+	 *
+	 * Codec {
+	 *	ports {
+	 *		port@0 { codec0_ep: ... }; };
+	 *		port@1 { codec1_ep: ... }; };
+	 *	};
+	 * };
+	 */
+	of_node_get(lnk);
+	port0 = lnk;
+	ports = of_get_parent(port0);
+	port1 = of_get_next_child(ports, lnk);
+
+	if (!of_get_property(ports, "rate", &val)) {
+		struct device *dev = simple_priv_to_dev(priv);
+
+		dev_err(dev, "Codec2Codec needs rate settings\n");
+		goto err1;
+	}
+
+	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;
+
+	ep0 = port_to_endpoint(port0);
+	ep1 = port_to_endpoint(port1);
+
+	codec0_port = of_graph_get_remote_port(ep0);
+	codec1_port = of_graph_get_remote_port(ep1);
+
+	/*
+	 * call Codec first.
+	 * see
+	 *	__graph_parse_node() :: DAI Naming
+	 */
+	ret = graph_parse_node(priv, GRAPH_C2C, codec1_port, li, 0);
+	if (ret < 0)
+		goto err2;
+
+	/*
+	 * call CPU, and set DAI Name
+	 */
+	ret = graph_parse_node(priv, GRAPH_C2C, codec0_port, li, 1);
+	if (ret < 0)
+		goto err2;
+
+	graph_link_init(priv, codec0_port, li, 1);
+err2:
+	of_node_put(ep0);
+	of_node_put(ep1);
+	of_node_put(codec0_port);
+	of_node_put(codec1_port);
+err1:
+	of_node_put(ports);
+	of_node_put(port0);
+	of_node_put(port1);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_c2c);
+
 static int graph_link(struct asoc_simple_priv *priv,
 		      struct graph2_custom_hooks *hooks,
 		      enum graph_type gtype,
@@ -815,6 +950,12 @@ static int graph_link(struct asoc_simple_priv *priv,
 		else
 			func = audio_graph2_link_dpcm;
 		break;
+	case GRAPH_C2C:
+		if (hooks && hooks->custom_c2c)
+			func = hooks->custom_c2c;
+		else
+			func = audio_graph2_link_c2c;
+		break;
 	default:
 		break;
 	}
@@ -916,6 +1057,43 @@ static int graph_count_dpcm(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)
+{
+	struct device_node *ports = of_get_parent(lnk);
+	struct device_node *port0 = lnk;
+	struct device_node *port1 = of_get_next_child(ports, lnk);
+	struct device_node *ep0 = port_to_endpoint(port0);
+	struct device_node *ep1 = port_to_endpoint(port1);
+	struct device_node *codec0 = of_graph_get_remote_port(ep0);
+	struct device_node *codec1 = of_graph_get_remote_port(ep1);
+
+	of_node_get(lnk);
+
+	/*
+	 * codec2codec {
+	 *	ports {
+	 * =>	lnk:	port@0 { endpoint { ... }; };
+	 *		port@1 { endpoint { ... }; };
+	 *	};
+	 * };
+	 */
+	li->num[li->link].cpus		=
+	li->num[li->link].platforms	= graph_counter(codec0);
+	li->num[li->link].codecs	= graph_counter(codec1);
+	li->num[li->link].c2c		= 1;
+
+	of_node_put(ports);
+	of_node_put(port1);
+	of_node_put(ep0);
+	of_node_put(ep1);
+	of_node_put(codec0);
+	of_node_put(codec1);
+
+	return 0;
+}
+
 static int graph_count(struct asoc_simple_priv *priv,
 		       struct graph2_custom_hooks *hooks,
 		       enum graph_type gtype,
@@ -938,6 +1116,9 @@ static int graph_count(struct asoc_simple_priv *priv,
 	case GRAPH_DPCM:
 		func = graph_count_dpcm;
 		break;
+	case GRAPH_C2C:
+		func = graph_count_c2c;
+		break;
 	default:
 		break;
 	}
-- 
2.25.1




[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Pulse Audio]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux