cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

simple-card-utils.c (23100B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// simple-card-utils.c
      4//
      5// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
      6
      7#include <linux/clk.h>
      8#include <linux/gpio.h>
      9#include <linux/gpio/consumer.h>
     10#include <linux/module.h>
     11#include <linux/of.h>
     12#include <linux/of_gpio.h>
     13#include <linux/of_graph.h>
     14#include <sound/jack.h>
     15#include <sound/pcm_params.h>
     16#include <sound/simple_card_utils.h>
     17
     18void asoc_simple_convert_fixup(struct asoc_simple_data *data,
     19			       struct snd_pcm_hw_params *params)
     20{
     21	struct snd_interval *rate = hw_param_interval(params,
     22						SNDRV_PCM_HW_PARAM_RATE);
     23	struct snd_interval *channels = hw_param_interval(params,
     24						SNDRV_PCM_HW_PARAM_CHANNELS);
     25
     26	if (data->convert_rate)
     27		rate->min =
     28		rate->max = data->convert_rate;
     29
     30	if (data->convert_channels)
     31		channels->min =
     32		channels->max = data->convert_channels;
     33}
     34EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
     35
     36void asoc_simple_parse_convert(struct device_node *np,
     37			       char *prefix,
     38			       struct asoc_simple_data *data)
     39{
     40	char prop[128];
     41
     42	if (!prefix)
     43		prefix = "";
     44
     45	/* sampling rate convert */
     46	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
     47	of_property_read_u32(np, prop, &data->convert_rate);
     48
     49	/* channels transfer */
     50	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
     51	of_property_read_u32(np, prop, &data->convert_channels);
     52}
     53EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
     54
     55int asoc_simple_parse_daifmt(struct device *dev,
     56			     struct device_node *node,
     57			     struct device_node *codec,
     58			     char *prefix,
     59			     unsigned int *retfmt)
     60{
     61	struct device_node *bitclkmaster = NULL;
     62	struct device_node *framemaster = NULL;
     63	unsigned int daifmt;
     64
     65	daifmt = snd_soc_daifmt_parse_format(node, prefix);
     66
     67	snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);
     68	if (!bitclkmaster && !framemaster) {
     69		/*
     70		 * No dai-link level and master setting was not found from
     71		 * sound node level, revert back to legacy DT parsing and
     72		 * take the settings from codec node.
     73		 */
     74		dev_dbg(dev, "Revert to legacy daifmt parsing\n");
     75
     76		daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);
     77	} else {
     78		daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(
     79				((codec == bitclkmaster) << 4) | (codec == framemaster));
     80	}
     81
     82	of_node_put(bitclkmaster);
     83	of_node_put(framemaster);
     84
     85	*retfmt = daifmt;
     86
     87	return 0;
     88}
     89EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
     90
     91int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
     92				    struct asoc_simple_dai *dai)
     93{
     94	u32 *array_values, *p;
     95	int n, i, ret;
     96
     97	if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))
     98		return 0;
     99
    100	n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
    101	if (n % 3) {
    102		dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
    103		return -EINVAL;
    104	}
    105
    106	dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
    107	if (!dai->tdm_width_map)
    108		return -ENOMEM;
    109
    110	array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
    111	if (!array_values)
    112		return -ENOMEM;
    113
    114	ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
    115	if (ret < 0) {
    116		dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
    117		goto out;
    118	}
    119
    120	p = array_values;
    121	for (i = 0; i < n / 3; ++i) {
    122		dai->tdm_width_map[i].sample_bits = *p++;
    123		dai->tdm_width_map[i].slot_width = *p++;
    124		dai->tdm_width_map[i].slot_count = *p++;
    125	}
    126
    127	dai->n_tdm_widths = i;
    128	ret = 0;
    129out:
    130	kfree(array_values);
    131
    132	return ret;
    133}
    134EXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map);
    135
    136int asoc_simple_set_dailink_name(struct device *dev,
    137				 struct snd_soc_dai_link *dai_link,
    138				 const char *fmt, ...)
    139{
    140	va_list ap;
    141	char *name = NULL;
    142	int ret = -ENOMEM;
    143
    144	va_start(ap, fmt);
    145	name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
    146	va_end(ap);
    147
    148	if (name) {
    149		ret = 0;
    150
    151		dai_link->name		= name;
    152		dai_link->stream_name	= name;
    153	}
    154
    155	return ret;
    156}
    157EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
    158
    159int asoc_simple_parse_card_name(struct snd_soc_card *card,
    160				char *prefix)
    161{
    162	int ret;
    163
    164	if (!prefix)
    165		prefix = "";
    166
    167	/* Parse the card name from DT */
    168	ret = snd_soc_of_parse_card_name(card, "label");
    169	if (ret < 0 || !card->name) {
    170		char prop[128];
    171
    172		snprintf(prop, sizeof(prop), "%sname", prefix);
    173		ret = snd_soc_of_parse_card_name(card, prop);
    174		if (ret < 0)
    175			return ret;
    176	}
    177
    178	if (!card->name && card->dai_link)
    179		card->name = card->dai_link->name;
    180
    181	return 0;
    182}
    183EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
    184
    185static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
    186{
    187	if (dai)
    188		return clk_prepare_enable(dai->clk);
    189
    190	return 0;
    191}
    192
    193static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
    194{
    195	if (dai)
    196		clk_disable_unprepare(dai->clk);
    197}
    198
    199int asoc_simple_parse_clk(struct device *dev,
    200			  struct device_node *node,
    201			  struct asoc_simple_dai *simple_dai,
    202			  struct snd_soc_dai_link_component *dlc)
    203{
    204	struct clk *clk;
    205	u32 val;
    206
    207	/*
    208	 * Parse dai->sysclk come from "clocks = <&xxx>"
    209	 * (if system has common clock)
    210	 *  or "system-clock-frequency = <xxx>"
    211	 *  or device's module clock.
    212	 */
    213	clk = devm_get_clk_from_child(dev, node, NULL);
    214	simple_dai->clk_fixed = of_property_read_bool(
    215		node, "system-clock-fixed");
    216	if (!IS_ERR(clk)) {
    217		simple_dai->sysclk = clk_get_rate(clk);
    218
    219		simple_dai->clk = clk;
    220	} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
    221		simple_dai->sysclk = val;
    222		simple_dai->clk_fixed = true;
    223	} else {
    224		clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
    225		if (!IS_ERR(clk))
    226			simple_dai->sysclk = clk_get_rate(clk);
    227	}
    228
    229	if (of_property_read_bool(node, "system-clock-direction-out"))
    230		simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
    231
    232	return 0;
    233}
    234EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
    235
    236static int asoc_simple_check_fixed_sysclk(struct device *dev,
    237					  struct asoc_simple_dai *dai,
    238					  unsigned int *fixed_sysclk)
    239{
    240	if (dai->clk_fixed) {
    241		if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {
    242			dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",
    243				*fixed_sysclk, dai->sysclk);
    244			return -EINVAL;
    245		}
    246		*fixed_sysclk = dai->sysclk;
    247	}
    248
    249	return 0;
    250}
    251
    252int asoc_simple_startup(struct snd_pcm_substream *substream)
    253{
    254	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    255	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    256	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
    257	struct asoc_simple_dai *dai;
    258	unsigned int fixed_sysclk = 0;
    259	int i1, i2, i;
    260	int ret;
    261
    262	for_each_prop_dai_cpu(props, i1, dai) {
    263		ret = asoc_simple_clk_enable(dai);
    264		if (ret)
    265			goto cpu_err;
    266		ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
    267		if (ret)
    268			goto cpu_err;
    269	}
    270
    271	for_each_prop_dai_codec(props, i2, dai) {
    272		ret = asoc_simple_clk_enable(dai);
    273		if (ret)
    274			goto codec_err;
    275		ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
    276		if (ret)
    277			goto codec_err;
    278	}
    279
    280	if (fixed_sysclk && props->mclk_fs) {
    281		unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;
    282
    283		if (fixed_sysclk % props->mclk_fs) {
    284			dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",
    285				fixed_sysclk, props->mclk_fs);
    286			return -EINVAL;
    287		}
    288		ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,
    289			fixed_rate, fixed_rate);
    290		if (ret)
    291			goto codec_err;
    292	}
    293
    294	return 0;
    295
    296codec_err:
    297	for_each_prop_dai_codec(props, i, dai) {
    298		if (i >= i2)
    299			break;
    300		asoc_simple_clk_disable(dai);
    301	}
    302cpu_err:
    303	for_each_prop_dai_cpu(props, i, dai) {
    304		if (i >= i1)
    305			break;
    306		asoc_simple_clk_disable(dai);
    307	}
    308	return ret;
    309}
    310EXPORT_SYMBOL_GPL(asoc_simple_startup);
    311
    312void asoc_simple_shutdown(struct snd_pcm_substream *substream)
    313{
    314	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    315	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    316	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
    317	struct asoc_simple_dai *dai;
    318	int i;
    319
    320	for_each_prop_dai_cpu(props, i, dai) {
    321		struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i);
    322
    323		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
    324			snd_soc_dai_set_sysclk(cpu_dai,
    325					       0, 0, SND_SOC_CLOCK_OUT);
    326
    327		asoc_simple_clk_disable(dai);
    328	}
    329	for_each_prop_dai_codec(props, i, dai) {
    330		struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i);
    331
    332		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
    333			snd_soc_dai_set_sysclk(codec_dai,
    334					       0, 0, SND_SOC_CLOCK_IN);
    335
    336		asoc_simple_clk_disable(dai);
    337	}
    338}
    339EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
    340
    341static int asoc_simple_set_clk_rate(struct device *dev,
    342				    struct asoc_simple_dai *simple_dai,
    343				    unsigned long rate)
    344{
    345	if (!simple_dai)
    346		return 0;
    347
    348	if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
    349		dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
    350		return -EINVAL;
    351	}
    352
    353	if (!simple_dai->clk)
    354		return 0;
    355
    356	if (clk_get_rate(simple_dai->clk) == rate)
    357		return 0;
    358
    359	return clk_set_rate(simple_dai->clk, rate);
    360}
    361
    362static int asoc_simple_set_tdm(struct snd_soc_dai *dai,
    363				struct asoc_simple_dai *simple_dai,
    364				struct snd_pcm_hw_params *params)
    365{
    366	int sample_bits = params_width(params);
    367	int slot_width, slot_count;
    368	int i, ret;
    369
    370	if (!simple_dai || !simple_dai->tdm_width_map)
    371		return 0;
    372
    373	slot_width = simple_dai->slot_width;
    374	slot_count = simple_dai->slots;
    375
    376	if (slot_width == 0)
    377		slot_width = sample_bits;
    378
    379	for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
    380		if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
    381			slot_width = simple_dai->tdm_width_map[i].slot_width;
    382			slot_count = simple_dai->tdm_width_map[i].slot_count;
    383			break;
    384		}
    385	}
    386
    387	ret = snd_soc_dai_set_tdm_slot(dai,
    388				       simple_dai->tx_slot_mask,
    389				       simple_dai->rx_slot_mask,
    390				       slot_count,
    391				       slot_width);
    392	if (ret && ret != -ENOTSUPP) {
    393		dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
    394		return ret;
    395	}
    396
    397	return 0;
    398}
    399
    400int asoc_simple_hw_params(struct snd_pcm_substream *substream,
    401			  struct snd_pcm_hw_params *params)
    402{
    403	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    404	struct asoc_simple_dai *pdai;
    405	struct snd_soc_dai *sdai;
    406	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    407	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
    408	unsigned int mclk, mclk_fs = 0;
    409	int i, ret;
    410
    411	if (props->mclk_fs)
    412		mclk_fs = props->mclk_fs;
    413
    414	if (mclk_fs) {
    415		struct snd_soc_component *component;
    416		mclk = params_rate(params) * mclk_fs;
    417
    418		for_each_prop_dai_codec(props, i, pdai) {
    419			ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
    420			if (ret < 0)
    421				return ret;
    422		}
    423
    424		for_each_prop_dai_cpu(props, i, pdai) {
    425			ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
    426			if (ret < 0)
    427				return ret;
    428		}
    429
    430		/* Ensure sysclk is set on all components in case any
    431		 * (such as platform components) are missed by calls to
    432		 * snd_soc_dai_set_sysclk.
    433		 */
    434		for_each_rtd_components(rtd, i, component) {
    435			ret = snd_soc_component_set_sysclk(component, 0, 0,
    436							   mclk, SND_SOC_CLOCK_IN);
    437			if (ret && ret != -ENOTSUPP)
    438				return ret;
    439		}
    440
    441		for_each_rtd_codec_dais(rtd, i, sdai) {
    442			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);
    443			if (ret && ret != -ENOTSUPP)
    444				return ret;
    445		}
    446
    447		for_each_rtd_cpu_dais(rtd, i, sdai) {
    448			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);
    449			if (ret && ret != -ENOTSUPP)
    450				return ret;
    451		}
    452	}
    453
    454	for_each_prop_dai_codec(props, i, pdai) {
    455		sdai = asoc_rtd_to_codec(rtd, i);
    456		ret = asoc_simple_set_tdm(sdai, pdai, params);
    457		if (ret < 0)
    458			return ret;
    459	}
    460
    461	for_each_prop_dai_cpu(props, i, pdai) {
    462		sdai = asoc_rtd_to_cpu(rtd, i);
    463		ret = asoc_simple_set_tdm(sdai, pdai, params);
    464		if (ret < 0)
    465			return ret;
    466	}
    467
    468	return 0;
    469}
    470EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
    471
    472int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
    473				   struct snd_pcm_hw_params *params)
    474{
    475	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    476	struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
    477
    478	asoc_simple_convert_fixup(&dai_props->adata, params);
    479
    480	return 0;
    481}
    482EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
    483
    484static int asoc_simple_init_dai(struct snd_soc_dai *dai,
    485				     struct asoc_simple_dai *simple_dai)
    486{
    487	int ret;
    488
    489	if (!simple_dai)
    490		return 0;
    491
    492	if (simple_dai->sysclk) {
    493		ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
    494					     simple_dai->clk_direction);
    495		if (ret && ret != -ENOTSUPP) {
    496			dev_err(dai->dev, "simple-card: set_sysclk error\n");
    497			return ret;
    498		}
    499	}
    500
    501	if (simple_dai->slots) {
    502		ret = snd_soc_dai_set_tdm_slot(dai,
    503					       simple_dai->tx_slot_mask,
    504					       simple_dai->rx_slot_mask,
    505					       simple_dai->slots,
    506					       simple_dai->slot_width);
    507		if (ret && ret != -ENOTSUPP) {
    508			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
    509			return ret;
    510		}
    511	}
    512
    513	return 0;
    514}
    515
    516static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd,
    517					    struct simple_dai_props *dai_props)
    518{
    519	struct snd_soc_dai_link *dai_link = rtd->dai_link;
    520	struct snd_soc_component *component;
    521	struct snd_soc_pcm_stream *params;
    522	struct snd_pcm_hardware hw;
    523	int i, ret, stream;
    524
    525	/* Only Codecs */
    526	for_each_rtd_components(rtd, i, component) {
    527		if (!snd_soc_component_is_codec(component))
    528			return 0;
    529	}
    530
    531	/* Assumes the capabilities are the same for all supported streams */
    532	for_each_pcm_streams(stream) {
    533		ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
    534		if (ret == 0)
    535			break;
    536	}
    537
    538	if (ret < 0) {
    539		dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
    540		return ret;
    541	}
    542
    543	params = devm_kzalloc(rtd->dev, sizeof(*params), GFP_KERNEL);
    544	if (!params)
    545		return -ENOMEM;
    546
    547	params->formats = hw.formats;
    548	params->rates = hw.rates;
    549	params->rate_min = hw.rate_min;
    550	params->rate_max = hw.rate_max;
    551	params->channels_min = hw.channels_min;
    552	params->channels_max = hw.channels_max;
    553
    554	dai_link->params = params;
    555	dai_link->num_params = 1;
    556
    557	return 0;
    558}
    559
    560int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
    561{
    562	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    563	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
    564	struct asoc_simple_dai *dai;
    565	int i, ret;
    566
    567	for_each_prop_dai_codec(props, i, dai) {
    568		ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai);
    569		if (ret < 0)
    570			return ret;
    571	}
    572	for_each_prop_dai_cpu(props, i, dai) {
    573		ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai);
    574		if (ret < 0)
    575			return ret;
    576	}
    577
    578	ret = asoc_simple_init_dai_link_params(rtd, props);
    579	if (ret < 0)
    580		return ret;
    581
    582	return 0;
    583}
    584EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
    585
    586void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
    587				       struct snd_soc_dai_link_component *cpus)
    588{
    589	/* Assumes platform == cpu */
    590	if (!platforms->of_node)
    591		platforms->of_node = cpus->of_node;
    592}
    593EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
    594
    595void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,
    596				  int is_single_links)
    597{
    598	/*
    599	 * In soc_bind_dai_link() will check cpu name after
    600	 * of_node matching if dai_link has cpu_dai_name.
    601	 * but, it will never match if name was created by
    602	 * fmt_single_name() remove cpu_dai_name if cpu_args
    603	 * was 0. See:
    604	 *	fmt_single_name()
    605	 *	fmt_multiple_name()
    606	 */
    607	if (is_single_links)
    608		cpus->dai_name = NULL;
    609}
    610EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
    611
    612int asoc_simple_clean_reference(struct snd_soc_card *card)
    613{
    614	struct snd_soc_dai_link *dai_link;
    615	struct snd_soc_dai_link_component *cpu;
    616	struct snd_soc_dai_link_component *codec;
    617	int i, j;
    618
    619	for_each_card_prelinks(card, i, dai_link) {
    620		for_each_link_cpus(dai_link, j, cpu)
    621			of_node_put(cpu->of_node);
    622		for_each_link_codecs(dai_link, j, codec)
    623			of_node_put(codec->of_node);
    624	}
    625	return 0;
    626}
    627EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
    628
    629int asoc_simple_parse_routing(struct snd_soc_card *card,
    630			      char *prefix)
    631{
    632	struct device_node *node = card->dev->of_node;
    633	char prop[128];
    634
    635	if (!prefix)
    636		prefix = "";
    637
    638	snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
    639
    640	if (!of_property_read_bool(node, prop))
    641		return 0;
    642
    643	return snd_soc_of_parse_audio_routing(card, prop);
    644}
    645EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
    646
    647int asoc_simple_parse_widgets(struct snd_soc_card *card,
    648			      char *prefix)
    649{
    650	struct device_node *node = card->dev->of_node;
    651	char prop[128];
    652
    653	if (!prefix)
    654		prefix = "";
    655
    656	snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
    657
    658	if (of_property_read_bool(node, prop))
    659		return snd_soc_of_parse_audio_simple_widgets(card, prop);
    660
    661	/* no widgets is not error */
    662	return 0;
    663}
    664EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
    665
    666int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
    667				   char *prefix)
    668{
    669	char prop[128];
    670
    671	if (!prefix)
    672		prefix = "";
    673
    674	snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
    675
    676	return snd_soc_of_parse_pin_switches(card, prop);
    677}
    678EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
    679
    680int asoc_simple_init_jack(struct snd_soc_card *card,
    681			  struct asoc_simple_jack *sjack,
    682			  int is_hp, char *prefix,
    683			  char *pin)
    684{
    685	struct device *dev = card->dev;
    686	enum of_gpio_flags flags;
    687	char prop[128];
    688	char *pin_name;
    689	char *gpio_name;
    690	int mask;
    691	int det;
    692
    693	if (!prefix)
    694		prefix = "";
    695
    696	sjack->gpio.gpio = -ENOENT;
    697
    698	if (is_hp) {
    699		snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
    700		pin_name	= pin ? pin : "Headphones";
    701		gpio_name	= "Headphone detection";
    702		mask		= SND_JACK_HEADPHONE;
    703	} else {
    704		snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
    705		pin_name	= pin ? pin : "Mic Jack";
    706		gpio_name	= "Mic detection";
    707		mask		= SND_JACK_MICROPHONE;
    708	}
    709
    710	det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
    711	if (det == -EPROBE_DEFER)
    712		return -EPROBE_DEFER;
    713
    714	if (gpio_is_valid(det)) {
    715		sjack->pin.pin		= pin_name;
    716		sjack->pin.mask		= mask;
    717
    718		sjack->gpio.name	= gpio_name;
    719		sjack->gpio.report	= mask;
    720		sjack->gpio.gpio	= det;
    721		sjack->gpio.invert	= !!(flags & OF_GPIO_ACTIVE_LOW);
    722		sjack->gpio.debounce_time = 150;
    723
    724		snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
    725					   &sjack->pin, 1);
    726
    727		snd_soc_jack_add_gpios(&sjack->jack, 1,
    728				       &sjack->gpio);
    729	}
    730
    731	return 0;
    732}
    733EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
    734
    735int asoc_simple_init_priv(struct asoc_simple_priv *priv,
    736			  struct link_info *li)
    737{
    738	struct snd_soc_card *card = simple_priv_to_card(priv);
    739	struct device *dev = simple_priv_to_dev(priv);
    740	struct snd_soc_dai_link *dai_link;
    741	struct simple_dai_props *dai_props;
    742	struct asoc_simple_dai *dais;
    743	struct snd_soc_dai_link_component *dlcs;
    744	struct snd_soc_codec_conf *cconf = NULL;
    745	struct snd_soc_pcm_stream *c2c_conf = NULL;
    746	int i, dai_num = 0, dlc_num = 0, cnf_num = 0, c2c_num = 0;
    747
    748	dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
    749	dai_link  = devm_kcalloc(dev, li->link, sizeof(*dai_link),  GFP_KERNEL);
    750	if (!dai_props || !dai_link)
    751		return -ENOMEM;
    752
    753	/*
    754	 * dais (= CPU+Codec)
    755	 * dlcs (= CPU+Codec+Platform)
    756	 */
    757	for (i = 0; i < li->link; i++) {
    758		int cc = li->num[i].cpus + li->num[i].codecs;
    759
    760		dai_num += cc;
    761		dlc_num += cc + li->num[i].platforms;
    762
    763		if (!li->num[i].cpus)
    764			cnf_num += li->num[i].codecs;
    765
    766		c2c_num += li->num[i].c2c;
    767	}
    768
    769	dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
    770	dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);
    771	if (!dais || !dlcs)
    772		return -ENOMEM;
    773
    774	if (cnf_num) {
    775		cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);
    776		if (!cconf)
    777			return -ENOMEM;
    778	}
    779
    780	if (c2c_num) {
    781		c2c_conf = devm_kcalloc(dev, c2c_num, sizeof(*c2c_conf), GFP_KERNEL);
    782		if (!c2c_conf)
    783			return -ENOMEM;
    784	}
    785
    786	dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
    787		li->link, dai_num, cnf_num);
    788
    789	/* dummy CPU/Codec */
    790	priv->dummy.of_node	= NULL;
    791	priv->dummy.dai_name	= "snd-soc-dummy-dai";
    792	priv->dummy.name	= "snd-soc-dummy";
    793
    794	priv->dai_props		= dai_props;
    795	priv->dai_link		= dai_link;
    796	priv->dais		= dais;
    797	priv->dlcs		= dlcs;
    798	priv->codec_conf	= cconf;
    799	priv->c2c_conf		= c2c_conf;
    800
    801	card->dai_link		= priv->dai_link;
    802	card->num_links		= li->link;
    803	card->codec_conf	= cconf;
    804	card->num_configs	= cnf_num;
    805
    806	for (i = 0; i < li->link; i++) {
    807		if (li->num[i].cpus) {
    808			/* Normal CPU */
    809			dai_props[i].cpus	=
    810			dai_link[i].cpus	= dlcs;
    811			dai_props[i].num.cpus	=
    812			dai_link[i].num_cpus	= li->num[i].cpus;
    813			dai_props[i].cpu_dai	= dais;
    814
    815			dlcs += li->num[i].cpus;
    816			dais += li->num[i].cpus;
    817
    818			if (li->num[i].c2c) {
    819				/* Codec2Codec */
    820				dai_props[i].c2c_conf = c2c_conf;
    821				c2c_conf += li->num[i].c2c;
    822			}
    823		} else {
    824			/* DPCM Be's CPU = dummy */
    825			dai_props[i].cpus	=
    826			dai_link[i].cpus	= &priv->dummy;
    827			dai_props[i].num.cpus	=
    828			dai_link[i].num_cpus	= 1;
    829		}
    830
    831		if (li->num[i].codecs) {
    832			/* Normal Codec */
    833			dai_props[i].codecs	=
    834			dai_link[i].codecs	= dlcs;
    835			dai_props[i].num.codecs	=
    836			dai_link[i].num_codecs	= li->num[i].codecs;
    837			dai_props[i].codec_dai	= dais;
    838
    839			dlcs += li->num[i].codecs;
    840			dais += li->num[i].codecs;
    841
    842			if (!li->num[i].cpus) {
    843				/* DPCM Be's Codec */
    844				dai_props[i].codec_conf = cconf;
    845				cconf += li->num[i].codecs;
    846			}
    847		} else {
    848			/* DPCM Fe's Codec = dummy */
    849			dai_props[i].codecs	=
    850			dai_link[i].codecs	= &priv->dummy;
    851			dai_props[i].num.codecs	=
    852			dai_link[i].num_codecs	= 1;
    853		}
    854
    855		if (li->num[i].platforms) {
    856			/* Have Platform */
    857			dai_props[i].platforms		=
    858			dai_link[i].platforms		= dlcs;
    859			dai_props[i].num.platforms	=
    860			dai_link[i].num_platforms	= li->num[i].platforms;
    861
    862			dlcs += li->num[i].platforms;
    863		} else {
    864			/* Doesn't have Platform */
    865			dai_props[i].platforms		=
    866			dai_link[i].platforms		= NULL;
    867			dai_props[i].num.platforms	=
    868			dai_link[i].num_platforms	= 0;
    869		}
    870	}
    871
    872	return 0;
    873}
    874EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
    875
    876int asoc_simple_remove(struct platform_device *pdev)
    877{
    878	struct snd_soc_card *card = platform_get_drvdata(pdev);
    879
    880	return asoc_simple_clean_reference(card);
    881}
    882EXPORT_SYMBOL_GPL(asoc_simple_remove);
    883
    884int asoc_graph_card_probe(struct snd_soc_card *card)
    885{
    886	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
    887	int ret;
    888
    889	ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
    890	if (ret < 0)
    891		return ret;
    892
    893	ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL);
    894	if (ret < 0)
    895		return ret;
    896
    897	return 0;
    898}
    899EXPORT_SYMBOL_GPL(asoc_graph_card_probe);
    900
    901int asoc_graph_is_ports0(struct device_node *np)
    902{
    903	struct device_node *port, *ports, *ports0, *top;
    904	int ret;
    905
    906	/* np is "endpoint" or "port" */
    907	if (of_node_name_eq(np, "endpoint")) {
    908		port = of_get_parent(np);
    909	} else {
    910		port = np;
    911		of_node_get(port);
    912	}
    913
    914	ports	= of_get_parent(port);
    915	top	= of_get_parent(ports);
    916	ports0	= of_get_child_by_name(top, "ports");
    917
    918	ret = ports0 == ports;
    919
    920	of_node_put(port);
    921	of_node_put(ports);
    922	of_node_put(ports0);
    923	of_node_put(top);
    924
    925	return ret;
    926}
    927EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
    928
    929/* Module information */
    930MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
    931MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
    932MODULE_LICENSE("GPL v2");