ASoC: audio-graph-card2: add Multi CPU/Codec support
This patch adds Multi CPU/Codec support to audio-graph-card2. Multi CPU/Codec will have connection part (= X) and CPU/Codec list part (= Y). links indicates connection part of CPU side (= A). +-+ (A) +-+ CPU1 --(Y) | | <-(X)--(X)-> | | (Y)-- Codec1 CPU2 --(Y) | | | | (Y)-- Codec2 +-+ +-+ sound { compatible = "audio-graph-card2"; (A) links = <&mcpu>; multi { ports@0 { (X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; (Y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; (Y) port@1 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; }; ports@1 { (X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; (Y) port@0 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; (Y) port@1 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; }; }; }; CPU { ports { bitclock-master; frame-master; port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; }; }; Codec { ports { port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; }; }; Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/20210804171748.GC26252@sirena.org.uk Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Link: https://lore.kernel.org/r/871r4qn8pk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
6e5f68fe3f
commit
c8c74939f7
1 changed files with 186 additions and 10 deletions
|
@ -69,18 +69,95 @@
|
||||||
port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; };
|
port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
************************************
|
||||||
|
Multi-CPU/Codec
|
||||||
|
************************************
|
||||||
|
|
||||||
|
It has connection part (= X) and list part (= y).
|
||||||
|
links indicates connection part of CPU side (= A).
|
||||||
|
|
||||||
|
+-+ (A) +-+
|
||||||
|
CPU1 --(y) | | <-(X)--(X)-> | | (y)-- Codec1
|
||||||
|
CPU2 --(y) | | | | (y)-- Codec2
|
||||||
|
+-+ +-+
|
||||||
|
|
||||||
|
sound {
|
||||||
|
compatible = "audio-graph-card2";
|
||||||
|
|
||||||
|
(A) links = <&mcpu>;
|
||||||
|
|
||||||
|
multi {
|
||||||
|
ports@0 {
|
||||||
|
(X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
|
||||||
|
(y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
|
||||||
|
(y) port@1 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
|
||||||
|
};
|
||||||
|
ports@1 {
|
||||||
|
(X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
|
||||||
|
(y) port@0 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
|
||||||
|
(y) port@1 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
CPU {
|
||||||
|
ports {
|
||||||
|
bitclock-master;
|
||||||
|
frame-master;
|
||||||
|
port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
|
||||||
|
port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Codec {
|
||||||
|
ports {
|
||||||
|
port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
|
||||||
|
port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum graph_type {
|
enum graph_type {
|
||||||
GRAPH_NORMAL,
|
GRAPH_NORMAL,
|
||||||
|
|
||||||
|
GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define GRAPH_NODENAME_MULTI "multi"
|
||||||
|
|
||||||
#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
|
#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")
|
||||||
|
|
||||||
|
static enum graph_type __graph_get_type(struct device_node *lnk)
|
||||||
|
{
|
||||||
|
struct device_node *np;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* target {
|
||||||
|
* ports {
|
||||||
|
* => lnk: port@0 { ... };
|
||||||
|
* port@1 { ... };
|
||||||
|
* };
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
np = of_get_parent(lnk);
|
||||||
|
if (of_node_name_eq(np, "ports"))
|
||||||
|
np = of_get_parent(np);
|
||||||
|
|
||||||
|
if (of_node_name_eq(np, GRAPH_NODENAME_MULTI))
|
||||||
|
return GRAPH_MULTI;
|
||||||
|
|
||||||
|
return GRAPH_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
|
static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
|
||||||
struct device_node *lnk)
|
struct device_node *lnk)
|
||||||
{
|
{
|
||||||
enum graph_type type = GRAPH_NORMAL;
|
enum graph_type type = __graph_get_type(lnk);
|
||||||
|
|
||||||
|
/* GRAPH_MULTI here means GRAPH_NORMAL */
|
||||||
|
if (type == GRAPH_MULTI)
|
||||||
|
type = GRAPH_NORMAL;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
{
|
{
|
||||||
|
@ -93,6 +170,49 @@ static enum graph_type graph_get_type(struct asoc_simple_priv *priv,
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int graph_lnk_is_multi(struct device_node *lnk)
|
||||||
|
{
|
||||||
|
return __graph_get_type(lnk) == GRAPH_MULTI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_node *graph_get_next_multi_ep(struct device_node **port)
|
||||||
|
{
|
||||||
|
struct device_node *ports = of_get_parent(*port);
|
||||||
|
struct device_node *ep = NULL;
|
||||||
|
struct device_node *rep = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* multi {
|
||||||
|
* ports {
|
||||||
|
* => lnk: port@0 { ... };
|
||||||
|
* port@1 { ep { ... = rep0 } };
|
||||||
|
* port@2 { ep { ... = rep1 } };
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* xxx {
|
||||||
|
* port@0 { rep0 };
|
||||||
|
* port@1 { rep1 };
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
*port = of_get_next_child(ports, *port);
|
||||||
|
if (!*port)
|
||||||
|
break;
|
||||||
|
} while (!of_node_name_eq(*port, "port"));
|
||||||
|
|
||||||
|
if (*port) {
|
||||||
|
ep = port_to_endpoint(*port);
|
||||||
|
rep = of_graph_get_remote_endpoint(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
of_node_put(ep);
|
||||||
|
of_node_put(ports);
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct snd_soc_ops graph_ops = {
|
static const struct snd_soc_ops graph_ops = {
|
||||||
.startup = asoc_simple_startup,
|
.startup = asoc_simple_startup,
|
||||||
.shutdown = asoc_simple_shutdown,
|
.shutdown = asoc_simple_shutdown,
|
||||||
|
@ -258,13 +378,21 @@ static int __graph_parse_node(struct asoc_simple_priv *priv,
|
||||||
if (!dai_link->name) {
|
if (!dai_link->name) {
|
||||||
struct snd_soc_dai_link_component *cpus = dlc;
|
struct snd_soc_dai_link_component *cpus = dlc;
|
||||||
struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx);
|
struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx);
|
||||||
|
char *cpu_multi = "";
|
||||||
|
char *codec_multi = "";
|
||||||
|
|
||||||
|
if (dai_link->num_cpus > 1)
|
||||||
|
cpu_multi = "_multi";
|
||||||
|
if (dai_link->num_codecs > 1)
|
||||||
|
codec_multi = "_multi";
|
||||||
|
|
||||||
switch (gtype) {
|
switch (gtype) {
|
||||||
case GRAPH_NORMAL:
|
case GRAPH_NORMAL:
|
||||||
/* run is_cpu only. see audio_graph2_link_normal() */
|
/* run is_cpu only. see audio_graph2_link_normal() */
|
||||||
if (is_cpu)
|
if (is_cpu)
|
||||||
asoc_simple_set_dailink_name(dev, dai_link, "%s-%s",
|
asoc_simple_set_dailink_name(dev, dai_link, "%s%s-%s%s",
|
||||||
cpus->dai_name, codecs->dai_name);
|
cpus->dai_name, cpu_multi,
|
||||||
|
codecs->dai_name, codec_multi);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -287,10 +415,33 @@ static int graph_parse_node(struct asoc_simple_priv *priv,
|
||||||
struct device_node *port,
|
struct device_node *port,
|
||||||
struct link_info *li, int is_cpu)
|
struct link_info *li, int is_cpu)
|
||||||
{
|
{
|
||||||
struct device_node *ep = port_to_endpoint(port);
|
struct device_node *ep;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
/* Need Multi support later */
|
if (graph_lnk_is_multi(port)) {
|
||||||
return __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
|
int idx;
|
||||||
|
|
||||||
|
of_node_get(port);
|
||||||
|
|
||||||
|
for (idx = 0;; idx++) {
|
||||||
|
ep = graph_get_next_multi_ep(&port);
|
||||||
|
if (!ep)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = __graph_parse_node(priv, gtype, ep,
|
||||||
|
li, is_cpu, idx);
|
||||||
|
of_node_put(ep);
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Single CPU / Codec */
|
||||||
|
ep = port_to_endpoint(port);
|
||||||
|
ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
|
||||||
|
of_node_put(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void graph_parse_daifmt(struct device_node *node,
|
static void graph_parse_daifmt(struct device_node *node,
|
||||||
|
@ -354,8 +505,14 @@ static void graph_link_init(struct asoc_simple_priv *priv,
|
||||||
unsigned int daifmt = 0, daiclk = 0;
|
unsigned int daifmt = 0, daiclk = 0;
|
||||||
unsigned int bit_frame = 0;
|
unsigned int bit_frame = 0;
|
||||||
|
|
||||||
/* Need Multi support later */
|
if (graph_lnk_is_multi(port)) {
|
||||||
ep = port_to_endpoint(port);
|
of_node_get(port);
|
||||||
|
ep = graph_get_next_multi_ep(&port);
|
||||||
|
port = of_get_parent(ep);
|
||||||
|
} else {
|
||||||
|
ep = port_to_endpoint(port);
|
||||||
|
}
|
||||||
|
|
||||||
ports = of_get_parent(port);
|
ports = of_get_parent(port);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -462,8 +619,27 @@ err:
|
||||||
|
|
||||||
static int graph_counter(struct device_node *lnk)
|
static int graph_counter(struct device_node *lnk)
|
||||||
{
|
{
|
||||||
/* Need Multi support later */
|
/*
|
||||||
return 1;
|
* Multi CPU / Codec
|
||||||
|
*
|
||||||
|
* multi {
|
||||||
|
* ports {
|
||||||
|
* => lnk: port@0 { ... };
|
||||||
|
* port@1 { ... };
|
||||||
|
* port@2 { ... };
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* ignore first lnk part
|
||||||
|
*/
|
||||||
|
if (graph_lnk_is_multi(lnk))
|
||||||
|
return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1;
|
||||||
|
/*
|
||||||
|
* Single CPU / Codec
|
||||||
|
*/
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int graph_count_normal(struct asoc_simple_priv *priv,
|
static int graph_count_normal(struct asoc_simple_priv *priv,
|
||||||
|
|
Loading…
Add table
Reference in a new issue