Commit b2b6b2d8 authored by Mark Brown's avatar Mark Brown

ASoC: makes CPU/Codec channel connection map more

Merge series from Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>:

Current ASoC is supporting CPU/Codec = N:M (N < M) connection by using
ch_map idea. This patch-set expands it that all connection uses this idea,
and no longer N < M limit [1][2].

Link: https://lore.kernel.org/r/87fs6wuszr.wl-kuninori.morimoto.gx@renesas.com [1]
Link: https://lore.kernel.org/r/878r7yqeo4.wl-kuninori.morimoto.gx@renesas.com [2]

ASoC core code ([PATCH 1/5]) is same as v6 and it was tested by Pierre-Louis,
and Jerome. Big change on v7 is basically for Audio-Graph-Card2.
parents 9b3cd8eb 792846d9
...@@ -655,8 +655,45 @@ struct snd_soc_dai_link_component { ...@@ -655,8 +655,45 @@ struct snd_soc_dai_link_component {
struct of_phandle_args *dai_args; struct of_phandle_args *dai_args;
}; };
struct snd_soc_dai_link_codec_ch_map { /*
unsigned int connected_cpu_id; * [dai_link->ch_maps Image sample]
*
*-------------------------
* CPU0 <---> Codec0
*
* ch-map[0].cpu = 0 ch-map[0].codec = 0
*
*-------------------------
* CPU0 <---> Codec0
* CPU1 <---> Codec1
* CPU2 <---> Codec2
*
* ch-map[0].cpu = 0 ch-map[0].codec = 0
* ch-map[1].cpu = 1 ch-map[1].codec = 1
* ch-map[2].cpu = 2 ch-map[2].codec = 2
*
*-------------------------
* CPU0 <---> Codec0
* CPU1 <-+-> Codec1
* CPU2 <-/
*
* ch-map[0].cpu = 0 ch-map[0].codec = 0
* ch-map[1].cpu = 1 ch-map[1].codec = 1
* ch-map[2].cpu = 2 ch-map[2].codec = 1
*
*-------------------------
* CPU0 <---> Codec0
* CPU1 <-+-> Codec1
* \-> Codec2
*
* ch-map[0].cpu = 0 ch-map[0].codec = 0
* ch-map[1].cpu = 1 ch-map[1].codec = 1
* ch-map[2].cpu = 1 ch-map[2].codec = 2
*
*/
struct snd_soc_dai_link_ch_map {
unsigned int cpu;
unsigned int codec;
unsigned int ch_mask; unsigned int ch_mask;
}; };
...@@ -688,7 +725,9 @@ struct snd_soc_dai_link { ...@@ -688,7 +725,9 @@ struct snd_soc_dai_link {
struct snd_soc_dai_link_component *codecs; struct snd_soc_dai_link_component *codecs;
unsigned int num_codecs; unsigned int num_codecs;
struct snd_soc_dai_link_codec_ch_map *codec_ch_maps; /* num_ch_maps = max(num_cpu, num_codecs) */
struct snd_soc_dai_link_ch_map *ch_maps;
/* /*
* You MAY specify the link's platform/PCM/DMA driver, either by * You MAY specify the link's platform/PCM/DMA driver, either by
* device name, or by DT/OF node, but not both. Some forms of link * device name, or by DT/OF node, but not both. Some forms of link
...@@ -775,6 +814,10 @@ struct snd_soc_dai_link { ...@@ -775,6 +814,10 @@ struct snd_soc_dai_link {
#endif #endif
}; };
static inline int snd_soc_link_num_ch_map(struct snd_soc_dai_link *link) {
return max(link->num_cpus, link->num_codecs);
}
static inline struct snd_soc_dai_link_component* static inline struct snd_soc_dai_link_component*
snd_soc_link_to_cpu(struct snd_soc_dai_link *link, int n) { snd_soc_link_to_cpu(struct snd_soc_dai_link *link, int n) {
return &(link)->cpus[n]; return &(link)->cpus[n];
...@@ -808,6 +851,12 @@ snd_soc_link_to_platform(struct snd_soc_dai_link *link, int n) { ...@@ -808,6 +851,12 @@ snd_soc_link_to_platform(struct snd_soc_dai_link *link, int n) {
((cpu) = snd_soc_link_to_cpu(link, i)); \ ((cpu) = snd_soc_link_to_cpu(link, i)); \
(i)++) (i)++)
#define for_each_link_ch_maps(link, i, ch_map) \
for ((i) = 0; \
((i) < snd_soc_link_num_ch_map(link) && \
((ch_map) = link->ch_maps + i)); \
(i)++)
/* /*
* Sample 1 : Single CPU/Codec/Platform * Sample 1 : Single CPU/Codec/Platform
* *
...@@ -1163,6 +1212,7 @@ struct snd_soc_pcm_runtime { ...@@ -1163,6 +1212,7 @@ struct snd_soc_pcm_runtime {
((i) < (rtd)->dai_link->num_cpus + (rtd)->dai_link->num_codecs) && \ ((i) < (rtd)->dai_link->num_cpus + (rtd)->dai_link->num_codecs) && \
((dai) = (rtd)->dais[i]); \ ((dai) = (rtd)->dais[i]); \
(i)++) (i)++)
#define for_each_rtd_ch_maps(rtd, i, ch_maps) for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd); void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd);
......
...@@ -83,32 +83,32 @@ ...@@ -83,32 +83,32 @@
Multi-CPU/Codec Multi-CPU/Codec
************************************ ************************************
It has connection part (= X) and list part (= y). It has link connection part (= X,x) and list part (= A,B,a,b).
links indicates connection part of CPU side (= A). "links" is connection part of CPU side (= @).
+-+ (A) +-+ +----+ +---+
CPU1 --(y) | | <-(X)--(X)-> | | (y)-- Codec1 CPU1 --|A X| <-@----> |x a|-- Codec1
CPU2 --(y) | | | | (y)-- Codec2 CPU2 --|B | | b|-- Codec2
+-+ +-+ +----+ +---+
sound { sound {
compatible = "audio-graph-card2"; compatible = "audio-graph-card2";
(A) links = <&mcpu>; (@) links = <&mcpu>;
multi { multi {
ports@0 { ports@0 {
(X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; (@) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; // (X) to pair
(y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; // (A) Multi Element
(y) port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; // (B) Multi Element
}; };
ports@1 { ports@1 {
(X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; // (x) to pair
(y) port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; // (a) Multi Element
(y) port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; // (b) Multi Element
};
}; };
}; };
};
CPU { CPU {
ports { ports {
...@@ -328,9 +328,9 @@ static struct device_node *graph_get_next_multi_ep(struct device_node **port) ...@@ -328,9 +328,9 @@ static struct device_node *graph_get_next_multi_ep(struct device_node **port)
/* /*
* multi { * multi {
* ports { * ports {
* => lnk: port@0 { ... }; * => lnk: port@0 { ... }; // to pair
* port@1 { ep { ... = rep0 } }; * port@1 { ep { ... = rep0 } }; // Multi Element
* port@2 { ep { ... = rep1 } }; * port@2 { ep { ... = rep1 } }; // Multi Element
* ... * ...
* }; * };
* }; * };
...@@ -504,40 +504,203 @@ static int __graph_parse_node(struct simple_util_priv *priv, ...@@ -504,40 +504,203 @@ static int __graph_parse_node(struct simple_util_priv *priv,
return 0; return 0;
} }
static int graph_parse_node(struct simple_util_priv *priv, static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link,
enum graph_type gtype, int *nm_idx, int cpu_idx,
struct device_node *port, struct device_node *mcpu_port)
struct link_info *li, int is_cpu)
{ {
struct device_node *ep; /*
int ret = 0; * +---+ +---+
* | X|<-@------->|x |
* | | | |
* cpu0 <--|A 1|<--------->|4 a|-> codec0
* cpu1 <--|B 2|<-----+--->|5 b|-> codec1
* cpu2 <--|C 3|<----/ +---+
* +---+
*
* multi {
* ports {
* port@0 { mcpu_top_ep {... = mcodec_ep; }; }; // (X) to pair
* <mcpu_port> port@1 { mcpu0_ep { ... = cpu0_ep; }; // (A) Multi Element
* mcpu0_ep_0 { ... = mcodec0_ep_0; }; }; // (1) connected Codec
* port@2 { mcpu1_ep { ... = cpu1_ep; }; // (B) Multi Element
* mcpu1_ep_0 { ... = mcodec1_ep_0; }; }; // (2) connected Codec
* port@3 { mcpu2_ep { ... = cpu2_ep; }; // (C) Multi Element
* mcpu2_ep_0 { ... = mcodec1_ep_1; }; }; // (3) connected Codec
* };
*
* ports {
* port@0 { mcodec_top_ep {... = mcpu_ep; }; }; // (x) to pair
* <mcodec_port>port@1 { mcodec0_ep { ... = codec0_ep; }; // (a) Multi Element
* mcodec0_ep_0 { ... = mcpu0_ep_0; }; }; // (4) connected CPU
* port@2 { mcodec1_ep { ... = codec1_ep; }; // (b) Multi Element
* mcodec1_ep_0 { ... = mcpu1_ep_0; }; // (5) connected CPU
* mcodec1_ep_1 { ... = mcpu2_ep_0; }; }; // (5) connected CPU
* };
* };
*/
struct device_node *mcpu_ep = port_to_endpoint(mcpu_port);
struct device_node *mcpu_ep_n = mcpu_ep;
struct device_node *mcpu_port_top = of_get_next_child(of_get_parent(mcpu_port), NULL);
struct device_node *mcpu_ep_top = port_to_endpoint(mcpu_port_top);
struct device_node *mcodec_ep_top = of_graph_get_remote_endpoint(mcpu_ep_top);
struct device_node *mcodec_port_top = of_get_parent(mcodec_ep_top);
struct device_node *mcodec_ports = of_get_parent(mcodec_port_top);
int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
int ret = -EINVAL;
if (graph_lnk_is_multi(port)) { if (cpu_idx > dai_link->num_cpus)
int idx; goto mcpu_err;
of_node_get(port); while (1) {
struct device_node *mcodec_ep_n;
struct device_node *mcodec_port_i;
struct device_node *mcodec_port;
int codec_idx;
for (idx = 0;; idx++) { if (*nm_idx > nm_max)
ep = graph_get_next_multi_ep(&port); break;
if (!ep)
break;
ret = __graph_parse_node(priv, gtype, ep, mcpu_ep_n = of_get_next_child(mcpu_port, mcpu_ep_n);
li, is_cpu, idx); if (!mcpu_ep_n) {
of_node_put(ep); ret = 0;
if (ret < 0) break;
}
mcodec_ep_n = of_graph_get_remote_endpoint(mcpu_ep_n);
mcodec_port = of_get_parent(mcodec_ep_n);
if (mcodec_ports != of_get_parent(mcodec_port))
goto mcpu_err;
codec_idx = 0;
mcodec_port_i = of_get_next_child(mcodec_ports, NULL);
while (1) {
if (codec_idx > dai_link->num_codecs)
goto mcodec_err;
mcodec_port_i = of_get_next_child(mcodec_ports, mcodec_port_i);
if (!mcodec_port_i)
goto mcodec_err;
if (mcodec_port_i == mcodec_port)
break; break;
codec_idx++;
} }
} else {
/* Single CPU / Codec */ dai_link->ch_maps[*nm_idx].cpu = cpu_idx;
ep = port_to_endpoint(port); dai_link->ch_maps[*nm_idx].codec = codec_idx;
ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
(*nm_idx)++;
of_node_put(mcodec_port_i);
mcodec_err:
of_node_put(mcodec_port);
of_node_put(mcpu_ep_n);
of_node_put(mcodec_ep_n);
}
mcpu_err:
of_node_put(mcpu_ep);
of_node_put(mcpu_port_top);
of_node_put(mcpu_ep_top);
of_node_put(mcodec_ep_top);
of_node_put(mcodec_port_top);
of_node_put(mcodec_ports);
return ret;
}
static int graph_parse_node_multi(struct simple_util_priv *priv,
enum graph_type gtype,
struct device_node *port,
struct link_info *li, int is_cpu)
{
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
struct device *dev = simple_priv_to_dev(priv);
struct device_node *ep;
int ret = -ENOMEM;
int nm_idx = 0;
int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
/*
* create ch_maps if CPU:Codec = N:M
* DPCM is out of scope
*/
if (gtype != GRAPH_DPCM && !dai_link->ch_maps &&
dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
dai_link->num_cpus != dai_link->num_codecs) {
dai_link->ch_maps = devm_kcalloc(dev, nm_max,
sizeof(struct snd_soc_dai_link_ch_map), GFP_KERNEL);
if (!dai_link->ch_maps)
goto multi_err;
}
for (int idx = 0;; idx++) {
/*
* multi {
* ports {
* <port> port@0 { ... }; // to pair
* port@1 { mcpu1_ep { ... = cpu1_ep };}; // Multi Element
* port@2 { mcpu2_ep { ... = cpu2_ep };}; // Multi Element
* };
* };
*
* cpu {
* ports {
* <ep> port@0 { cpu1_ep { ... = mcpu1_ep };};
* };
* };
*/
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); of_node_put(ep);
if (ret < 0)
goto multi_err;
/* CPU:Codec = N:M */
if (is_cpu && dai_link->ch_maps) {
ret = graph_parse_node_multi_nm(dai_link, &nm_idx, idx, port);
if (ret < 0)
goto multi_err;
}
} }
if (is_cpu && dai_link->ch_maps && (nm_idx != nm_max))
ret = -EINVAL;
multi_err:
return ret; return ret;
} }
static int graph_parse_node_single(struct simple_util_priv *priv,
enum graph_type gtype,
struct device_node *port,
struct link_info *li, int is_cpu)
{
struct device_node *ep = port_to_endpoint(port);
int ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
of_node_put(ep);
return ret;
}
static int graph_parse_node(struct simple_util_priv *priv,
enum graph_type gtype,
struct device_node *port,
struct link_info *li, int is_cpu)
{
if (graph_lnk_is_multi(port))
return graph_parse_node_multi(priv, gtype, port, li, is_cpu);
else
return graph_parse_node_single(priv, gtype, port, li, is_cpu);
}
static void graph_parse_daifmt(struct device_node *node, static void graph_parse_daifmt(struct device_node *node,
unsigned int *daifmt, unsigned int *bit_frame) unsigned int *daifmt, unsigned int *bit_frame)
{ {
...@@ -920,17 +1083,33 @@ static int graph_counter(struct device_node *lnk) ...@@ -920,17 +1083,33 @@ static int graph_counter(struct device_node *lnk)
* *
* multi { * multi {
* ports { * ports {
* => lnk: port@0 { ... }; * => lnk: port@0 { ... }; // to pair
* port@1 { ... }; * port@1 { ... }; // Multi Element
* port@2 { ... }; * port@2 { ... }; // Multi Element
* ... * ...
* }; * };
* }; * };
* *
* ignore first lnk part * ignore first lnk part
*/ */
if (graph_lnk_is_multi(lnk)) if (graph_lnk_is_multi(lnk)) {
return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1; struct device_node *ports = of_get_parent(lnk);
struct device_node *port = NULL;
int cnt = 0;
/*
* CPU/Codec = N:M case has many endpoints.
* We can't use of_graph_get_endpoint_count() here
*/
while(1) {
port = of_get_next_child(ports, port);
if (!port)
break;
cnt++;
}
return cnt - 1;
}
/* /*
* Single CPU / Codec * Single CPU / Codec
*/ */
......
...@@ -570,16 +570,14 @@ int sdw_hw_params(struct snd_pcm_substream *substream, ...@@ -570,16 +570,14 @@ int sdw_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai_link_ch_map *ch_maps;
int ch = params_channels(params); int ch = params_channels(params);
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
unsigned int ch_mask; unsigned int ch_mask;
int num_codecs; int num_codecs;
int step; int step;
int i; int i;
int j;
if (!rtd->dai_link->codec_ch_maps) if (!rtd->dai_link->ch_maps)
return 0; return 0;
/* Identical data will be sent to all codecs in playback */ /* Identical data will be sent to all codecs in playback */
...@@ -605,13 +603,9 @@ int sdw_hw_params(struct snd_pcm_substream *substream, ...@@ -605,13 +603,9 @@ int sdw_hw_params(struct snd_pcm_substream *substream,
* link has more than one codec DAIs. Set codec channel mask and * link has more than one codec DAIs. Set codec channel mask and
* ASoC will set the corresponding channel numbers for each cpu dai. * ASoC will set the corresponding channel numbers for each cpu dai.
*/ */
for_each_rtd_cpu_dais(rtd, i, cpu_dai) { for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
for_each_rtd_codec_dais(rtd, j, codec_dai) { ch_maps->ch_mask = ch_mask << (i * step);
if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id != i)
continue;
rtd->dai_link->codec_ch_maps[j].ch_mask = ch_mask << (j * step);
}
}
return 0; return 0;
} }
...@@ -1350,15 +1344,17 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, ...@@ -1350,15 +1344,17 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
return 0; return 0;
} }
static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps, static void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps,
int codec_num, int cpu_num) int codec_num, int cpu_num)
{ {
int step; int step;
int i; int i;
step = codec_num / cpu_num; step = codec_num / cpu_num;
for (i = 0; i < codec_num; i++) for (i = 0; i < codec_num; i++) {
sdw_codec_ch_maps[i].connected_cpu_id = i / step; sdw_codec_ch_maps[i].cpu = i / step;
sdw_codec_ch_maps[i].codec = i;
}
} }
static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
...@@ -1453,7 +1449,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, ...@@ -1453,7 +1449,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index,
*ignore_pch_dmic = true; *ignore_pch_dmic = true;
for_each_pcm_streams(stream) { for_each_pcm_streams(stream) {
struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps; struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps;
char *name, *cpu_name; char *name, *cpu_name;
int playback, capture; int playback, capture;
static const char * const sdw_stream_name[] = { static const char * const sdw_stream_name[] = {
...@@ -1530,7 +1526,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, ...@@ -1530,7 +1526,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index,
dai_links[*link_index].nonatomic = true; dai_links[*link_index].nonatomic = true;
set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num); set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num);
dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps; dai_links[*link_index].ch_maps = sdw_codec_ch_maps;
ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++, ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++,
playback, group_id, adr_index, dai_index); playback, group_id, adr_index, dai_index);
if (ret < 0) { if (ret < 0) {
......
...@@ -1015,6 +1015,94 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, ...@@ -1015,6 +1015,94 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card,
return -EINVAL; return -EINVAL;
} }
#define MAX_DEFAULT_CH_MAP_SIZE 7
static struct snd_soc_dai_link_ch_map default_ch_map_sync[MAX_DEFAULT_CH_MAP_SIZE] = {
{ .cpu = 0, .codec = 0 },
{ .cpu = 1, .codec = 1 },
{ .cpu = 2, .codec = 2 },
{ .cpu = 3, .codec = 3 },
{ .cpu = 4, .codec = 4 },
{ .cpu = 5, .codec = 5 },
{ .cpu = 6, .codec = 6 },
};
static struct snd_soc_dai_link_ch_map default_ch_map_1cpu[MAX_DEFAULT_CH_MAP_SIZE] = {
{ .cpu = 0, .codec = 0 },
{ .cpu = 0, .codec = 1 },
{ .cpu = 0, .codec = 2 },
{ .cpu = 0, .codec = 3 },
{ .cpu = 0, .codec = 4 },
{ .cpu = 0, .codec = 5 },
{ .cpu = 0, .codec = 6 },
};
static struct snd_soc_dai_link_ch_map default_ch_map_1codec[MAX_DEFAULT_CH_MAP_SIZE] = {
{ .cpu = 0, .codec = 0 },
{ .cpu = 1, .codec = 0 },
{ .cpu = 2, .codec = 0 },
{ .cpu = 3, .codec = 0 },
{ .cpu = 4, .codec = 0 },
{ .cpu = 5, .codec = 0 },
{ .cpu = 6, .codec = 0 },
};
static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link)
{
struct snd_soc_dai_link_ch_map *ch_maps;
int i;
/*
* dai_link->ch_maps indicates how CPU/Codec are connected.
* It will be a map seen from a larger number of DAI.
* see
* soc.h :: [dai_link->ch_maps Image sample]
*/
/* it should have ch_maps if connection was N:M */
if (dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
dai_link->num_cpus != dai_link->num_codecs && !dai_link->ch_maps) {
dev_err(card->dev, "need to have ch_maps when N:M connction (%s)",
dai_link->name);
return -EINVAL;
}
/* do nothing if it has own maps */
if (dai_link->ch_maps)
goto sanity_check;
/* check default map size */
if (dai_link->num_cpus > MAX_DEFAULT_CH_MAP_SIZE ||
dai_link->num_codecs > MAX_DEFAULT_CH_MAP_SIZE) {
dev_err(card->dev, "soc-core.c needs update default_connection_maps");
return -EINVAL;
}
/* Compensate missing map for ... */
if (dai_link->num_cpus == dai_link->num_codecs)
dai_link->ch_maps = default_ch_map_sync; /* for 1:1 or N:N */
else if (dai_link->num_cpus < dai_link->num_codecs)
dai_link->ch_maps = default_ch_map_1cpu; /* for 1:N */
else
dai_link->ch_maps = default_ch_map_1codec; /* for N:1 */
sanity_check:
dev_dbg(card->dev, "dai_link %s\n", dai_link->stream_name);
for_each_link_ch_maps(dai_link, i, ch_maps) {
if ((ch_maps->cpu >= dai_link->num_cpus) ||
(ch_maps->codec >= dai_link->num_codecs)) {
dev_err(card->dev,
"unexpected dai_link->ch_maps[%d] index (cpu(%d/%d) codec(%d/%d))",
i,
ch_maps->cpu, dai_link->num_cpus,
ch_maps->codec, dai_link->num_codecs);
return -EINVAL;
}
dev_dbg(card->dev, " [%d] cpu%d <-> codec%d\n",
i, ch_maps->cpu, ch_maps->codec);
}
return 0;
}
/** /**
* snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card
* @card: The ASoC card to which the pcm_runtime has * @card: The ASoC card to which the pcm_runtime has
...@@ -1121,8 +1209,13 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, ...@@ -1121,8 +1209,13 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card,
int num_dai_link) int num_dai_link)
{ {
for (int i = 0; i < num_dai_link; i++) { for (int i = 0; i < num_dai_link; i++) {
int ret = snd_soc_add_pcm_runtime(card, dai_link + i); int ret;
ret = snd_soc_compensate_channel_connection_map(card, dai_link + i);
if (ret < 0)
return ret;
ret = snd_soc_add_pcm_runtime(card, dai_link + i);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
......
...@@ -4436,11 +4436,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, ...@@ -4436,11 +4436,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
{ {
struct snd_soc_pcm_runtime *rtd; struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *cpu_dai;
struct snd_soc_dai *codec_dai; struct snd_soc_dai *codec_dai;
int i;
/* for each BE DAI link... */ /* for each BE DAI link... */
for_each_card_rtds(card, rtd) { for_each_card_rtds(card, rtd) {
struct snd_soc_dai_link_ch_map *ch_maps;
int i;
/* /*
* dynamic FE links have no fixed DAI mapping. * dynamic FE links have no fixed DAI mapping.
* CODEC<->CODEC links have no direct connection. * CODEC<->CODEC links have no direct connection.
...@@ -4448,39 +4451,15 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) ...@@ -4448,39 +4451,15 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
if (rtd->dai_link->dynamic) if (rtd->dai_link->dynamic)
continue; continue;
if (rtd->dai_link->num_cpus == 1) { /*
for_each_rtd_codec_dais(rtd, i, codec_dai) * see
dapm_connect_dai_pair(card, rtd, codec_dai, * soc.h :: [dai_link->ch_maps Image sample]
snd_soc_rtd_to_cpu(rtd, 0)); */
} else if (rtd->dai_link->num_codecs == rtd->dai_link->num_cpus) { for_each_rtd_ch_maps(rtd, i, ch_maps) {
for_each_rtd_codec_dais(rtd, i, codec_dai) cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu);
dapm_connect_dai_pair(card, rtd, codec_dai, codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec);
snd_soc_rtd_to_cpu(rtd, i));
} else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) {
int cpu_id;
if (!rtd->dai_link->codec_ch_maps) {
dev_err(card->dev, "%s: no codec channel mapping table provided\n",
__func__);
continue;
}
for_each_rtd_codec_dais(rtd, i, codec_dai) { dapm_connect_dai_pair(card, rtd, codec_dai, cpu_dai);
cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
if (cpu_id >= rtd->dai_link->num_cpus) {
dev_err(card->dev,
"%s: dai_link %s cpu_id %d too large, num_cpus is %d\n",
__func__, rtd->dai_link->name, cpu_id,
rtd->dai_link->num_cpus);
continue;
}
dapm_connect_dai_pair(card, rtd, codec_dai,
snd_soc_rtd_to_cpu(rtd, cpu_id));
}
} else {
dev_err(card->dev,
"%s: codec number %d < cpu number %d is not supported\n",
__func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus);
} }
} }
} }
......
...@@ -1050,6 +1050,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, ...@@ -1050,6 +1050,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
} }
for_each_rtd_cpu_dais(rtd, i, cpu_dai) { for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
struct snd_soc_dai_link_ch_map *ch_maps;
unsigned int ch_mask = 0; unsigned int ch_mask = 0;
int j; int j;
...@@ -1063,22 +1064,20 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, ...@@ -1063,22 +1064,20 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
/* copy params for each cpu */ /* copy params for each cpu */
tmp_params = *params; tmp_params = *params;
if (!rtd->dai_link->codec_ch_maps)
goto hw_params;
/* /*
* construct cpu channel mask by combining ch_mask of each * construct cpu channel mask by combining ch_mask of each
* codec which maps to the cpu. * codec which maps to the cpu.
* see
* soc.h :: [dai_link->ch_maps Image sample]
*/ */
for_each_rtd_codec_dais(rtd, j, codec_dai) { for_each_rtd_ch_maps(rtd, j, ch_maps)
if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == i) if (ch_maps->cpu == i)
ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask; ch_mask |= ch_maps->ch_mask;
}
/* fixup cpu channel number */ /* fixup cpu channel number */
if (ch_mask) if (ch_mask)
soc_pcm_codec_params_fixup(&tmp_params, ch_mask); soc_pcm_codec_params_fixup(&tmp_params, ch_mask);
hw_params:
ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params); ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -2826,35 +2825,20 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, ...@@ -2826,35 +2825,20 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
} }
} }
} else { } else {
struct snd_soc_dai_link_ch_map *ch_maps;
struct snd_soc_dai *codec_dai; struct snd_soc_dai *codec_dai;
/* Adapt stream for codec2codec links */ /* Adapt stream for codec2codec links */
int cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE); int cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE);
int cpu_playback = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_PLAYBACK); int cpu_playback = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_PLAYBACK);
for_each_rtd_codec_dais(rtd, i, codec_dai) { /*
if (dai_link->num_cpus == 1) { * see
cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); * soc.h :: [dai_link->ch_maps Image sample]
} else if (dai_link->num_cpus == dai_link->num_codecs) { */
cpu_dai = snd_soc_rtd_to_cpu(rtd, i); for_each_rtd_ch_maps(rtd, i, ch_maps) {
} else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) { cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu);
int cpu_id; codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec);
if (!rtd->dai_link->codec_ch_maps) {
dev_err(rtd->card->dev, "%s: no codec channel mapping table provided\n",
__func__);
return -EINVAL;
}
cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
cpu_dai = snd_soc_rtd_to_cpu(rtd, cpu_id);
} else {
dev_err(rtd->card->dev,
"%s codec number %d < cpu number %d is not supported\n",
__func__, rtd->dai_link->num_codecs,
rtd->dai_link->num_cpus);
return -EINVAL;
}
if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) snd_soc_dai_stream_valid(cpu_dai, cpu_playback))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment