162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// simple-card-utils.c
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/gpio.h>
962306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_graph.h>
1362306a36Sopenharmony_ci#include <sound/jack.h>
1462306a36Sopenharmony_ci#include <sound/pcm_params.h>
1562306a36Sopenharmony_ci#include <sound/simple_card_utils.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic void asoc_simple_fixup_sample_fmt(struct asoc_simple_data *data,
1862306a36Sopenharmony_ci					 struct snd_pcm_hw_params *params)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	int i;
2162306a36Sopenharmony_ci	struct snd_mask *mask = hw_param_mask(params,
2262306a36Sopenharmony_ci					      SNDRV_PCM_HW_PARAM_FORMAT);
2362306a36Sopenharmony_ci	struct {
2462306a36Sopenharmony_ci		char *fmt;
2562306a36Sopenharmony_ci		u32 val;
2662306a36Sopenharmony_ci	} of_sample_fmt_table[] = {
2762306a36Sopenharmony_ci		{ "s8",		SNDRV_PCM_FORMAT_S8},
2862306a36Sopenharmony_ci		{ "s16_le",	SNDRV_PCM_FORMAT_S16_LE},
2962306a36Sopenharmony_ci		{ "s24_le",	SNDRV_PCM_FORMAT_S24_LE},
3062306a36Sopenharmony_ci		{ "s24_3le",	SNDRV_PCM_FORMAT_S24_3LE},
3162306a36Sopenharmony_ci		{ "s32_le",	SNDRV_PCM_FORMAT_S32_LE},
3262306a36Sopenharmony_ci	};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) {
3562306a36Sopenharmony_ci		if (!strcmp(data->convert_sample_format,
3662306a36Sopenharmony_ci			    of_sample_fmt_table[i].fmt)) {
3762306a36Sopenharmony_ci			snd_mask_none(mask);
3862306a36Sopenharmony_ci			snd_mask_set(mask, of_sample_fmt_table[i].val);
3962306a36Sopenharmony_ci			break;
4062306a36Sopenharmony_ci		}
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_civoid asoc_simple_parse_convert(struct device_node *np,
4562306a36Sopenharmony_ci			       char *prefix,
4662306a36Sopenharmony_ci			       struct asoc_simple_data *data)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	char prop[128];
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (!prefix)
5162306a36Sopenharmony_ci		prefix = "";
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* sampling rate convert */
5462306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
5562306a36Sopenharmony_ci	of_property_read_u32(np, prop, &data->convert_rate);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* channels transfer */
5862306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
5962306a36Sopenharmony_ci	of_property_read_u32(np, prop, &data->convert_channels);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* convert sample format */
6262306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format");
6362306a36Sopenharmony_ci	of_property_read_string(np, prop, &data->convert_sample_format);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/**
6862306a36Sopenharmony_ci * asoc_simple_is_convert_required() - Query if HW param conversion was requested
6962306a36Sopenharmony_ci * @data: Link data.
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * Returns true if any HW param conversion was requested for this DAI link with
7262306a36Sopenharmony_ci * any "convert-xxx" properties.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cibool asoc_simple_is_convert_required(const struct asoc_simple_data *data)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return data->convert_rate ||
7762306a36Sopenharmony_ci	       data->convert_channels ||
7862306a36Sopenharmony_ci	       data->convert_sample_format;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_is_convert_required);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint asoc_simple_parse_daifmt(struct device *dev,
8362306a36Sopenharmony_ci			     struct device_node *node,
8462306a36Sopenharmony_ci			     struct device_node *codec,
8562306a36Sopenharmony_ci			     char *prefix,
8662306a36Sopenharmony_ci			     unsigned int *retfmt)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct device_node *bitclkmaster = NULL;
8962306a36Sopenharmony_ci	struct device_node *framemaster = NULL;
9062306a36Sopenharmony_ci	unsigned int daifmt;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	daifmt = snd_soc_daifmt_parse_format(node, prefix);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);
9562306a36Sopenharmony_ci	if (!bitclkmaster && !framemaster) {
9662306a36Sopenharmony_ci		/*
9762306a36Sopenharmony_ci		 * No dai-link level and master setting was not found from
9862306a36Sopenharmony_ci		 * sound node level, revert back to legacy DT parsing and
9962306a36Sopenharmony_ci		 * take the settings from codec node.
10062306a36Sopenharmony_ci		 */
10162306a36Sopenharmony_ci		dev_dbg(dev, "Revert to legacy daifmt parsing\n");
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);
10462306a36Sopenharmony_ci	} else {
10562306a36Sopenharmony_ci		daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(
10662306a36Sopenharmony_ci				((codec == bitclkmaster) << 4) | (codec == framemaster));
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	of_node_put(bitclkmaster);
11062306a36Sopenharmony_ci	of_node_put(framemaster);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	*retfmt = daifmt;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ciint asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
11962306a36Sopenharmony_ci				    struct asoc_simple_dai *dai)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	u32 *array_values, *p;
12262306a36Sopenharmony_ci	int n, i, ret;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))
12562306a36Sopenharmony_ci		return 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
12862306a36Sopenharmony_ci	if (n % 3) {
12962306a36Sopenharmony_ci		dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
13062306a36Sopenharmony_ci		return -EINVAL;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
13462306a36Sopenharmony_ci	if (!dai->tdm_width_map)
13562306a36Sopenharmony_ci		return -ENOMEM;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
13862306a36Sopenharmony_ci	if (!array_values)
13962306a36Sopenharmony_ci		return -ENOMEM;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
14262306a36Sopenharmony_ci	if (ret < 0) {
14362306a36Sopenharmony_ci		dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
14462306a36Sopenharmony_ci		goto out;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	p = array_values;
14862306a36Sopenharmony_ci	for (i = 0; i < n / 3; ++i) {
14962306a36Sopenharmony_ci		dai->tdm_width_map[i].sample_bits = *p++;
15062306a36Sopenharmony_ci		dai->tdm_width_map[i].slot_width = *p++;
15162306a36Sopenharmony_ci		dai->tdm_width_map[i].slot_count = *p++;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	dai->n_tdm_widths = i;
15562306a36Sopenharmony_ci	ret = 0;
15662306a36Sopenharmony_ciout:
15762306a36Sopenharmony_ci	kfree(array_values);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return ret;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciint asoc_simple_set_dailink_name(struct device *dev,
16462306a36Sopenharmony_ci				 struct snd_soc_dai_link *dai_link,
16562306a36Sopenharmony_ci				 const char *fmt, ...)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	va_list ap;
16862306a36Sopenharmony_ci	char *name = NULL;
16962306a36Sopenharmony_ci	int ret = -ENOMEM;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	va_start(ap, fmt);
17262306a36Sopenharmony_ci	name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
17362306a36Sopenharmony_ci	va_end(ap);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (name) {
17662306a36Sopenharmony_ci		ret = 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		dai_link->name		= name;
17962306a36Sopenharmony_ci		dai_link->stream_name	= name;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return ret;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciint asoc_simple_parse_card_name(struct snd_soc_card *card,
18762306a36Sopenharmony_ci				char *prefix)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	int ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!prefix)
19262306a36Sopenharmony_ci		prefix = "";
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Parse the card name from DT */
19562306a36Sopenharmony_ci	ret = snd_soc_of_parse_card_name(card, "label");
19662306a36Sopenharmony_ci	if (ret < 0 || !card->name) {
19762306a36Sopenharmony_ci		char prop[128];
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		snprintf(prop, sizeof(prop), "%sname", prefix);
20062306a36Sopenharmony_ci		ret = snd_soc_of_parse_card_name(card, prop);
20162306a36Sopenharmony_ci		if (ret < 0)
20262306a36Sopenharmony_ci			return ret;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (!card->name && card->dai_link)
20662306a36Sopenharmony_ci		card->name = card->dai_link->name;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	if (dai)
21562306a36Sopenharmony_ci		return clk_prepare_enable(dai->clk);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return 0;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	if (dai)
22362306a36Sopenharmony_ci		clk_disable_unprepare(dai->clk);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciint asoc_simple_parse_clk(struct device *dev,
22762306a36Sopenharmony_ci			  struct device_node *node,
22862306a36Sopenharmony_ci			  struct asoc_simple_dai *simple_dai,
22962306a36Sopenharmony_ci			  struct snd_soc_dai_link_component *dlc)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct clk *clk;
23262306a36Sopenharmony_ci	u32 val;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * Parse dai->sysclk come from "clocks = <&xxx>"
23662306a36Sopenharmony_ci	 * (if system has common clock)
23762306a36Sopenharmony_ci	 *  or "system-clock-frequency = <xxx>"
23862306a36Sopenharmony_ci	 *  or device's module clock.
23962306a36Sopenharmony_ci	 */
24062306a36Sopenharmony_ci	clk = devm_get_clk_from_child(dev, node, NULL);
24162306a36Sopenharmony_ci	simple_dai->clk_fixed = of_property_read_bool(
24262306a36Sopenharmony_ci		node, "system-clock-fixed");
24362306a36Sopenharmony_ci	if (!IS_ERR(clk)) {
24462306a36Sopenharmony_ci		simple_dai->sysclk = clk_get_rate(clk);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		simple_dai->clk = clk;
24762306a36Sopenharmony_ci	} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
24862306a36Sopenharmony_ci		simple_dai->sysclk = val;
24962306a36Sopenharmony_ci		simple_dai->clk_fixed = true;
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
25262306a36Sopenharmony_ci		if (!IS_ERR(clk))
25362306a36Sopenharmony_ci			simple_dai->sysclk = clk_get_rate(clk);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (of_property_read_bool(node, "system-clock-direction-out"))
25762306a36Sopenharmony_ci		simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int asoc_simple_check_fixed_sysclk(struct device *dev,
26462306a36Sopenharmony_ci					  struct asoc_simple_dai *dai,
26562306a36Sopenharmony_ci					  unsigned int *fixed_sysclk)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	if (dai->clk_fixed) {
26862306a36Sopenharmony_ci		if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {
26962306a36Sopenharmony_ci			dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",
27062306a36Sopenharmony_ci				*fixed_sysclk, dai->sysclk);
27162306a36Sopenharmony_ci			return -EINVAL;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci		*fixed_sysclk = dai->sysclk;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciint asoc_simple_startup(struct snd_pcm_substream *substream)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
28262306a36Sopenharmony_ci	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
28362306a36Sopenharmony_ci	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
28462306a36Sopenharmony_ci	struct asoc_simple_dai *dai;
28562306a36Sopenharmony_ci	unsigned int fixed_sysclk = 0;
28662306a36Sopenharmony_ci	int i1, i2, i;
28762306a36Sopenharmony_ci	int ret;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	for_each_prop_dai_cpu(props, i1, dai) {
29062306a36Sopenharmony_ci		ret = asoc_simple_clk_enable(dai);
29162306a36Sopenharmony_ci		if (ret)
29262306a36Sopenharmony_ci			goto cpu_err;
29362306a36Sopenharmony_ci		ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
29462306a36Sopenharmony_ci		if (ret)
29562306a36Sopenharmony_ci			goto cpu_err;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	for_each_prop_dai_codec(props, i2, dai) {
29962306a36Sopenharmony_ci		ret = asoc_simple_clk_enable(dai);
30062306a36Sopenharmony_ci		if (ret)
30162306a36Sopenharmony_ci			goto codec_err;
30262306a36Sopenharmony_ci		ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
30362306a36Sopenharmony_ci		if (ret)
30462306a36Sopenharmony_ci			goto codec_err;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (fixed_sysclk && props->mclk_fs) {
30862306a36Sopenharmony_ci		unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		if (fixed_sysclk % props->mclk_fs) {
31162306a36Sopenharmony_ci			dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",
31262306a36Sopenharmony_ci				fixed_sysclk, props->mclk_fs);
31362306a36Sopenharmony_ci			ret = -EINVAL;
31462306a36Sopenharmony_ci			goto codec_err;
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci		ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,
31762306a36Sopenharmony_ci			fixed_rate, fixed_rate);
31862306a36Sopenharmony_ci		if (ret < 0)
31962306a36Sopenharmony_ci			goto codec_err;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cicodec_err:
32562306a36Sopenharmony_ci	for_each_prop_dai_codec(props, i, dai) {
32662306a36Sopenharmony_ci		if (i >= i2)
32762306a36Sopenharmony_ci			break;
32862306a36Sopenharmony_ci		asoc_simple_clk_disable(dai);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_cicpu_err:
33162306a36Sopenharmony_ci	for_each_prop_dai_cpu(props, i, dai) {
33262306a36Sopenharmony_ci		if (i >= i1)
33362306a36Sopenharmony_ci			break;
33462306a36Sopenharmony_ci		asoc_simple_clk_disable(dai);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	return ret;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_startup);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_civoid asoc_simple_shutdown(struct snd_pcm_substream *substream)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
34362306a36Sopenharmony_ci	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
34462306a36Sopenharmony_ci	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
34562306a36Sopenharmony_ci	struct asoc_simple_dai *dai;
34662306a36Sopenharmony_ci	int i;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	for_each_prop_dai_cpu(props, i, dai) {
34962306a36Sopenharmony_ci		struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
35262306a36Sopenharmony_ci			snd_soc_dai_set_sysclk(cpu_dai,
35362306a36Sopenharmony_ci					       0, 0, SND_SOC_CLOCK_OUT);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		asoc_simple_clk_disable(dai);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	for_each_prop_dai_codec(props, i, dai) {
35862306a36Sopenharmony_ci		struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
36162306a36Sopenharmony_ci			snd_soc_dai_set_sysclk(codec_dai,
36262306a36Sopenharmony_ci					       0, 0, SND_SOC_CLOCK_IN);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		asoc_simple_clk_disable(dai);
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_shutdown);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int asoc_simple_set_clk_rate(struct device *dev,
37062306a36Sopenharmony_ci				    struct asoc_simple_dai *simple_dai,
37162306a36Sopenharmony_ci				    unsigned long rate)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	if (!simple_dai)
37462306a36Sopenharmony_ci		return 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
37762306a36Sopenharmony_ci		dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
37862306a36Sopenharmony_ci		return -EINVAL;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (!simple_dai->clk)
38262306a36Sopenharmony_ci		return 0;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (clk_get_rate(simple_dai->clk) == rate)
38562306a36Sopenharmony_ci		return 0;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return clk_set_rate(simple_dai->clk, rate);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int asoc_simple_set_tdm(struct snd_soc_dai *dai,
39162306a36Sopenharmony_ci				struct asoc_simple_dai *simple_dai,
39262306a36Sopenharmony_ci				struct snd_pcm_hw_params *params)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	int sample_bits = params_width(params);
39562306a36Sopenharmony_ci	int slot_width, slot_count;
39662306a36Sopenharmony_ci	int i, ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (!simple_dai || !simple_dai->tdm_width_map)
39962306a36Sopenharmony_ci		return 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	slot_width = simple_dai->slot_width;
40262306a36Sopenharmony_ci	slot_count = simple_dai->slots;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (slot_width == 0)
40562306a36Sopenharmony_ci		slot_width = sample_bits;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
40862306a36Sopenharmony_ci		if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
40962306a36Sopenharmony_ci			slot_width = simple_dai->tdm_width_map[i].slot_width;
41062306a36Sopenharmony_ci			slot_count = simple_dai->tdm_width_map[i].slot_count;
41162306a36Sopenharmony_ci			break;
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ret = snd_soc_dai_set_tdm_slot(dai,
41662306a36Sopenharmony_ci				       simple_dai->tx_slot_mask,
41762306a36Sopenharmony_ci				       simple_dai->rx_slot_mask,
41862306a36Sopenharmony_ci				       slot_count,
41962306a36Sopenharmony_ci				       slot_width);
42062306a36Sopenharmony_ci	if (ret && ret != -ENOTSUPP) {
42162306a36Sopenharmony_ci		dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
42262306a36Sopenharmony_ci		return ret;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ciint asoc_simple_hw_params(struct snd_pcm_substream *substream,
42962306a36Sopenharmony_ci			  struct snd_pcm_hw_params *params)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
43262306a36Sopenharmony_ci	struct asoc_simple_dai *pdai;
43362306a36Sopenharmony_ci	struct snd_soc_dai *sdai;
43462306a36Sopenharmony_ci	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
43562306a36Sopenharmony_ci	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
43662306a36Sopenharmony_ci	unsigned int mclk, mclk_fs = 0;
43762306a36Sopenharmony_ci	int i, ret;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (props->mclk_fs)
44062306a36Sopenharmony_ci		mclk_fs = props->mclk_fs;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (mclk_fs) {
44362306a36Sopenharmony_ci		struct snd_soc_component *component;
44462306a36Sopenharmony_ci		mclk = params_rate(params) * mclk_fs;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		for_each_prop_dai_codec(props, i, pdai) {
44762306a36Sopenharmony_ci			ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
44862306a36Sopenharmony_ci			if (ret < 0)
44962306a36Sopenharmony_ci				return ret;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		for_each_prop_dai_cpu(props, i, pdai) {
45362306a36Sopenharmony_ci			ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
45462306a36Sopenharmony_ci			if (ret < 0)
45562306a36Sopenharmony_ci				return ret;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		/* Ensure sysclk is set on all components in case any
45962306a36Sopenharmony_ci		 * (such as platform components) are missed by calls to
46062306a36Sopenharmony_ci		 * snd_soc_dai_set_sysclk.
46162306a36Sopenharmony_ci		 */
46262306a36Sopenharmony_ci		for_each_rtd_components(rtd, i, component) {
46362306a36Sopenharmony_ci			ret = snd_soc_component_set_sysclk(component, 0, 0,
46462306a36Sopenharmony_ci							   mclk, SND_SOC_CLOCK_IN);
46562306a36Sopenharmony_ci			if (ret && ret != -ENOTSUPP)
46662306a36Sopenharmony_ci				return ret;
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		for_each_rtd_codec_dais(rtd, i, sdai) {
47062306a36Sopenharmony_ci			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);
47162306a36Sopenharmony_ci			if (ret && ret != -ENOTSUPP)
47262306a36Sopenharmony_ci				return ret;
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		for_each_rtd_cpu_dais(rtd, i, sdai) {
47662306a36Sopenharmony_ci			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);
47762306a36Sopenharmony_ci			if (ret && ret != -ENOTSUPP)
47862306a36Sopenharmony_ci				return ret;
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	for_each_prop_dai_codec(props, i, pdai) {
48362306a36Sopenharmony_ci		sdai = asoc_rtd_to_codec(rtd, i);
48462306a36Sopenharmony_ci		ret = asoc_simple_set_tdm(sdai, pdai, params);
48562306a36Sopenharmony_ci		if (ret < 0)
48662306a36Sopenharmony_ci			return ret;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	for_each_prop_dai_cpu(props, i, pdai) {
49062306a36Sopenharmony_ci		sdai = asoc_rtd_to_cpu(rtd, i);
49162306a36Sopenharmony_ci		ret = asoc_simple_set_tdm(sdai, pdai, params);
49262306a36Sopenharmony_ci		if (ret < 0)
49362306a36Sopenharmony_ci			return ret;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_hw_params);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ciint asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
50162306a36Sopenharmony_ci				   struct snd_pcm_hw_params *params)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
50462306a36Sopenharmony_ci	struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
50562306a36Sopenharmony_ci	struct asoc_simple_data *data = &dai_props->adata;
50662306a36Sopenharmony_ci	struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
50762306a36Sopenharmony_ci	struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (data->convert_rate)
51062306a36Sopenharmony_ci		rate->min =
51162306a36Sopenharmony_ci		rate->max = data->convert_rate;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (data->convert_channels)
51462306a36Sopenharmony_ci		channels->min =
51562306a36Sopenharmony_ci		channels->max = data->convert_channels;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (data->convert_sample_format)
51862306a36Sopenharmony_ci		asoc_simple_fixup_sample_fmt(data, params);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return 0;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int asoc_simple_init_dai(struct snd_soc_dai *dai,
52562306a36Sopenharmony_ci				     struct asoc_simple_dai *simple_dai)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	int ret;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!simple_dai)
53062306a36Sopenharmony_ci		return 0;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (simple_dai->sysclk) {
53362306a36Sopenharmony_ci		ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
53462306a36Sopenharmony_ci					     simple_dai->clk_direction);
53562306a36Sopenharmony_ci		if (ret && ret != -ENOTSUPP) {
53662306a36Sopenharmony_ci			dev_err(dai->dev, "simple-card: set_sysclk error\n");
53762306a36Sopenharmony_ci			return ret;
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (simple_dai->slots) {
54262306a36Sopenharmony_ci		ret = snd_soc_dai_set_tdm_slot(dai,
54362306a36Sopenharmony_ci					       simple_dai->tx_slot_mask,
54462306a36Sopenharmony_ci					       simple_dai->rx_slot_mask,
54562306a36Sopenharmony_ci					       simple_dai->slots,
54662306a36Sopenharmony_ci					       simple_dai->slot_width);
54762306a36Sopenharmony_ci		if (ret && ret != -ENOTSUPP) {
54862306a36Sopenharmony_ci			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
54962306a36Sopenharmony_ci			return ret;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return 0;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic inline int asoc_simple_component_is_codec(struct snd_soc_component *component)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	return component->driver->endianness;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
56262306a36Sopenharmony_ci					    struct simple_dai_props *dai_props)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct snd_soc_dai_link *dai_link = rtd->dai_link;
56562306a36Sopenharmony_ci	struct snd_soc_component *component;
56662306a36Sopenharmony_ci	struct snd_soc_pcm_stream *c2c_params;
56762306a36Sopenharmony_ci	struct snd_pcm_hardware hw;
56862306a36Sopenharmony_ci	int i, ret, stream;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* Do nothing if it already has Codec2Codec settings */
57162306a36Sopenharmony_ci	if (dai_link->c2c_params)
57262306a36Sopenharmony_ci		return 0;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* Do nothing if it was DPCM :: BE */
57562306a36Sopenharmony_ci	if (dai_link->no_pcm)
57662306a36Sopenharmony_ci		return 0;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* Only Codecs */
57962306a36Sopenharmony_ci	for_each_rtd_components(rtd, i, component) {
58062306a36Sopenharmony_ci		if (!asoc_simple_component_is_codec(component))
58162306a36Sopenharmony_ci			return 0;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* Assumes the capabilities are the same for all supported streams */
58562306a36Sopenharmony_ci	for_each_pcm_streams(stream) {
58662306a36Sopenharmony_ci		ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
58762306a36Sopenharmony_ci		if (ret == 0)
58862306a36Sopenharmony_ci			break;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (ret < 0) {
59262306a36Sopenharmony_ci		dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
59362306a36Sopenharmony_ci		return ret;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL);
59762306a36Sopenharmony_ci	if (!c2c_params)
59862306a36Sopenharmony_ci		return -ENOMEM;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	c2c_params->formats		= hw.formats;
60162306a36Sopenharmony_ci	c2c_params->rates		= hw.rates;
60262306a36Sopenharmony_ci	c2c_params->rate_min		= hw.rate_min;
60362306a36Sopenharmony_ci	c2c_params->rate_max		= hw.rate_max;
60462306a36Sopenharmony_ci	c2c_params->channels_min	= hw.channels_min;
60562306a36Sopenharmony_ci	c2c_params->channels_max	= hw.channels_max;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	dai_link->c2c_params		= c2c_params;
60862306a36Sopenharmony_ci	dai_link->num_c2c_params	= 1;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return 0;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciint asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
61662306a36Sopenharmony_ci	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
61762306a36Sopenharmony_ci	struct asoc_simple_dai *dai;
61862306a36Sopenharmony_ci	int i, ret;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	for_each_prop_dai_codec(props, i, dai) {
62162306a36Sopenharmony_ci		ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai);
62262306a36Sopenharmony_ci		if (ret < 0)
62362306a36Sopenharmony_ci			return ret;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci	for_each_prop_dai_cpu(props, i, dai) {
62662306a36Sopenharmony_ci		ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai);
62762306a36Sopenharmony_ci		if (ret < 0)
62862306a36Sopenharmony_ci			return ret;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	ret = asoc_simple_init_for_codec2codec(rtd, props);
63262306a36Sopenharmony_ci	if (ret < 0)
63362306a36Sopenharmony_ci		return ret;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return 0;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_dai_init);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_civoid asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
64062306a36Sopenharmony_ci				       struct snd_soc_dai_link_component *cpus)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	/*
64362306a36Sopenharmony_ci	 * Assumes Platform == CPU
64462306a36Sopenharmony_ci	 *
64562306a36Sopenharmony_ci	 * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
64662306a36Sopenharmony_ci	 * are different Component, but are sharing same component->dev.
64762306a36Sopenharmony_ci	 *
64862306a36Sopenharmony_ci	 * Let's assume Platform is same as CPU if it doesn't identify Platform on DT.
64962306a36Sopenharmony_ci	 * see
65062306a36Sopenharmony_ci	 *	simple-card.c :: simple_count_noml()
65162306a36Sopenharmony_ci	 */
65262306a36Sopenharmony_ci	if (!platforms->of_node)
65362306a36Sopenharmony_ci		snd_soc_dlc_use_cpu_as_platform(platforms, cpus);
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_civoid asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,
65862306a36Sopenharmony_ci				  int is_single_links)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	/*
66162306a36Sopenharmony_ci	 * In soc_bind_dai_link() will check cpu name after
66262306a36Sopenharmony_ci	 * of_node matching if dai_link has cpu_dai_name.
66362306a36Sopenharmony_ci	 * but, it will never match if name was created by
66462306a36Sopenharmony_ci	 * fmt_single_name() remove cpu_dai_name if cpu_args
66562306a36Sopenharmony_ci	 * was 0. See:
66662306a36Sopenharmony_ci	 *	fmt_single_name()
66762306a36Sopenharmony_ci	 *	fmt_multiple_name()
66862306a36Sopenharmony_ci	 */
66962306a36Sopenharmony_ci	if (is_single_links)
67062306a36Sopenharmony_ci		cpus->dai_name = NULL;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_civoid asoc_simple_clean_reference(struct snd_soc_card *card)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct snd_soc_dai_link *dai_link;
67762306a36Sopenharmony_ci	struct snd_soc_dai_link_component *cpu;
67862306a36Sopenharmony_ci	struct snd_soc_dai_link_component *codec;
67962306a36Sopenharmony_ci	int i, j;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	for_each_card_prelinks(card, i, dai_link) {
68262306a36Sopenharmony_ci		for_each_link_cpus(dai_link, j, cpu)
68362306a36Sopenharmony_ci			of_node_put(cpu->of_node);
68462306a36Sopenharmony_ci		for_each_link_codecs(dai_link, j, codec)
68562306a36Sopenharmony_ci			of_node_put(codec->of_node);
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ciint asoc_simple_parse_routing(struct snd_soc_card *card,
69162306a36Sopenharmony_ci			      char *prefix)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct device_node *node = card->dev->of_node;
69462306a36Sopenharmony_ci	char prop[128];
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (!prefix)
69762306a36Sopenharmony_ci		prefix = "";
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (!of_property_read_bool(node, prop))
70262306a36Sopenharmony_ci		return 0;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return snd_soc_of_parse_audio_routing(card, prop);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ciint asoc_simple_parse_widgets(struct snd_soc_card *card,
70962306a36Sopenharmony_ci			      char *prefix)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	struct device_node *node = card->dev->of_node;
71262306a36Sopenharmony_ci	char prop[128];
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (!prefix)
71562306a36Sopenharmony_ci		prefix = "";
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (of_property_read_bool(node, prop))
72062306a36Sopenharmony_ci		return snd_soc_of_parse_audio_simple_widgets(card, prop);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* no widgets is not error */
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ciint asoc_simple_parse_pin_switches(struct snd_soc_card *card,
72862306a36Sopenharmony_ci				   char *prefix)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	char prop[128];
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (!prefix)
73362306a36Sopenharmony_ci		prefix = "";
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	return snd_soc_of_parse_pin_switches(card, prop);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ciint asoc_simple_init_jack(struct snd_soc_card *card,
74262306a36Sopenharmony_ci			  struct asoc_simple_jack *sjack,
74362306a36Sopenharmony_ci			  int is_hp, char *prefix,
74462306a36Sopenharmony_ci			  char *pin)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	struct device *dev = card->dev;
74762306a36Sopenharmony_ci	struct gpio_desc *desc;
74862306a36Sopenharmony_ci	char prop[128];
74962306a36Sopenharmony_ci	char *pin_name;
75062306a36Sopenharmony_ci	char *gpio_name;
75162306a36Sopenharmony_ci	int mask;
75262306a36Sopenharmony_ci	int error;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (!prefix)
75562306a36Sopenharmony_ci		prefix = "";
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	sjack->gpio.gpio = -ENOENT;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (is_hp) {
76062306a36Sopenharmony_ci		snprintf(prop, sizeof(prop), "%shp-det", prefix);
76162306a36Sopenharmony_ci		pin_name	= pin ? pin : "Headphones";
76262306a36Sopenharmony_ci		gpio_name	= "Headphone detection";
76362306a36Sopenharmony_ci		mask		= SND_JACK_HEADPHONE;
76462306a36Sopenharmony_ci	} else {
76562306a36Sopenharmony_ci		snprintf(prop, sizeof(prop), "%smic-det", prefix);
76662306a36Sopenharmony_ci		pin_name	= pin ? pin : "Mic Jack";
76762306a36Sopenharmony_ci		gpio_name	= "Mic detection";
76862306a36Sopenharmony_ci		mask		= SND_JACK_MICROPHONE;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	desc = gpiod_get_optional(dev, prop, GPIOD_IN);
77262306a36Sopenharmony_ci	error = PTR_ERR_OR_ZERO(desc);
77362306a36Sopenharmony_ci	if (error)
77462306a36Sopenharmony_ci		return error;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (desc) {
77762306a36Sopenharmony_ci		error = gpiod_set_consumer_name(desc, gpio_name);
77862306a36Sopenharmony_ci		if (error)
77962306a36Sopenharmony_ci			return error;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		sjack->pin.pin		= pin_name;
78262306a36Sopenharmony_ci		sjack->pin.mask		= mask;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		sjack->gpio.name	= gpio_name;
78562306a36Sopenharmony_ci		sjack->gpio.report	= mask;
78662306a36Sopenharmony_ci		sjack->gpio.desc	= desc;
78762306a36Sopenharmony_ci		sjack->gpio.debounce_time = 150;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
79062306a36Sopenharmony_ci					   &sjack->pin, 1);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio);
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	return 0;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_init_jack);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ciint asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv, char *prefix)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	struct snd_soc_card *card = simple_priv_to_card(priv);
80262306a36Sopenharmony_ci	struct snd_soc_component *component;
80362306a36Sopenharmony_ci	int found_jack_index = 0;
80462306a36Sopenharmony_ci	int type = 0;
80562306a36Sopenharmony_ci	int num = 0;
80662306a36Sopenharmony_ci	int ret;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (priv->aux_jacks)
80962306a36Sopenharmony_ci		return 0;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	for_each_card_auxs(card, component) {
81262306a36Sopenharmony_ci		type = snd_soc_component_get_jack_type(component);
81362306a36Sopenharmony_ci		if (type > 0)
81462306a36Sopenharmony_ci			num++;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci	if (num < 1)
81762306a36Sopenharmony_ci		return 0;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	priv->aux_jacks = devm_kcalloc(card->dev, num,
82062306a36Sopenharmony_ci				       sizeof(struct snd_soc_jack), GFP_KERNEL);
82162306a36Sopenharmony_ci	if (!priv->aux_jacks)
82262306a36Sopenharmony_ci		return -ENOMEM;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	for_each_card_auxs(card, component) {
82562306a36Sopenharmony_ci		char id[128];
82662306a36Sopenharmony_ci		struct snd_soc_jack *jack;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci		if (found_jack_index >= num)
82962306a36Sopenharmony_ci			break;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		type = snd_soc_component_get_jack_type(component);
83262306a36Sopenharmony_ci		if (type <= 0)
83362306a36Sopenharmony_ci			continue;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		/* create jack */
83662306a36Sopenharmony_ci		jack = &(priv->aux_jacks[found_jack_index++]);
83762306a36Sopenharmony_ci		snprintf(id, sizeof(id), "%s-jack", component->name);
83862306a36Sopenharmony_ci		ret = snd_soc_card_jack_new(card, id, type, jack);
83962306a36Sopenharmony_ci		if (ret)
84062306a36Sopenharmony_ci			continue;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		(void)snd_soc_component_set_jack(component, jack, NULL);
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_init_aux_jacks);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ciint asoc_simple_init_priv(struct asoc_simple_priv *priv,
84962306a36Sopenharmony_ci			  struct link_info *li)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct snd_soc_card *card = simple_priv_to_card(priv);
85262306a36Sopenharmony_ci	struct device *dev = simple_priv_to_dev(priv);
85362306a36Sopenharmony_ci	struct snd_soc_dai_link *dai_link;
85462306a36Sopenharmony_ci	struct simple_dai_props *dai_props;
85562306a36Sopenharmony_ci	struct asoc_simple_dai *dais;
85662306a36Sopenharmony_ci	struct snd_soc_dai_link_component *dlcs;
85762306a36Sopenharmony_ci	struct snd_soc_codec_conf *cconf = NULL;
85862306a36Sopenharmony_ci	int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
86162306a36Sopenharmony_ci	dai_link  = devm_kcalloc(dev, li->link, sizeof(*dai_link),  GFP_KERNEL);
86262306a36Sopenharmony_ci	if (!dai_props || !dai_link)
86362306a36Sopenharmony_ci		return -ENOMEM;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/*
86662306a36Sopenharmony_ci	 * dais (= CPU+Codec)
86762306a36Sopenharmony_ci	 * dlcs (= CPU+Codec+Platform)
86862306a36Sopenharmony_ci	 */
86962306a36Sopenharmony_ci	for (i = 0; i < li->link; i++) {
87062306a36Sopenharmony_ci		int cc = li->num[i].cpus + li->num[i].codecs;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		dai_num += cc;
87362306a36Sopenharmony_ci		dlc_num += cc + li->num[i].platforms;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		if (!li->num[i].cpus)
87662306a36Sopenharmony_ci			cnf_num += li->num[i].codecs;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
88062306a36Sopenharmony_ci	dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);
88162306a36Sopenharmony_ci	if (!dais || !dlcs)
88262306a36Sopenharmony_ci		return -ENOMEM;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	if (cnf_num) {
88562306a36Sopenharmony_ci		cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);
88662306a36Sopenharmony_ci		if (!cconf)
88762306a36Sopenharmony_ci			return -ENOMEM;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
89162306a36Sopenharmony_ci		li->link, dai_num, cnf_num);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	priv->dai_props		= dai_props;
89462306a36Sopenharmony_ci	priv->dai_link		= dai_link;
89562306a36Sopenharmony_ci	priv->dais		= dais;
89662306a36Sopenharmony_ci	priv->dlcs		= dlcs;
89762306a36Sopenharmony_ci	priv->codec_conf	= cconf;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	card->dai_link		= priv->dai_link;
90062306a36Sopenharmony_ci	card->num_links		= li->link;
90162306a36Sopenharmony_ci	card->codec_conf	= cconf;
90262306a36Sopenharmony_ci	card->num_configs	= cnf_num;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	for (i = 0; i < li->link; i++) {
90562306a36Sopenharmony_ci		if (li->num[i].cpus) {
90662306a36Sopenharmony_ci			/* Normal CPU */
90762306a36Sopenharmony_ci			dai_link[i].cpus	= dlcs;
90862306a36Sopenharmony_ci			dai_props[i].num.cpus	=
90962306a36Sopenharmony_ci			dai_link[i].num_cpus	= li->num[i].cpus;
91062306a36Sopenharmony_ci			dai_props[i].cpu_dai	= dais;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci			dlcs += li->num[i].cpus;
91362306a36Sopenharmony_ci			dais += li->num[i].cpus;
91462306a36Sopenharmony_ci		} else {
91562306a36Sopenharmony_ci			/* DPCM Be's CPU = dummy */
91662306a36Sopenharmony_ci			dai_link[i].cpus	= &asoc_dummy_dlc;
91762306a36Sopenharmony_ci			dai_props[i].num.cpus	=
91862306a36Sopenharmony_ci			dai_link[i].num_cpus	= 1;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		if (li->num[i].codecs) {
92262306a36Sopenharmony_ci			/* Normal Codec */
92362306a36Sopenharmony_ci			dai_link[i].codecs	= dlcs;
92462306a36Sopenharmony_ci			dai_props[i].num.codecs	=
92562306a36Sopenharmony_ci			dai_link[i].num_codecs	= li->num[i].codecs;
92662306a36Sopenharmony_ci			dai_props[i].codec_dai	= dais;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci			dlcs += li->num[i].codecs;
92962306a36Sopenharmony_ci			dais += li->num[i].codecs;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci			if (!li->num[i].cpus) {
93262306a36Sopenharmony_ci				/* DPCM Be's Codec */
93362306a36Sopenharmony_ci				dai_props[i].codec_conf = cconf;
93462306a36Sopenharmony_ci				cconf += li->num[i].codecs;
93562306a36Sopenharmony_ci			}
93662306a36Sopenharmony_ci		} else {
93762306a36Sopenharmony_ci			/* DPCM Fe's Codec = dummy */
93862306a36Sopenharmony_ci			dai_link[i].codecs	= &asoc_dummy_dlc;
93962306a36Sopenharmony_ci			dai_props[i].num.codecs	=
94062306a36Sopenharmony_ci			dai_link[i].num_codecs	= 1;
94162306a36Sopenharmony_ci		}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		if (li->num[i].platforms) {
94462306a36Sopenharmony_ci			/* Have Platform */
94562306a36Sopenharmony_ci			dai_link[i].platforms		= dlcs;
94662306a36Sopenharmony_ci			dai_props[i].num.platforms	=
94762306a36Sopenharmony_ci			dai_link[i].num_platforms	= li->num[i].platforms;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci			dlcs += li->num[i].platforms;
95062306a36Sopenharmony_ci		} else {
95162306a36Sopenharmony_ci			/* Doesn't have Platform */
95262306a36Sopenharmony_ci			dai_link[i].platforms		= NULL;
95362306a36Sopenharmony_ci			dai_props[i].num.platforms	=
95462306a36Sopenharmony_ci			dai_link[i].num_platforms	= 0;
95562306a36Sopenharmony_ci		}
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	return 0;
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_init_priv);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ciint asoc_simple_remove(struct platform_device *pdev)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct snd_soc_card *card = platform_get_drvdata(pdev);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	asoc_simple_clean_reference(card);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	return 0;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_remove);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ciint asoc_graph_card_probe(struct snd_soc_card *card)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
97562306a36Sopenharmony_ci	int ret;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
97862306a36Sopenharmony_ci	if (ret < 0)
97962306a36Sopenharmony_ci		return ret;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL);
98262306a36Sopenharmony_ci	if (ret < 0)
98362306a36Sopenharmony_ci		return ret;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_graph_card_probe);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ciint asoc_graph_is_ports0(struct device_node *np)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	struct device_node *port, *ports, *ports0, *top;
99262306a36Sopenharmony_ci	int ret;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/* np is "endpoint" or "port" */
99562306a36Sopenharmony_ci	if (of_node_name_eq(np, "endpoint")) {
99662306a36Sopenharmony_ci		port = of_get_parent(np);
99762306a36Sopenharmony_ci	} else {
99862306a36Sopenharmony_ci		port = np;
99962306a36Sopenharmony_ci		of_node_get(port);
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	ports	= of_get_parent(port);
100362306a36Sopenharmony_ci	top	= of_get_parent(ports);
100462306a36Sopenharmony_ci	ports0	= of_get_child_by_name(top, "ports");
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	ret = ports0 == ports;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	of_node_put(port);
100962306a36Sopenharmony_ci	of_node_put(ports);
101062306a36Sopenharmony_ci	of_node_put(ports0);
101162306a36Sopenharmony_ci	of_node_put(top);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	return ret;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic int graph_get_dai_id(struct device_node *ep)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct device_node *node;
102062306a36Sopenharmony_ci	struct device_node *endpoint;
102162306a36Sopenharmony_ci	struct of_endpoint info;
102262306a36Sopenharmony_ci	int i, id;
102362306a36Sopenharmony_ci	int ret;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/* use driver specified DAI ID if exist */
102662306a36Sopenharmony_ci	ret = snd_soc_get_dai_id(ep);
102762306a36Sopenharmony_ci	if (ret != -ENOTSUPP)
102862306a36Sopenharmony_ci		return ret;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	/* use endpoint/port reg if exist */
103162306a36Sopenharmony_ci	ret = of_graph_parse_endpoint(ep, &info);
103262306a36Sopenharmony_ci	if (ret == 0) {
103362306a36Sopenharmony_ci		/*
103462306a36Sopenharmony_ci		 * Because it will count port/endpoint if it doesn't have "reg".
103562306a36Sopenharmony_ci		 * But, we can't judge whether it has "no reg", or "reg = <0>"
103662306a36Sopenharmony_ci		 * only of_graph_parse_endpoint().
103762306a36Sopenharmony_ci		 * We need to check "reg" property
103862306a36Sopenharmony_ci		 */
103962306a36Sopenharmony_ci		if (of_property_present(ep,   "reg"))
104062306a36Sopenharmony_ci			return info.id;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		node = of_get_parent(ep);
104362306a36Sopenharmony_ci		ret = of_property_present(node, "reg");
104462306a36Sopenharmony_ci		of_node_put(node);
104562306a36Sopenharmony_ci		if (ret)
104662306a36Sopenharmony_ci			return info.port;
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci	node = of_graph_get_port_parent(ep);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	/*
105162306a36Sopenharmony_ci	 * Non HDMI sound case, counting port/endpoint on its DT
105262306a36Sopenharmony_ci	 * is enough. Let's count it.
105362306a36Sopenharmony_ci	 */
105462306a36Sopenharmony_ci	i = 0;
105562306a36Sopenharmony_ci	id = -1;
105662306a36Sopenharmony_ci	for_each_endpoint_of_node(node, endpoint) {
105762306a36Sopenharmony_ci		if (endpoint == ep)
105862306a36Sopenharmony_ci			id = i;
105962306a36Sopenharmony_ci		i++;
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	of_node_put(node);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	if (id < 0)
106562306a36Sopenharmony_ci		return -ENODEV;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	return id;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ciint asoc_graph_parse_dai(struct device *dev, struct device_node *ep,
107162306a36Sopenharmony_ci			 struct snd_soc_dai_link_component *dlc, int *is_single_link)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct device_node *node;
107462306a36Sopenharmony_ci	struct of_phandle_args args = {};
107562306a36Sopenharmony_ci	struct snd_soc_dai *dai;
107662306a36Sopenharmony_ci	int ret;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (!ep)
107962306a36Sopenharmony_ci		return 0;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	node = of_graph_get_port_parent(ep);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/*
108462306a36Sopenharmony_ci	 * Try to find from DAI node
108562306a36Sopenharmony_ci	 */
108662306a36Sopenharmony_ci	args.np = ep;
108762306a36Sopenharmony_ci	dai = snd_soc_get_dai_via_args(&args);
108862306a36Sopenharmony_ci	if (dai) {
108962306a36Sopenharmony_ci		dlc->dai_name = snd_soc_dai_name_get(dai);
109062306a36Sopenharmony_ci		dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
109162306a36Sopenharmony_ci		if (!dlc->dai_args)
109262306a36Sopenharmony_ci			return -ENOMEM;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		goto parse_dai_end;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* Get dai->name */
109862306a36Sopenharmony_ci	args.np		= node;
109962306a36Sopenharmony_ci	args.args[0]	= graph_get_dai_id(ep);
110062306a36Sopenharmony_ci	args.args_count	= (of_graph_get_endpoint_count(node) > 1);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	/*
110362306a36Sopenharmony_ci	 * FIXME
110462306a36Sopenharmony_ci	 *
110562306a36Sopenharmony_ci	 * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
110662306a36Sopenharmony_ci	 * If user unbinded CPU or Codec driver, but not for Sound Card,
110762306a36Sopenharmony_ci	 * dlc->dai_name is keeping unbinded CPU or Codec
110862306a36Sopenharmony_ci	 * driver's pointer.
110962306a36Sopenharmony_ci	 *
111062306a36Sopenharmony_ci	 * If user re-bind CPU or Codec driver again, ALSA SoC will try
111162306a36Sopenharmony_ci	 * to rebind Card via snd_soc_try_rebind_card(), but because of
111262306a36Sopenharmony_ci	 * above reason, it might can't bind Sound Card.
111362306a36Sopenharmony_ci	 * Because Sound Card is pointing to released dai_name pointer.
111462306a36Sopenharmony_ci	 *
111562306a36Sopenharmony_ci	 * To avoid this rebind Card issue,
111662306a36Sopenharmony_ci	 * 1) It needs to alloc memory to keep dai_name eventhough
111762306a36Sopenharmony_ci	 *    CPU or Codec driver was unbinded, or
111862306a36Sopenharmony_ci	 * 2) user need to rebind Sound Card everytime
111962306a36Sopenharmony_ci	 *    if he unbinded CPU or Codec.
112062306a36Sopenharmony_ci	 */
112162306a36Sopenharmony_ci	ret = snd_soc_get_dlc(&args, dlc);
112262306a36Sopenharmony_ci	if (ret < 0) {
112362306a36Sopenharmony_ci		of_node_put(node);
112462306a36Sopenharmony_ci		return ret;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ciparse_dai_end:
112862306a36Sopenharmony_ci	if (is_single_link)
112962306a36Sopenharmony_ci		*is_single_link = of_graph_get_endpoint_count(node) == 1;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	return 0;
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_graph_parse_dai);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci/* Module information */
113662306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
113762306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
113862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1139