162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// ASoC simple sound card support
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2012 Renesas Solutions Corp.
662306a36Sopenharmony_ci// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_device.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <sound/simple_card.h>
1662306a36Sopenharmony_ci#include <sound/soc-dai.h>
1762306a36Sopenharmony_ci#include <sound/soc.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define DPCM_SELECTABLE 1
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define DAI	"sound-dai"
2262306a36Sopenharmony_ci#define CELL	"#sound-dai-cells"
2362306a36Sopenharmony_ci#define PREFIX	"simple-audio-card,"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic const struct snd_soc_ops simple_ops = {
2662306a36Sopenharmony_ci	.startup	= asoc_simple_startup,
2762306a36Sopenharmony_ci	.shutdown	= asoc_simple_shutdown,
2862306a36Sopenharmony_ci	.hw_params	= asoc_simple_hw_params,
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int asoc_simple_parse_platform(struct device_node *node,
3262306a36Sopenharmony_ci				      struct snd_soc_dai_link_component *dlc)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct of_phandle_args args;
3562306a36Sopenharmony_ci	int ret;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (!node)
3862306a36Sopenharmony_ci		return 0;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/*
4162306a36Sopenharmony_ci	 * Get node via "sound-dai = <&phandle port>"
4262306a36Sopenharmony_ci	 * it will be used as xxx_of_node on soc_bind_dai_link()
4362306a36Sopenharmony_ci	 */
4462306a36Sopenharmony_ci	ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
4562306a36Sopenharmony_ci	if (ret)
4662306a36Sopenharmony_ci		return ret;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* dai_name is not required and may not exist for plat component */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	dlc->of_node = args.np;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return 0;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int asoc_simple_parse_dai(struct device *dev,
5662306a36Sopenharmony_ci				 struct device_node *node,
5762306a36Sopenharmony_ci				 struct snd_soc_dai_link_component *dlc,
5862306a36Sopenharmony_ci				 int *is_single_link)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct of_phandle_args args;
6162306a36Sopenharmony_ci	struct snd_soc_dai *dai;
6262306a36Sopenharmony_ci	int ret;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (!node)
6562306a36Sopenharmony_ci		return 0;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/*
6862306a36Sopenharmony_ci	 * Get node via "sound-dai = <&phandle port>"
6962306a36Sopenharmony_ci	 * it will be used as xxx_of_node on soc_bind_dai_link()
7062306a36Sopenharmony_ci	 */
7162306a36Sopenharmony_ci	ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
7262306a36Sopenharmony_ci	if (ret)
7362306a36Sopenharmony_ci		return ret;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/*
7662306a36Sopenharmony_ci	 * Try to find from DAI args
7762306a36Sopenharmony_ci	 */
7862306a36Sopenharmony_ci	dai = snd_soc_get_dai_via_args(&args);
7962306a36Sopenharmony_ci	if (dai) {
8062306a36Sopenharmony_ci		dlc->dai_name = snd_soc_dai_name_get(dai);
8162306a36Sopenharmony_ci		dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
8262306a36Sopenharmony_ci		if (!dlc->dai_args)
8362306a36Sopenharmony_ci			return -ENOMEM;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		goto parse_dai_end;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * FIXME
9062306a36Sopenharmony_ci	 *
9162306a36Sopenharmony_ci	 * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
9262306a36Sopenharmony_ci	 * If user unbinded CPU or Codec driver, but not for Sound Card,
9362306a36Sopenharmony_ci	 * dlc->dai_name is keeping unbinded CPU or Codec
9462306a36Sopenharmony_ci	 * driver's pointer.
9562306a36Sopenharmony_ci	 *
9662306a36Sopenharmony_ci	 * If user re-bind CPU or Codec driver again, ALSA SoC will try
9762306a36Sopenharmony_ci	 * to rebind Card via snd_soc_try_rebind_card(), but because of
9862306a36Sopenharmony_ci	 * above reason, it might can't bind Sound Card.
9962306a36Sopenharmony_ci	 * Because Sound Card is pointing to released dai_name pointer.
10062306a36Sopenharmony_ci	 *
10162306a36Sopenharmony_ci	 * To avoid this rebind Card issue,
10262306a36Sopenharmony_ci	 * 1) It needs to alloc memory to keep dai_name eventhough
10362306a36Sopenharmony_ci	 *    CPU or Codec driver was unbinded, or
10462306a36Sopenharmony_ci	 * 2) user need to rebind Sound Card everytime
10562306a36Sopenharmony_ci	 *    if he unbinded CPU or Codec.
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	ret = snd_soc_get_dlc(&args, dlc);
10862306a36Sopenharmony_ci	if (ret < 0)
10962306a36Sopenharmony_ci		return ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciparse_dai_end:
11262306a36Sopenharmony_ci	if (is_single_link)
11362306a36Sopenharmony_ci		*is_single_link = !args.args_count;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void simple_parse_convert(struct device *dev,
11962306a36Sopenharmony_ci				 struct device_node *np,
12062306a36Sopenharmony_ci				 struct asoc_simple_data *adata)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct device_node *top = dev->of_node;
12362306a36Sopenharmony_ci	struct device_node *node = of_get_parent(np);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	asoc_simple_parse_convert(top,  PREFIX, adata);
12662306a36Sopenharmony_ci	asoc_simple_parse_convert(node, PREFIX, adata);
12762306a36Sopenharmony_ci	asoc_simple_parse_convert(node, NULL,   adata);
12862306a36Sopenharmony_ci	asoc_simple_parse_convert(np,   NULL,   adata);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	of_node_put(node);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void simple_parse_mclk_fs(struct device_node *top,
13462306a36Sopenharmony_ci				 struct device_node *np,
13562306a36Sopenharmony_ci				 struct simple_dai_props *props,
13662306a36Sopenharmony_ci				 char *prefix)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct device_node *node = of_get_parent(np);
13962306a36Sopenharmony_ci	char prop[128];
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%smclk-fs", PREFIX);
14262306a36Sopenharmony_ci	of_property_read_u32(top,	prop, &props->mclk_fs);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%smclk-fs", prefix);
14562306a36Sopenharmony_ci	of_property_read_u32(node,	prop, &props->mclk_fs);
14662306a36Sopenharmony_ci	of_property_read_u32(np,	prop, &props->mclk_fs);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	of_node_put(node);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int simple_parse_node(struct asoc_simple_priv *priv,
15262306a36Sopenharmony_ci			     struct device_node *np,
15362306a36Sopenharmony_ci			     struct link_info *li,
15462306a36Sopenharmony_ci			     char *prefix,
15562306a36Sopenharmony_ci			     int *cpu)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
15862306a36Sopenharmony_ci	struct device_node *top = dev->of_node;
15962306a36Sopenharmony_ci	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
16062306a36Sopenharmony_ci	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
16162306a36Sopenharmony_ci	struct snd_soc_dai_link_component *dlc;
16262306a36Sopenharmony_ci	struct asoc_simple_dai *dai;
16362306a36Sopenharmony_ci	int ret;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (cpu) {
16662306a36Sopenharmony_ci		dlc = asoc_link_to_cpu(dai_link, 0);
16762306a36Sopenharmony_ci		dai = simple_props_to_dai_cpu(dai_props, 0);
16862306a36Sopenharmony_ci	} else {
16962306a36Sopenharmony_ci		dlc = asoc_link_to_codec(dai_link, 0);
17062306a36Sopenharmony_ci		dai = simple_props_to_dai_codec(dai_props, 0);
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	simple_parse_mclk_fs(top, np, dai_props, prefix);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	ret = asoc_simple_parse_dai(dev, np, dlc, cpu);
17662306a36Sopenharmony_ci	if (ret)
17762306a36Sopenharmony_ci		return ret;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = asoc_simple_parse_clk(dev, np, dai, dlc);
18062306a36Sopenharmony_ci	if (ret)
18162306a36Sopenharmony_ci		return ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ret = asoc_simple_parse_tdm(np, dai);
18462306a36Sopenharmony_ci	if (ret)
18562306a36Sopenharmony_ci		return ret;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int simple_link_init(struct asoc_simple_priv *priv,
19162306a36Sopenharmony_ci			    struct device_node *node,
19262306a36Sopenharmony_ci			    struct device_node *codec,
19362306a36Sopenharmony_ci			    struct link_info *li,
19462306a36Sopenharmony_ci			    char *prefix, char *name)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
19762306a36Sopenharmony_ci	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
19862306a36Sopenharmony_ci	int ret;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	ret = asoc_simple_parse_daifmt(dev, node, codec,
20162306a36Sopenharmony_ci				       prefix, &dai_link->dai_fmt);
20262306a36Sopenharmony_ci	if (ret < 0)
20362306a36Sopenharmony_ci		return 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	dai_link->init			= asoc_simple_dai_init;
20662306a36Sopenharmony_ci	dai_link->ops			= &simple_ops;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return asoc_simple_set_dailink_name(dev, dai_link, name);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
21262306a36Sopenharmony_ci				   struct device_node *np,
21362306a36Sopenharmony_ci				   struct device_node *codec,
21462306a36Sopenharmony_ci				   struct link_info *li,
21562306a36Sopenharmony_ci				   bool is_top)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
21862306a36Sopenharmony_ci	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
21962306a36Sopenharmony_ci	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
22062306a36Sopenharmony_ci	struct device_node *top = dev->of_node;
22162306a36Sopenharmony_ci	struct device_node *node = of_get_parent(np);
22262306a36Sopenharmony_ci	char *prefix = "";
22362306a36Sopenharmony_ci	char dai_name[64];
22462306a36Sopenharmony_ci	int ret;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	dev_dbg(dev, "link_of DPCM (%pOF)\n", np);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* For single DAI link & old style of DT node */
22962306a36Sopenharmony_ci	if (is_top)
23062306a36Sopenharmony_ci		prefix = PREFIX;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (li->cpu) {
23362306a36Sopenharmony_ci		struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
23462306a36Sopenharmony_ci		struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
23562306a36Sopenharmony_ci		int is_single_links = 0;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		/* Codec is dummy */
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		/* FE settings */
24062306a36Sopenharmony_ci		dai_link->dynamic		= 1;
24162306a36Sopenharmony_ci		dai_link->dpcm_merged_format	= 1;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		ret = simple_parse_node(priv, np, li, prefix, &is_single_links);
24462306a36Sopenharmony_ci		if (ret < 0)
24562306a36Sopenharmony_ci			goto out_put_node;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		snprintf(dai_name, sizeof(dai_name), "fe.%s", cpus->dai_name);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		asoc_simple_canonicalize_cpu(cpus, is_single_links);
25062306a36Sopenharmony_ci		asoc_simple_canonicalize_platform(platforms, cpus);
25162306a36Sopenharmony_ci	} else {
25262306a36Sopenharmony_ci		struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
25362306a36Sopenharmony_ci		struct snd_soc_codec_conf *cconf;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		/* CPU is dummy */
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		/* BE settings */
25862306a36Sopenharmony_ci		dai_link->no_pcm		= 1;
25962306a36Sopenharmony_ci		dai_link->be_hw_params_fixup	= asoc_simple_be_hw_params_fixup;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		cconf	= simple_props_to_codec_conf(dai_props, 0);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		ret = simple_parse_node(priv, np, li, prefix, NULL);
26462306a36Sopenharmony_ci		if (ret < 0)
26562306a36Sopenharmony_ci			goto out_put_node;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		snprintf(dai_name, sizeof(dai_name), "be.%s", codecs->dai_name);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		/* check "prefix" from top node */
27062306a36Sopenharmony_ci		snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
27162306a36Sopenharmony_ci					      PREFIX "prefix");
27262306a36Sopenharmony_ci		snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node,
27362306a36Sopenharmony_ci					     "prefix");
27462306a36Sopenharmony_ci		snd_soc_of_parse_node_prefix(np, cconf, codecs->of_node,
27562306a36Sopenharmony_ci					     "prefix");
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	simple_parse_convert(dev, np, &dai_props->adata);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	snd_soc_dai_link_set_capabilities(dai_link);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ret = simple_link_init(priv, node, codec, li, prefix, dai_name);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ciout_put_node:
28562306a36Sopenharmony_ci	li->link++;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	of_node_put(node);
28862306a36Sopenharmony_ci	return ret;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int simple_dai_link_of(struct asoc_simple_priv *priv,
29262306a36Sopenharmony_ci			      struct device_node *np,
29362306a36Sopenharmony_ci			      struct device_node *codec,
29462306a36Sopenharmony_ci			      struct link_info *li,
29562306a36Sopenharmony_ci			      bool is_top)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
29862306a36Sopenharmony_ci	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
29962306a36Sopenharmony_ci	struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
30062306a36Sopenharmony_ci	struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
30162306a36Sopenharmony_ci	struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
30262306a36Sopenharmony_ci	struct device_node *cpu = NULL;
30362306a36Sopenharmony_ci	struct device_node *node = NULL;
30462306a36Sopenharmony_ci	struct device_node *plat = NULL;
30562306a36Sopenharmony_ci	char dai_name[64];
30662306a36Sopenharmony_ci	char prop[128];
30762306a36Sopenharmony_ci	char *prefix = "";
30862306a36Sopenharmony_ci	int ret, single_cpu = 0;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	cpu  = np;
31162306a36Sopenharmony_ci	node = of_get_parent(np);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	dev_dbg(dev, "link_of (%pOF)\n", node);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* For single DAI link & old style of DT node */
31662306a36Sopenharmony_ci	if (is_top)
31762306a36Sopenharmony_ci		prefix = PREFIX;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%splat", prefix);
32062306a36Sopenharmony_ci	plat = of_get_child_by_name(node, prop);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	ret = simple_parse_node(priv, cpu, li, prefix, &single_cpu);
32362306a36Sopenharmony_ci	if (ret < 0)
32462306a36Sopenharmony_ci		goto dai_link_of_err;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	ret = simple_parse_node(priv, codec, li, prefix, NULL);
32762306a36Sopenharmony_ci	if (ret < 0)
32862306a36Sopenharmony_ci		goto dai_link_of_err;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	ret = asoc_simple_parse_platform(plat, platforms);
33162306a36Sopenharmony_ci	if (ret < 0)
33262306a36Sopenharmony_ci		goto dai_link_of_err;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	snprintf(dai_name, sizeof(dai_name),
33562306a36Sopenharmony_ci		 "%s-%s", cpus->dai_name, codecs->dai_name);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	asoc_simple_canonicalize_cpu(cpus, single_cpu);
33862306a36Sopenharmony_ci	asoc_simple_canonicalize_platform(platforms, cpus);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ret = simple_link_init(priv, node, codec, li, prefix, dai_name);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cidai_link_of_err:
34362306a36Sopenharmony_ci	of_node_put(plat);
34462306a36Sopenharmony_ci	of_node_put(node);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	li->link++;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return ret;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int __simple_for_each_link(struct asoc_simple_priv *priv,
35262306a36Sopenharmony_ci			struct link_info *li,
35362306a36Sopenharmony_ci			int (*func_noml)(struct asoc_simple_priv *priv,
35462306a36Sopenharmony_ci					 struct device_node *np,
35562306a36Sopenharmony_ci					 struct device_node *codec,
35662306a36Sopenharmony_ci					 struct link_info *li, bool is_top),
35762306a36Sopenharmony_ci			int (*func_dpcm)(struct asoc_simple_priv *priv,
35862306a36Sopenharmony_ci					 struct device_node *np,
35962306a36Sopenharmony_ci					 struct device_node *codec,
36062306a36Sopenharmony_ci					 struct link_info *li, bool is_top))
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
36362306a36Sopenharmony_ci	struct device_node *top = dev->of_node;
36462306a36Sopenharmony_ci	struct device_node *node;
36562306a36Sopenharmony_ci	struct device_node *add_devs;
36662306a36Sopenharmony_ci	uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev);
36762306a36Sopenharmony_ci	bool is_top = 0;
36862306a36Sopenharmony_ci	int ret = 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Check if it has dai-link */
37162306a36Sopenharmony_ci	node = of_get_child_by_name(top, PREFIX "dai-link");
37262306a36Sopenharmony_ci	if (!node) {
37362306a36Sopenharmony_ci		node = of_node_get(top);
37462306a36Sopenharmony_ci		is_top = 1;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	add_devs = of_get_child_by_name(top, PREFIX "additional-devs");
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* loop for all dai-link */
38062306a36Sopenharmony_ci	do {
38162306a36Sopenharmony_ci		struct asoc_simple_data adata;
38262306a36Sopenharmony_ci		struct device_node *codec;
38362306a36Sopenharmony_ci		struct device_node *plat;
38462306a36Sopenharmony_ci		struct device_node *np;
38562306a36Sopenharmony_ci		int num = of_get_child_count(node);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		/* Skip additional-devs node */
38862306a36Sopenharmony_ci		if (node == add_devs) {
38962306a36Sopenharmony_ci			node = of_get_next_child(top, node);
39062306a36Sopenharmony_ci			continue;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* get codec */
39462306a36Sopenharmony_ci		codec = of_get_child_by_name(node, is_top ?
39562306a36Sopenharmony_ci					     PREFIX "codec" : "codec");
39662306a36Sopenharmony_ci		if (!codec) {
39762306a36Sopenharmony_ci			ret = -ENODEV;
39862306a36Sopenharmony_ci			goto error;
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci		/* get platform */
40162306a36Sopenharmony_ci		plat = of_get_child_by_name(node, is_top ?
40262306a36Sopenharmony_ci					    PREFIX "plat" : "plat");
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		/* get convert-xxx property */
40562306a36Sopenharmony_ci		memset(&adata, 0, sizeof(adata));
40662306a36Sopenharmony_ci		for_each_child_of_node(node, np) {
40762306a36Sopenharmony_ci			if (np == add_devs)
40862306a36Sopenharmony_ci				continue;
40962306a36Sopenharmony_ci			simple_parse_convert(dev, np, &adata);
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/* loop for all CPU/Codec node */
41362306a36Sopenharmony_ci		for_each_child_of_node(node, np) {
41462306a36Sopenharmony_ci			if (plat == np || add_devs == np)
41562306a36Sopenharmony_ci				continue;
41662306a36Sopenharmony_ci			/*
41762306a36Sopenharmony_ci			 * It is DPCM
41862306a36Sopenharmony_ci			 * if it has many CPUs,
41962306a36Sopenharmony_ci			 * or has convert-xxx property
42062306a36Sopenharmony_ci			 */
42162306a36Sopenharmony_ci			if (dpcm_selectable &&
42262306a36Sopenharmony_ci			    (num > 2 || asoc_simple_is_convert_required(&adata))) {
42362306a36Sopenharmony_ci				/*
42462306a36Sopenharmony_ci				 * np
42562306a36Sopenharmony_ci				 *	 |1(CPU)|0(Codec)  li->cpu
42662306a36Sopenharmony_ci				 * CPU	 |Pass  |return
42762306a36Sopenharmony_ci				 * Codec |return|Pass
42862306a36Sopenharmony_ci				 */
42962306a36Sopenharmony_ci				if (li->cpu != (np == codec))
43062306a36Sopenharmony_ci					ret = func_dpcm(priv, np, codec, li, is_top);
43162306a36Sopenharmony_ci			/* else normal sound */
43262306a36Sopenharmony_ci			} else {
43362306a36Sopenharmony_ci				/*
43462306a36Sopenharmony_ci				 * np
43562306a36Sopenharmony_ci				 *	 |1(CPU)|0(Codec)  li->cpu
43662306a36Sopenharmony_ci				 * CPU	 |Pass  |return
43762306a36Sopenharmony_ci				 * Codec |return|return
43862306a36Sopenharmony_ci				 */
43962306a36Sopenharmony_ci				if (li->cpu && (np != codec))
44062306a36Sopenharmony_ci					ret = func_noml(priv, np, codec, li, is_top);
44162306a36Sopenharmony_ci			}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci			if (ret < 0) {
44462306a36Sopenharmony_ci				of_node_put(codec);
44562306a36Sopenharmony_ci				of_node_put(plat);
44662306a36Sopenharmony_ci				of_node_put(np);
44762306a36Sopenharmony_ci				goto error;
44862306a36Sopenharmony_ci			}
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		of_node_put(codec);
45262306a36Sopenharmony_ci		of_node_put(plat);
45362306a36Sopenharmony_ci		node = of_get_next_child(top, node);
45462306a36Sopenharmony_ci	} while (!is_top && node);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci error:
45762306a36Sopenharmony_ci	of_node_put(add_devs);
45862306a36Sopenharmony_ci	of_node_put(node);
45962306a36Sopenharmony_ci	return ret;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int simple_for_each_link(struct asoc_simple_priv *priv,
46362306a36Sopenharmony_ci				struct link_info *li,
46462306a36Sopenharmony_ci				int (*func_noml)(struct asoc_simple_priv *priv,
46562306a36Sopenharmony_ci						 struct device_node *np,
46662306a36Sopenharmony_ci						 struct device_node *codec,
46762306a36Sopenharmony_ci						 struct link_info *li, bool is_top),
46862306a36Sopenharmony_ci				int (*func_dpcm)(struct asoc_simple_priv *priv,
46962306a36Sopenharmony_ci						 struct device_node *np,
47062306a36Sopenharmony_ci						 struct device_node *codec,
47162306a36Sopenharmony_ci						 struct link_info *li, bool is_top))
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	int ret;
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * Detect all CPU first, and Detect all Codec 2nd.
47662306a36Sopenharmony_ci	 *
47762306a36Sopenharmony_ci	 * In Normal sound case, all DAIs are detected
47862306a36Sopenharmony_ci	 * as "CPU-Codec".
47962306a36Sopenharmony_ci	 *
48062306a36Sopenharmony_ci	 * In DPCM sound case,
48162306a36Sopenharmony_ci	 * all CPUs   are detected as "CPU-dummy", and
48262306a36Sopenharmony_ci	 * all Codecs are detected as "dummy-Codec".
48362306a36Sopenharmony_ci	 * To avoid random sub-device numbering,
48462306a36Sopenharmony_ci	 * detect "dummy-Codec" in last;
48562306a36Sopenharmony_ci	 */
48662306a36Sopenharmony_ci	for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
48762306a36Sopenharmony_ci		ret = __simple_for_each_link(priv, li, func_noml, func_dpcm);
48862306a36Sopenharmony_ci		if (ret < 0)
48962306a36Sopenharmony_ci			break;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return ret;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic void simple_depopulate_aux(void *data)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct asoc_simple_priv *priv = data;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	of_platform_depopulate(simple_priv_to_dev(priv));
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int simple_populate_aux(struct asoc_simple_priv *priv)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
50562306a36Sopenharmony_ci	struct device_node *node;
50662306a36Sopenharmony_ci	int ret;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	node = of_get_child_by_name(dev->of_node, PREFIX "additional-devs");
50962306a36Sopenharmony_ci	if (!node)
51062306a36Sopenharmony_ci		return 0;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	ret = of_platform_populate(node, NULL, NULL, dev);
51362306a36Sopenharmony_ci	of_node_put(node);
51462306a36Sopenharmony_ci	if (ret)
51562306a36Sopenharmony_ci		return ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return devm_add_action_or_reset(dev, simple_depopulate_aux, priv);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic int simple_parse_of(struct asoc_simple_priv *priv, struct link_info *li)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct snd_soc_card *card = simple_priv_to_card(priv);
52362306a36Sopenharmony_ci	int ret;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	ret = asoc_simple_parse_widgets(card, PREFIX);
52662306a36Sopenharmony_ci	if (ret < 0)
52762306a36Sopenharmony_ci		return ret;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	ret = asoc_simple_parse_routing(card, PREFIX);
53062306a36Sopenharmony_ci	if (ret < 0)
53162306a36Sopenharmony_ci		return ret;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	ret = asoc_simple_parse_pin_switches(card, PREFIX);
53462306a36Sopenharmony_ci	if (ret < 0)
53562306a36Sopenharmony_ci		return ret;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* Single/Muti DAI link(s) & New style of DT node */
53862306a36Sopenharmony_ci	memset(li, 0, sizeof(*li));
53962306a36Sopenharmony_ci	ret = simple_for_each_link(priv, li,
54062306a36Sopenharmony_ci				   simple_dai_link_of,
54162306a36Sopenharmony_ci				   simple_dai_link_of_dpcm);
54262306a36Sopenharmony_ci	if (ret < 0)
54362306a36Sopenharmony_ci		return ret;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	ret = asoc_simple_parse_card_name(card, PREFIX);
54662306a36Sopenharmony_ci	if (ret < 0)
54762306a36Sopenharmony_ci		return ret;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	ret = simple_populate_aux(priv);
55062306a36Sopenharmony_ci	if (ret < 0)
55162306a36Sopenharmony_ci		return ret;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs");
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return ret;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic int simple_count_noml(struct asoc_simple_priv *priv,
55962306a36Sopenharmony_ci			     struct device_node *np,
56062306a36Sopenharmony_ci			     struct device_node *codec,
56162306a36Sopenharmony_ci			     struct link_info *li, bool is_top)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	if (li->link >= SNDRV_MAX_LINKS) {
56462306a36Sopenharmony_ci		struct device *dev = simple_priv_to_dev(priv);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		dev_err(dev, "too many links\n");
56762306a36Sopenharmony_ci		return -EINVAL;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/*
57162306a36Sopenharmony_ci	 * DON'T REMOVE platforms
57262306a36Sopenharmony_ci	 *
57362306a36Sopenharmony_ci	 * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
57462306a36Sopenharmony_ci	 * are different Component, but are sharing same component->dev.
57562306a36Sopenharmony_ci	 * Simple Card had been supported it without special Platform selection.
57662306a36Sopenharmony_ci	 * We need platforms here.
57762306a36Sopenharmony_ci	 *
57862306a36Sopenharmony_ci	 * In case of no Platform, it will be Platform == CPU, but Platform will be
57962306a36Sopenharmony_ci	 * ignored by snd_soc_rtd_add_component().
58062306a36Sopenharmony_ci	 *
58162306a36Sopenharmony_ci	 * see
58262306a36Sopenharmony_ci	 *	simple-card-utils.c :: asoc_simple_canonicalize_platform()
58362306a36Sopenharmony_ci	 */
58462306a36Sopenharmony_ci	li->num[li->link].cpus		= 1;
58562306a36Sopenharmony_ci	li->num[li->link].platforms	= 1;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	li->num[li->link].codecs	= 1;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	li->link += 1;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return 0;
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic int simple_count_dpcm(struct asoc_simple_priv *priv,
59562306a36Sopenharmony_ci			     struct device_node *np,
59662306a36Sopenharmony_ci			     struct device_node *codec,
59762306a36Sopenharmony_ci			     struct link_info *li, bool is_top)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	if (li->link >= SNDRV_MAX_LINKS) {
60062306a36Sopenharmony_ci		struct device *dev = simple_priv_to_dev(priv);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		dev_err(dev, "too many links\n");
60362306a36Sopenharmony_ci		return -EINVAL;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (li->cpu) {
60762306a36Sopenharmony_ci		/*
60862306a36Sopenharmony_ci		 * DON'T REMOVE platforms
60962306a36Sopenharmony_ci		 * see
61062306a36Sopenharmony_ci		 *	simple_count_noml()
61162306a36Sopenharmony_ci		 */
61262306a36Sopenharmony_ci		li->num[li->link].cpus		= 1;
61362306a36Sopenharmony_ci		li->num[li->link].platforms	= 1;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		li->link++; /* CPU-dummy */
61662306a36Sopenharmony_ci	} else {
61762306a36Sopenharmony_ci		li->num[li->link].codecs	= 1;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		li->link++; /* dummy-Codec */
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return 0;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int simple_get_dais_count(struct asoc_simple_priv *priv,
62662306a36Sopenharmony_ci				 struct link_info *li)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
62962306a36Sopenharmony_ci	struct device_node *top = dev->of_node;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/*
63262306a36Sopenharmony_ci	 * link_num :	number of links.
63362306a36Sopenharmony_ci	 *		CPU-Codec / CPU-dummy / dummy-Codec
63462306a36Sopenharmony_ci	 * dais_num :	number of DAIs
63562306a36Sopenharmony_ci	 * ccnf_num :	number of codec_conf
63662306a36Sopenharmony_ci	 *		same number for "dummy-Codec"
63762306a36Sopenharmony_ci	 *
63862306a36Sopenharmony_ci	 * ex1)
63962306a36Sopenharmony_ci	 * CPU0 --- Codec0	link : 5
64062306a36Sopenharmony_ci	 * CPU1 --- Codec1	dais : 7
64162306a36Sopenharmony_ci	 * CPU2 -/		ccnf : 1
64262306a36Sopenharmony_ci	 * CPU3 --- Codec2
64362306a36Sopenharmony_ci	 *
64462306a36Sopenharmony_ci	 *	=> 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
64562306a36Sopenharmony_ci	 *	=> 7 DAIs  = 4xCPU + 3xCodec
64662306a36Sopenharmony_ci	 *	=> 1 ccnf  = 1xdummy-Codec
64762306a36Sopenharmony_ci	 *
64862306a36Sopenharmony_ci	 * ex2)
64962306a36Sopenharmony_ci	 * CPU0 --- Codec0	link : 5
65062306a36Sopenharmony_ci	 * CPU1 --- Codec1	dais : 6
65162306a36Sopenharmony_ci	 * CPU2 -/		ccnf : 1
65262306a36Sopenharmony_ci	 * CPU3 -/
65362306a36Sopenharmony_ci	 *
65462306a36Sopenharmony_ci	 *	=> 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
65562306a36Sopenharmony_ci	 *	=> 6 DAIs  = 4xCPU + 2xCodec
65662306a36Sopenharmony_ci	 *	=> 1 ccnf  = 1xdummy-Codec
65762306a36Sopenharmony_ci	 *
65862306a36Sopenharmony_ci	 * ex3)
65962306a36Sopenharmony_ci	 * CPU0 --- Codec0	link : 6
66062306a36Sopenharmony_ci	 * CPU1 -/		dais : 6
66162306a36Sopenharmony_ci	 * CPU2 --- Codec1	ccnf : 2
66262306a36Sopenharmony_ci	 * CPU3 -/
66362306a36Sopenharmony_ci	 *
66462306a36Sopenharmony_ci	 *	=> 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
66562306a36Sopenharmony_ci	 *	=> 6 DAIs  = 4xCPU + 2xCodec
66662306a36Sopenharmony_ci	 *	=> 2 ccnf  = 2xdummy-Codec
66762306a36Sopenharmony_ci	 *
66862306a36Sopenharmony_ci	 * ex4)
66962306a36Sopenharmony_ci	 * CPU0 --- Codec0 (convert-rate)	link : 3
67062306a36Sopenharmony_ci	 * CPU1 --- Codec1			dais : 4
67162306a36Sopenharmony_ci	 *					ccnf : 1
67262306a36Sopenharmony_ci	 *
67362306a36Sopenharmony_ci	 *	=> 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
67462306a36Sopenharmony_ci	 *	=> 4 DAIs  = 2xCPU + 2xCodec
67562306a36Sopenharmony_ci	 *	=> 1 ccnf  = 1xdummy-Codec
67662306a36Sopenharmony_ci	 */
67762306a36Sopenharmony_ci	if (!top) {
67862306a36Sopenharmony_ci		li->num[0].cpus		= 1;
67962306a36Sopenharmony_ci		li->num[0].codecs	= 1;
68062306a36Sopenharmony_ci		li->num[0].platforms	= 1;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		li->link = 1;
68362306a36Sopenharmony_ci		return 0;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return simple_for_each_link(priv, li,
68762306a36Sopenharmony_ci				    simple_count_noml,
68862306a36Sopenharmony_ci				    simple_count_dpcm);
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int simple_soc_probe(struct snd_soc_card *card)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
69462306a36Sopenharmony_ci	int ret;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	ret = asoc_simple_init_hp(card, &priv->hp_jack, PREFIX);
69762306a36Sopenharmony_ci	if (ret < 0)
69862306a36Sopenharmony_ci		return ret;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	ret = asoc_simple_init_mic(card, &priv->mic_jack, PREFIX);
70162306a36Sopenharmony_ci	if (ret < 0)
70262306a36Sopenharmony_ci		return ret;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	ret = asoc_simple_init_aux_jacks(priv, PREFIX);
70562306a36Sopenharmony_ci	if (ret < 0)
70662306a36Sopenharmony_ci		return ret;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return 0;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic int asoc_simple_probe(struct platform_device *pdev)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct asoc_simple_priv *priv;
71462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
71562306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
71662306a36Sopenharmony_ci	struct snd_soc_card *card;
71762306a36Sopenharmony_ci	struct link_info *li;
71862306a36Sopenharmony_ci	int ret;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* Allocate the private data and the DAI link array */
72162306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
72262306a36Sopenharmony_ci	if (!priv)
72362306a36Sopenharmony_ci		return -ENOMEM;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	card = simple_priv_to_card(priv);
72662306a36Sopenharmony_ci	card->owner		= THIS_MODULE;
72762306a36Sopenharmony_ci	card->dev		= dev;
72862306a36Sopenharmony_ci	card->probe		= simple_soc_probe;
72962306a36Sopenharmony_ci	card->driver_name       = "simple-card";
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL);
73262306a36Sopenharmony_ci	if (!li)
73362306a36Sopenharmony_ci		return -ENOMEM;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	ret = simple_get_dais_count(priv, li);
73662306a36Sopenharmony_ci	if (ret < 0)
73762306a36Sopenharmony_ci		return ret;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	if (!li->link)
74062306a36Sopenharmony_ci		return -EINVAL;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	ret = asoc_simple_init_priv(priv, li);
74362306a36Sopenharmony_ci	if (ret < 0)
74462306a36Sopenharmony_ci		return ret;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (np && of_device_is_available(np)) {
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		ret = simple_parse_of(priv, li);
74962306a36Sopenharmony_ci		if (ret < 0) {
75062306a36Sopenharmony_ci			dev_err_probe(dev, ret, "parse error\n");
75162306a36Sopenharmony_ci			goto err;
75262306a36Sopenharmony_ci		}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	} else {
75562306a36Sopenharmony_ci		struct asoc_simple_card_info *cinfo;
75662306a36Sopenharmony_ci		struct snd_soc_dai_link_component *cpus;
75762306a36Sopenharmony_ci		struct snd_soc_dai_link_component *codecs;
75862306a36Sopenharmony_ci		struct snd_soc_dai_link_component *platform;
75962306a36Sopenharmony_ci		struct snd_soc_dai_link *dai_link = priv->dai_link;
76062306a36Sopenharmony_ci		struct simple_dai_props *dai_props = priv->dai_props;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci		ret = -EINVAL;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		cinfo = dev->platform_data;
76562306a36Sopenharmony_ci		if (!cinfo) {
76662306a36Sopenharmony_ci			dev_err(dev, "no info for asoc-simple-card\n");
76762306a36Sopenharmony_ci			goto err;
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		if (!cinfo->name ||
77162306a36Sopenharmony_ci		    !cinfo->codec_dai.name ||
77262306a36Sopenharmony_ci		    !cinfo->codec ||
77362306a36Sopenharmony_ci		    !cinfo->platform ||
77462306a36Sopenharmony_ci		    !cinfo->cpu_dai.name) {
77562306a36Sopenharmony_ci			dev_err(dev, "insufficient asoc_simple_card_info settings\n");
77662306a36Sopenharmony_ci			goto err;
77762306a36Sopenharmony_ci		}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		cpus			= dai_link->cpus;
78062306a36Sopenharmony_ci		cpus->dai_name		= cinfo->cpu_dai.name;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		codecs			= dai_link->codecs;
78362306a36Sopenharmony_ci		codecs->name		= cinfo->codec;
78462306a36Sopenharmony_ci		codecs->dai_name	= cinfo->codec_dai.name;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		platform		= dai_link->platforms;
78762306a36Sopenharmony_ci		platform->name		= cinfo->platform;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		card->name		= (cinfo->card) ? cinfo->card : cinfo->name;
79062306a36Sopenharmony_ci		dai_link->name		= cinfo->name;
79162306a36Sopenharmony_ci		dai_link->stream_name	= cinfo->name;
79262306a36Sopenharmony_ci		dai_link->dai_fmt	= cinfo->daifmt;
79362306a36Sopenharmony_ci		dai_link->init		= asoc_simple_dai_init;
79462306a36Sopenharmony_ci		memcpy(dai_props->cpu_dai, &cinfo->cpu_dai,
79562306a36Sopenharmony_ci					sizeof(*dai_props->cpu_dai));
79662306a36Sopenharmony_ci		memcpy(dai_props->codec_dai, &cinfo->codec_dai,
79762306a36Sopenharmony_ci					sizeof(*dai_props->codec_dai));
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	snd_soc_card_set_drvdata(card, priv);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	asoc_simple_debug_info(priv);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	ret = devm_snd_soc_register_card(dev, card);
80562306a36Sopenharmony_ci	if (ret < 0)
80662306a36Sopenharmony_ci		goto err;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	devm_kfree(dev, li);
80962306a36Sopenharmony_ci	return 0;
81062306a36Sopenharmony_cierr:
81162306a36Sopenharmony_ci	asoc_simple_clean_reference(card);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return ret;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic const struct of_device_id simple_of_match[] = {
81762306a36Sopenharmony_ci	{ .compatible = "simple-audio-card", },
81862306a36Sopenharmony_ci	{ .compatible = "simple-scu-audio-card",
81962306a36Sopenharmony_ci	  .data = (void *)DPCM_SELECTABLE },
82062306a36Sopenharmony_ci	{},
82162306a36Sopenharmony_ci};
82262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, simple_of_match);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic struct platform_driver asoc_simple_card = {
82562306a36Sopenharmony_ci	.driver = {
82662306a36Sopenharmony_ci		.name = "asoc-simple-card",
82762306a36Sopenharmony_ci		.pm = &snd_soc_pm_ops,
82862306a36Sopenharmony_ci		.of_match_table = simple_of_match,
82962306a36Sopenharmony_ci	},
83062306a36Sopenharmony_ci	.probe = asoc_simple_probe,
83162306a36Sopenharmony_ci	.remove = asoc_simple_remove,
83262306a36Sopenharmony_ci};
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cimodule_platform_driver(asoc_simple_card);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ciMODULE_ALIAS("platform:asoc-simple-card");
83762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
83862306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC Simple Sound Card");
83962306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
840