brintos

brintos / linux-shallow public Read only

0
0
Text · 29.9 KiB · fedae7f Raw
1233 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// simple-card-utils.c4//5// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>6 7#include <dt-bindings/sound/audio-graph.h>8#include <linux/cleanup.h>9#include <linux/clk.h>10#include <linux/gpio/consumer.h>11#include <linux/module.h>12#include <linux/of.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 18int simple_util_get_sample_fmt(struct simple_util_data *data)19{20	int i;21	int val = -EINVAL;22 23	struct {24		char *fmt;25		u32 val;26	} of_sample_fmt_table[] = {27		{ "s8",		SNDRV_PCM_FORMAT_S8},28		{ "s16_le",	SNDRV_PCM_FORMAT_S16_LE},29		{ "s24_le",	SNDRV_PCM_FORMAT_S24_LE},30		{ "s24_3le",	SNDRV_PCM_FORMAT_S24_3LE},31		{ "s32_le",	SNDRV_PCM_FORMAT_S32_LE},32	};33 34	for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) {35		if (!strcmp(data->convert_sample_format,36			    of_sample_fmt_table[i].fmt)) {37			val = of_sample_fmt_table[i].val;38			break;39		}40	}41	return val;42}43EXPORT_SYMBOL_GPL(simple_util_get_sample_fmt);44 45static void simple_fixup_sample_fmt(struct simple_util_data *data,46				    struct snd_pcm_hw_params *params)47{48	int val;49	struct snd_mask *mask = hw_param_mask(params,50					      SNDRV_PCM_HW_PARAM_FORMAT);51 52	val = simple_util_get_sample_fmt(data);53	if (val >= 0) {54		snd_mask_none(mask);55		snd_mask_set(mask, val);56	}57}58 59void simple_util_parse_convert(struct device_node *np,60			       char *prefix,61			       struct simple_util_data *data)62{63	char prop[128];64 65	if (!np)66		return;67 68	if (!prefix)69		prefix = "";70 71	/* sampling rate convert */72	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");73	of_property_read_u32(np, prop, &data->convert_rate);74 75	/* channels transfer */76	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");77	of_property_read_u32(np, prop, &data->convert_channels);78 79	/* convert sample format */80	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format");81	of_property_read_string(np, prop, &data->convert_sample_format);82}83EXPORT_SYMBOL_GPL(simple_util_parse_convert);84 85/**86 * simple_util_is_convert_required() - Query if HW param conversion was requested87 * @data: Link data.88 *89 * Returns true if any HW param conversion was requested for this DAI link with90 * any "convert-xxx" properties.91 */92bool simple_util_is_convert_required(const struct simple_util_data *data)93{94	return data->convert_rate ||95	       data->convert_channels ||96	       data->convert_sample_format;97}98EXPORT_SYMBOL_GPL(simple_util_is_convert_required);99 100int simple_util_parse_daifmt(struct device *dev,101			     struct device_node *node,102			     struct device_node *codec,103			     char *prefix,104			     unsigned int *retfmt)105{106	struct device_node *bitclkmaster = NULL;107	struct device_node *framemaster = NULL;108	unsigned int daifmt;109 110	daifmt = snd_soc_daifmt_parse_format(node, prefix);111 112	snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);113	if (!bitclkmaster && !framemaster) {114		/*115		 * No dai-link level and master setting was not found from116		 * sound node level, revert back to legacy DT parsing and117		 * take the settings from codec node.118		 */119		dev_dbg(dev, "Revert to legacy daifmt parsing\n");120 121		daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);122	} else {123		daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(124				((codec == bitclkmaster) << 4) | (codec == framemaster));125	}126 127	of_node_put(bitclkmaster);128	of_node_put(framemaster);129 130	*retfmt = daifmt;131 132	return 0;133}134EXPORT_SYMBOL_GPL(simple_util_parse_daifmt);135 136int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np,137				    struct simple_util_dai *dai)138{139	int n, i, ret;140	u32 *p;141 142	if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))143		return 0;144 145	n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));146	if (n % 3) {147		dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");148		return -EINVAL;149	}150 151	dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);152	if (!dai->tdm_width_map)153		return -ENOMEM;154 155	u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values),156						  GFP_KERNEL);157	if (!array_values)158		return -ENOMEM;159 160	ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);161	if (ret < 0) {162		dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);163		return ret;164	}165 166	p = array_values;167	for (i = 0; i < n / 3; ++i) {168		dai->tdm_width_map[i].sample_bits = *p++;169		dai->tdm_width_map[i].slot_width = *p++;170		dai->tdm_width_map[i].slot_count = *p++;171	}172 173	dai->n_tdm_widths = i;174 175	return 0;176}177EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map);178 179int simple_util_set_dailink_name(struct device *dev,180				 struct snd_soc_dai_link *dai_link,181				 const char *fmt, ...)182{183	va_list ap;184	char *name = NULL;185	int ret = -ENOMEM;186 187	va_start(ap, fmt);188	name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);189	va_end(ap);190 191	if (name) {192		ret = 0;193 194		dai_link->name		= name;195		dai_link->stream_name	= name;196	}197 198	return ret;199}200EXPORT_SYMBOL_GPL(simple_util_set_dailink_name);201 202int simple_util_parse_card_name(struct snd_soc_card *card,203				char *prefix)204{205	int ret;206 207	if (!prefix)208		prefix = "";209 210	/* Parse the card name from DT */211	ret = snd_soc_of_parse_card_name(card, "label");212	if (ret < 0 || !card->name) {213		char prop[128];214 215		snprintf(prop, sizeof(prop), "%sname", prefix);216		ret = snd_soc_of_parse_card_name(card, prop);217		if (ret < 0)218			return ret;219	}220 221	if (!card->name && card->dai_link)222		card->name = card->dai_link->name;223 224	return 0;225}226EXPORT_SYMBOL_GPL(simple_util_parse_card_name);227 228static int simple_clk_enable(struct simple_util_dai *dai)229{230	if (dai)231		return clk_prepare_enable(dai->clk);232 233	return 0;234}235 236static void simple_clk_disable(struct simple_util_dai *dai)237{238	if (dai)239		clk_disable_unprepare(dai->clk);240}241 242int simple_util_parse_clk(struct device *dev,243			  struct device_node *node,244			  struct simple_util_dai *simple_dai,245			  struct snd_soc_dai_link_component *dlc)246{247	struct clk *clk;248	u32 val;249 250	/*251	 * Parse dai->sysclk come from "clocks = <&xxx>"252	 * (if system has common clock)253	 *  or "system-clock-frequency = <xxx>"254	 *  or device's module clock.255	 */256	clk = devm_get_clk_from_child(dev, node, NULL);257	simple_dai->clk_fixed = of_property_read_bool(258		node, "system-clock-fixed");259	if (!IS_ERR(clk)) {260		simple_dai->sysclk = clk_get_rate(clk);261 262		simple_dai->clk = clk;263	} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {264		simple_dai->sysclk = val;265		simple_dai->clk_fixed = true;266	} else {267		clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);268		if (!IS_ERR(clk))269			simple_dai->sysclk = clk_get_rate(clk);270	}271 272	if (of_property_read_bool(node, "system-clock-direction-out"))273		simple_dai->clk_direction = SND_SOC_CLOCK_OUT;274 275	return 0;276}277EXPORT_SYMBOL_GPL(simple_util_parse_clk);278 279static int simple_check_fixed_sysclk(struct device *dev,280					  struct simple_util_dai *dai,281					  unsigned int *fixed_sysclk)282{283	if (dai->clk_fixed) {284		if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {285			dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",286				*fixed_sysclk, dai->sysclk);287			return -EINVAL;288		}289		*fixed_sysclk = dai->sysclk;290	}291 292	return 0;293}294 295int simple_util_startup(struct snd_pcm_substream *substream)296{297	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);298	struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);299	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);300	struct simple_util_dai *dai;301	unsigned int fixed_sysclk = 0;302	int i1, i2, i;303	int ret;304 305	for_each_prop_dai_cpu(props, i1, dai) {306		ret = simple_clk_enable(dai);307		if (ret)308			goto cpu_err;309		ret = simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);310		if (ret)311			goto cpu_err;312	}313 314	for_each_prop_dai_codec(props, i2, dai) {315		ret = simple_clk_enable(dai);316		if (ret)317			goto codec_err;318		ret = simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);319		if (ret)320			goto codec_err;321	}322 323	if (fixed_sysclk && props->mclk_fs) {324		unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;325 326		if (fixed_sysclk % props->mclk_fs) {327			dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",328				fixed_sysclk, props->mclk_fs);329			ret = -EINVAL;330			goto codec_err;331		}332		ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,333			fixed_rate, fixed_rate);334		if (ret < 0)335			goto codec_err;336	}337 338	return 0;339 340codec_err:341	for_each_prop_dai_codec(props, i, dai) {342		if (i >= i2)343			break;344		simple_clk_disable(dai);345	}346cpu_err:347	for_each_prop_dai_cpu(props, i, dai) {348		if (i >= i1)349			break;350		simple_clk_disable(dai);351	}352	return ret;353}354EXPORT_SYMBOL_GPL(simple_util_startup);355 356void simple_util_shutdown(struct snd_pcm_substream *substream)357{358	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);359	struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);360	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);361	struct simple_util_dai *dai;362	int i;363 364	for_each_prop_dai_cpu(props, i, dai) {365		struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, i);366 367		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))368			snd_soc_dai_set_sysclk(cpu_dai,369					       0, 0, SND_SOC_CLOCK_OUT);370 371		simple_clk_disable(dai);372	}373	for_each_prop_dai_codec(props, i, dai) {374		struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, i);375 376		if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))377			snd_soc_dai_set_sysclk(codec_dai,378					       0, 0, SND_SOC_CLOCK_IN);379 380		simple_clk_disable(dai);381	}382}383EXPORT_SYMBOL_GPL(simple_util_shutdown);384 385static int simple_set_clk_rate(struct device *dev,386				    struct simple_util_dai *simple_dai,387				    unsigned long rate)388{389	if (!simple_dai)390		return 0;391 392	if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {393		dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);394		return -EINVAL;395	}396 397	if (!simple_dai->clk)398		return 0;399 400	if (clk_get_rate(simple_dai->clk) == rate)401		return 0;402 403	return clk_set_rate(simple_dai->clk, rate);404}405 406static int simple_set_tdm(struct snd_soc_dai *dai,407				struct simple_util_dai *simple_dai,408				struct snd_pcm_hw_params *params)409{410	int sample_bits = params_width(params);411	int slot_width, slot_count;412	int i, ret;413 414	if (!simple_dai || !simple_dai->tdm_width_map)415		return 0;416 417	slot_width = simple_dai->slot_width;418	slot_count = simple_dai->slots;419 420	if (slot_width == 0)421		slot_width = sample_bits;422 423	for (i = 0; i < simple_dai->n_tdm_widths; ++i) {424		if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {425			slot_width = simple_dai->tdm_width_map[i].slot_width;426			slot_count = simple_dai->tdm_width_map[i].slot_count;427			break;428		}429	}430 431	ret = snd_soc_dai_set_tdm_slot(dai,432				       simple_dai->tx_slot_mask,433				       simple_dai->rx_slot_mask,434				       slot_count,435				       slot_width);436	if (ret && ret != -ENOTSUPP) {437		dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);438		return ret;439	}440 441	return 0;442}443 444int simple_util_hw_params(struct snd_pcm_substream *substream,445			  struct snd_pcm_hw_params *params)446{447	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);448	struct simple_util_dai *pdai;449	struct snd_soc_dai *sdai;450	struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);451	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);452	unsigned int mclk, mclk_fs = 0;453	int i, ret;454 455	if (props->mclk_fs)456		mclk_fs = props->mclk_fs;457 458	if (mclk_fs) {459		struct snd_soc_component *component;460		mclk = params_rate(params) * mclk_fs;461 462		for_each_prop_dai_codec(props, i, pdai) {463			ret = simple_set_clk_rate(rtd->dev, pdai, mclk);464			if (ret < 0)465				return ret;466		}467 468		for_each_prop_dai_cpu(props, i, pdai) {469			ret = simple_set_clk_rate(rtd->dev, pdai, mclk);470			if (ret < 0)471				return ret;472		}473 474		/* Ensure sysclk is set on all components in case any475		 * (such as platform components) are missed by calls to476		 * snd_soc_dai_set_sysclk.477		 */478		for_each_rtd_components(rtd, i, component) {479			ret = snd_soc_component_set_sysclk(component, 0, 0,480							   mclk, SND_SOC_CLOCK_IN);481			if (ret && ret != -ENOTSUPP)482				return ret;483		}484 485		for_each_rtd_codec_dais(rtd, i, sdai) {486			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);487			if (ret && ret != -ENOTSUPP)488				return ret;489		}490 491		for_each_rtd_cpu_dais(rtd, i, sdai) {492			ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);493			if (ret && ret != -ENOTSUPP)494				return ret;495		}496	}497 498	for_each_prop_dai_codec(props, i, pdai) {499		sdai = snd_soc_rtd_to_codec(rtd, i);500		ret = simple_set_tdm(sdai, pdai, params);501		if (ret < 0)502			return ret;503	}504 505	for_each_prop_dai_cpu(props, i, pdai) {506		sdai = snd_soc_rtd_to_cpu(rtd, i);507		ret = simple_set_tdm(sdai, pdai, params);508		if (ret < 0)509			return ret;510	}511 512	return 0;513}514EXPORT_SYMBOL_GPL(simple_util_hw_params);515 516int simple_util_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,517				   struct snd_pcm_hw_params *params)518{519	struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);520	struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);521	struct simple_util_data *data = &dai_props->adata;522	struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);523	struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);524 525	if (data->convert_rate)526		rate->min =527		rate->max = data->convert_rate;528 529	if (data->convert_channels)530		channels->min =531		channels->max = data->convert_channels;532 533	if (data->convert_sample_format)534		simple_fixup_sample_fmt(data, params);535 536	return 0;537}538EXPORT_SYMBOL_GPL(simple_util_be_hw_params_fixup);539 540static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simple_dai)541{542	int ret;543 544	if (!simple_dai)545		return 0;546 547	if (simple_dai->sysclk) {548		ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,549					     simple_dai->clk_direction);550		if (ret && ret != -ENOTSUPP) {551			dev_err(dai->dev, "simple-card: set_sysclk error\n");552			return ret;553		}554	}555 556	if (simple_dai->slots) {557		ret = snd_soc_dai_set_tdm_slot(dai,558					       simple_dai->tx_slot_mask,559					       simple_dai->rx_slot_mask,560					       simple_dai->slots,561					       simple_dai->slot_width);562		if (ret && ret != -ENOTSUPP) {563			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");564			return ret;565		}566	}567 568	return 0;569}570 571static inline int simple_component_is_codec(struct snd_soc_component *component)572{573	return component->driver->endianness;574}575 576static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,577				       struct simple_dai_props *dai_props)578{579	struct snd_soc_dai_link *dai_link = rtd->dai_link;580	struct snd_soc_component *component;581	struct snd_soc_pcm_stream *c2c_params;582	struct snd_pcm_hardware hw;583	int i, ret, stream;584 585	/* Do nothing if it already has Codec2Codec settings */586	if (dai_link->c2c_params)587		return 0;588 589	/* Do nothing if it was DPCM :: BE */590	if (dai_link->no_pcm)591		return 0;592 593	/* Only Codecs */594	for_each_rtd_components(rtd, i, component) {595		if (!simple_component_is_codec(component))596			return 0;597	}598 599	/* Assumes the capabilities are the same for all supported streams */600	for_each_pcm_streams(stream) {601		ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);602		if (ret == 0)603			break;604	}605 606	if (ret < 0) {607		dev_err(rtd->dev, "simple-card: no valid dai_link params\n");608		return ret;609	}610 611	c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL);612	if (!c2c_params)613		return -ENOMEM;614 615	c2c_params->formats		= hw.formats;616	c2c_params->rates		= hw.rates;617	c2c_params->rate_min		= hw.rate_min;618	c2c_params->rate_max		= hw.rate_max;619	c2c_params->channels_min	= hw.channels_min;620	c2c_params->channels_max	= hw.channels_max;621 622	dai_link->c2c_params		= c2c_params;623	dai_link->num_c2c_params	= 1;624 625	return 0;626}627 628int simple_util_dai_init(struct snd_soc_pcm_runtime *rtd)629{630	struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card);631	struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);632	struct simple_util_dai *dai;633	int i, ret;634 635	for_each_prop_dai_codec(props, i, dai) {636		ret = simple_init_dai(snd_soc_rtd_to_codec(rtd, i), dai);637		if (ret < 0)638			return ret;639	}640	for_each_prop_dai_cpu(props, i, dai) {641		ret = simple_init_dai(snd_soc_rtd_to_cpu(rtd, i), dai);642		if (ret < 0)643			return ret;644	}645 646	ret = simple_init_for_codec2codec(rtd, props);647	if (ret < 0)648		return ret;649 650	return 0;651}652EXPORT_SYMBOL_GPL(simple_util_dai_init);653 654void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platforms,655				       struct snd_soc_dai_link_component *cpus)656{657	/*658	 * Assumes Platform == CPU659	 *660	 * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform661	 * are different Component, but are sharing same component->dev.662	 *663	 * Let's assume Platform is same as CPU if it doesn't identify Platform on DT.664	 * see665	 *	simple-card.c :: simple_count_noml()666	 */667	if (!platforms->of_node)668		snd_soc_dlc_use_cpu_as_platform(platforms, cpus);669}670EXPORT_SYMBOL_GPL(simple_util_canonicalize_platform);671 672void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,673				  int is_single_links)674{675	/*676	 * In soc_bind_dai_link() will check cpu name after677	 * of_node matching if dai_link has cpu_dai_name.678	 * but, it will never match if name was created by679	 * fmt_single_name() remove cpu_dai_name if cpu_args680	 * was 0. See:681	 *	fmt_single_name()682	 *	fmt_multiple_name()683	 */684	if (is_single_links)685		cpus->dai_name = NULL;686}687EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu);688 689void simple_util_clean_reference(struct snd_soc_card *card)690{691	struct snd_soc_dai_link *dai_link;692	struct snd_soc_dai_link_component *cpu;693	struct snd_soc_dai_link_component *codec;694	int i, j;695 696	for_each_card_prelinks(card, i, dai_link) {697		for_each_link_cpus(dai_link, j, cpu)698			of_node_put(cpu->of_node);699		for_each_link_codecs(dai_link, j, codec)700			of_node_put(codec->of_node);701	}702}703EXPORT_SYMBOL_GPL(simple_util_clean_reference);704 705int simple_util_parse_routing(struct snd_soc_card *card,706			      char *prefix)707{708	struct device_node *node = card->dev->of_node;709	char prop[128];710 711	if (!prefix)712		prefix = "";713 714	snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");715 716	if (!of_property_read_bool(node, prop))717		return 0;718 719	return snd_soc_of_parse_audio_routing(card, prop);720}721EXPORT_SYMBOL_GPL(simple_util_parse_routing);722 723int simple_util_parse_widgets(struct snd_soc_card *card,724			      char *prefix)725{726	struct device_node *node = card->dev->of_node;727	char prop[128];728 729	if (!prefix)730		prefix = "";731 732	snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");733 734	if (of_property_read_bool(node, prop))735		return snd_soc_of_parse_audio_simple_widgets(card, prop);736 737	/* no widgets is not error */738	return 0;739}740EXPORT_SYMBOL_GPL(simple_util_parse_widgets);741 742int simple_util_parse_pin_switches(struct snd_soc_card *card,743				   char *prefix)744{745	char prop[128];746 747	if (!prefix)748		prefix = "";749 750	snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");751 752	return snd_soc_of_parse_pin_switches(card, prop);753}754EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches);755 756int simple_util_init_jack(struct snd_soc_card *card,757			  struct simple_util_jack *sjack,758			  int is_hp, char *prefix,759			  char *pin)760{761	struct device *dev = card->dev;762	struct gpio_desc *desc;763	char prop[128];764	char *pin_name;765	char *gpio_name;766	int mask;767	int error;768 769	if (!prefix)770		prefix = "";771 772	if (is_hp) {773		snprintf(prop, sizeof(prop), "%shp-det", prefix);774		pin_name	= pin ? pin : "Headphones";775		gpio_name	= "Headphone detection";776		mask		= SND_JACK_HEADPHONE;777	} else {778		snprintf(prop, sizeof(prop), "%smic-det", prefix);779		pin_name	= pin ? pin : "Mic Jack";780		gpio_name	= "Mic detection";781		mask		= SND_JACK_MICROPHONE;782	}783 784	desc = gpiod_get_optional(dev, prop, GPIOD_IN);785	error = PTR_ERR_OR_ZERO(desc);786	if (error)787		return error;788 789	if (desc) {790		error = gpiod_set_consumer_name(desc, gpio_name);791		if (error)792			return error;793 794		sjack->pin.pin		= pin_name;795		sjack->pin.mask		= mask;796 797		sjack->gpio.name	= gpio_name;798		sjack->gpio.report	= mask;799		sjack->gpio.desc	= desc;800		sjack->gpio.debounce_time = 150;801 802		snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,803					   &sjack->pin, 1);804 805		snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio);806	}807 808	return 0;809}810EXPORT_SYMBOL_GPL(simple_util_init_jack);811 812int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix)813{814	struct snd_soc_card *card = simple_priv_to_card(priv);815	struct snd_soc_component *component;816	int found_jack_index = 0;817	int type = 0;818	int num = 0;819	int ret;820 821	if (priv->aux_jacks)822		return 0;823 824	for_each_card_auxs(card, component) {825		type = snd_soc_component_get_jack_type(component);826		if (type > 0)827			num++;828	}829	if (num < 1)830		return 0;831 832	priv->aux_jacks = devm_kcalloc(card->dev, num,833				       sizeof(struct snd_soc_jack), GFP_KERNEL);834	if (!priv->aux_jacks)835		return -ENOMEM;836 837	for_each_card_auxs(card, component) {838		char id[128];839		struct snd_soc_jack *jack;840 841		if (found_jack_index >= num)842			break;843 844		type = snd_soc_component_get_jack_type(component);845		if (type <= 0)846			continue;847 848		/* create jack */849		jack = &(priv->aux_jacks[found_jack_index++]);850		snprintf(id, sizeof(id), "%s-jack", component->name);851		ret = snd_soc_card_jack_new(card, id, type, jack);852		if (ret)853			continue;854 855		(void)snd_soc_component_set_jack(component, jack, NULL);856	}857	return 0;858}859EXPORT_SYMBOL_GPL(simple_util_init_aux_jacks);860 861int simple_util_init_priv(struct simple_util_priv *priv,862			  struct link_info *li)863{864	struct snd_soc_card *card = simple_priv_to_card(priv);865	struct device *dev = simple_priv_to_dev(priv);866	struct snd_soc_dai_link *dai_link;867	struct simple_dai_props *dai_props;868	struct simple_util_dai *dais;869	struct snd_soc_dai_link_component *dlcs;870	struct snd_soc_codec_conf *cconf = NULL;871	int i, dai_num = 0, dlc_num = 0, cnf_num = 0;872 873	dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);874	dai_link  = devm_kcalloc(dev, li->link, sizeof(*dai_link),  GFP_KERNEL);875	if (!dai_props || !dai_link)876		return -ENOMEM;877 878	/*879	 * dais (= CPU+Codec)880	 * dlcs (= CPU+Codec+Platform)881	 */882	for (i = 0; i < li->link; i++) {883		int cc = li->num[i].cpus + li->num[i].codecs;884 885		dai_num += cc;886		dlc_num += cc + li->num[i].platforms;887 888		if (!li->num[i].cpus)889			cnf_num += li->num[i].codecs;890	}891 892	dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);893	dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);894	if (!dais || !dlcs)895		return -ENOMEM;896 897	if (cnf_num) {898		cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);899		if (!cconf)900			return -ENOMEM;901	}902 903	dev_dbg(dev, "link %d, dais %d, ccnf %d\n",904		li->link, dai_num, cnf_num);905 906	priv->dai_props		= dai_props;907	priv->dai_link		= dai_link;908	priv->dais		= dais;909	priv->dlcs		= dlcs;910	priv->codec_conf	= cconf;911 912	card->dai_link		= priv->dai_link;913	card->num_links		= li->link;914	card->codec_conf	= cconf;915	card->num_configs	= cnf_num;916 917	for (i = 0; i < li->link; i++) {918		if (li->num[i].cpus) {919			/* Normal CPU */920			dai_link[i].cpus	= dlcs;921			dai_props[i].num.cpus	=922			dai_link[i].num_cpus	= li->num[i].cpus;923			dai_props[i].cpu_dai	= dais;924 925			dlcs += li->num[i].cpus;926			dais += li->num[i].cpus;927		} else {928			/* DPCM Be's CPU = dummy */929			dai_link[i].cpus	= &snd_soc_dummy_dlc;930			dai_props[i].num.cpus	=931			dai_link[i].num_cpus	= 1;932		}933 934		if (li->num[i].codecs) {935			/* Normal Codec */936			dai_link[i].codecs	= dlcs;937			dai_props[i].num.codecs	=938			dai_link[i].num_codecs	= li->num[i].codecs;939			dai_props[i].codec_dai	= dais;940 941			dlcs += li->num[i].codecs;942			dais += li->num[i].codecs;943 944			if (!li->num[i].cpus) {945				/* DPCM Be's Codec */946				dai_props[i].codec_conf = cconf;947				cconf += li->num[i].codecs;948			}949		} else {950			/* DPCM Fe's Codec = dummy */951			dai_link[i].codecs	= &snd_soc_dummy_dlc;952			dai_props[i].num.codecs	=953			dai_link[i].num_codecs	= 1;954		}955 956		if (li->num[i].platforms) {957			/* Have Platform */958			dai_link[i].platforms		= dlcs;959			dai_props[i].num.platforms	=960			dai_link[i].num_platforms	= li->num[i].platforms;961 962			dlcs += li->num[i].platforms;963		} else {964			/* Doesn't have Platform */965			dai_link[i].platforms		= NULL;966			dai_props[i].num.platforms	=967			dai_link[i].num_platforms	= 0;968		}969	}970 971	return 0;972}973EXPORT_SYMBOL_GPL(simple_util_init_priv);974 975void simple_util_remove(struct platform_device *pdev)976{977	struct snd_soc_card *card = platform_get_drvdata(pdev);978 979	simple_util_clean_reference(card);980}981EXPORT_SYMBOL_GPL(simple_util_remove);982 983int graph_util_card_probe(struct snd_soc_card *card)984{985	struct simple_util_priv *priv = snd_soc_card_get_drvdata(card);986	int ret;987 988	ret = simple_util_init_hp(card, &priv->hp_jack, NULL);989	if (ret < 0)990		return ret;991 992	ret = simple_util_init_mic(card, &priv->mic_jack, NULL);993	if (ret < 0)994		return ret;995 996	return 0;997}998EXPORT_SYMBOL_GPL(graph_util_card_probe);999 1000int graph_util_is_ports0(struct device_node *np)1001{1002	struct device_node *port, *ports, *ports0, *top;1003	int ret;1004 1005	/* np is "endpoint" or "port" */1006	if (of_node_name_eq(np, "endpoint")) {1007		port = of_get_parent(np);1008	} else {1009		port = np;1010		of_node_get(port);1011	}1012 1013	ports	= of_get_parent(port);1014	top	= of_get_parent(ports);1015	ports0	= of_get_child_by_name(top, "ports");1016 1017	ret = ports0 == ports;1018 1019	of_node_put(port);1020	of_node_put(ports);1021	of_node_put(ports0);1022	of_node_put(top);1023 1024	return ret;1025}1026EXPORT_SYMBOL_GPL(graph_util_is_ports0);1027 1028static int graph_get_dai_id(struct device_node *ep)1029{1030	struct device_node *node;1031	struct device_node *endpoint;1032	struct of_endpoint info;1033	int i, id;1034	int ret;1035 1036	/* use driver specified DAI ID if exist */1037	ret = snd_soc_get_dai_id(ep);1038	if (ret != -ENOTSUPP)1039		return ret;1040 1041	/* use endpoint/port reg if exist */1042	ret = of_graph_parse_endpoint(ep, &info);1043	if (ret == 0) {1044		/*1045		 * Because it will count port/endpoint if it doesn't have "reg".1046		 * But, we can't judge whether it has "no reg", or "reg = <0>"1047		 * only of_graph_parse_endpoint().1048		 * We need to check "reg" property1049		 */1050		if (of_property_present(ep,   "reg"))1051			return info.id;1052 1053		node = of_get_parent(ep);1054		ret = of_property_present(node, "reg");1055		of_node_put(node);1056		if (ret)1057			return info.port;1058	}1059	node = of_graph_get_port_parent(ep);1060 1061	/*1062	 * Non HDMI sound case, counting port/endpoint on its DT1063	 * is enough. Let's count it.1064	 */1065	i = 0;1066	id = -1;1067	for_each_endpoint_of_node(node, endpoint) {1068		if (endpoint == ep)1069			id = i;1070		i++;1071	}1072 1073	of_node_put(node);1074 1075	if (id < 0)1076		return -ENODEV;1077 1078	return id;1079}1080 1081int graph_util_parse_dai(struct device *dev, struct device_node *ep,1082			 struct snd_soc_dai_link_component *dlc, int *is_single_link)1083{1084	struct device_node *node;1085	struct of_phandle_args args = {};1086	struct snd_soc_dai *dai;1087	int ret;1088 1089	if (!ep)1090		return 0;1091 1092	node = of_graph_get_port_parent(ep);1093 1094	/*1095	 * Try to find from DAI node1096	 */1097	args.np = ep;1098	dai = snd_soc_get_dai_via_args(&args);1099	if (dai) {1100		dlc->dai_name = snd_soc_dai_name_get(dai);1101		dlc->dai_args = snd_soc_copy_dai_args(dev, &args);1102		if (!dlc->dai_args)1103			return -ENOMEM;1104 1105		goto parse_dai_end;1106	}1107 1108	/* Get dai->name */1109	args.np		= node;1110	args.args[0]	= graph_get_dai_id(ep);1111	args.args_count	= (of_graph_get_endpoint_count(node) > 1);1112 1113	/*1114	 * FIXME1115	 *1116	 * Here, dlc->dai_name is pointer to CPU/Codec DAI name.1117	 * If user unbinded CPU or Codec driver, but not for Sound Card,1118	 * dlc->dai_name is keeping unbinded CPU or Codec1119	 * driver's pointer.1120	 *1121	 * If user re-bind CPU or Codec driver again, ALSA SoC will try1122	 * to rebind Card via snd_soc_try_rebind_card(), but because of1123	 * above reason, it might can't bind Sound Card.1124	 * Because Sound Card is pointing to released dai_name pointer.1125	 *1126	 * To avoid this rebind Card issue,1127	 * 1) It needs to alloc memory to keep dai_name eventhough1128	 *    CPU or Codec driver was unbinded, or1129	 * 2) user need to rebind Sound Card everytime1130	 *    if he unbinded CPU or Codec.1131	 */1132	ret = snd_soc_get_dlc(&args, dlc);1133	if (ret < 0) {1134		of_node_put(node);1135		return ret;1136	}1137 1138parse_dai_end:1139	if (is_single_link)1140		*is_single_link = of_graph_get_endpoint_count(node) == 1;1141 1142	return 0;1143}1144EXPORT_SYMBOL_GPL(graph_util_parse_dai);1145 1146void graph_util_parse_link_direction(struct device_node *np,1147				    bool *playback_only, bool *capture_only)1148{1149	bool is_playback_only = of_property_read_bool(np, "playback-only");1150	bool is_capture_only  = of_property_read_bool(np, "capture-only");1151 1152	if (is_playback_only)1153		*playback_only = is_playback_only;1154	if (is_capture_only)1155		*capture_only = is_capture_only;1156}1157EXPORT_SYMBOL_GPL(graph_util_parse_link_direction);1158 1159static enum snd_soc_trigger_order1160__graph_util_parse_trigger_order(struct simple_util_priv *priv,1161				 struct device_node *np,1162				 const char *prop)1163{1164	u32 val[SND_SOC_TRIGGER_SIZE];1165	int ret;1166 1167	ret = of_property_read_u32_array(np, prop, val, SND_SOC_TRIGGER_SIZE);1168	if (ret == 0) {1169		struct device *dev = simple_priv_to_dev(priv);1170		u32 order =	(val[0] << 8) +1171			(val[1] << 4) +1172			(val[2]);1173 1174		switch (order) {1175		case	(SND_SOC_TRIGGER_LINK		<< 8) +1176			(SND_SOC_TRIGGER_COMPONENT	<< 4) +1177			(SND_SOC_TRIGGER_DAI):1178			return SND_SOC_TRIGGER_ORDER_DEFAULT;1179 1180		case	(SND_SOC_TRIGGER_LINK		<< 8) +1181			(SND_SOC_TRIGGER_DAI		<< 4) +1182			(SND_SOC_TRIGGER_COMPONENT):1183			return SND_SOC_TRIGGER_ORDER_LDC;1184 1185		default:1186			dev_err(dev, "unsupported trigger order [0x%x]\n", order);1187		}1188	}1189 1190	/* SND_SOC_TRIGGER_ORDER_MAX means error */1191	return SND_SOC_TRIGGER_ORDER_MAX;1192}1193 1194void graph_util_parse_trigger_order(struct simple_util_priv *priv,1195				    struct device_node *np,1196				    enum snd_soc_trigger_order *trigger_start,1197				    enum snd_soc_trigger_order *trigger_stop)1198{1199	static enum snd_soc_trigger_order order;1200 1201	/*1202	 * We can use it like below1203	 *1204	 * #include <dt-bindings/sound/audio-graph.h>1205	 *1206	 * link-trigger-order = <SND_SOC_TRIGGER_LINK1207	 *			 SND_SOC_TRIGGER_COMPONENT1208	 *			 SND_SOC_TRIGGER_DAI>;1209	 */1210 1211	order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order");1212	if (order < SND_SOC_TRIGGER_ORDER_MAX) {1213		*trigger_start = order;1214		*trigger_stop  = order;1215	}1216 1217	order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-start");1218	if (order < SND_SOC_TRIGGER_ORDER_MAX)1219		*trigger_start = order;1220 1221	order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-stop");1222	if (order < SND_SOC_TRIGGER_ORDER_MAX)1223		*trigger_stop  = order;1224 1225	return;1226}1227EXPORT_SYMBOL_GPL(graph_util_parse_trigger_order);1228 1229/* Module information */1230MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");1231MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");1232MODULE_LICENSE("GPL v2");1233