@@ -17,6 +17,7 @@ struct graph_custom_hooks {
int (*hook_pre)(struct asoc_simple_priv *priv);
int (*hook_post)(struct asoc_simple_priv *priv);
GRAPH_CUSTOM custom_normal;
+ GRAPH_CUSTOM custom_dpcm;
};
int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev);
@@ -25,5 +26,7 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev,
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);
#endif /* __GRAPH_CARD_H */
@@ -71,12 +71,78 @@
port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; };
};
+
+ ************************************
+ DSP Audio-Graph
+ ************************************
+
+ *******
+ PCM0 <--> * * <--> DAI0: Codec Headset
+ PCM1 <--> * * <--> DAI1: Codec Speakers
+ PCM2 <--> * DSP * <--> DAI2: MODEM
+ PCM3 <--> * * <--> DAI3: BT
+ * * <--> DAI4: DMIC
+ * * <--> DAI5: FM
+ *******
+
+ sound {
+ compatible = "audio-graph-card2";
+
+ // indicate routing
+ routing = "xxx Playback", "xxx Playback",
+ "xxx Playback", "xxx Playback",
+ "xxx Playback", "xxx Playback";
+
+ // indicate all Front-End, Back-End in DPCM case
+ links = <&dsp_fe0, &dsp_fe1, &dsp_fe2, &dsp_fe3,
+ &dsp_be0, &dsp_be1, &dsp_be2, &dsp_be3, &dsp_be4, &dsp_be5>;
+ };
+
+ DSP {
+ compatible = "audio-graph-card2-dsp";
+
+ // Front-End
+ ports@0 {
+ dsp_fe0: port@0 { dsp_fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; };
+ dsp_fe1: port@1 { dsp_fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; };
+ ...
+ };
+
+ // Back-End
+ ports@1 {
+ dsp_be0: port@0 { dsp_be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; };
+ dsp_be1: port@1 { dsp_be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; };
+ ...
+ };
+ ...
+ };
+
+ CPU {
+ ports {
+ bitclock-master;
+ frame-master;
+ port@0 { pcm0_ep: endpoint { remote-endpoint = <&dsp_fe0_ep>; }; };
+ port@1 { pcm1_ep: endpoint { remote-endpoint = <&dsp_fe1_ep>; }; };
+ ...
+ };
+ };
+
+ Codec {
+ ports {
+ port@0 { dai0_ep: endpoint { remote-endpoint = <&dsp_be0_ep>; }; };
+ port@1 { dai1_ep: endpoint { remote-endpoint = <&dsp_be1_ep>; }; };
+ ...
+ };
+ };
*/
enum graph_type {
GRAPH_NORMAL,
+ GRAPH_DPCM,
};
+#define GRAPH_COMPATIBLE_DPCM "audio-graph-card2-dsp"
+
#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
@@ -98,11 +164,24 @@ static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
if (ret < 0)
goto end;
+ if (strcmp(string, GRAPH_COMPATIBLE_DPCM) == 0)
+ type = GRAPH_DPCM;
end:
#ifdef DEBUG
{
const char *str = "Normal";
-
+ struct device *dev = simple_priv_to_dev(priv);
+
+ switch (type) {
+ case GRAPH_DPCM:
+ if (asoc_graph_is_ports0(link))
+ str = "DPCM Front-End";
+ else
+ str = "DPCM Back-End";
+ break;
+ default:
+ break;
+ }
dev_dbg(dev, "%pOF (%s)", link, str);
}
#endif
@@ -220,6 +299,22 @@ static int asoc_simple_parse_dai(struct device_node *ep,
return 0;
}
+static void graph_parse_convert(struct device_node *ep,
+ struct simple_dai_props *props)
+{
+ struct device_node *port = of_get_parent(ep);
+ struct device_node *ports = of_get_parent(port);
+ struct asoc_simple_data *adata = &props->adata;
+
+ if (of_node_name_eq(ports, "ports"))
+ asoc_simple_parse_convert(ports, NULL, adata);
+ asoc_simple_parse_convert(port, NULL, adata);
+ asoc_simple_parse_convert(ep, NULL, adata);
+
+ of_node_put(port);
+ of_node_put(ports);
+}
+
static void graph_parse_mclk_fs(struct device_node *ep,
struct simple_dai_props *props)
{
@@ -432,6 +527,128 @@ int audio_graph2_link_normal(struct asoc_simple_priv *priv,
}
EXPORT_SYMBOL_GPL(audio_graph2_link_normal);
+int audio_graph2_link_dpcm(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ struct device_node *ep = port_to_endpoint(lnk);
+ struct device_node *rep = of_graph_get_remote_endpoint(ep);
+ 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);
+ char dai_name[64];
+ int is_cpu = asoc_graph_is_ports0(lnk);
+ int ret;
+
+ if (is_cpu) {
+ struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
+ int is_single_links = 0;
+
+ /*
+ * DSP {
+ * compatible = "audio-graph-card2-dsp";
+ *
+ * // Front-End
+ * ports@0 {
+ * => lnk: port@0 { ep: endpoint { remote-endpoint = <&rep>; }; };
+ * ...
+ * };
+ * // Back-End
+ * ports@0 {
+ * ...
+ * };
+ * };
+ *
+ * CPU {
+ * rports: ports {
+ * rport: port@0 { rep: endpoint { ... }; };
+ * }
+ * }
+ */
+ /*
+ * setup CPU here, Codec is already set as dummy.
+ * see
+ * asoc_simple_init_priv()
+ */
+ dai_link->dynamic = 1;
+ dai_link->dpcm_merged_format = 1;
+
+ ret = graph_parse_node(priv, rep, li, 0, &is_single_links);
+ if (ret)
+ goto err;
+
+ snprintf(dai_name, sizeof(dai_name),
+ "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
+
+ asoc_simple_canonicalize_cpu(cpus, is_single_links);
+ } else {
+ struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
+ struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
+ struct device_node *rport;
+ struct device_node *rports;
+
+ /*
+ * DSP {
+ * compatible = "audio-graph-card2-dsp";
+ *
+ * // Front-End
+ * ports@0 {
+ * ...
+ * };
+ * // Back-End
+ * ports@0 {
+ * => lnk: port@0 { ep: endpoint { remote-endpoint = <&rep>; }; };
+ * ...
+ * };
+ * };
+ *
+ * Codec {
+ * rports: ports {
+ * rport: port@0 { rep: endpoint { ... }; };
+ * }
+ * }
+ */
+ /*
+ * setup Codec here, CPU is already set as dummy.
+ * see
+ * asoc_simple_init_priv()
+ */
+
+ /* BE settings */
+ dai_link->no_pcm = 1;
+ dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
+
+ ret = graph_parse_node(priv, rep, li, 0, NULL);
+ if (ret < 0)
+ goto err;
+
+ snprintf(dai_name, sizeof(dai_name),
+ "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
+
+ /* check "prefix" from top node */
+ rport = of_get_parent(rep);
+ rports = of_get_parent(rport);
+
+ if (of_node_name_eq(rports, "ports"))
+ snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix");
+ snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix");
+
+ of_node_put(rport);
+ of_node_put(rports);
+ }
+
+ graph_parse_convert(rep, dai_props);
+
+ snd_soc_dai_link_set_capabilities(dai_link);
+
+ ret = graph_link_init(priv, rep, li, is_cpu, dai_name);
+err:
+ of_node_put(ep);
+ of_node_put(rep);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm);
+
static int graph_link(struct asoc_simple_priv *priv,
struct graph_custom_hooks *hooks,
enum graph_type gtype,
@@ -449,6 +666,12 @@ static int graph_link(struct asoc_simple_priv *priv,
else
func = audio_graph2_link_normal;
break;
+ case GRAPH_DPCM:
+ if (hooks && hooks->custom_dpcm)
+ func = hooks->custom_dpcm;
+ else
+ func = audio_graph2_link_dpcm;
+ break;
}
if (!func) {
@@ -480,6 +703,37 @@ static int graph_count_normal(struct asoc_simple_priv *priv,
return 0;
}
+static int graph_count_dsp(struct asoc_simple_priv *priv,
+ struct device_node *lnk,
+ struct link_info *li)
+{
+ /*
+ * DSP {
+ * compatible = "audio-graph-card2-dsp";
+ *
+ * // Front-End
+ * ports@0 {
+ * => lnk: port@0 { endpoint { ... }; };
+ * ...
+ * };
+ * // Back-End
+ * ports@1 {
+ * => lnk: port@0 { endpoint { ... }; };
+ * ...
+ * };
+ * };
+ */
+ if (asoc_graph_is_ports0(lnk)) {
+ /* Front-End */
+ li->num[li->link].cpus = 1;
+ } else {
+ /* Back-End */
+ li->num[li->link].codecs = 1;
+ }
+
+ return 0;
+}
+
static int graph_count(struct asoc_simple_priv *priv,
struct graph_custom_hooks *hooks,
enum graph_type gtype,
@@ -499,6 +753,9 @@ static int graph_count(struct asoc_simple_priv *priv,
case GRAPH_NORMAL:
func = graph_count_normal;
break;
+ case GRAPH_DPCM:
+ func = graph_count_dsp;
+ break;
}
if (!func) {