ASoC: audio-graph-card2: add DPCM support
This patch adds DPCM support to audio-graph-card2. It uses "dpcm" node (= D), needs to have routing (= A), need to indicate both FE/BE at links (= B, C). dpcm ports@0 is for FE (= B), port@1 is for BE (= C). remote-endpoint can use both Single/Multi connection. DSP ************ PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers PCM2 <--> * fe2 be2 * <--> DAI2: MODEM PCM3 <--> * fe3 be3 * <--> DAI3: BT * be4 * <--> DAI4: DMIC * be5 * <--> 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 = <&fe0, &fe1, ... (C) &be0, &be1, ... (D) dpcm { // Front-End ports@0 { (B) fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; }; (B) fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; }; ... }; // Back-End ports@1 { (C) be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; }; (C) be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; }; ... }; }; }; CPU { ports { bitclock-master; frame-master; port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; }; port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; }; ... }; }; Codec { ports { port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; }; port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; }; ... }; }; Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Link: https://lore.kernel.org/r/87zgrelu4v.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
c8c74939f7
commit
f03beb55a8
|
@ -17,6 +17,7 @@ struct graph2_custom_hooks {
|
||||||
int (*hook_pre)(struct asoc_simple_priv *priv);
|
int (*hook_pre)(struct asoc_simple_priv *priv);
|
||||||
int (*hook_post)(struct asoc_simple_priv *priv);
|
int (*hook_post)(struct asoc_simple_priv *priv);
|
||||||
GRAPH2_CUSTOM custom_normal;
|
GRAPH2_CUSTOM custom_normal;
|
||||||
|
GRAPH2_CUSTOM custom_dpcm;
|
||||||
};
|
};
|
||||||
|
|
||||||
int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev);
|
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,
|
int audio_graph2_link_normal(struct asoc_simple_priv *priv,
|
||||||
struct device_node *lnk, struct link_info *li);
|
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 */
|
#endif /* __GRAPH_CARD_H */
|
||||||
|
|
|
@ -116,15 +116,77 @@ links indicates connection part of CPU side (= A).
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
************************************
|
||||||
|
DPCM
|
||||||
|
************************************
|
||||||
|
|
||||||
|
DSP
|
||||||
|
************
|
||||||
|
PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset
|
||||||
|
PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers
|
||||||
|
PCM2 <--> * fe2 be2 * <--> DAI2: MODEM
|
||||||
|
PCM3 <--> * fe3 be3 * <--> DAI3: BT
|
||||||
|
* be4 * <--> DAI4: DMIC
|
||||||
|
* be5 * <--> 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
|
||||||
|
links = <&fe0, &fe1, ...,
|
||||||
|
&be0, &be1, ...>;
|
||||||
|
|
||||||
|
dpcm {
|
||||||
|
// Front-End
|
||||||
|
ports@0 {
|
||||||
|
fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; };
|
||||||
|
fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; };
|
||||||
|
...
|
||||||
|
};
|
||||||
|
// Back-End
|
||||||
|
ports@1 {
|
||||||
|
be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; };
|
||||||
|
be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; };
|
||||||
|
...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
CPU {
|
||||||
|
ports {
|
||||||
|
bitclock-master;
|
||||||
|
frame-master;
|
||||||
|
port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; };
|
||||||
|
port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; };
|
||||||
|
...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Codec {
|
||||||
|
ports {
|
||||||
|
port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; };
|
||||||
|
port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; };
|
||||||
|
...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum graph_type {
|
enum graph_type {
|
||||||
GRAPH_NORMAL,
|
GRAPH_NORMAL,
|
||||||
|
GRAPH_DPCM,
|
||||||
|
|
||||||
GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */
|
GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GRAPH_NODENAME_MULTI "multi"
|
#define GRAPH_NODENAME_MULTI "multi"
|
||||||
|
#define GRAPH_NODENAME_DPCM "dpcm"
|
||||||
|
|
||||||
#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
|
#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
|
||||||
|
|
||||||
|
@ -147,6 +209,9 @@ static enum graph_type __graph_get_type(struct device_node *lnk)
|
||||||
if (of_node_name_eq(np, GRAPH_NODENAME_MULTI))
|
if (of_node_name_eq(np, GRAPH_NODENAME_MULTI))
|
||||||
return GRAPH_MULTI;
|
return GRAPH_MULTI;
|
||||||
|
|
||||||
|
if (of_node_name_eq(np, GRAPH_NODENAME_DPCM))
|
||||||
|
return GRAPH_DPCM;
|
||||||
|
|
||||||
return GRAPH_NORMAL;
|
return GRAPH_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +229,17 @@ static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
|
||||||
struct device *dev = simple_priv_to_dev(priv);
|
struct device *dev = simple_priv_to_dev(priv);
|
||||||
const char *str = "Normal";
|
const char *str = "Normal";
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case GRAPH_DPCM:
|
||||||
|
if (asoc_graph_is_ports0(lnk))
|
||||||
|
str = "DPCM Front-End";
|
||||||
|
else
|
||||||
|
str = "DPCM Back-End";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "%pOF (%s)", lnk, str);
|
dev_dbg(dev, "%pOF (%s)", lnk, str);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -322,6 +398,22 @@ static int asoc_simple_parse_dai(struct device_node *ep,
|
||||||
return 0;
|
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,
|
static void graph_parse_mclk_fs(struct device_node *ep,
|
||||||
struct simple_dai_props *props)
|
struct simple_dai_props *props)
|
||||||
{
|
{
|
||||||
|
@ -394,11 +486,37 @@ static int __graph_parse_node(struct asoc_simple_priv *priv,
|
||||||
cpus->dai_name, cpu_multi,
|
cpus->dai_name, cpu_multi,
|
||||||
codecs->dai_name, codec_multi);
|
codecs->dai_name, codec_multi);
|
||||||
break;
|
break;
|
||||||
|
case GRAPH_DPCM:
|
||||||
|
if (is_cpu)
|
||||||
|
asoc_simple_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s",
|
||||||
|
cpus->of_node, cpus->dai_name, cpu_multi);
|
||||||
|
else
|
||||||
|
asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s",
|
||||||
|
codecs->of_node, codecs->dai_name, codec_multi);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check "prefix" from top node
|
||||||
|
* if DPCM-BE case
|
||||||
|
*/
|
||||||
|
if (!is_cpu && gtype == GRAPH_DPCM) {
|
||||||
|
struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx);
|
||||||
|
struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx);
|
||||||
|
struct device_node *rport = of_get_parent(ep);
|
||||||
|
struct device_node *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);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_cpu) {
|
if (is_cpu) {
|
||||||
struct snd_soc_dai_link_component *cpus = dlc;
|
struct snd_soc_dai_link_component *cpus = dlc;
|
||||||
struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, idx);
|
struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, idx);
|
||||||
|
@ -582,6 +700,98 @@ err:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(audio_graph2_link_normal);
|
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 device_node *rport = of_graph_get_remote_port(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);
|
||||||
|
int is_cpu = asoc_graph_is_ports0(lnk);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (is_cpu) {
|
||||||
|
/*
|
||||||
|
* dpcm {
|
||||||
|
* // Front-End
|
||||||
|
* ports@0 {
|
||||||
|
* => lnk: port@0 { ep: { ... = rep }; };
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* // Back-End
|
||||||
|
* ports@0 {
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* CPU {
|
||||||
|
* rports: ports {
|
||||||
|
* rport: port@0 { rep: { ... = ep } };
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* 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, GRAPH_DPCM, rport, li, 1);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* dpcm {
|
||||||
|
* // Front-End
|
||||||
|
* ports@0 {
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* // Back-End
|
||||||
|
* ports@0 {
|
||||||
|
* => lnk: port@0 { ep: { ... = rep; }; };
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Codec {
|
||||||
|
* rports: ports {
|
||||||
|
* rport: port@0 { rep: { ... = ep; }; };
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* 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, GRAPH_DPCM, rport, li, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph_parse_convert(rep, dai_props);
|
||||||
|
|
||||||
|
snd_soc_dai_link_set_capabilities(dai_link);
|
||||||
|
|
||||||
|
graph_link_init(priv, rport, li, is_cpu);
|
||||||
|
err:
|
||||||
|
of_node_put(ep);
|
||||||
|
of_node_put(rep);
|
||||||
|
of_node_put(rport);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm);
|
||||||
|
|
||||||
static int graph_link(struct asoc_simple_priv *priv,
|
static int graph_link(struct asoc_simple_priv *priv,
|
||||||
struct graph2_custom_hooks *hooks,
|
struct graph2_custom_hooks *hooks,
|
||||||
enum graph_type gtype,
|
enum graph_type gtype,
|
||||||
|
@ -599,6 +809,12 @@ static int graph_link(struct asoc_simple_priv *priv,
|
||||||
else
|
else
|
||||||
func = audio_graph2_link_normal;
|
func = audio_graph2_link_normal;
|
||||||
break;
|
break;
|
||||||
|
case GRAPH_DPCM:
|
||||||
|
if (hooks && hooks->custom_dpcm)
|
||||||
|
func = hooks->custom_dpcm;
|
||||||
|
else
|
||||||
|
func = audio_graph2_link_dpcm;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -665,6 +881,41 @@ static int graph_count_normal(struct asoc_simple_priv *priv,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int graph_count_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 *rport = of_graph_get_remote_port(ep);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dpcm {
|
||||||
|
* // Front-End
|
||||||
|
* ports@0 {
|
||||||
|
* => lnk: port@0 { endpoint { ... }; };
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* // Back-End
|
||||||
|
* ports@1 {
|
||||||
|
* => lnk: port@0 { endpoint { ... }; };
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (asoc_graph_is_ports0(lnk)) {
|
||||||
|
li->num[li->link].cpus = graph_counter(rport); /* FE */
|
||||||
|
li->num[li->link].platforms = graph_counter(rport);
|
||||||
|
} else {
|
||||||
|
li->num[li->link].codecs = graph_counter(rport); /* BE */
|
||||||
|
}
|
||||||
|
|
||||||
|
of_node_put(ep);
|
||||||
|
of_node_put(rport);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int graph_count(struct asoc_simple_priv *priv,
|
static int graph_count(struct asoc_simple_priv *priv,
|
||||||
struct graph2_custom_hooks *hooks,
|
struct graph2_custom_hooks *hooks,
|
||||||
enum graph_type gtype,
|
enum graph_type gtype,
|
||||||
|
@ -684,6 +935,9 @@ static int graph_count(struct asoc_simple_priv *priv,
|
||||||
case GRAPH_NORMAL:
|
case GRAPH_NORMAL:
|
||||||
func = graph_count_normal;
|
func = graph_count_normal;
|
||||||
break;
|
break;
|
||||||
|
case GRAPH_DPCM:
|
||||||
|
func = graph_count_dpcm;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue