brintos

brintos / linux-shallow public Read only

0
0
Text · 26.2 KiB · d9d1e02 Raw
936 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com4 *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>5 */6 7#include <linux/clk.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11 12#include <sound/core.h>13#include <sound/pcm.h>14#include <sound/pcm_params.h>15#include <sound/soc.h>16 17#include "davinci-mcasp.h"18 19/*20 * Maximum number of configuration entries for prefixes:21 * CPB: 2 (mcasp10 + codec)22 * IVI: 3 (mcasp0 + 2x codec)23 */24#define J721E_CODEC_CONF_COUNT	525 26enum j721e_audio_domain_id {27	J721E_AUDIO_DOMAIN_CPB = 0,28	J721E_AUDIO_DOMAIN_IVI,29	J721E_AUDIO_DOMAIN_LAST,30};31 32#define J721E_CLK_PARENT_48000	033#define J721E_CLK_PARENT_44100	134 35#define J721E_MAX_CLK_HSDIV	12836#define PCM1368A_MAX_SYSCLK	3686400037 38#define J721E_DAI_FMT		(SND_SOC_DAIFMT_RIGHT_J | \39				 SND_SOC_DAIFMT_NB_NF |   \40				 SND_SOC_DAIFMT_CBS_CFS)41 42enum j721e_board_type {43	J721E_BOARD_CPB = 1,44	J721E_BOARD_CPB_IVI,45};46 47struct j721e_audio_match_data {48	enum j721e_board_type board_type;49	int num_links;50	unsigned int pll_rates[2];51};52 53static unsigned int ratios_for_pcm3168a[] = {54	256,55	512,56	768,57};58 59struct j721e_audio_clocks {60	struct clk *target;61	struct clk *parent[2];62};63 64struct j721e_audio_domain {65	struct j721e_audio_clocks codec;66	struct j721e_audio_clocks mcasp;67	int parent_clk_id;68 69	int active;70	unsigned int active_link;71	unsigned int rate;72};73 74struct j721e_priv {75	struct device *dev;76	struct snd_soc_card card;77	struct snd_soc_dai_link *dai_links;78	struct snd_soc_codec_conf codec_conf[J721E_CODEC_CONF_COUNT];79	struct snd_interval rate_range;80	const struct j721e_audio_match_data *match_data;81	u32 pll_rates[2];82	unsigned int hsdiv_rates[2];83 84	struct j721e_audio_domain audio_domains[J721E_AUDIO_DOMAIN_LAST];85 86	struct mutex mutex;87};88 89static const struct snd_soc_dapm_widget j721e_cpb_dapm_widgets[] = {90	SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL),91	SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL),92	SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL),93	SND_SOC_DAPM_LINE("CPB Line Out", NULL),94	SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL),95	SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL),96	SND_SOC_DAPM_LINE("CPB Line In", NULL),97};98 99static const struct snd_soc_dapm_route j721e_cpb_dapm_routes[] = {100	{"CPB Stereo HP 1", NULL, "codec-1 AOUT1L"},101	{"CPB Stereo HP 1", NULL, "codec-1 AOUT1R"},102	{"CPB Stereo HP 2", NULL, "codec-1 AOUT2L"},103	{"CPB Stereo HP 2", NULL, "codec-1 AOUT2R"},104	{"CPB Stereo HP 3", NULL, "codec-1 AOUT3L"},105	{"CPB Stereo HP 3", NULL, "codec-1 AOUT3R"},106	{"CPB Line Out", NULL, "codec-1 AOUT4L"},107	{"CPB Line Out", NULL, "codec-1 AOUT4R"},108 109	{"codec-1 AIN1L", NULL, "CPB Stereo Mic 1"},110	{"codec-1 AIN1R", NULL, "CPB Stereo Mic 1"},111	{"codec-1 AIN2L", NULL, "CPB Stereo Mic 2"},112	{"codec-1 AIN2R", NULL, "CPB Stereo Mic 2"},113	{"codec-1 AIN3L", NULL, "CPB Line In"},114	{"codec-1 AIN3R", NULL, "CPB Line In"},115};116 117static const struct snd_soc_dapm_widget j721e_ivi_codec_a_dapm_widgets[] = {118	SND_SOC_DAPM_LINE("IVI A Line Out 1", NULL),119	SND_SOC_DAPM_LINE("IVI A Line Out 2", NULL),120	SND_SOC_DAPM_LINE("IVI A Line Out 3", NULL),121	SND_SOC_DAPM_LINE("IVI A Line Out 4", NULL),122	SND_SOC_DAPM_MIC("IVI A Stereo Mic 1", NULL),123	SND_SOC_DAPM_MIC("IVI A Stereo Mic 2", NULL),124	SND_SOC_DAPM_LINE("IVI A Line In", NULL),125};126 127static const struct snd_soc_dapm_route j721e_codec_a_dapm_routes[] = {128	{"IVI A Line Out 1", NULL, "codec-a AOUT1L"},129	{"IVI A Line Out 1", NULL, "codec-a AOUT1R"},130	{"IVI A Line Out 2", NULL, "codec-a AOUT2L"},131	{"IVI A Line Out 2", NULL, "codec-a AOUT2R"},132	{"IVI A Line Out 3", NULL, "codec-a AOUT3L"},133	{"IVI A Line Out 3", NULL, "codec-a AOUT3R"},134	{"IVI A Line Out 4", NULL, "codec-a AOUT4L"},135	{"IVI A Line Out 4", NULL, "codec-a AOUT4R"},136 137	{"codec-a AIN1L", NULL, "IVI A Stereo Mic 1"},138	{"codec-a AIN1R", NULL, "IVI A Stereo Mic 1"},139	{"codec-a AIN2L", NULL, "IVI A Stereo Mic 2"},140	{"codec-a AIN2R", NULL, "IVI A Stereo Mic 2"},141	{"codec-a AIN3L", NULL, "IVI A Line In"},142	{"codec-a AIN3R", NULL, "IVI A Line In"},143};144 145static const struct snd_soc_dapm_widget j721e_ivi_codec_b_dapm_widgets[] = {146	SND_SOC_DAPM_LINE("IVI B Line Out 1", NULL),147	SND_SOC_DAPM_LINE("IVI B Line Out 2", NULL),148	SND_SOC_DAPM_LINE("IVI B Line Out 3", NULL),149	SND_SOC_DAPM_LINE("IVI B Line Out 4", NULL),150	SND_SOC_DAPM_MIC("IVI B Stereo Mic 1", NULL),151	SND_SOC_DAPM_MIC("IVI B Stereo Mic 2", NULL),152	SND_SOC_DAPM_LINE("IVI B Line In", NULL),153};154 155static const struct snd_soc_dapm_route j721e_codec_b_dapm_routes[] = {156	{"IVI B Line Out 1", NULL, "codec-b AOUT1L"},157	{"IVI B Line Out 1", NULL, "codec-b AOUT1R"},158	{"IVI B Line Out 2", NULL, "codec-b AOUT2L"},159	{"IVI B Line Out 2", NULL, "codec-b AOUT2R"},160	{"IVI B Line Out 3", NULL, "codec-b AOUT3L"},161	{"IVI B Line Out 3", NULL, "codec-b AOUT3R"},162	{"IVI B Line Out 4", NULL, "codec-b AOUT4L"},163	{"IVI B Line Out 4", NULL, "codec-b AOUT4R"},164 165	{"codec-b AIN1L", NULL, "IVI B Stereo Mic 1"},166	{"codec-b AIN1R", NULL, "IVI B Stereo Mic 1"},167	{"codec-b AIN2L", NULL, "IVI B Stereo Mic 2"},168	{"codec-b AIN2R", NULL, "IVI B Stereo Mic 2"},169	{"codec-b AIN3L", NULL, "IVI B Line In"},170	{"codec-b AIN3R", NULL, "IVI B Line In"},171};172 173static int j721e_configure_refclk(struct j721e_priv *priv,174				  unsigned int audio_domain, unsigned int rate)175{176	struct j721e_audio_domain *domain = &priv->audio_domains[audio_domain];177	unsigned int scki;178	int ret = -EINVAL;179	int i, clk_id;180 181	if (!(rate % 8000) && priv->pll_rates[J721E_CLK_PARENT_48000])182		clk_id = J721E_CLK_PARENT_48000;183	else if (!(rate % 11025) && priv->pll_rates[J721E_CLK_PARENT_44100])184		clk_id = J721E_CLK_PARENT_44100;185	else186		return ret;187 188	for (i = 0; i < ARRAY_SIZE(ratios_for_pcm3168a); i++) {189		scki = ratios_for_pcm3168a[i] * rate;190 191		if (priv->pll_rates[clk_id] / scki <= J721E_MAX_CLK_HSDIV) {192			ret = 0;193			break;194		}195	}196 197	if (ret) {198		dev_err(priv->dev, "No valid clock configuration for %u Hz\n",199			rate);200		return ret;201	}202 203	if (domain->parent_clk_id == -1 || priv->hsdiv_rates[domain->parent_clk_id] != scki) {204		dev_dbg(priv->dev,205			"domain%u configuration for %u Hz: %s, %dxFS (SCKI: %u Hz)\n",206			audio_domain, rate,207			clk_id == J721E_CLK_PARENT_48000 ? "PLL4" : "PLL15",208			ratios_for_pcm3168a[i], scki);209 210		if (domain->parent_clk_id != clk_id) {211			ret = clk_set_parent(domain->codec.target,212					     domain->codec.parent[clk_id]);213			if (ret)214				return ret;215 216			ret = clk_set_parent(domain->mcasp.target,217					     domain->mcasp.parent[clk_id]);218			if (ret)219				return ret;220 221			domain->parent_clk_id = clk_id;222		}223 224		ret = clk_set_rate(domain->codec.target, scki);225		if (ret) {226			dev_err(priv->dev, "codec set rate failed for %u Hz\n",227				scki);228			return ret;229		}230 231		ret = clk_set_rate(domain->mcasp.target, scki);232		if (!ret) {233			priv->hsdiv_rates[domain->parent_clk_id] = scki;234		} else {235			dev_err(priv->dev, "mcasp set rate failed for %u Hz\n",236				scki);237			return ret;238		}239	}240 241	return ret;242}243 244static int j721e_rule_rate(struct snd_pcm_hw_params *params,245			   struct snd_pcm_hw_rule *rule)246{247	struct snd_interval *t = rule->private;248 249	return snd_interval_refine(hw_param_interval(params, rule->var), t);250}251 252static int j721e_audio_startup(struct snd_pcm_substream *substream)253{254	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);255	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);256	unsigned int domain_id = rtd->dai_link->id;257	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];258	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);259	struct snd_soc_dai *codec_dai;260	unsigned int active_rate;261	int ret = 0;262	int i;263 264	mutex_lock(&priv->mutex);265 266	domain->active++;267 268	for (i = 0; i < J721E_AUDIO_DOMAIN_LAST; i++) {269		active_rate = priv->audio_domains[i].rate;270		if (active_rate)271			break;272	}273 274	if (active_rate)275		ret = snd_pcm_hw_constraint_single(substream->runtime,276						   SNDRV_PCM_HW_PARAM_RATE,277						   active_rate);278	else279		ret = snd_pcm_hw_rule_add(substream->runtime, 0,280					  SNDRV_PCM_HW_PARAM_RATE,281					  j721e_rule_rate, &priv->rate_range,282					  SNDRV_PCM_HW_PARAM_RATE, -1);283 284 285	if (ret)286		goto out;287 288	/* Reset TDM slots to 32 */289	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);290	if (ret && ret != -ENOTSUPP)291		goto out;292 293	for_each_rtd_codec_dais(rtd, i, codec_dai) {294		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32);295		if (ret && ret != -ENOTSUPP)296			goto out;297	}298 299	if (ret == -ENOTSUPP)300		ret = 0;301out:302	if (ret)303		domain->active--;304	mutex_unlock(&priv->mutex);305 306	return ret;307}308 309static int j721e_audio_hw_params(struct snd_pcm_substream *substream,310				 struct snd_pcm_hw_params *params)311{312	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);313	struct snd_soc_card *card = rtd->card;314	struct j721e_priv *priv = snd_soc_card_get_drvdata(card);315	unsigned int domain_id = rtd->dai_link->id;316	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];317	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);318	struct snd_soc_dai *codec_dai;319	unsigned int sysclk_rate;320	int slot_width = 32;321	int ret;322	int i;323 324	mutex_lock(&priv->mutex);325 326	if (domain->rate && domain->rate != params_rate(params)) {327		ret = -EINVAL;328		goto out;329	}330 331	if (params_width(params) == 16)332		slot_width = 16;333 334	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, slot_width);335	if (ret && ret != -ENOTSUPP)336		goto out;337 338	for_each_rtd_codec_dais(rtd, i, codec_dai) {339		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2,340					       slot_width);341		if (ret && ret != -ENOTSUPP)342			goto out;343	}344 345	ret = j721e_configure_refclk(priv, domain_id, params_rate(params));346	if (ret)347		goto out;348 349	sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id];350	for_each_rtd_codec_dais(rtd, i, codec_dai) {351		ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate,352					     SND_SOC_CLOCK_IN);353		if (ret && ret != -ENOTSUPP) {354			dev_err(priv->dev,355				"codec set_sysclk failed for %u Hz\n",356				sysclk_rate);357			goto out;358		}359	}360 361	ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK,362				     sysclk_rate, SND_SOC_CLOCK_IN);363 364	if (ret && ret != -ENOTSUPP) {365		dev_err(priv->dev, "mcasp set_sysclk failed for %u Hz\n",366			sysclk_rate);367	} else {368		domain->rate = params_rate(params);369		ret = 0;370	}371 372out:373	mutex_unlock(&priv->mutex);374	return ret;375}376 377static void j721e_audio_shutdown(struct snd_pcm_substream *substream)378{379	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);380	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);381	unsigned int domain_id = rtd->dai_link->id;382	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];383 384	mutex_lock(&priv->mutex);385 386	domain->active--;387	if (!domain->active) {388		domain->rate = 0;389		domain->active_link = 0;390	}391 392	mutex_unlock(&priv->mutex);393}394 395static const struct snd_soc_ops j721e_audio_ops = {396	.startup = j721e_audio_startup,397	.hw_params = j721e_audio_hw_params,398	.shutdown = j721e_audio_shutdown,399};400 401static int j721e_audio_init(struct snd_soc_pcm_runtime *rtd)402{403	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);404	unsigned int domain_id = rtd->dai_link->id;405	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];406	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);407	struct snd_soc_dai *codec_dai;408	unsigned int sysclk_rate;409	int i, ret;410 411	/* Set up initial clock configuration */412	ret = j721e_configure_refclk(priv, domain_id, 48000);413	if (ret)414		return ret;415 416	sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id];417	for_each_rtd_codec_dais(rtd, i, codec_dai) {418		ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate,419					     SND_SOC_CLOCK_IN);420		if (ret && ret != -ENOTSUPP)421			return ret;422	}423 424	ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK,425				     sysclk_rate, SND_SOC_CLOCK_IN);426	if (ret && ret != -ENOTSUPP)427		return ret;428 429	/* Set initial tdm slots */430	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);431	if (ret && ret != -ENOTSUPP)432		return ret;433 434	for_each_rtd_codec_dais(rtd, i, codec_dai) {435		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32);436		if (ret && ret != -ENOTSUPP)437			return ret;438	}439 440	return 0;441}442 443static int j721e_audio_init_ivi(struct snd_soc_pcm_runtime *rtd)444{445	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;446 447	snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_a_dapm_widgets,448				  ARRAY_SIZE(j721e_ivi_codec_a_dapm_widgets));449	snd_soc_dapm_add_routes(dapm, j721e_codec_a_dapm_routes,450				ARRAY_SIZE(j721e_codec_a_dapm_routes));451	snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_b_dapm_widgets,452				  ARRAY_SIZE(j721e_ivi_codec_b_dapm_widgets));453	snd_soc_dapm_add_routes(dapm, j721e_codec_b_dapm_routes,454				ARRAY_SIZE(j721e_codec_b_dapm_routes));455 456	return j721e_audio_init(rtd);457}458 459static int j721e_get_clocks(struct device *dev,460			    struct j721e_audio_clocks *clocks, char *prefix)461{462	struct clk *parent;463	char *clk_name;464	int ret;465 466	clocks->target = devm_clk_get(dev, prefix);467	if (IS_ERR(clocks->target))468		return dev_err_probe(dev, PTR_ERR(clocks->target),469				     "failed to acquire %s\n", prefix);470 471	clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix);472	if (clk_name) {473		parent = devm_clk_get(dev, clk_name);474		kfree(clk_name);475		if (IS_ERR(parent)) {476			ret = PTR_ERR(parent);477			if (ret == -EPROBE_DEFER)478				return ret;479 480			dev_dbg(dev, "no 48KHz parent for %s: %d\n", prefix, ret);481			parent = NULL;482		}483		clocks->parent[J721E_CLK_PARENT_48000] = parent;484	} else {485		return -ENOMEM;486	}487 488	clk_name = kasprintf(GFP_KERNEL, "%s-44100", prefix);489	if (clk_name) {490		parent = devm_clk_get(dev, clk_name);491		kfree(clk_name);492		if (IS_ERR(parent)) {493			ret = PTR_ERR(parent);494			if (ret == -EPROBE_DEFER)495				return ret;496 497			dev_dbg(dev, "no 44.1KHz parent for %s: %d\n", prefix, ret);498			parent = NULL;499		}500		clocks->parent[J721E_CLK_PARENT_44100] = parent;501	} else {502		return -ENOMEM;503	}504 505	if (!clocks->parent[J721E_CLK_PARENT_44100] &&506	    !clocks->parent[J721E_CLK_PARENT_48000]) {507		dev_err(dev, "At least one parent clock is needed for %s\n",508			prefix);509		return -EINVAL;510	}511 512	return 0;513}514 515static const struct j721e_audio_match_data j721e_cpb_data = {516	.board_type = J721E_BOARD_CPB,517	.num_links = 2, /* CPB pcm3168a */518	.pll_rates = {519		[J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */520		[J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */521	},522};523 524static const struct j721e_audio_match_data j721e_cpb_ivi_data = {525	.board_type = J721E_BOARD_CPB_IVI,526	.num_links = 4, /* CPB pcm3168a + 2x pcm3168a on IVI */527	.pll_rates = {528		[J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */529		[J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */530	},531};532 533static const struct j721e_audio_match_data j7200_cpb_data = {534	.board_type = J721E_BOARD_CPB,535	.num_links = 2, /* CPB pcm3168a */536	.pll_rates = {537		[J721E_CLK_PARENT_48000] = 2359296000u, /* PLL4 */538	},539};540 541static const struct of_device_id j721e_audio_of_match[] = {542	{543		.compatible = "ti,j721e-cpb-audio",544		.data = &j721e_cpb_data,545	}, {546		.compatible = "ti,j721e-cpb-ivi-audio",547		.data = &j721e_cpb_ivi_data,548	}, {549		.compatible = "ti,j7200-cpb-audio",550		.data = &j7200_cpb_data,551	},552	{ },553};554MODULE_DEVICE_TABLE(of, j721e_audio_of_match);555 556static int j721e_calculate_rate_range(struct j721e_priv *priv)557{558	const struct j721e_audio_match_data *match_data = priv->match_data;559	struct j721e_audio_clocks *domain_clocks;560	unsigned int min_rate, max_rate, pll_rate;561	struct clk *pll;562 563	domain_clocks = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].mcasp;564 565	pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_44100]);566	if (IS_ERR_OR_NULL(pll)) {567		priv->pll_rates[J721E_CLK_PARENT_44100] =568				match_data->pll_rates[J721E_CLK_PARENT_44100];569	} else {570		priv->pll_rates[J721E_CLK_PARENT_44100] = clk_get_rate(pll);571		clk_put(pll);572	}573 574	pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_48000]);575	if (IS_ERR_OR_NULL(pll)) {576		priv->pll_rates[J721E_CLK_PARENT_48000] =577				match_data->pll_rates[J721E_CLK_PARENT_48000];578	} else {579		priv->pll_rates[J721E_CLK_PARENT_48000] = clk_get_rate(pll);580		clk_put(pll);581	}582 583	if (!priv->pll_rates[J721E_CLK_PARENT_44100] &&584	    !priv->pll_rates[J721E_CLK_PARENT_48000]) {585		dev_err(priv->dev, "At least one PLL is needed\n");586		return -EINVAL;587	}588 589	if (priv->pll_rates[J721E_CLK_PARENT_44100])590		pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100];591	else592		pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000];593 594	min_rate = pll_rate / J721E_MAX_CLK_HSDIV;595	min_rate /= ratios_for_pcm3168a[ARRAY_SIZE(ratios_for_pcm3168a) - 1];596 597	if (priv->pll_rates[J721E_CLK_PARENT_48000])598		pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000];599	else600		pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100];601 602	if (pll_rate > PCM1368A_MAX_SYSCLK)603		pll_rate = PCM1368A_MAX_SYSCLK;604 605	max_rate = pll_rate / ratios_for_pcm3168a[0];606 607	snd_interval_any(&priv->rate_range);608	priv->rate_range.min = min_rate;609	priv->rate_range.max = max_rate;610 611	return 0;612}613 614static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,615			       int *conf_idx)616{617	struct device_node *node = priv->dev->of_node;618	struct snd_soc_dai_link_component *compnent;619	struct device_node *dai_node, *codec_node;620	struct j721e_audio_domain *domain;621	int comp_count, comp_idx;622	int ret;623 624	dai_node = of_parse_phandle(node, "ti,cpb-mcasp", 0);625	if (!dai_node) {626		dev_err(priv->dev, "CPB McASP node is not provided\n");627		return -EINVAL;628	}629 630	codec_node = of_parse_phandle(node, "ti,cpb-codec", 0);631	if (!codec_node) {632		dev_err(priv->dev, "CPB codec node is not provided\n");633		ret = -EINVAL;634		goto put_dai_node;635	}636 637	domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB];638	ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki");639	if (ret)640		goto put_codec_node;641 642	ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk");643	if (ret)644		goto put_codec_node;645 646	/*647	 * Common Processor Board, two links648	 * Link 1: McASP10 -> pcm3168a_1 DAC649	 * Link 2: McASP10 <- pcm3168a_1 ADC650	 */651	comp_count = 6;652	compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent),653				GFP_KERNEL);654	if (!compnent) {655		ret = -ENOMEM;656		goto put_codec_node;657	}658 659	comp_idx = 0;660	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];661	priv->dai_links[*link_idx].num_cpus = 1;662	priv->dai_links[*link_idx].codecs = &compnent[comp_idx++];663	priv->dai_links[*link_idx].num_codecs = 1;664	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];665	priv->dai_links[*link_idx].num_platforms = 1;666 667	priv->dai_links[*link_idx].name = "CPB PCM3168A Playback";668	priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog";669	priv->dai_links[*link_idx].cpus->of_node = dai_node;670	priv->dai_links[*link_idx].platforms->of_node = dai_node;671	priv->dai_links[*link_idx].codecs->of_node = codec_node;672	priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-dac";673	priv->dai_links[*link_idx].playback_only = 1;674	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB;675	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;676	priv->dai_links[*link_idx].init = j721e_audio_init;677	priv->dai_links[*link_idx].ops = &j721e_audio_ops;678	(*link_idx)++;679 680	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];681	priv->dai_links[*link_idx].num_cpus = 1;682	priv->dai_links[*link_idx].codecs = &compnent[comp_idx++];683	priv->dai_links[*link_idx].num_codecs = 1;684	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];685	priv->dai_links[*link_idx].num_platforms = 1;686 687	priv->dai_links[*link_idx].name = "CPB PCM3168A Capture";688	priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog";689	priv->dai_links[*link_idx].cpus->of_node = dai_node;690	priv->dai_links[*link_idx].platforms->of_node = dai_node;691	priv->dai_links[*link_idx].codecs->of_node = codec_node;692	priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-adc";693	priv->dai_links[*link_idx].capture_only = 1;694	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB;695	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;696	priv->dai_links[*link_idx].init = j721e_audio_init;697	priv->dai_links[*link_idx].ops = &j721e_audio_ops;698	(*link_idx)++;699 700	priv->codec_conf[*conf_idx].dlc.of_node = codec_node;701	priv->codec_conf[*conf_idx].name_prefix = "codec-1";702	(*conf_idx)++;703	priv->codec_conf[*conf_idx].dlc.of_node = dai_node;704	priv->codec_conf[*conf_idx].name_prefix = "McASP10";705	(*conf_idx)++;706 707	return 0;708 709put_codec_node:710	of_node_put(codec_node);711put_dai_node:712	of_node_put(dai_node);713	return ret;714}715 716static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,717			       int *conf_idx)718{719	struct device_node *node = priv->dev->of_node;720	struct snd_soc_dai_link_component *compnent;721	struct device_node *dai_node, *codeca_node, *codecb_node;722	struct j721e_audio_domain *domain;723	int comp_count, comp_idx;724	int ret;725 726	if (priv->match_data->board_type != J721E_BOARD_CPB_IVI)727		return 0;728 729	dai_node = of_parse_phandle(node, "ti,ivi-mcasp", 0);730	if (!dai_node) {731		dev_err(priv->dev, "IVI McASP node is not provided\n");732		return -EINVAL;733	}734 735	codeca_node = of_parse_phandle(node, "ti,ivi-codec-a", 0);736	if (!codeca_node) {737		dev_err(priv->dev, "IVI codec-a node is not provided\n");738		ret = -EINVAL;739		goto put_dai_node;740	}741 742	codecb_node = of_parse_phandle(node, "ti,ivi-codec-b", 0);743	if (!codecb_node) {744		dev_warn(priv->dev, "IVI codec-b node is not provided\n");745		ret = 0;746		goto put_codeca_node;747	}748 749	domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_IVI];750	ret = j721e_get_clocks(priv->dev, &domain->codec, "ivi-codec-scki");751	if (ret)752		goto put_codecb_node;753 754	ret = j721e_get_clocks(priv->dev, &domain->mcasp, "ivi-mcasp-auxclk");755	if (ret)756		goto put_codecb_node;757 758	/*759	 * IVI extension, two links760	 * Link 1: McASP0 -> pcm3168a_a DAC761	 *		  \> pcm3168a_b DAC762	 * Link 2: McASP0 <- pcm3168a_a ADC763	 *		   \ pcm3168a_b ADC764	 */765	comp_count = 8;766	compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent),767				GFP_KERNEL);768	if (!compnent) {769		ret = -ENOMEM;770		goto put_codecb_node;771	}772 773	comp_idx = 0;774	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];775	priv->dai_links[*link_idx].num_cpus = 1;776	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];777	priv->dai_links[*link_idx].num_platforms = 1;778	priv->dai_links[*link_idx].codecs = &compnent[comp_idx];779	priv->dai_links[*link_idx].num_codecs = 2;780	comp_idx += 2;781 782	priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Playback";783	priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog";784	priv->dai_links[*link_idx].cpus->of_node = dai_node;785	priv->dai_links[*link_idx].platforms->of_node = dai_node;786	priv->dai_links[*link_idx].codecs[0].of_node = codeca_node;787	priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-dac";788	priv->dai_links[*link_idx].codecs[1].of_node = codecb_node;789	priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-dac";790	priv->dai_links[*link_idx].playback_only = 1;791	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI;792	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;793	priv->dai_links[*link_idx].init = j721e_audio_init_ivi;794	priv->dai_links[*link_idx].ops = &j721e_audio_ops;795	(*link_idx)++;796 797	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];798	priv->dai_links[*link_idx].num_cpus = 1;799	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];800	priv->dai_links[*link_idx].num_platforms = 1;801	priv->dai_links[*link_idx].codecs = &compnent[comp_idx];802	priv->dai_links[*link_idx].num_codecs = 2;803 804	priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Capture";805	priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog";806	priv->dai_links[*link_idx].cpus->of_node = dai_node;807	priv->dai_links[*link_idx].platforms->of_node = dai_node;808	priv->dai_links[*link_idx].codecs[0].of_node = codeca_node;809	priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-adc";810	priv->dai_links[*link_idx].codecs[1].of_node = codecb_node;811	priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-adc";812	priv->dai_links[*link_idx].capture_only = 1;813	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI;814	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;815	priv->dai_links[*link_idx].init = j721e_audio_init;816	priv->dai_links[*link_idx].ops = &j721e_audio_ops;817	(*link_idx)++;818 819	priv->codec_conf[*conf_idx].dlc.of_node = codeca_node;820	priv->codec_conf[*conf_idx].name_prefix = "codec-a";821	(*conf_idx)++;822 823	priv->codec_conf[*conf_idx].dlc.of_node = codecb_node;824	priv->codec_conf[*conf_idx].name_prefix = "codec-b";825	(*conf_idx)++;826 827	priv->codec_conf[*conf_idx].dlc.of_node = dai_node;828	priv->codec_conf[*conf_idx].name_prefix = "McASP0";829	(*conf_idx)++;830 831	return 0;832 833 834put_codecb_node:835	of_node_put(codecb_node);836put_codeca_node:837	of_node_put(codeca_node);838put_dai_node:839	of_node_put(dai_node);840	return ret;841}842 843static int j721e_soc_probe(struct platform_device *pdev)844{845	struct device_node *node = pdev->dev.of_node;846	struct snd_soc_card *card;847	const struct of_device_id *match;848	struct j721e_priv *priv;849	int link_cnt, conf_cnt, ret, i;850 851	if (!node) {852		dev_err(&pdev->dev, "of node is missing.\n");853		return -ENODEV;854	}855 856	match = of_match_node(j721e_audio_of_match, node);857	if (!match) {858		dev_err(&pdev->dev, "No compatible match found\n");859		return -ENODEV;860	}861 862	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);863	if (!priv)864		return -ENOMEM;865 866	priv->match_data = match->data;867 868	priv->dai_links = devm_kcalloc(&pdev->dev, priv->match_data->num_links,869				       sizeof(*priv->dai_links), GFP_KERNEL);870	if (!priv->dai_links)871		return -ENOMEM;872 873	for (i = 0; i < J721E_AUDIO_DOMAIN_LAST; i++)874		priv->audio_domains[i].parent_clk_id = -1;875 876	priv->dev = &pdev->dev;877	card = &priv->card;878	card->dev = &pdev->dev;879	card->owner = THIS_MODULE;880	card->dapm_widgets = j721e_cpb_dapm_widgets;881	card->num_dapm_widgets = ARRAY_SIZE(j721e_cpb_dapm_widgets);882	card->dapm_routes = j721e_cpb_dapm_routes;883	card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes);884	card->fully_routed = 1;885 886	if (snd_soc_of_parse_card_name(card, "model")) {887		dev_err(&pdev->dev, "Card name is not provided\n");888		return -ENODEV;889	}890 891	link_cnt = 0;892	conf_cnt = 0;893	ret = j721e_soc_probe_cpb(priv, &link_cnt, &conf_cnt);894	if (ret)895		return ret;896 897	ret = j721e_soc_probe_ivi(priv, &link_cnt, &conf_cnt);898	if (ret)899		return ret;900 901	card->dai_link = priv->dai_links;902	card->num_links = link_cnt;903 904	card->codec_conf = priv->codec_conf;905	card->num_configs = conf_cnt;906 907	ret = j721e_calculate_rate_range(priv);908	if (ret)909		return ret;910 911	snd_soc_card_set_drvdata(card, priv);912 913	mutex_init(&priv->mutex);914	ret = devm_snd_soc_register_card(&pdev->dev, card);915	if (ret)916		dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",917			ret);918 919	return ret;920}921 922static struct platform_driver j721e_soc_driver = {923	.driver = {924		.name = "j721e-audio",925		.pm = &snd_soc_pm_ops,926		.of_match_table = j721e_audio_of_match,927	},928	.probe = j721e_soc_probe,929};930 931module_platform_driver(j721e_soc_driver);932 933MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");934MODULE_DESCRIPTION("ASoC machine driver for j721e Common Processor Board");935MODULE_LICENSE("GPL v2");936