@@ -33,6 +33,13 @@ struct simple_card_data {
struct snd_soc_codec_conf *codec_conf;
};
+struct link_dpcm {
+ int dais; /* number of dai */
+ int link; /* number of link */
+ int conf; /* number of codec_conf */
+ int cpu; /* CPU / Codec */
+};
+
#define simple_priv_to_card(priv) (&(priv)->snd_card)
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
@@ -180,30 +187,42 @@ static void asoc_simple_card_get_conversion(struct device *dev,
of_node_put(node);
}
-static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
- struct device_node *node,
+static int asoc_simple_card_dai_link_of_dpcm(struct simple_card_data *priv,
struct device_node *np,
struct device_node *codec,
- struct simple_card_data *priv,
- int *dai_idx, int link_idx,
- int *conf_idx, int is_fe,
- bool is_top_level_node)
+ struct link_dpcm *ld, int is_top)
{
struct device *dev = simple_priv_to_dev(priv);
- struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
- struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, ld->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, ld->link);
struct asoc_simple_dai *dai;
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
-
+ struct device_node *top = dev->of_node;
+ struct device_node *node = of_get_parent(np);
char prop[128];
char *prefix = "";
int ret;
+ /*
+ * |CPU |Codec : turn
+ * CPU |Pass |return
+ * Codec |return|Pass
+ * np
+ */
+ if (ld->cpu == (np == codec))
+ return 0;
+
+ dev_dbg(dev, "link_of DPCM (%pOF)\n", np);
+
+ ld->link++;
+
+ of_node_put(node);
+
/* For single DAI link & old style of DT node */
- if (is_top_level_node)
+ if (is_top)
prefix = PREFIX;
- if (is_fe) {
+ if (ld->cpu) {
int is_single_links = 0;
/* BE is dummy */
@@ -216,7 +235,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
dai_link->dpcm_merged_format = 1;
dai =
- dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
+ dai_props->cpu_dai = &priv->dais[ld->dais++];
ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
&is_single_links);
@@ -247,10 +266,10 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup;
dai =
- dai_props->codec_dai = &priv->dais[(*dai_idx)++];
+ dai_props->codec_dai = &priv->dais[ld->dais++];
cconf =
- dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++];
+ dai_props->codec_conf = &priv->codec_conf[ld->conf++];
ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
if (ret < 0)
@@ -303,53 +322,50 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top,
return 0;
}
-static int asoc_simple_card_dai_link_of(struct device_node *top,
- struct device_node *node,
- struct simple_card_data *priv,
- int *dai_idx, int link_idx,
- bool is_top_level_node)
+static int asoc_simple_card_dai_link_of(struct simple_card_data *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_dpcm *ld, int is_top)
{
struct device *dev = simple_priv_to_dev(priv);
- struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
- struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, ld->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, ld->link);
struct asoc_simple_dai *cpu_dai;
struct asoc_simple_dai *codec_dai;
+ struct device_node *top = dev->of_node;
struct device_node *cpu = NULL;
+ struct device_node *node = NULL;
struct device_node *plat = NULL;
- struct device_node *codec = NULL;
char prop[128];
char *prefix = "";
int ret, single_cpu;
- /* For single DAI link & old style of DT node */
- if (is_top_level_node)
- prefix = PREFIX;
+ /*
+ * |CPU |Codec : turn
+ * CPU |Pass |return
+ * Codec |return|return
+ * np
+ */
+ if (!ld->cpu || np == codec)
+ return 0;
- snprintf(prop, sizeof(prop), "%scpu", prefix);
- cpu = of_get_child_by_name(node, prop);
+ cpu = np;
+ node = of_get_parent(np);
+ ld->link++;
- if (!cpu) {
- ret = -EINVAL;
- dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
- goto dai_link_of_err;
- }
+ dev_dbg(dev, "link_of (%pOF)\n", node);
+
+ /* For single DAI link & old style of DT node */
+ if (is_top)
+ prefix = PREFIX;
snprintf(prop, sizeof(prop), "%splat", prefix);
plat = of_get_child_by_name(node, prop);
- snprintf(prop, sizeof(prop), "%scodec", prefix);
- codec = of_get_child_by_name(node, prop);
-
- if (!codec) {
- ret = -EINVAL;
- dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
- goto dai_link_of_err;
- }
-
cpu_dai =
- dai_props->cpu_dai = &priv->dais[(*dai_idx)++];
+ dai_props->cpu_dai = &priv->dais[ld->dais++];
codec_dai =
- dai_props->codec_dai = &priv->dais[(*dai_idx)++];
+ dai_props->codec_dai = &priv->dais[ld->dais++];
ret = asoc_simple_card_parse_daifmt(dev, node, codec,
prefix, &dai_link->dai_fmt);
@@ -408,7 +424,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *top,
asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
dai_link_of_err:
- of_node_put(cpu);
+ of_node_put(node);
of_node_put(codec);
return ret;
@@ -445,18 +461,79 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
return 0;
}
+static int asoc_simple_card_for_each_link(struct simple_card_data *priv,
+ struct link_dpcm *ld,
+ int (*func_noml)(struct simple_card_data *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_dpcm *ld, int is_top),
+ int (*func_dpcm)(struct simple_card_data *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_dpcm *ld, int is_top))
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *top = dev->of_node;
+ struct device_node *node;
+ int is_top = 0;
+
+ /* Check if it has dai-link */
+ node = of_get_child_by_name(top, PREFIX "dai-link");
+ if (!node) {
+ node = top;
+ is_top = 1;
+ }
+
+ /* loop for all dai-link */
+ do {
+ struct asoc_simple_card_data adata;
+ struct device_node *codec;
+ struct device_node *np;
+ int num = of_get_child_count(node);
+ int ret;
+
+ /* get codec */
+ codec = of_get_child_by_name(node, is_top ?
+ PREFIX "codec" : "codec");
+ if (!codec)
+ return -ENODEV;
+
+ /* get convert-xxx property */
+ memset(&adata, 0, sizeof(adata));
+ for_each_child_of_node(node, np)
+ asoc_simple_card_get_conversion(dev, np, &adata);
+
+ /* loop for all CPU/Codec node */
+ for_each_child_of_node(node, np) {
+ /*
+ * It is DPCM
+ * if it has many CPUs,
+ * or has convert-xxx property
+ */
+ if (num > 2 ||
+ adata.convert_rate || adata.convert_channels)
+ ret = func_dpcm(priv, np, codec, ld, is_top);
+ /* else normal sound */
+ else
+ ret = func_noml(priv, np, codec, ld, is_top);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ node = of_get_next_child(top, node);
+ } while (!is_top && node);
+
+ return 0;
+}
+
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
{
struct device *dev = simple_priv_to_dev(priv);
struct device_node *top = dev->of_node;
struct snd_soc_card *card = simple_priv_to_card(priv);
- struct device_node *node;
- struct device_node *np;
- struct device_node *codec;
- struct asoc_simple_card_data adata;
- bool is_fe;
- int ret, loop;
- int dai_idx, link_idx, conf_idx;
+ struct link_dpcm ld;
+ int ret;
if (!top)
return -EINVAL;
@@ -470,51 +547,27 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
return ret;
/* Single/Muti DAI link(s) & New style of DT node */
- loop = 1;
- link_idx = 0;
- dai_idx = 0;
- conf_idx = 0;
- node = of_get_child_by_name(top, PREFIX "dai-link");
- if (!node) {
- node = dev->of_node;
- loop = 0;
+ memset(&ld, 0, sizeof(ld));
+ for (ld.cpu = 1; ld.cpu >= 0; ld.cpu--) {
+ /*
+ * Detect all CPU first, and Detect all Codec 2nd.
+ *
+ * In Normal sound case, all DAIs are detected
+ * as "CPU-Codec".
+ *
+ * In DPCM sound case,
+ * all CPUs are detected as "CPU-dummy", and
+ * all Codecs are detected as "dummy-Codec".
+ * To avoid random sub-device numbering,
+ * detect "dummy-Codec" in last;
+ */
+ ret = asoc_simple_card_for_each_link(priv, &ld,
+ asoc_simple_card_dai_link_of,
+ asoc_simple_card_dai_link_of_dpcm);
+ if (ret < 0)
+ return ret;
}
- do {
- memset(&adata, 0, sizeof(adata));
- for_each_child_of_node(node, np)
- asoc_simple_card_get_conversion(dev, np, &adata);
-
- /* DPCM */
- if (of_get_child_count(node) > 2 ||
- adata.convert_rate || adata.convert_channels) {
- for_each_child_of_node(node, np) {
- codec = of_get_child_by_name(node,
- loop ? "codec" :
- PREFIX "codec");
- if (!codec)
- return -ENODEV;
-
- is_fe = (np != codec);
-
- ret = asoc_simple_card_dai_link_of_dpcm(
- top, node, np, codec, priv,
- &dai_idx, link_idx++, &conf_idx,
- is_fe, !loop);
- if (ret < 0)
- return ret;
- }
- } else {
- ret = asoc_simple_card_dai_link_of(
- top, node, priv,
- &dai_idx, link_idx++, !loop);
- if (ret < 0)
- return ret;
- }
-
- node = of_get_next_child(top, node);
- } while (loop && node);
-
ret = asoc_simple_card_parse_card_name(card, PREFIX);
if (ret < 0)
return ret;
@@ -524,17 +577,35 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
return ret;
}
-static void asoc_simple_card_get_dais_count(struct device *dev,
- int *link_num,
- int *dais_num,
- int *ccnf_num)
+static int asoc_simple_card_count_noml(struct simple_card_data *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_dpcm *ld, int is_top)
{
- struct device_node *top = dev->of_node;
- struct device_node *node;
- struct device_node *np;
- struct asoc_simple_card_data adata;
- int loop;
- int num;
+ ld->dais++; /* CPU or Codec */
+ if (np != codec)
+ ld->link++; /* CPU-Codec */
+
+ return 0;
+}
+
+static int asoc_simple_card_count_dpcm(struct simple_card_data *priv,
+ struct device_node *np,
+ struct device_node *codec,
+ struct link_dpcm *ld, int is_top)
+{
+ ld->dais++; /* CPU or Codec */
+ ld->link++; /* CPU-dummy or dummy-Codec */
+ if (np == codec)
+ ld->conf++;
+
+ return 0;
+}
+
+static void asoc_simple_card_get_dais_count(struct simple_card_data *priv,
+ struct link_dpcm *ld)
+{
+ struct device *dev = simple_priv_to_dev(priv);
/*
* link_num : number of links.
@@ -582,36 +653,12 @@ static void asoc_simple_card_get_dais_count(struct device *dev,
* => 4 DAIs = 2xCPU + 2xCodec
* => 1 ccnf = 1xdummy-Codec
*/
- if (!top) {
- (*link_num) = 1;
- (*dais_num) = 2;
- (*ccnf_num) = 0;
- return;
- }
-
- loop = 1;
- node = of_get_child_by_name(top, PREFIX "dai-link");
- if (!node) {
- node = top;
- loop = 0;
- }
-
- do {
- memset(&adata, 0, sizeof(adata));
- for_each_child_of_node(node, np)
- asoc_simple_card_get_conversion(dev, np, &adata);
+ asoc_simple_card_for_each_link(priv, ld,
+ asoc_simple_card_count_noml,
+ asoc_simple_card_count_dpcm);
- num = of_get_child_count(node);
- (*dais_num) += num;
- if (num > 2 ||
- adata.convert_rate || adata.convert_channels) {
- (*link_num) += num;
- (*ccnf_num)++;
- } else {
- (*link_num)++;
- }
- node = of_get_next_child(top, node);
- } while (loop && node);
+ dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
+ ld->link, ld->dais, ld->conf);
}
static int asoc_simple_soc_card_probe(struct snd_soc_card *card)
@@ -640,7 +687,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct snd_soc_card *card;
struct snd_soc_codec_conf *cconf;
- int lnum = 0, dnum = 0, cnum = 0;
+ struct link_dpcm ld;
int ret, i;
/* Allocate the private data and the DAI link array */
@@ -648,14 +695,20 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum);
- if (!lnum || !dnum)
+ card = simple_priv_to_card(priv);
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+ card->probe = asoc_simple_soc_card_probe;
+
+ memset(&ld, 0, sizeof(ld));
+ asoc_simple_card_get_dais_count(priv, &ld);
+ if (!ld.link || !ld.dais)
return -EINVAL;
- dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
- dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL);
- dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL);
- cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL);
+ dai_props = devm_kcalloc(dev, ld.link, sizeof(*dai_props), GFP_KERNEL);
+ dai_link = devm_kcalloc(dev, ld.link, sizeof(*dai_link), GFP_KERNEL);
+ dais = devm_kcalloc(dev, ld.dais, sizeof(*dais), GFP_KERNEL);
+ cconf = devm_kcalloc(dev, ld.conf, sizeof(*cconf), GFP_KERNEL);
if (!dai_props || !dai_link || !dais)
return -ENOMEM;
@@ -665,26 +718,21 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
* see
* soc-core.c :: snd_soc_init_multicodec()
*/
- for (i = 0; i < lnum; i++) {
+ for (i = 0; i < ld.link; i++) {
dai_link[i].codecs = &dai_props[i].codecs;
dai_link[i].num_codecs = 1;
dai_link[i].platform = &dai_props[i].platform;
}
- priv->dai_props = dai_props;
- priv->dai_link = dai_link;
- priv->dais = dais;
- priv->codec_conf = cconf;
+ priv->dai_props = dai_props;
+ priv->dai_link = dai_link;
+ priv->dais = dais;
+ priv->codec_conf = cconf;
- /* Init snd_soc_card */
- card = simple_priv_to_card(priv);
- card->owner = THIS_MODULE;
- card->dev = dev;
- card->dai_link = priv->dai_link;
- card->num_links = lnum;
+ card->dai_link = dai_link;
+ card->num_links = ld.link;
card->codec_conf = cconf;
- card->num_configs = cnum;
- card->probe = asoc_simple_soc_card_probe;
+ card->num_configs = ld.conf;
if (np && of_device_is_available(np)) {