1d613a7f4SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
2d613a7f4SKuninori Morimoto //
3d613a7f4SKuninori Morimoto // simple-card-utils.c
4d613a7f4SKuninori Morimoto //
5d613a7f4SKuninori Morimoto // Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6d613a7f4SKuninori Morimoto 
7bb6fc620SKuninori Morimoto #include <linux/clk.h>
862c2c9fcSKatsuhiro Suzuki #include <linux/gpio.h>
962c2c9fcSKatsuhiro Suzuki #include <linux/gpio/consumer.h>
101f85e118SKuninori Morimoto #include <linux/module.h>
11abd3147eSKuninori Morimoto #include <linux/of.h>
121689333fSKuninori Morimoto #include <linux/of_graph.h>
1362c2c9fcSKatsuhiro Suzuki #include <sound/jack.h>
141e974e5bSRichard Fitzgerald #include <sound/pcm_params.h>
15abd3147eSKuninori Morimoto #include <sound/simple_card_utils.h>
16abd3147eSKuninori Morimoto 
asoc_simple_fixup_sample_fmt(struct asoc_simple_data * data,struct snd_pcm_hw_params * params)17047a0536SSameer Pujar static void asoc_simple_fixup_sample_fmt(struct asoc_simple_data *data,
18047a0536SSameer Pujar 					 struct snd_pcm_hw_params *params)
19047a0536SSameer Pujar {
20047a0536SSameer Pujar 	int i;
21047a0536SSameer Pujar 	struct snd_mask *mask = hw_param_mask(params,
22047a0536SSameer Pujar 					      SNDRV_PCM_HW_PARAM_FORMAT);
23047a0536SSameer Pujar 	struct {
24047a0536SSameer Pujar 		char *fmt;
25047a0536SSameer Pujar 		u32 val;
26047a0536SSameer Pujar 	} of_sample_fmt_table[] = {
27047a0536SSameer Pujar 		{ "s8",		SNDRV_PCM_FORMAT_S8},
28047a0536SSameer Pujar 		{ "s16_le",	SNDRV_PCM_FORMAT_S16_LE},
29047a0536SSameer Pujar 		{ "s24_le",	SNDRV_PCM_FORMAT_S24_LE},
30047a0536SSameer Pujar 		{ "s24_3le",	SNDRV_PCM_FORMAT_S24_3LE},
31047a0536SSameer Pujar 		{ "s32_le",	SNDRV_PCM_FORMAT_S32_LE},
32047a0536SSameer Pujar 	};
33047a0536SSameer Pujar 
34047a0536SSameer Pujar 	for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) {
35047a0536SSameer Pujar 		if (!strcmp(data->convert_sample_format,
36047a0536SSameer Pujar 			    of_sample_fmt_table[i].fmt)) {
37047a0536SSameer Pujar 			snd_mask_none(mask);
38047a0536SSameer Pujar 			snd_mask_set(mask, of_sample_fmt_table[i].val);
39047a0536SSameer Pujar 			break;
40047a0536SSameer Pujar 		}
41047a0536SSameer Pujar 	}
42047a0536SSameer Pujar }
43047a0536SSameer Pujar 
asoc_simple_parse_convert(struct device_node * np,char * prefix,struct asoc_simple_data * data)44fcfd763bSKuninori Morimoto void asoc_simple_parse_convert(struct device_node *np,
45a48bf02bSKuninori Morimoto 			       char *prefix,
46ad11e59fSKuninori Morimoto 			       struct asoc_simple_data *data)
4713bb1cc0SKuninori Morimoto {
4813bb1cc0SKuninori Morimoto 	char prop[128];
4913bb1cc0SKuninori Morimoto 
5013bb1cc0SKuninori Morimoto 	if (!prefix)
5113bb1cc0SKuninori Morimoto 		prefix = "";
5213bb1cc0SKuninori Morimoto 
5313bb1cc0SKuninori Morimoto 	/* sampling rate convert */
5413bb1cc0SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
5513bb1cc0SKuninori Morimoto 	of_property_read_u32(np, prop, &data->convert_rate);
5613bb1cc0SKuninori Morimoto 
5713bb1cc0SKuninori Morimoto 	/* channels transfer */
5813bb1cc0SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
5913bb1cc0SKuninori Morimoto 	of_property_read_u32(np, prop, &data->convert_channels);
60047a0536SSameer Pujar 
61047a0536SSameer Pujar 	/* convert sample format */
62047a0536SSameer Pujar 	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format");
63047a0536SSameer Pujar 	of_property_read_string(np, prop, &data->convert_sample_format);
6413bb1cc0SKuninori Morimoto }
65ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
6613bb1cc0SKuninori Morimoto 
6732def55dSAidan MacDonald /**
6832def55dSAidan MacDonald  * asoc_simple_is_convert_required() - Query if HW param conversion was requested
6932def55dSAidan MacDonald  * @data: Link data.
7032def55dSAidan MacDonald  *
7132def55dSAidan MacDonald  * Returns true if any HW param conversion was requested for this DAI link with
7232def55dSAidan MacDonald  * any "convert-xxx" properties.
7332def55dSAidan MacDonald  */
asoc_simple_is_convert_required(const struct asoc_simple_data * data)7432def55dSAidan MacDonald bool asoc_simple_is_convert_required(const struct asoc_simple_data *data)
7532def55dSAidan MacDonald {
7632def55dSAidan MacDonald 	return data->convert_rate ||
7732def55dSAidan MacDonald 	       data->convert_channels ||
7832def55dSAidan MacDonald 	       data->convert_sample_format;
7932def55dSAidan MacDonald }
8032def55dSAidan MacDonald EXPORT_SYMBOL_GPL(asoc_simple_is_convert_required);
8132def55dSAidan MacDonald 
asoc_simple_parse_daifmt(struct device * dev,struct device_node * node,struct device_node * codec,char * prefix,unsigned int * retfmt)82ad11e59fSKuninori Morimoto int asoc_simple_parse_daifmt(struct device *dev,
83abd3147eSKuninori Morimoto 			     struct device_node *node,
84abd3147eSKuninori Morimoto 			     struct device_node *codec,
85abd3147eSKuninori Morimoto 			     char *prefix,
86abd3147eSKuninori Morimoto 			     unsigned int *retfmt)
87abd3147eSKuninori Morimoto {
88abd3147eSKuninori Morimoto 	struct device_node *bitclkmaster = NULL;
89abd3147eSKuninori Morimoto 	struct device_node *framemaster = NULL;
90abd3147eSKuninori Morimoto 	unsigned int daifmt;
91abd3147eSKuninori Morimoto 
922c7fd9deSKuninori Morimoto 	daifmt = snd_soc_daifmt_parse_format(node, prefix);
93abd3147eSKuninori Morimoto 
942c7fd9deSKuninori Morimoto 	snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);
95155b8f3aSKuninori Morimoto 	if (!bitclkmaster && !framemaster) {
96abd3147eSKuninori Morimoto 		/*
97abd3147eSKuninori Morimoto 		 * No dai-link level and master setting was not found from
98abd3147eSKuninori Morimoto 		 * sound node level, revert back to legacy DT parsing and
99abd3147eSKuninori Morimoto 		 * take the settings from codec node.
100abd3147eSKuninori Morimoto 		 */
101abd3147eSKuninori Morimoto 		dev_dbg(dev, "Revert to legacy daifmt parsing\n");
102abd3147eSKuninori Morimoto 
1032c7fd9deSKuninori Morimoto 		daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);
104abd3147eSKuninori Morimoto 	} else {
1052c7fd9deSKuninori Morimoto 		daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(
1062c7fd9deSKuninori Morimoto 				((codec == bitclkmaster) << 4) | (codec == framemaster));
107abd3147eSKuninori Morimoto 	}
108abd3147eSKuninori Morimoto 
109abd3147eSKuninori Morimoto 	of_node_put(bitclkmaster);
110abd3147eSKuninori Morimoto 	of_node_put(framemaster);
111abd3147eSKuninori Morimoto 
112abd3147eSKuninori Morimoto 	*retfmt = daifmt;
113abd3147eSKuninori Morimoto 
114abd3147eSKuninori Morimoto 	return 0;
115abd3147eSKuninori Morimoto }
116ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
1171db3312eSKuninori Morimoto 
asoc_simple_parse_tdm_width_map(struct device * dev,struct device_node * np,struct asoc_simple_dai * dai)1181e974e5bSRichard Fitzgerald int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
1191e974e5bSRichard Fitzgerald 				    struct asoc_simple_dai *dai)
1201e974e5bSRichard Fitzgerald {
1211e974e5bSRichard Fitzgerald 	u32 *array_values, *p;
1221e974e5bSRichard Fitzgerald 	int n, i, ret;
1231e974e5bSRichard Fitzgerald 
1241e974e5bSRichard Fitzgerald 	if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))
1251e974e5bSRichard Fitzgerald 		return 0;
1261e974e5bSRichard Fitzgerald 
1271e974e5bSRichard Fitzgerald 	n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
1281e974e5bSRichard Fitzgerald 	if (n % 3) {
1291e974e5bSRichard Fitzgerald 		dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
1301e974e5bSRichard Fitzgerald 		return -EINVAL;
1311e974e5bSRichard Fitzgerald 	}
1321e974e5bSRichard Fitzgerald 
1331e974e5bSRichard Fitzgerald 	dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
1341e974e5bSRichard Fitzgerald 	if (!dai->tdm_width_map)
1351e974e5bSRichard Fitzgerald 		return -ENOMEM;
1361e974e5bSRichard Fitzgerald 
1371e974e5bSRichard Fitzgerald 	array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
1381e974e5bSRichard Fitzgerald 	if (!array_values)
1391e974e5bSRichard Fitzgerald 		return -ENOMEM;
1401e974e5bSRichard Fitzgerald 
1411e974e5bSRichard Fitzgerald 	ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
1421e974e5bSRichard Fitzgerald 	if (ret < 0) {
1431e974e5bSRichard Fitzgerald 		dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
1441e974e5bSRichard Fitzgerald 		goto out;
1451e974e5bSRichard Fitzgerald 	}
1461e974e5bSRichard Fitzgerald 
1471e974e5bSRichard Fitzgerald 	p = array_values;
1481e974e5bSRichard Fitzgerald 	for (i = 0; i < n / 3; ++i) {
1491e974e5bSRichard Fitzgerald 		dai->tdm_width_map[i].sample_bits = *p++;
1501e974e5bSRichard Fitzgerald 		dai->tdm_width_map[i].slot_width = *p++;
1511e974e5bSRichard Fitzgerald 		dai->tdm_width_map[i].slot_count = *p++;
1521e974e5bSRichard Fitzgerald 	}
1531e974e5bSRichard Fitzgerald 
1541e974e5bSRichard Fitzgerald 	dai->n_tdm_widths = i;
1551e974e5bSRichard Fitzgerald 	ret = 0;
1561e974e5bSRichard Fitzgerald out:
1571e974e5bSRichard Fitzgerald 	kfree(array_values);
1581e974e5bSRichard Fitzgerald 
1591e974e5bSRichard Fitzgerald 	return ret;
1601e974e5bSRichard Fitzgerald }
1611e974e5bSRichard Fitzgerald EXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map);
1621e974e5bSRichard Fitzgerald 
asoc_simple_set_dailink_name(struct device * dev,struct snd_soc_dai_link * dai_link,const char * fmt,...)163ad11e59fSKuninori Morimoto int asoc_simple_set_dailink_name(struct device *dev,
1641db3312eSKuninori Morimoto 				 struct snd_soc_dai_link *dai_link,
1651db3312eSKuninori Morimoto 				 const char *fmt, ...)
1661db3312eSKuninori Morimoto {
1671db3312eSKuninori Morimoto 	va_list ap;
1681db3312eSKuninori Morimoto 	char *name = NULL;
1691db3312eSKuninori Morimoto 	int ret = -ENOMEM;
1701db3312eSKuninori Morimoto 
1711db3312eSKuninori Morimoto 	va_start(ap, fmt);
1721db3312eSKuninori Morimoto 	name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
1731db3312eSKuninori Morimoto 	va_end(ap);
1741db3312eSKuninori Morimoto 
1751db3312eSKuninori Morimoto 	if (name) {
1761db3312eSKuninori Morimoto 		ret = 0;
1771db3312eSKuninori Morimoto 
1781db3312eSKuninori Morimoto 		dai_link->name		= name;
1791db3312eSKuninori Morimoto 		dai_link->stream_name	= name;
1801db3312eSKuninori Morimoto 	}
1811db3312eSKuninori Morimoto 
1821db3312eSKuninori Morimoto 	return ret;
1831db3312eSKuninori Morimoto }
184ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
185fc55c9b5SKuninori Morimoto 
asoc_simple_parse_card_name(struct snd_soc_card * card,char * prefix)186ad11e59fSKuninori Morimoto int asoc_simple_parse_card_name(struct snd_soc_card *card,
187fc55c9b5SKuninori Morimoto 				char *prefix)
188fc55c9b5SKuninori Morimoto {
189fc55c9b5SKuninori Morimoto 	int ret;
190fc55c9b5SKuninori Morimoto 
191dedfaa1eSKuninori Morimoto 	if (!prefix)
192dedfaa1eSKuninori Morimoto 		prefix = "";
193fc55c9b5SKuninori Morimoto 
194fc55c9b5SKuninori Morimoto 	/* Parse the card name from DT */
195dedfaa1eSKuninori Morimoto 	ret = snd_soc_of_parse_card_name(card, "label");
1961b4a56cdSLucas Stach 	if (ret < 0 || !card->name) {
197dedfaa1eSKuninori Morimoto 		char prop[128];
198dedfaa1eSKuninori Morimoto 
199dedfaa1eSKuninori Morimoto 		snprintf(prop, sizeof(prop), "%sname", prefix);
200fc55c9b5SKuninori Morimoto 		ret = snd_soc_of_parse_card_name(card, prop);
201fc55c9b5SKuninori Morimoto 		if (ret < 0)
202fc55c9b5SKuninori Morimoto 			return ret;
203dedfaa1eSKuninori Morimoto 	}
204fc55c9b5SKuninori Morimoto 
205fc55c9b5SKuninori Morimoto 	if (!card->name && card->dai_link)
206fc55c9b5SKuninori Morimoto 		card->name = card->dai_link->name;
207fc55c9b5SKuninori Morimoto 
208fc55c9b5SKuninori Morimoto 	return 0;
209fc55c9b5SKuninori Morimoto }
210ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
2111f85e118SKuninori Morimoto 
asoc_simple_clk_enable(struct asoc_simple_dai * dai)212ad11e59fSKuninori Morimoto static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
213891caea4SKuninori Morimoto {
214f31a1710SKuninori Morimoto 	if (dai)
215891caea4SKuninori Morimoto 		return clk_prepare_enable(dai->clk);
216f31a1710SKuninori Morimoto 
217f31a1710SKuninori Morimoto 	return 0;
218891caea4SKuninori Morimoto }
219891caea4SKuninori Morimoto 
asoc_simple_clk_disable(struct asoc_simple_dai * dai)220ad11e59fSKuninori Morimoto static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
221891caea4SKuninori Morimoto {
222f31a1710SKuninori Morimoto 	if (dai)
223891caea4SKuninori Morimoto 		clk_disable_unprepare(dai->clk);
224891caea4SKuninori Morimoto }
225891caea4SKuninori Morimoto 
asoc_simple_parse_clk(struct device * dev,struct device_node * node,struct asoc_simple_dai * simple_dai,struct snd_soc_dai_link_component * dlc)226ad11e59fSKuninori Morimoto int asoc_simple_parse_clk(struct device *dev,
227e984fd61SKuninori Morimoto 			  struct device_node *node,
2288e166382SKuninori Morimoto 			  struct asoc_simple_dai *simple_dai,
229e664de68SKuninori Morimoto 			  struct snd_soc_dai_link_component *dlc)
230bb6fc620SKuninori Morimoto {
231bb6fc620SKuninori Morimoto 	struct clk *clk;
232bb6fc620SKuninori Morimoto 	u32 val;
233bb6fc620SKuninori Morimoto 
234bb6fc620SKuninori Morimoto 	/*
235bb6fc620SKuninori Morimoto 	 * Parse dai->sysclk come from "clocks = <&xxx>"
236bb6fc620SKuninori Morimoto 	 * (if system has common clock)
237bb6fc620SKuninori Morimoto 	 *  or "system-clock-frequency = <xxx>"
238bb6fc620SKuninori Morimoto 	 *  or device's module clock.
239bb6fc620SKuninori Morimoto 	 */
240e984fd61SKuninori Morimoto 	clk = devm_get_clk_from_child(dev, node, NULL);
2415ca2ab45SRobert Hancock 	simple_dai->clk_fixed = of_property_read_bool(
2425ca2ab45SRobert Hancock 		node, "system-clock-fixed");
2431e30f642SSameer Pujar 	if (!IS_ERR(clk)) {
244bb6fc620SKuninori Morimoto 		simple_dai->sysclk = clk_get_rate(clk);
2458ca88d53SSameer Pujar 
2468ca88d53SSameer Pujar 		simple_dai->clk = clk;
2478ca88d53SSameer Pujar 	} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
2481e30f642SSameer Pujar 		simple_dai->sysclk = val;
2495ca2ab45SRobert Hancock 		simple_dai->clk_fixed = true;
2508ca88d53SSameer Pujar 	} else {
2518ca88d53SSameer Pujar 		clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
2528ca88d53SSameer Pujar 		if (!IS_ERR(clk))
2538ca88d53SSameer Pujar 			simple_dai->sysclk = clk_get_rate(clk);
254bb6fc620SKuninori Morimoto 	}
255bb6fc620SKuninori Morimoto 
256a728f560SVitaly Wool 	if (of_property_read_bool(node, "system-clock-direction-out"))
257a728f560SVitaly Wool 		simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
258a728f560SVitaly Wool 
259bb6fc620SKuninori Morimoto 	return 0;
260bb6fc620SKuninori Morimoto }
261ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
262bb6fc620SKuninori Morimoto 
asoc_simple_check_fixed_sysclk(struct device * dev,struct asoc_simple_dai * dai,unsigned int * fixed_sysclk)2635ca2ab45SRobert Hancock static int asoc_simple_check_fixed_sysclk(struct device *dev,
2645ca2ab45SRobert Hancock 					  struct asoc_simple_dai *dai,
2655ca2ab45SRobert Hancock 					  unsigned int *fixed_sysclk)
2665ca2ab45SRobert Hancock {
2675ca2ab45SRobert Hancock 	if (dai->clk_fixed) {
2685ca2ab45SRobert Hancock 		if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {
2695ca2ab45SRobert Hancock 			dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",
2705ca2ab45SRobert Hancock 				*fixed_sysclk, dai->sysclk);
2715ca2ab45SRobert Hancock 			return -EINVAL;
2725ca2ab45SRobert Hancock 		}
2735ca2ab45SRobert Hancock 		*fixed_sysclk = dai->sysclk;
2745ca2ab45SRobert Hancock 	}
2755ca2ab45SRobert Hancock 
2765ca2ab45SRobert Hancock 	return 0;
2775ca2ab45SRobert Hancock }
2785ca2ab45SRobert Hancock 
asoc_simple_startup(struct snd_pcm_substream * substream)279f38df5bfSKuninori Morimoto int asoc_simple_startup(struct snd_pcm_substream *substream)
280f38df5bfSKuninori Morimoto {
2819ae035e2SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
282f38df5bfSKuninori Morimoto 	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
283fafc05aaSKuninori Morimoto 	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
284fafc05aaSKuninori Morimoto 	struct asoc_simple_dai *dai;
2855ca2ab45SRobert Hancock 	unsigned int fixed_sysclk = 0;
286fafc05aaSKuninori Morimoto 	int i1, i2, i;
287f38df5bfSKuninori Morimoto 	int ret;
288f38df5bfSKuninori Morimoto 
289fafc05aaSKuninori Morimoto 	for_each_prop_dai_cpu(props, i1, dai) {
290fafc05aaSKuninori Morimoto 		ret = asoc_simple_clk_enable(dai);
291f38df5bfSKuninori Morimoto 		if (ret)
292fafc05aaSKuninori Morimoto 			goto cpu_err;
2935ca2ab45SRobert Hancock 		ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
2945ca2ab45SRobert Hancock 		if (ret)
2955ca2ab45SRobert Hancock 			goto cpu_err;
296fafc05aaSKuninori Morimoto 	}
297f38df5bfSKuninori Morimoto 
298fafc05aaSKuninori Morimoto 	for_each_prop_dai_codec(props, i2, dai) {
299fafc05aaSKuninori Morimoto 		ret = asoc_simple_clk_enable(dai);
300f38df5bfSKuninori Morimoto 		if (ret)
301fafc05aaSKuninori Morimoto 			goto codec_err;
3025ca2ab45SRobert Hancock 		ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
3035ca2ab45SRobert Hancock 		if (ret)
3045ca2ab45SRobert Hancock 			goto codec_err;
3055ca2ab45SRobert Hancock 	}
3065ca2ab45SRobert Hancock 
3075ca2ab45SRobert Hancock 	if (fixed_sysclk && props->mclk_fs) {
3085ca2ab45SRobert Hancock 		unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;
3095ca2ab45SRobert Hancock 
3105ca2ab45SRobert Hancock 		if (fixed_sysclk % props->mclk_fs) {
3115ca2ab45SRobert Hancock 			dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",
3125ca2ab45SRobert Hancock 				fixed_sysclk, props->mclk_fs);
313*69cf63b6SKuninori Morimoto 			ret = -EINVAL;
314*69cf63b6SKuninori Morimoto 			goto codec_err;
3155ca2ab45SRobert Hancock 		}
3165ca2ab45SRobert Hancock 		ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,
3175ca2ab45SRobert Hancock 			fixed_rate, fixed_rate);
318635071f5SRobert Hancock 		if (ret < 0)
3195ca2ab45SRobert Hancock 			goto codec_err;
320fafc05aaSKuninori Morimoto 	}
321f38df5bfSKuninori Morimoto 
322fafc05aaSKuninori Morimoto 	return 0;
323fafc05aaSKuninori Morimoto 
324fafc05aaSKuninori Morimoto codec_err:
325fafc05aaSKuninori Morimoto 	for_each_prop_dai_codec(props, i, dai) {
326fafc05aaSKuninori Morimoto 		if (i >= i2)
327fafc05aaSKuninori Morimoto 			break;
328fafc05aaSKuninori Morimoto 		asoc_simple_clk_disable(dai);
329fafc05aaSKuninori Morimoto 	}
330fafc05aaSKuninori Morimoto cpu_err:
331fafc05aaSKuninori Morimoto 	for_each_prop_dai_cpu(props, i, dai) {
332fafc05aaSKuninori Morimoto 		if (i >= i1)
333fafc05aaSKuninori Morimoto 			break;
334fafc05aaSKuninori Morimoto 		asoc_simple_clk_disable(dai);
335fafc05aaSKuninori Morimoto 	}
336f38df5bfSKuninori Morimoto 	return ret;
337f38df5bfSKuninori Morimoto }
338f38df5bfSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_startup);
339f38df5bfSKuninori Morimoto 
asoc_simple_shutdown(struct snd_pcm_substream * substream)340686911b4SKuninori Morimoto void asoc_simple_shutdown(struct snd_pcm_substream *substream)
341686911b4SKuninori Morimoto {
3429ae035e2SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
343686911b4SKuninori Morimoto 	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
344fafc05aaSKuninori Morimoto 	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
345fafc05aaSKuninori Morimoto 	struct asoc_simple_dai *dai;
346fafc05aaSKuninori Morimoto 	int i;
347686911b4SKuninori Morimoto 
3485ca2ab45SRobert Hancock 	for_each_prop_dai_cpu(props, i, dai) {
3495bbe2918SSameer Pujar 		struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i);
3505bbe2918SSameer Pujar 
3515bbe2918SSameer Pujar 		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
3525bbe2918SSameer Pujar 			snd_soc_dai_set_sysclk(cpu_dai,
3533756aa16SOlivier Moysan 					       0, 0, SND_SOC_CLOCK_OUT);
3542458adb8SKatsuhiro Suzuki 
355fafc05aaSKuninori Morimoto 		asoc_simple_clk_disable(dai);
3565ca2ab45SRobert Hancock 	}
3575ca2ab45SRobert Hancock 	for_each_prop_dai_codec(props, i, dai) {
3585bbe2918SSameer Pujar 		struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i);
3595bbe2918SSameer Pujar 
3605bbe2918SSameer Pujar 		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
3615bbe2918SSameer Pujar 			snd_soc_dai_set_sysclk(codec_dai,
3625ca2ab45SRobert Hancock 					       0, 0, SND_SOC_CLOCK_IN);
3635ca2ab45SRobert Hancock 
364fafc05aaSKuninori Morimoto 		asoc_simple_clk_disable(dai);
365686911b4SKuninori Morimoto 	}
3665ca2ab45SRobert Hancock }
367686911b4SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
368686911b4SKuninori Morimoto 
asoc_simple_set_clk_rate(struct device * dev,struct asoc_simple_dai * simple_dai,unsigned long rate)3695ca2ab45SRobert Hancock static int asoc_simple_set_clk_rate(struct device *dev,
3705ca2ab45SRobert Hancock 				    struct asoc_simple_dai *simple_dai,
371f48dcbb6SKuninori Morimoto 				    unsigned long rate)
372f48dcbb6SKuninori Morimoto {
373f48dcbb6SKuninori Morimoto 	if (!simple_dai)
374f48dcbb6SKuninori Morimoto 		return 0;
375f48dcbb6SKuninori Morimoto 
3765ca2ab45SRobert Hancock 	if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
3775ca2ab45SRobert Hancock 		dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
3785ca2ab45SRobert Hancock 		return -EINVAL;
3795ca2ab45SRobert Hancock 	}
3805ca2ab45SRobert Hancock 
381f48dcbb6SKuninori Morimoto 	if (!simple_dai->clk)
382f48dcbb6SKuninori Morimoto 		return 0;
383f48dcbb6SKuninori Morimoto 
384f48dcbb6SKuninori Morimoto 	if (clk_get_rate(simple_dai->clk) == rate)
385f48dcbb6SKuninori Morimoto 		return 0;
386f48dcbb6SKuninori Morimoto 
387f48dcbb6SKuninori Morimoto 	return clk_set_rate(simple_dai->clk, rate);
388f48dcbb6SKuninori Morimoto }
389f48dcbb6SKuninori Morimoto 
asoc_simple_set_tdm(struct snd_soc_dai * dai,struct asoc_simple_dai * simple_dai,struct snd_pcm_hw_params * params)3901e974e5bSRichard Fitzgerald static int asoc_simple_set_tdm(struct snd_soc_dai *dai,
3911e974e5bSRichard Fitzgerald 				struct asoc_simple_dai *simple_dai,
3921e974e5bSRichard Fitzgerald 				struct snd_pcm_hw_params *params)
3931e974e5bSRichard Fitzgerald {
3941e974e5bSRichard Fitzgerald 	int sample_bits = params_width(params);
39551a630a7SRichard Fitzgerald 	int slot_width, slot_count;
3961e974e5bSRichard Fitzgerald 	int i, ret;
3971e974e5bSRichard Fitzgerald 
3981e974e5bSRichard Fitzgerald 	if (!simple_dai || !simple_dai->tdm_width_map)
3991e974e5bSRichard Fitzgerald 		return 0;
4001e974e5bSRichard Fitzgerald 
40151a630a7SRichard Fitzgerald 	slot_width = simple_dai->slot_width;
40251a630a7SRichard Fitzgerald 	slot_count = simple_dai->slots;
40351a630a7SRichard Fitzgerald 
4041e974e5bSRichard Fitzgerald 	if (slot_width == 0)
4051e974e5bSRichard Fitzgerald 		slot_width = sample_bits;
4061e974e5bSRichard Fitzgerald 
4071e974e5bSRichard Fitzgerald 	for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
4081e974e5bSRichard Fitzgerald 		if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
4091e974e5bSRichard Fitzgerald 			slot_width = simple_dai->tdm_width_map[i].slot_width;
4101e974e5bSRichard Fitzgerald 			slot_count = simple_dai->tdm_width_map[i].slot_count;
4111e974e5bSRichard Fitzgerald 			break;
4121e974e5bSRichard Fitzgerald 		}
4131e974e5bSRichard Fitzgerald 	}
4141e974e5bSRichard Fitzgerald 
4151e974e5bSRichard Fitzgerald 	ret = snd_soc_dai_set_tdm_slot(dai,
4161e974e5bSRichard Fitzgerald 				       simple_dai->tx_slot_mask,
4171e974e5bSRichard Fitzgerald 				       simple_dai->rx_slot_mask,
4181e974e5bSRichard Fitzgerald 				       slot_count,
4191e974e5bSRichard Fitzgerald 				       slot_width);
4201e974e5bSRichard Fitzgerald 	if (ret && ret != -ENOTSUPP) {
4211e974e5bSRichard Fitzgerald 		dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
4221e974e5bSRichard Fitzgerald 		return ret;
4231e974e5bSRichard Fitzgerald 	}
4241e974e5bSRichard Fitzgerald 
4251e974e5bSRichard Fitzgerald 	return 0;
4261e974e5bSRichard Fitzgerald }
4271e974e5bSRichard Fitzgerald 
asoc_simple_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)428f48dcbb6SKuninori Morimoto int asoc_simple_hw_params(struct snd_pcm_substream *substream,
429f48dcbb6SKuninori Morimoto 			  struct snd_pcm_hw_params *params)
430f48dcbb6SKuninori Morimoto {
4319ae035e2SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
432fafc05aaSKuninori Morimoto 	struct asoc_simple_dai *pdai;
433fafc05aaSKuninori Morimoto 	struct snd_soc_dai *sdai;
434f48dcbb6SKuninori Morimoto 	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
435fafc05aaSKuninori Morimoto 	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
436f48dcbb6SKuninori Morimoto 	unsigned int mclk, mclk_fs = 0;
437fafc05aaSKuninori Morimoto 	int i, ret;
438f48dcbb6SKuninori Morimoto 
439fafc05aaSKuninori Morimoto 	if (props->mclk_fs)
440fafc05aaSKuninori Morimoto 		mclk_fs = props->mclk_fs;
441f48dcbb6SKuninori Morimoto 
442f48dcbb6SKuninori Morimoto 	if (mclk_fs) {
443ce2f7b8dSRobert Hancock 		struct snd_soc_component *component;
444f48dcbb6SKuninori Morimoto 		mclk = params_rate(params) * mclk_fs;
445f48dcbb6SKuninori Morimoto 
446fafc05aaSKuninori Morimoto 		for_each_prop_dai_codec(props, i, pdai) {
4475ca2ab45SRobert Hancock 			ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
448f48dcbb6SKuninori Morimoto 			if (ret < 0)
449f48dcbb6SKuninori Morimoto 				return ret;
450fafc05aaSKuninori Morimoto 		}
451ce2f7b8dSRobert Hancock 
452fafc05aaSKuninori Morimoto 		for_each_prop_dai_cpu(props, i, pdai) {
4535ca2ab45SRobert Hancock 			ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
454f48dcbb6SKuninori Morimoto 			if (ret < 0)
455f48dcbb6SKuninori Morimoto 				return ret;
456fafc05aaSKuninori Morimoto 		}
457ce2f7b8dSRobert Hancock 
458ce2f7b8dSRobert Hancock 		/* Ensure sysclk is set on all components in case any
459ce2f7b8dSRobert Hancock 		 * (such as platform components) are missed by calls to
460ce2f7b8dSRobert Hancock 		 * snd_soc_dai_set_sysclk.
461ce2f7b8dSRobert Hancock 		 */
462ce2f7b8dSRobert Hancock 		for_each_rtd_components(rtd, i, component) {
463ce2f7b8dSRobert Hancock 			ret = snd_soc_component_set_sysclk(component, 0, 0,
464ce2f7b8dSRobert Hancock 							   mclk, SND_SOC_CLOCK_IN);
465ce2f7b8dSRobert Hancock 			if (ret && ret != -ENOTSUPP)
466ce2f7b8dSRobert Hancock 				return ret;
467ce2f7b8dSRobert Hancock 		}
468ce2f7b8dSRobert Hancock 
469fafc05aaSKuninori Morimoto 		for_each_rtd_codec_dais(rtd, i, sdai) {
470fafc05aaSKuninori Morimoto 			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);
471f48dcbb6SKuninori Morimoto 			if (ret && ret != -ENOTSUPP)
472fafc05aaSKuninori Morimoto 				return ret;
473fafc05aaSKuninori Morimoto 		}
474ce2f7b8dSRobert Hancock 
475fafc05aaSKuninori Morimoto 		for_each_rtd_cpu_dais(rtd, i, sdai) {
476fafc05aaSKuninori Morimoto 			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);
477f48dcbb6SKuninori Morimoto 			if (ret && ret != -ENOTSUPP)
478fafc05aaSKuninori Morimoto 				return ret;
479fafc05aaSKuninori Morimoto 		}
480f48dcbb6SKuninori Morimoto 	}
4811e974e5bSRichard Fitzgerald 
4821e974e5bSRichard Fitzgerald 	for_each_prop_dai_codec(props, i, pdai) {
4831e974e5bSRichard Fitzgerald 		sdai = asoc_rtd_to_codec(rtd, i);
4841e974e5bSRichard Fitzgerald 		ret = asoc_simple_set_tdm(sdai, pdai, params);
4851e974e5bSRichard Fitzgerald 		if (ret < 0)
4861e974e5bSRichard Fitzgerald 			return ret;
4871e974e5bSRichard Fitzgerald 	}
4881e974e5bSRichard Fitzgerald 
4891e974e5bSRichard Fitzgerald 	for_each_prop_dai_cpu(props, i, pdai) {
4901e974e5bSRichard Fitzgerald 		sdai = asoc_rtd_to_cpu(rtd, i);
4911e974e5bSRichard Fitzgerald 		ret = asoc_simple_set_tdm(sdai, pdai, params);
4921e974e5bSRichard Fitzgerald 		if (ret < 0)
4931e974e5bSRichard Fitzgerald 			return ret;
4941e974e5bSRichard Fitzgerald 	}
4951e974e5bSRichard Fitzgerald 
496f48dcbb6SKuninori Morimoto 	return 0;
497f48dcbb6SKuninori Morimoto }
498f48dcbb6SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
499f48dcbb6SKuninori Morimoto 
asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)500629f7544SKuninori Morimoto int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
501629f7544SKuninori Morimoto 				   struct snd_pcm_hw_params *params)
502629f7544SKuninori Morimoto {
503629f7544SKuninori Morimoto 	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
504629f7544SKuninori Morimoto 	struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
5055c065eafSKuninori Morimoto 	struct asoc_simple_data *data = &dai_props->adata;
5065c065eafSKuninori Morimoto 	struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5075c065eafSKuninori Morimoto 	struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
508629f7544SKuninori Morimoto 
5095c065eafSKuninori Morimoto 	if (data->convert_rate)
5105c065eafSKuninori Morimoto 		rate->min =
5115c065eafSKuninori Morimoto 		rate->max = data->convert_rate;
5125c065eafSKuninori Morimoto 
5135c065eafSKuninori Morimoto 	if (data->convert_channels)
5145c065eafSKuninori Morimoto 		channels->min =
5155c065eafSKuninori Morimoto 		channels->max = data->convert_channels;
5165c065eafSKuninori Morimoto 
5175c065eafSKuninori Morimoto 	if (data->convert_sample_format)
5185c065eafSKuninori Morimoto 		asoc_simple_fixup_sample_fmt(data, params);
519629f7544SKuninori Morimoto 
520629f7544SKuninori Morimoto 	return 0;
521629f7544SKuninori Morimoto }
522629f7544SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
523629f7544SKuninori Morimoto 
asoc_simple_init_dai(struct snd_soc_dai * dai,struct asoc_simple_dai * simple_dai)524ad11e59fSKuninori Morimoto static int asoc_simple_init_dai(struct snd_soc_dai *dai,
52521ba62f8SKuninori Morimoto 				     struct asoc_simple_dai *simple_dai)
52621ba62f8SKuninori Morimoto {
52721ba62f8SKuninori Morimoto 	int ret;
52821ba62f8SKuninori Morimoto 
529f31a1710SKuninori Morimoto 	if (!simple_dai)
530f31a1710SKuninori Morimoto 		return 0;
531f31a1710SKuninori Morimoto 
53221ba62f8SKuninori Morimoto 	if (simple_dai->sysclk) {
533a728f560SVitaly Wool 		ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
534a728f560SVitaly Wool 					     simple_dai->clk_direction);
53521ba62f8SKuninori Morimoto 		if (ret && ret != -ENOTSUPP) {
53621ba62f8SKuninori Morimoto 			dev_err(dai->dev, "simple-card: set_sysclk error\n");
53721ba62f8SKuninori Morimoto 			return ret;
53821ba62f8SKuninori Morimoto 		}
53921ba62f8SKuninori Morimoto 	}
54021ba62f8SKuninori Morimoto 
54121ba62f8SKuninori Morimoto 	if (simple_dai->slots) {
54221ba62f8SKuninori Morimoto 		ret = snd_soc_dai_set_tdm_slot(dai,
54321ba62f8SKuninori Morimoto 					       simple_dai->tx_slot_mask,
54421ba62f8SKuninori Morimoto 					       simple_dai->rx_slot_mask,
54521ba62f8SKuninori Morimoto 					       simple_dai->slots,
54621ba62f8SKuninori Morimoto 					       simple_dai->slot_width);
54721ba62f8SKuninori Morimoto 		if (ret && ret != -ENOTSUPP) {
54821ba62f8SKuninori Morimoto 			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
54921ba62f8SKuninori Morimoto 			return ret;
55021ba62f8SKuninori Morimoto 		}
55121ba62f8SKuninori Morimoto 	}
55221ba62f8SKuninori Morimoto 
55321ba62f8SKuninori Morimoto 	return 0;
55421ba62f8SKuninori Morimoto }
555ad934ca8SKuninori Morimoto 
asoc_simple_component_is_codec(struct snd_soc_component * component)55628086d05SCharles Keepax static inline int asoc_simple_component_is_codec(struct snd_soc_component *component)
55728086d05SCharles Keepax {
55828086d05SCharles Keepax 	return component->driver->endianness;
55928086d05SCharles Keepax }
56028086d05SCharles Keepax 
asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime * rtd,struct simple_dai_props * dai_props)561ff31753fSKuninori Morimoto static int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
56295cfc0a0SSamuel Holland 					    struct simple_dai_props *dai_props)
56395cfc0a0SSamuel Holland {
56495cfc0a0SSamuel Holland 	struct snd_soc_dai_link *dai_link = rtd->dai_link;
56595cfc0a0SSamuel Holland 	struct snd_soc_component *component;
566a1cd7e80SKuninori Morimoto 	struct snd_soc_pcm_stream *c2c_params;
56795cfc0a0SSamuel Holland 	struct snd_pcm_hardware hw;
56895cfc0a0SSamuel Holland 	int i, ret, stream;
56995cfc0a0SSamuel Holland 
57075d1b390SKuninori Morimoto 	/* Do nothing if it already has Codec2Codec settings */
571a1cd7e80SKuninori Morimoto 	if (dai_link->c2c_params)
57275d1b390SKuninori Morimoto 		return 0;
57375d1b390SKuninori Morimoto 
57416b7ba9cSKuninori Morimoto 	/* Do nothing if it was DPCM :: BE */
57516b7ba9cSKuninori Morimoto 	if (dai_link->no_pcm)
57616b7ba9cSKuninori Morimoto 		return 0;
57716b7ba9cSKuninori Morimoto 
57801e90ee1SKuninori Morimoto 	/* Only Codecs */
57995cfc0a0SSamuel Holland 	for_each_rtd_components(rtd, i, component) {
58028086d05SCharles Keepax 		if (!asoc_simple_component_is_codec(component))
58195cfc0a0SSamuel Holland 			return 0;
58295cfc0a0SSamuel Holland 	}
58395cfc0a0SSamuel Holland 
58495cfc0a0SSamuel Holland 	/* Assumes the capabilities are the same for all supported streams */
58540a92dbcSKuninori Morimoto 	for_each_pcm_streams(stream) {
58695cfc0a0SSamuel Holland 		ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
58795cfc0a0SSamuel Holland 		if (ret == 0)
58895cfc0a0SSamuel Holland 			break;
58995cfc0a0SSamuel Holland 	}
59095cfc0a0SSamuel Holland 
59195cfc0a0SSamuel Holland 	if (ret < 0) {
59295cfc0a0SSamuel Holland 		dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
59395cfc0a0SSamuel Holland 		return ret;
59495cfc0a0SSamuel Holland 	}
59595cfc0a0SSamuel Holland 
596a1cd7e80SKuninori Morimoto 	c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL);
597a1cd7e80SKuninori Morimoto 	if (!c2c_params)
59895cfc0a0SSamuel Holland 		return -ENOMEM;
59995cfc0a0SSamuel Holland 
600a1cd7e80SKuninori Morimoto 	c2c_params->formats		= hw.formats;
601a1cd7e80SKuninori Morimoto 	c2c_params->rates		= hw.rates;
602a1cd7e80SKuninori Morimoto 	c2c_params->rate_min		= hw.rate_min;
603a1cd7e80SKuninori Morimoto 	c2c_params->rate_max		= hw.rate_max;
604a1cd7e80SKuninori Morimoto 	c2c_params->channels_min	= hw.channels_min;
605a1cd7e80SKuninori Morimoto 	c2c_params->channels_max	= hw.channels_max;
60695cfc0a0SSamuel Holland 
607a1cd7e80SKuninori Morimoto 	dai_link->c2c_params		= c2c_params;
608a1cd7e80SKuninori Morimoto 	dai_link->num_c2c_params	= 1;
60995cfc0a0SSamuel Holland 
61095cfc0a0SSamuel Holland 	return 0;
61195cfc0a0SSamuel Holland }
61295cfc0a0SSamuel Holland 
asoc_simple_dai_init(struct snd_soc_pcm_runtime * rtd)613ad934ca8SKuninori Morimoto int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
614ad934ca8SKuninori Morimoto {
615ad934ca8SKuninori Morimoto 	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
616fafc05aaSKuninori Morimoto 	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
617fafc05aaSKuninori Morimoto 	struct asoc_simple_dai *dai;
618fafc05aaSKuninori Morimoto 	int i, ret;
619ad934ca8SKuninori Morimoto 
620fafc05aaSKuninori Morimoto 	for_each_prop_dai_codec(props, i, dai) {
621fafc05aaSKuninori Morimoto 		ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai);
622ad934ca8SKuninori Morimoto 		if (ret < 0)
623ad934ca8SKuninori Morimoto 			return ret;
624fafc05aaSKuninori Morimoto 	}
625fafc05aaSKuninori Morimoto 	for_each_prop_dai_cpu(props, i, dai) {
626fafc05aaSKuninori Morimoto 		ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai);
627ad934ca8SKuninori Morimoto 		if (ret < 0)
628ad934ca8SKuninori Morimoto 			return ret;
629fafc05aaSKuninori Morimoto 	}
630ad934ca8SKuninori Morimoto 
631ff31753fSKuninori Morimoto 	ret = asoc_simple_init_for_codec2codec(rtd, props);
63295cfc0a0SSamuel Holland 	if (ret < 0)
63395cfc0a0SSamuel Holland 		return ret;
63495cfc0a0SSamuel Holland 
635ad934ca8SKuninori Morimoto 	return 0;
636ad934ca8SKuninori Morimoto }
637ad934ca8SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
63821ba62f8SKuninori Morimoto 
asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component * platforms,struct snd_soc_dai_link_component * cpus)639c826ec03SKuninori Morimoto void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
640c826ec03SKuninori Morimoto 				       struct snd_soc_dai_link_component *cpus)
641c262c9abSKuninori Morimoto {
642dcf08424SKuninori Morimoto 	/*
643dcf08424SKuninori Morimoto 	 * Assumes Platform == CPU
644dcf08424SKuninori Morimoto 	 *
645dcf08424SKuninori Morimoto 	 * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
646dcf08424SKuninori Morimoto 	 * are different Component, but are sharing same component->dev.
647dcf08424SKuninori Morimoto 	 *
648dcf08424SKuninori Morimoto 	 * Let's assume Platform is same as CPU if it doesn't identify Platform on DT.
649dcf08424SKuninori Morimoto 	 * see
650dcf08424SKuninori Morimoto 	 *	simple-card.c :: simple_count_noml()
651dcf08424SKuninori Morimoto 	 */
652c826ec03SKuninori Morimoto 	if (!platforms->of_node)
653988bad5eSKuninori Morimoto 		snd_soc_dlc_use_cpu_as_platform(platforms, cpus);
654c262c9abSKuninori Morimoto }
655ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
656c262c9abSKuninori Morimoto 
asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component * cpus,int is_single_links)657c826ec03SKuninori Morimoto void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,
658983cebd6SKuninori Morimoto 				  int is_single_links)
659983cebd6SKuninori Morimoto {
660983cebd6SKuninori Morimoto 	/*
661983cebd6SKuninori Morimoto 	 * In soc_bind_dai_link() will check cpu name after
662983cebd6SKuninori Morimoto 	 * of_node matching if dai_link has cpu_dai_name.
663983cebd6SKuninori Morimoto 	 * but, it will never match if name was created by
664983cebd6SKuninori Morimoto 	 * fmt_single_name() remove cpu_dai_name if cpu_args
665983cebd6SKuninori Morimoto 	 * was 0. See:
666983cebd6SKuninori Morimoto 	 *	fmt_single_name()
667983cebd6SKuninori Morimoto 	 *	fmt_multiple_name()
668983cebd6SKuninori Morimoto 	 */
669983cebd6SKuninori Morimoto 	if (is_single_links)
670c826ec03SKuninori Morimoto 		cpus->dai_name = NULL;
671983cebd6SKuninori Morimoto }
672ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
673983cebd6SKuninori Morimoto 
asoc_simple_clean_reference(struct snd_soc_card * card)674e6f08af6SUwe Kleine-König void asoc_simple_clean_reference(struct snd_soc_card *card)
6750f4e0711SKuninori Morimoto {
6760f4e0711SKuninori Morimoto 	struct snd_soc_dai_link *dai_link;
6771cf68057SKuninori Morimoto 	struct snd_soc_dai_link_component *cpu;
6781cf68057SKuninori Morimoto 	struct snd_soc_dai_link_component *codec;
6791cf68057SKuninori Morimoto 	int i, j;
6800f4e0711SKuninori Morimoto 
6817fe072b4SKuninori Morimoto 	for_each_card_prelinks(card, i, dai_link) {
6821cf68057SKuninori Morimoto 		for_each_link_cpus(dai_link, j, cpu)
6831cf68057SKuninori Morimoto 			of_node_put(cpu->of_node);
6841cf68057SKuninori Morimoto 		for_each_link_codecs(dai_link, j, codec)
6851cf68057SKuninori Morimoto 			of_node_put(codec->of_node);
6860f4e0711SKuninori Morimoto 	}
6870f4e0711SKuninori Morimoto }
688ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
6890f4e0711SKuninori Morimoto 
asoc_simple_parse_routing(struct snd_soc_card * card,char * prefix)690ad11e59fSKuninori Morimoto int asoc_simple_parse_routing(struct snd_soc_card *card,
69133404f3fSKuninori Morimoto 			      char *prefix)
6923296d078SKuninori Morimoto {
6933296d078SKuninori Morimoto 	struct device_node *node = card->dev->of_node;
6943296d078SKuninori Morimoto 	char prop[128];
6953296d078SKuninori Morimoto 
6963296d078SKuninori Morimoto 	if (!prefix)
6973296d078SKuninori Morimoto 		prefix = "";
6983296d078SKuninori Morimoto 
6993296d078SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
7003296d078SKuninori Morimoto 
70133404f3fSKuninori Morimoto 	if (!of_property_read_bool(node, prop))
7023296d078SKuninori Morimoto 		return 0;
7033296d078SKuninori Morimoto 
7043296d078SKuninori Morimoto 	return snd_soc_of_parse_audio_routing(card, prop);
7053296d078SKuninori Morimoto }
706ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
7073296d078SKuninori Morimoto 
asoc_simple_parse_widgets(struct snd_soc_card * card,char * prefix)708ad11e59fSKuninori Morimoto int asoc_simple_parse_widgets(struct snd_soc_card *card,
709b31f11d0SKuninori Morimoto 			      char *prefix)
710b31f11d0SKuninori Morimoto {
711b31f11d0SKuninori Morimoto 	struct device_node *node = card->dev->of_node;
712b31f11d0SKuninori Morimoto 	char prop[128];
713b31f11d0SKuninori Morimoto 
714b31f11d0SKuninori Morimoto 	if (!prefix)
715b31f11d0SKuninori Morimoto 		prefix = "";
716b31f11d0SKuninori Morimoto 
717b31f11d0SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
718b31f11d0SKuninori Morimoto 
719b31f11d0SKuninori Morimoto 	if (of_property_read_bool(node, prop))
720b31f11d0SKuninori Morimoto 		return snd_soc_of_parse_audio_simple_widgets(card, prop);
721b31f11d0SKuninori Morimoto 
722b31f11d0SKuninori Morimoto 	/* no widgets is not error */
723b31f11d0SKuninori Morimoto 	return 0;
724b31f11d0SKuninori Morimoto }
725ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
726b31f11d0SKuninori Morimoto 
asoc_simple_parse_pin_switches(struct snd_soc_card * card,char * prefix)72790194281SPaul Cercueil int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
72890194281SPaul Cercueil 				   char *prefix)
72990194281SPaul Cercueil {
73090194281SPaul Cercueil 	char prop[128];
73190194281SPaul Cercueil 
73290194281SPaul Cercueil 	if (!prefix)
73390194281SPaul Cercueil 		prefix = "";
73490194281SPaul Cercueil 
73590194281SPaul Cercueil 	snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
73690194281SPaul Cercueil 
7373d4641a4SStephan Gerhold 	return snd_soc_of_parse_pin_switches(card, prop);
73890194281SPaul Cercueil }
73990194281SPaul Cercueil EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
74090194281SPaul Cercueil 
asoc_simple_init_jack(struct snd_soc_card * card,struct asoc_simple_jack * sjack,int is_hp,char * prefix,char * pin)741ad11e59fSKuninori Morimoto int asoc_simple_init_jack(struct snd_soc_card *card,
74262c2c9fcSKatsuhiro Suzuki 			  struct asoc_simple_jack *sjack,
743764aafdbSShengjiu Wang 			  int is_hp, char *prefix,
744764aafdbSShengjiu Wang 			  char *pin)
74562c2c9fcSKatsuhiro Suzuki {
74662c2c9fcSKatsuhiro Suzuki 	struct device *dev = card->dev;
747355beeedSDmitry Torokhov 	struct gpio_desc *desc;
74862c2c9fcSKatsuhiro Suzuki 	char prop[128];
74962c2c9fcSKatsuhiro Suzuki 	char *pin_name;
75062c2c9fcSKatsuhiro Suzuki 	char *gpio_name;
75162c2c9fcSKatsuhiro Suzuki 	int mask;
752355beeedSDmitry Torokhov 	int error;
75362c2c9fcSKatsuhiro Suzuki 
75462c2c9fcSKatsuhiro Suzuki 	if (!prefix)
75562c2c9fcSKatsuhiro Suzuki 		prefix = "";
75662c2c9fcSKatsuhiro Suzuki 
75762c2c9fcSKatsuhiro Suzuki 	sjack->gpio.gpio = -ENOENT;
75862c2c9fcSKatsuhiro Suzuki 
75962c2c9fcSKatsuhiro Suzuki 	if (is_hp) {
760355beeedSDmitry Torokhov 		snprintf(prop, sizeof(prop), "%shp-det", prefix);
761764aafdbSShengjiu Wang 		pin_name	= pin ? pin : "Headphones";
76262c2c9fcSKatsuhiro Suzuki 		gpio_name	= "Headphone detection";
76362c2c9fcSKatsuhiro Suzuki 		mask		= SND_JACK_HEADPHONE;
76462c2c9fcSKatsuhiro Suzuki 	} else {
765355beeedSDmitry Torokhov 		snprintf(prop, sizeof(prop), "%smic-det", prefix);
766764aafdbSShengjiu Wang 		pin_name	= pin ? pin : "Mic Jack";
76762c2c9fcSKatsuhiro Suzuki 		gpio_name	= "Mic detection";
76862c2c9fcSKatsuhiro Suzuki 		mask		= SND_JACK_MICROPHONE;
76962c2c9fcSKatsuhiro Suzuki 	}
77062c2c9fcSKatsuhiro Suzuki 
771355beeedSDmitry Torokhov 	desc = gpiod_get_optional(dev, prop, GPIOD_IN);
772355beeedSDmitry Torokhov 	error = PTR_ERR_OR_ZERO(desc);
773355beeedSDmitry Torokhov 	if (error)
774355beeedSDmitry Torokhov 		return error;
77562c2c9fcSKatsuhiro Suzuki 
776355beeedSDmitry Torokhov 	if (desc) {
777355beeedSDmitry Torokhov 		error = gpiod_set_consumer_name(desc, gpio_name);
778355beeedSDmitry Torokhov 		if (error)
779355beeedSDmitry Torokhov 			return error;
780355beeedSDmitry Torokhov 
78162c2c9fcSKatsuhiro Suzuki 		sjack->pin.pin		= pin_name;
78262c2c9fcSKatsuhiro Suzuki 		sjack->pin.mask		= mask;
78362c2c9fcSKatsuhiro Suzuki 
78462c2c9fcSKatsuhiro Suzuki 		sjack->gpio.name	= gpio_name;
78562c2c9fcSKatsuhiro Suzuki 		sjack->gpio.report	= mask;
786355beeedSDmitry Torokhov 		sjack->gpio.desc	= desc;
78762c2c9fcSKatsuhiro Suzuki 		sjack->gpio.debounce_time = 150;
78862c2c9fcSKatsuhiro Suzuki 
78919aed2d6SAkihiko Odaki 		snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
79062c2c9fcSKatsuhiro Suzuki 					   &sjack->pin, 1);
79162c2c9fcSKatsuhiro Suzuki 
792355beeedSDmitry Torokhov 		snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio);
79362c2c9fcSKatsuhiro Suzuki 	}
79462c2c9fcSKatsuhiro Suzuki 
79562c2c9fcSKatsuhiro Suzuki 	return 0;
79662c2c9fcSKatsuhiro Suzuki }
797ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
79862c2c9fcSKatsuhiro Suzuki 
asoc_simple_init_aux_jacks(struct asoc_simple_priv * priv,char * prefix)7999b271207SAstrid Rost int asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv, char *prefix)
8009b271207SAstrid Rost {
8019b271207SAstrid Rost 	struct snd_soc_card *card = simple_priv_to_card(priv);
8029b271207SAstrid Rost 	struct snd_soc_component *component;
8039b271207SAstrid Rost 	int found_jack_index = 0;
8049b271207SAstrid Rost 	int type = 0;
8059b271207SAstrid Rost 	int num = 0;
8069b271207SAstrid Rost 	int ret;
8079b271207SAstrid Rost 
8089b271207SAstrid Rost 	if (priv->aux_jacks)
8099b271207SAstrid Rost 		return 0;
8109b271207SAstrid Rost 
8119b271207SAstrid Rost 	for_each_card_auxs(card, component) {
8129b271207SAstrid Rost 		type = snd_soc_component_get_jack_type(component);
8139b271207SAstrid Rost 		if (type > 0)
8149b271207SAstrid Rost 			num++;
8159b271207SAstrid Rost 	}
8169b271207SAstrid Rost 	if (num < 1)
8179b271207SAstrid Rost 		return 0;
8189b271207SAstrid Rost 
8199b271207SAstrid Rost 	priv->aux_jacks = devm_kcalloc(card->dev, num,
8209b271207SAstrid Rost 				       sizeof(struct snd_soc_jack), GFP_KERNEL);
8219b271207SAstrid Rost 	if (!priv->aux_jacks)
8229b271207SAstrid Rost 		return -ENOMEM;
8239b271207SAstrid Rost 
8249b271207SAstrid Rost 	for_each_card_auxs(card, component) {
8259b271207SAstrid Rost 		char id[128];
8269b271207SAstrid Rost 		struct snd_soc_jack *jack;
8279b271207SAstrid Rost 
8289b271207SAstrid Rost 		if (found_jack_index >= num)
8299b271207SAstrid Rost 			break;
8309b271207SAstrid Rost 
8319b271207SAstrid Rost 		type = snd_soc_component_get_jack_type(component);
8329b271207SAstrid Rost 		if (type <= 0)
8339b271207SAstrid Rost 			continue;
8349b271207SAstrid Rost 
8359b271207SAstrid Rost 		/* create jack */
8369b271207SAstrid Rost 		jack = &(priv->aux_jacks[found_jack_index++]);
8379b271207SAstrid Rost 		snprintf(id, sizeof(id), "%s-jack", component->name);
8389b271207SAstrid Rost 		ret = snd_soc_card_jack_new(card, id, type, jack);
8399b271207SAstrid Rost 		if (ret)
8409b271207SAstrid Rost 			continue;
8419b271207SAstrid Rost 
8429b271207SAstrid Rost 		(void)snd_soc_component_set_jack(component, jack, NULL);
8439b271207SAstrid Rost 	}
8449b271207SAstrid Rost 	return 0;
8459b271207SAstrid Rost }
8469b271207SAstrid Rost EXPORT_SYMBOL_GPL(asoc_simple_init_aux_jacks);
8479b271207SAstrid Rost 
asoc_simple_init_priv(struct asoc_simple_priv * priv,struct link_info * li)848ad11e59fSKuninori Morimoto int asoc_simple_init_priv(struct asoc_simple_priv *priv,
84965a5056bSKuninori Morimoto 			  struct link_info *li)
85065a5056bSKuninori Morimoto {
85165a5056bSKuninori Morimoto 	struct snd_soc_card *card = simple_priv_to_card(priv);
85265a5056bSKuninori Morimoto 	struct device *dev = simple_priv_to_dev(priv);
85365a5056bSKuninori Morimoto 	struct snd_soc_dai_link *dai_link;
85465a5056bSKuninori Morimoto 	struct simple_dai_props *dai_props;
85565a5056bSKuninori Morimoto 	struct asoc_simple_dai *dais;
856050c7950SKuninori Morimoto 	struct snd_soc_dai_link_component *dlcs;
857008fe4e5SKuninori Morimoto 	struct snd_soc_codec_conf *cconf = NULL;
8586976ed01SKuninori Morimoto 	int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
85965a5056bSKuninori Morimoto 
86065a5056bSKuninori Morimoto 	dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
86165a5056bSKuninori Morimoto 	dai_link  = devm_kcalloc(dev, li->link, sizeof(*dai_link),  GFP_KERNEL);
862f2138aedSKuninori Morimoto 	if (!dai_props || !dai_link)
863f2138aedSKuninori Morimoto 		return -ENOMEM;
864f2138aedSKuninori Morimoto 
865f2138aedSKuninori Morimoto 	/*
866f2138aedSKuninori Morimoto 	 * dais (= CPU+Codec)
867f2138aedSKuninori Morimoto 	 * dlcs (= CPU+Codec+Platform)
868f2138aedSKuninori Morimoto 	 */
869f2138aedSKuninori Morimoto 	for (i = 0; i < li->link; i++) {
870f2138aedSKuninori Morimoto 		int cc = li->num[i].cpus + li->num[i].codecs;
871f2138aedSKuninori Morimoto 
872f2138aedSKuninori Morimoto 		dai_num += cc;
873f2138aedSKuninori Morimoto 		dlc_num += cc + li->num[i].platforms;
874f899006dSKuninori Morimoto 
875f899006dSKuninori Morimoto 		if (!li->num[i].cpus)
876f899006dSKuninori Morimoto 			cnf_num += li->num[i].codecs;
877f2138aedSKuninori Morimoto 	}
878f2138aedSKuninori Morimoto 
879f2138aedSKuninori Morimoto 	dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
88036a9d79eSSamuel Holland 	dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);
881f2138aedSKuninori Morimoto 	if (!dais || !dlcs)
88265a5056bSKuninori Morimoto 		return -ENOMEM;
88365a5056bSKuninori Morimoto 
884f899006dSKuninori Morimoto 	if (cnf_num) {
885f899006dSKuninori Morimoto 		cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);
886008fe4e5SKuninori Morimoto 		if (!cconf)
887008fe4e5SKuninori Morimoto 			return -ENOMEM;
888008fe4e5SKuninori Morimoto 	}
889008fe4e5SKuninori Morimoto 
890f899006dSKuninori Morimoto 	dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
891f899006dSKuninori Morimoto 		li->link, dai_num, cnf_num);
892f899006dSKuninori Morimoto 
89365a5056bSKuninori Morimoto 	priv->dai_props		= dai_props;
89465a5056bSKuninori Morimoto 	priv->dai_link		= dai_link;
89565a5056bSKuninori Morimoto 	priv->dais		= dais;
896050c7950SKuninori Morimoto 	priv->dlcs		= dlcs;
89765a5056bSKuninori Morimoto 	priv->codec_conf	= cconf;
89865a5056bSKuninori Morimoto 
89965a5056bSKuninori Morimoto 	card->dai_link		= priv->dai_link;
90065a5056bSKuninori Morimoto 	card->num_links		= li->link;
90165a5056bSKuninori Morimoto 	card->codec_conf	= cconf;
902f899006dSKuninori Morimoto 	card->num_configs	= cnf_num;
90365a5056bSKuninori Morimoto 
904f2138aedSKuninori Morimoto 	for (i = 0; i < li->link; i++) {
905f2138aedSKuninori Morimoto 		if (li->num[i].cpus) {
906f2138aedSKuninori Morimoto 			/* Normal CPU */
907f2138aedSKuninori Morimoto 			dai_link[i].cpus	= dlcs;
908f2138aedSKuninori Morimoto 			dai_props[i].num.cpus	=
909f2138aedSKuninori Morimoto 			dai_link[i].num_cpus	= li->num[i].cpus;
91059f5cd96SKuninori Morimoto 			dai_props[i].cpu_dai	= dais;
911f2138aedSKuninori Morimoto 
912f2138aedSKuninori Morimoto 			dlcs += li->num[i].cpus;
91359f5cd96SKuninori Morimoto 			dais += li->num[i].cpus;
914205eb17eSKuninori Morimoto 		} else {
915205eb17eSKuninori Morimoto 			/* DPCM Be's CPU = dummy */
9164d626112SKuninori Morimoto 			dai_link[i].cpus	= &asoc_dummy_dlc;
917205eb17eSKuninori Morimoto 			dai_props[i].num.cpus	=
918205eb17eSKuninori Morimoto 			dai_link[i].num_cpus	= 1;
919f2138aedSKuninori Morimoto 		}
920f2138aedSKuninori Morimoto 
921f2138aedSKuninori Morimoto 		if (li->num[i].codecs) {
922f2138aedSKuninori Morimoto 			/* Normal Codec */
923f2138aedSKuninori Morimoto 			dai_link[i].codecs	= dlcs;
924f2138aedSKuninori Morimoto 			dai_props[i].num.codecs	=
925f2138aedSKuninori Morimoto 			dai_link[i].num_codecs	= li->num[i].codecs;
92659f5cd96SKuninori Morimoto 			dai_props[i].codec_dai	= dais;
927f2138aedSKuninori Morimoto 
928f2138aedSKuninori Morimoto 			dlcs += li->num[i].codecs;
92959f5cd96SKuninori Morimoto 			dais += li->num[i].codecs;
93059f5cd96SKuninori Morimoto 
93159f5cd96SKuninori Morimoto 			if (!li->num[i].cpus) {
93259f5cd96SKuninori Morimoto 				/* DPCM Be's Codec */
93359f5cd96SKuninori Morimoto 				dai_props[i].codec_conf = cconf;
93459f5cd96SKuninori Morimoto 				cconf += li->num[i].codecs;
93559f5cd96SKuninori Morimoto 			}
936205eb17eSKuninori Morimoto 		} else {
937205eb17eSKuninori Morimoto 			/* DPCM Fe's Codec = dummy */
9384d626112SKuninori Morimoto 			dai_link[i].codecs	= &asoc_dummy_dlc;
939205eb17eSKuninori Morimoto 			dai_props[i].num.codecs	=
940205eb17eSKuninori Morimoto 			dai_link[i].num_codecs	= 1;
941f2138aedSKuninori Morimoto 		}
942f2138aedSKuninori Morimoto 
943f2138aedSKuninori Morimoto 		if (li->num[i].platforms) {
944f2138aedSKuninori Morimoto 			/* Have Platform */
945f2138aedSKuninori Morimoto 			dai_link[i].platforms		= dlcs;
946f2138aedSKuninori Morimoto 			dai_props[i].num.platforms	=
947f2138aedSKuninori Morimoto 			dai_link[i].num_platforms	= li->num[i].platforms;
948f2138aedSKuninori Morimoto 
949f2138aedSKuninori Morimoto 			dlcs += li->num[i].platforms;
950205eb17eSKuninori Morimoto 		} else {
951205eb17eSKuninori Morimoto 			/* Doesn't have Platform */
952205eb17eSKuninori Morimoto 			dai_link[i].platforms		= NULL;
953205eb17eSKuninori Morimoto 			dai_props[i].num.platforms	=
954205eb17eSKuninori Morimoto 			dai_link[i].num_platforms	= 0;
955f2138aedSKuninori Morimoto 		}
956f2138aedSKuninori Morimoto 	}
957f2138aedSKuninori Morimoto 
95865a5056bSKuninori Morimoto 	return 0;
95965a5056bSKuninori Morimoto }
96065a5056bSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
96165a5056bSKuninori Morimoto 
asoc_simple_remove(struct platform_device * pdev)962f6fcc820SKuninori Morimoto int asoc_simple_remove(struct platform_device *pdev)
963f6fcc820SKuninori Morimoto {
964f6fcc820SKuninori Morimoto 	struct snd_soc_card *card = platform_get_drvdata(pdev);
965f6fcc820SKuninori Morimoto 
966e6f08af6SUwe Kleine-König 	asoc_simple_clean_reference(card);
967e6f08af6SUwe Kleine-König 
968e6f08af6SUwe Kleine-König 	return 0;
969f6fcc820SKuninori Morimoto }
970f6fcc820SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_remove);
971f6fcc820SKuninori Morimoto 
asoc_graph_card_probe(struct snd_soc_card * card)9721a456b1cSKuninori Morimoto int asoc_graph_card_probe(struct snd_soc_card *card)
9731a456b1cSKuninori Morimoto {
9741a456b1cSKuninori Morimoto 	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
9751a456b1cSKuninori Morimoto 	int ret;
9761a456b1cSKuninori Morimoto 
9771a456b1cSKuninori Morimoto 	ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
9781a456b1cSKuninori Morimoto 	if (ret < 0)
9791a456b1cSKuninori Morimoto 		return ret;
9801a456b1cSKuninori Morimoto 
9811a456b1cSKuninori Morimoto 	ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL);
9821a456b1cSKuninori Morimoto 	if (ret < 0)
9831a456b1cSKuninori Morimoto 		return ret;
9841a456b1cSKuninori Morimoto 
9851a456b1cSKuninori Morimoto 	return 0;
9861a456b1cSKuninori Morimoto }
9871a456b1cSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_graph_card_probe);
9881a456b1cSKuninori Morimoto 
asoc_graph_is_ports0(struct device_node * np)98992939252SKuninori Morimoto int asoc_graph_is_ports0(struct device_node *np)
99092939252SKuninori Morimoto {
99192939252SKuninori Morimoto 	struct device_node *port, *ports, *ports0, *top;
99292939252SKuninori Morimoto 	int ret;
99392939252SKuninori Morimoto 
99492939252SKuninori Morimoto 	/* np is "endpoint" or "port" */
99592939252SKuninori Morimoto 	if (of_node_name_eq(np, "endpoint")) {
99692939252SKuninori Morimoto 		port = of_get_parent(np);
99792939252SKuninori Morimoto 	} else {
99892939252SKuninori Morimoto 		port = np;
99992939252SKuninori Morimoto 		of_node_get(port);
100092939252SKuninori Morimoto 	}
100192939252SKuninori Morimoto 
100292939252SKuninori Morimoto 	ports	= of_get_parent(port);
100392939252SKuninori Morimoto 	top	= of_get_parent(ports);
100492939252SKuninori Morimoto 	ports0	= of_get_child_by_name(top, "ports");
100592939252SKuninori Morimoto 
100692939252SKuninori Morimoto 	ret = ports0 == ports;
100792939252SKuninori Morimoto 
100892939252SKuninori Morimoto 	of_node_put(port);
100992939252SKuninori Morimoto 	of_node_put(ports);
101092939252SKuninori Morimoto 	of_node_put(ports0);
101192939252SKuninori Morimoto 	of_node_put(top);
101292939252SKuninori Morimoto 
101392939252SKuninori Morimoto 	return ret;
101492939252SKuninori Morimoto }
101592939252SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
101692939252SKuninori Morimoto 
graph_get_dai_id(struct device_node * ep)1017fed4be31SKuninori Morimoto static int graph_get_dai_id(struct device_node *ep)
1018fed4be31SKuninori Morimoto {
1019fed4be31SKuninori Morimoto 	struct device_node *node;
1020fed4be31SKuninori Morimoto 	struct device_node *endpoint;
1021fed4be31SKuninori Morimoto 	struct of_endpoint info;
1022fed4be31SKuninori Morimoto 	int i, id;
1023fed4be31SKuninori Morimoto 	int ret;
1024fed4be31SKuninori Morimoto 
1025fed4be31SKuninori Morimoto 	/* use driver specified DAI ID if exist */
1026fed4be31SKuninori Morimoto 	ret = snd_soc_get_dai_id(ep);
1027fed4be31SKuninori Morimoto 	if (ret != -ENOTSUPP)
1028fed4be31SKuninori Morimoto 		return ret;
1029fed4be31SKuninori Morimoto 
1030fed4be31SKuninori Morimoto 	/* use endpoint/port reg if exist */
1031fed4be31SKuninori Morimoto 	ret = of_graph_parse_endpoint(ep, &info);
1032fed4be31SKuninori Morimoto 	if (ret == 0) {
1033fed4be31SKuninori Morimoto 		/*
1034fed4be31SKuninori Morimoto 		 * Because it will count port/endpoint if it doesn't have "reg".
1035fed4be31SKuninori Morimoto 		 * But, we can't judge whether it has "no reg", or "reg = <0>"
1036fed4be31SKuninori Morimoto 		 * only of_graph_parse_endpoint().
1037fed4be31SKuninori Morimoto 		 * We need to check "reg" property
1038fed4be31SKuninori Morimoto 		 */
1039fed4be31SKuninori Morimoto 		if (of_property_present(ep,   "reg"))
1040fed4be31SKuninori Morimoto 			return info.id;
1041fed4be31SKuninori Morimoto 
1042fed4be31SKuninori Morimoto 		node = of_get_parent(ep);
1043fed4be31SKuninori Morimoto 		ret = of_property_present(node, "reg");
1044fed4be31SKuninori Morimoto 		of_node_put(node);
1045fed4be31SKuninori Morimoto 		if (ret)
1046fed4be31SKuninori Morimoto 			return info.port;
1047fed4be31SKuninori Morimoto 	}
1048fed4be31SKuninori Morimoto 	node = of_graph_get_port_parent(ep);
1049fed4be31SKuninori Morimoto 
1050fed4be31SKuninori Morimoto 	/*
1051fed4be31SKuninori Morimoto 	 * Non HDMI sound case, counting port/endpoint on its DT
1052fed4be31SKuninori Morimoto 	 * is enough. Let's count it.
1053fed4be31SKuninori Morimoto 	 */
1054fed4be31SKuninori Morimoto 	i = 0;
1055fed4be31SKuninori Morimoto 	id = -1;
1056fed4be31SKuninori Morimoto 	for_each_endpoint_of_node(node, endpoint) {
1057fed4be31SKuninori Morimoto 		if (endpoint == ep)
1058fed4be31SKuninori Morimoto 			id = i;
1059fed4be31SKuninori Morimoto 		i++;
1060fed4be31SKuninori Morimoto 	}
1061fed4be31SKuninori Morimoto 
1062fed4be31SKuninori Morimoto 	of_node_put(node);
1063fed4be31SKuninori Morimoto 
1064fed4be31SKuninori Morimoto 	if (id < 0)
1065fed4be31SKuninori Morimoto 		return -ENODEV;
1066fed4be31SKuninori Morimoto 
1067fed4be31SKuninori Morimoto 	return id;
1068fed4be31SKuninori Morimoto }
1069fed4be31SKuninori Morimoto 
asoc_graph_parse_dai(struct device * dev,struct device_node * ep,struct snd_soc_dai_link_component * dlc,int * is_single_link)107090de551cSKuninori Morimoto int asoc_graph_parse_dai(struct device *dev, struct device_node *ep,
107190de551cSKuninori Morimoto 			 struct snd_soc_dai_link_component *dlc, int *is_single_link)
1072fed4be31SKuninori Morimoto {
1073fed4be31SKuninori Morimoto 	struct device_node *node;
1074fed4be31SKuninori Morimoto 	struct of_phandle_args args = {};
107590de551cSKuninori Morimoto 	struct snd_soc_dai *dai;
1076fed4be31SKuninori Morimoto 	int ret;
1077fed4be31SKuninori Morimoto 
1078fed4be31SKuninori Morimoto 	if (!ep)
1079fed4be31SKuninori Morimoto 		return 0;
1080fed4be31SKuninori Morimoto 
1081fed4be31SKuninori Morimoto 	node = of_graph_get_port_parent(ep);
1082fed4be31SKuninori Morimoto 
108390de551cSKuninori Morimoto 	/*
108490de551cSKuninori Morimoto 	 * Try to find from DAI node
108590de551cSKuninori Morimoto 	 */
108690de551cSKuninori Morimoto 	args.np = ep;
108790de551cSKuninori Morimoto 	dai = snd_soc_get_dai_via_args(&args);
108890de551cSKuninori Morimoto 	if (dai) {
108990de551cSKuninori Morimoto 		dlc->dai_name = snd_soc_dai_name_get(dai);
109090de551cSKuninori Morimoto 		dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
109190de551cSKuninori Morimoto 		if (!dlc->dai_args)
109290de551cSKuninori Morimoto 			return -ENOMEM;
109390de551cSKuninori Morimoto 
109490de551cSKuninori Morimoto 		goto parse_dai_end;
109590de551cSKuninori Morimoto 	}
109690de551cSKuninori Morimoto 
1097fed4be31SKuninori Morimoto 	/* Get dai->name */
1098fed4be31SKuninori Morimoto 	args.np		= node;
1099fed4be31SKuninori Morimoto 	args.args[0]	= graph_get_dai_id(ep);
1100fed4be31SKuninori Morimoto 	args.args_count	= (of_graph_get_endpoint_count(node) > 1);
1101fed4be31SKuninori Morimoto 
1102fed4be31SKuninori Morimoto 	/*
1103fed4be31SKuninori Morimoto 	 * FIXME
1104fed4be31SKuninori Morimoto 	 *
1105fed4be31SKuninori Morimoto 	 * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
1106fed4be31SKuninori Morimoto 	 * If user unbinded CPU or Codec driver, but not for Sound Card,
1107fed4be31SKuninori Morimoto 	 * dlc->dai_name is keeping unbinded CPU or Codec
1108fed4be31SKuninori Morimoto 	 * driver's pointer.
1109fed4be31SKuninori Morimoto 	 *
1110fed4be31SKuninori Morimoto 	 * If user re-bind CPU or Codec driver again, ALSA SoC will try
1111fed4be31SKuninori Morimoto 	 * to rebind Card via snd_soc_try_rebind_card(), but because of
1112fed4be31SKuninori Morimoto 	 * above reason, it might can't bind Sound Card.
1113fed4be31SKuninori Morimoto 	 * Because Sound Card is pointing to released dai_name pointer.
1114fed4be31SKuninori Morimoto 	 *
1115fed4be31SKuninori Morimoto 	 * To avoid this rebind Card issue,
1116fed4be31SKuninori Morimoto 	 * 1) It needs to alloc memory to keep dai_name eventhough
1117fed4be31SKuninori Morimoto 	 *    CPU or Codec driver was unbinded, or
1118fed4be31SKuninori Morimoto 	 * 2) user need to rebind Sound Card everytime
1119fed4be31SKuninori Morimoto 	 *    if he unbinded CPU or Codec.
1120fed4be31SKuninori Morimoto 	 */
11210baa2c3aSKuninori Morimoto 	ret = snd_soc_get_dlc(&args, dlc);
1122fed4be31SKuninori Morimoto 	if (ret < 0) {
1123fed4be31SKuninori Morimoto 		of_node_put(node);
1124fed4be31SKuninori Morimoto 		return ret;
1125fed4be31SKuninori Morimoto 	}
1126fed4be31SKuninori Morimoto 
112790de551cSKuninori Morimoto parse_dai_end:
1128fed4be31SKuninori Morimoto 	if (is_single_link)
1129fed4be31SKuninori Morimoto 		*is_single_link = of_graph_get_endpoint_count(node) == 1;
1130fed4be31SKuninori Morimoto 
1131fed4be31SKuninori Morimoto 	return 0;
1132fed4be31SKuninori Morimoto }
1133fed4be31SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_graph_parse_dai);
1134fed4be31SKuninori Morimoto 
11351f85e118SKuninori Morimoto /* Module information */
11361f85e118SKuninori Morimoto MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
11371f85e118SKuninori Morimoto MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
11381f85e118SKuninori Morimoto MODULE_LICENSE("GPL v2");
1139