From patchwork Tue Jun 22 01:15:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuninori Morimoto X-Patchwork-Id: 465203 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4EAEBC4743C for ; Tue, 22 Jun 2021 01:17:55 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C272B6124B for ; Tue, 22 Jun 2021 01:17:54 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C272B6124B Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=renesas.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 318D3826; Tue, 22 Jun 2021 03:17:03 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 318D3826 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1624324673; bh=OvQoMrFel7/HTM0Fzubjz8c8orULZ8ZIj3s9GEqN9iM=; h=Date:From:Subject:To:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=B05v55F0antt6CQyXPunwUby5ilGd1nZO6tckDoDOgVeZA9jhXVNinbRe0SjACoYI pp6JP6cSmT5FYmUs5QaiT80Hqt9M16McUsKFRUjlxQeH3/fpeHc27WeuSBKyOCt+dc S51FH8ZruSyefmcy2h//07EqUiE0dAdUDqeeSa54= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 397E4F804D2; Tue, 22 Jun 2021 03:15:15 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 57F63F804DA; Tue, 22 Jun 2021 03:15:14 +0200 (CEST) Received: from relmlie5.idc.renesas.com (relmlor1.renesas.com [210.160.252.171]) by alsa1.perex.cz (Postfix) with ESMTP id 8BB76F804D2 for ; Tue, 22 Jun 2021 03:15:07 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 8BB76F804D2 Date: 22 Jun 2021 10:15:06 +0900 X-IronPort-AV: E=Sophos;i="5.83,290,1616425200"; d="scan'208";a="85109276" Received: from unknown (HELO relmlir6.idc.renesas.com) ([10.200.68.152]) by relmlie5.idc.renesas.com with ESMTP; 22 Jun 2021 10:15:06 +0900 Received: from mercury.renesas.com (unknown [10.166.252.133]) by relmlir6.idc.renesas.com (Postfix) with ESMTP id 66E5E4149817; Tue, 22 Jun 2021 10:15:06 +0900 (JST) Message-ID: <87r1gu3dk5.wl-kuninori.morimoto.gx@renesas.com> From: Kuninori Morimoto Subject: [PATCH RFC 07/15] ASoC: audio-graph-card2: add DPCM support User-Agent: Wanderlust/2.15.9 Emacs/26.3 Mule/6.0 To: Mark Brown , Rob Herring In-Reply-To: <871r8u4s6q.wl-kuninori.morimoto.gx@renesas.com> References: <871r8u4s6q.wl-kuninori.morimoto.gx@renesas.com> MIME-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Cc: Linux-ALSA X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Kuninori Morimoto 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 --- 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) {