From: Kuninori Morimoto <kuninori.morimoto.gx@xxxxxxxxxxx> This patch adds DPCM support to audio-graph-card2. The big difference between audio-graph-card2 DPCM and audio-graph-card DPCM is that audio-graph-card2 uses extra node (X) for it. ******* 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 (A) routing = "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback"; // indicate all Front-End, Back-End in DPCM case (B) links = <&dsp_fe0, &dsp_fe1, &dsp_fe2, &dsp_fe3, &dsp_be0, &dsp_be1, &dsp_be2, &dsp_be3, &dsp_be4, &dsp_be5>; }; (X) DSP { compatible = "audio-graph-card2-dsp"; // Front-End ports@0 { (B) 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 { (B) 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>; }; }; ... }; }; It needs to add routing (A) when DPCM case. "links" needs to indicate both Front-End and Back-End (B), instead of CPU/Codec node. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@xxxxxxxxxxx> --- include/sound/graph_card.h | 3 + sound/soc/generic/audio-graph-card2.c | 259 +++++++++++++++++++++++++- 2 files changed, 261 insertions(+), 1 deletion(-) diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h index b3185783caa7..03df4c5a7151 100644 --- a/include/sound/graph_card.h +++ b/include/sound/graph_card.h @@ -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 */ diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index f5edf1368e12..b288975ffde2 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -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) { -- 2.25.1