xref: /linux/sound/soc/ti/j721e-evm.c (revision 59b44649a8f2380dfbf282db9922ef863c8812a6)
16748d055SPeter Ujfalusi // SPDX-License-Identifier: GPL-2.0
26748d055SPeter Ujfalusi /*
36748d055SPeter Ujfalusi  *  Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
46748d055SPeter Ujfalusi  *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
56748d055SPeter Ujfalusi  */
66748d055SPeter Ujfalusi 
76748d055SPeter Ujfalusi #include <linux/clk.h>
86748d055SPeter Ujfalusi #include <linux/module.h>
96748d055SPeter Ujfalusi #include <linux/of.h>
106748d055SPeter Ujfalusi #include <linux/platform_device.h>
116748d055SPeter Ujfalusi 
126748d055SPeter Ujfalusi #include <sound/core.h>
136748d055SPeter Ujfalusi #include <sound/pcm.h>
146748d055SPeter Ujfalusi #include <sound/pcm_params.h>
156748d055SPeter Ujfalusi #include <sound/soc.h>
166748d055SPeter Ujfalusi 
176748d055SPeter Ujfalusi #include "davinci-mcasp.h"
186748d055SPeter Ujfalusi 
196748d055SPeter Ujfalusi /*
206748d055SPeter Ujfalusi  * Maximum number of configuration entries for prefixes:
216748d055SPeter Ujfalusi  * CPB: 2 (mcasp10 + codec)
226748d055SPeter Ujfalusi  * IVI: 3 (mcasp0 + 2x codec)
236748d055SPeter Ujfalusi  */
246748d055SPeter Ujfalusi #define J721E_CODEC_CONF_COUNT	5
256748d055SPeter Ujfalusi 
266748d055SPeter Ujfalusi #define J721E_AUDIO_DOMAIN_CPB	0
276748d055SPeter Ujfalusi #define J721E_AUDIO_DOMAIN_IVI	1
286748d055SPeter Ujfalusi 
296748d055SPeter Ujfalusi #define J721E_CLK_PARENT_48000	0
306748d055SPeter Ujfalusi #define J721E_CLK_PARENT_44100	1
316748d055SPeter Ujfalusi 
326748d055SPeter Ujfalusi #define J721E_MAX_CLK_HSDIV	128
336748d055SPeter Ujfalusi #define PCM1368A_MAX_SYSCLK	36864000
346748d055SPeter Ujfalusi 
356748d055SPeter Ujfalusi #define J721E_DAI_FMT		(SND_SOC_DAIFMT_RIGHT_J | \
366748d055SPeter Ujfalusi 				 SND_SOC_DAIFMT_NB_NF |   \
376748d055SPeter Ujfalusi 				 SND_SOC_DAIFMT_CBS_CFS)
386748d055SPeter Ujfalusi 
396748d055SPeter Ujfalusi enum j721e_board_type {
406748d055SPeter Ujfalusi 	J721E_BOARD_CPB = 1,
416748d055SPeter Ujfalusi 	J721E_BOARD_CPB_IVI,
426748d055SPeter Ujfalusi };
436748d055SPeter Ujfalusi 
446748d055SPeter Ujfalusi struct j721e_audio_match_data {
456748d055SPeter Ujfalusi 	enum j721e_board_type board_type;
466748d055SPeter Ujfalusi 	int num_links;
476748d055SPeter Ujfalusi 	unsigned int pll_rates[2];
486748d055SPeter Ujfalusi };
496748d055SPeter Ujfalusi 
506748d055SPeter Ujfalusi static unsigned int ratios_for_pcm3168a[] = {
516748d055SPeter Ujfalusi 	256,
526748d055SPeter Ujfalusi 	512,
536748d055SPeter Ujfalusi 	768,
546748d055SPeter Ujfalusi };
556748d055SPeter Ujfalusi 
566748d055SPeter Ujfalusi struct j721e_audio_clocks {
576748d055SPeter Ujfalusi 	struct clk *target;
586748d055SPeter Ujfalusi 	struct clk *parent[2];
596748d055SPeter Ujfalusi };
606748d055SPeter Ujfalusi 
616748d055SPeter Ujfalusi struct j721e_audio_domain {
626748d055SPeter Ujfalusi 	struct j721e_audio_clocks codec;
636748d055SPeter Ujfalusi 	struct j721e_audio_clocks mcasp;
646748d055SPeter Ujfalusi 	int parent_clk_id;
656748d055SPeter Ujfalusi 
666748d055SPeter Ujfalusi 	int active;
676748d055SPeter Ujfalusi 	unsigned int active_link;
686748d055SPeter Ujfalusi 	unsigned int rate;
696748d055SPeter Ujfalusi };
706748d055SPeter Ujfalusi 
716748d055SPeter Ujfalusi struct j721e_priv {
726748d055SPeter Ujfalusi 	struct device *dev;
736748d055SPeter Ujfalusi 	struct snd_soc_card card;
746748d055SPeter Ujfalusi 	struct snd_soc_dai_link *dai_links;
756748d055SPeter Ujfalusi 	struct snd_soc_codec_conf codec_conf[J721E_CODEC_CONF_COUNT];
766748d055SPeter Ujfalusi 	struct snd_interval rate_range;
776748d055SPeter Ujfalusi 	const struct j721e_audio_match_data *match_data;
786748d055SPeter Ujfalusi 	u32 pll_rates[2];
796748d055SPeter Ujfalusi 	unsigned int hsdiv_rates[2];
806748d055SPeter Ujfalusi 
816748d055SPeter Ujfalusi 	struct j721e_audio_domain audio_domains[2];
826748d055SPeter Ujfalusi 
836748d055SPeter Ujfalusi 	struct mutex mutex;
846748d055SPeter Ujfalusi };
856748d055SPeter Ujfalusi 
866748d055SPeter Ujfalusi static const struct snd_soc_dapm_widget j721e_cpb_dapm_widgets[] = {
876748d055SPeter Ujfalusi 	SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL),
886748d055SPeter Ujfalusi 	SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL),
896748d055SPeter Ujfalusi 	SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL),
906748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("CPB Line Out", NULL),
916748d055SPeter Ujfalusi 	SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL),
926748d055SPeter Ujfalusi 	SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL),
936748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("CPB Line In", NULL),
946748d055SPeter Ujfalusi };
956748d055SPeter Ujfalusi 
966748d055SPeter Ujfalusi static const struct snd_soc_dapm_route j721e_cpb_dapm_routes[] = {
976748d055SPeter Ujfalusi 	{"CPB Stereo HP 1", NULL, "codec-1 AOUT1L"},
986748d055SPeter Ujfalusi 	{"CPB Stereo HP 1", NULL, "codec-1 AOUT1R"},
996748d055SPeter Ujfalusi 	{"CPB Stereo HP 2", NULL, "codec-1 AOUT2L"},
1006748d055SPeter Ujfalusi 	{"CPB Stereo HP 2", NULL, "codec-1 AOUT2R"},
1016748d055SPeter Ujfalusi 	{"CPB Stereo HP 3", NULL, "codec-1 AOUT3L"},
1026748d055SPeter Ujfalusi 	{"CPB Stereo HP 3", NULL, "codec-1 AOUT3R"},
1036748d055SPeter Ujfalusi 	{"CPB Line Out", NULL, "codec-1 AOUT4L"},
1046748d055SPeter Ujfalusi 	{"CPB Line Out", NULL, "codec-1 AOUT4R"},
1056748d055SPeter Ujfalusi 
1066748d055SPeter Ujfalusi 	{"codec-1 AIN1L", NULL, "CPB Stereo Mic 1"},
1076748d055SPeter Ujfalusi 	{"codec-1 AIN1R", NULL, "CPB Stereo Mic 1"},
1086748d055SPeter Ujfalusi 	{"codec-1 AIN2L", NULL, "CPB Stereo Mic 2"},
1096748d055SPeter Ujfalusi 	{"codec-1 AIN2R", NULL, "CPB Stereo Mic 2"},
1106748d055SPeter Ujfalusi 	{"codec-1 AIN3L", NULL, "CPB Line In"},
1116748d055SPeter Ujfalusi 	{"codec-1 AIN3R", NULL, "CPB Line In"},
1126748d055SPeter Ujfalusi };
1136748d055SPeter Ujfalusi 
1146748d055SPeter Ujfalusi static const struct snd_soc_dapm_widget j721e_ivi_codec_a_dapm_widgets[] = {
1156748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI A Line Out 1", NULL),
1166748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI A Line Out 2", NULL),
1176748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI A Line Out 3", NULL),
1186748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI A Line Out 4", NULL),
1196748d055SPeter Ujfalusi 	SND_SOC_DAPM_MIC("IVI A Stereo Mic 1", NULL),
1206748d055SPeter Ujfalusi 	SND_SOC_DAPM_MIC("IVI A Stereo Mic 2", NULL),
1216748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI A Line In", NULL),
1226748d055SPeter Ujfalusi };
1236748d055SPeter Ujfalusi 
1246748d055SPeter Ujfalusi static const struct snd_soc_dapm_route j721e_codec_a_dapm_routes[] = {
1256748d055SPeter Ujfalusi 	{"IVI A Line Out 1", NULL, "codec-a AOUT1L"},
1266748d055SPeter Ujfalusi 	{"IVI A Line Out 1", NULL, "codec-a AOUT1R"},
1276748d055SPeter Ujfalusi 	{"IVI A Line Out 2", NULL, "codec-a AOUT2L"},
1286748d055SPeter Ujfalusi 	{"IVI A Line Out 2", NULL, "codec-a AOUT2R"},
1296748d055SPeter Ujfalusi 	{"IVI A Line Out 3", NULL, "codec-a AOUT3L"},
1306748d055SPeter Ujfalusi 	{"IVI A Line Out 3", NULL, "codec-a AOUT3R"},
1316748d055SPeter Ujfalusi 	{"IVI A Line Out 4", NULL, "codec-a AOUT4L"},
1326748d055SPeter Ujfalusi 	{"IVI A Line Out 4", NULL, "codec-a AOUT4R"},
1336748d055SPeter Ujfalusi 
1346748d055SPeter Ujfalusi 	{"codec-a AIN1L", NULL, "IVI A Stereo Mic 1"},
1356748d055SPeter Ujfalusi 	{"codec-a AIN1R", NULL, "IVI A Stereo Mic 1"},
1366748d055SPeter Ujfalusi 	{"codec-a AIN2L", NULL, "IVI A Stereo Mic 2"},
1376748d055SPeter Ujfalusi 	{"codec-a AIN2R", NULL, "IVI A Stereo Mic 2"},
1386748d055SPeter Ujfalusi 	{"codec-a AIN3L", NULL, "IVI A Line In"},
1396748d055SPeter Ujfalusi 	{"codec-a AIN3R", NULL, "IVI A Line In"},
1406748d055SPeter Ujfalusi };
1416748d055SPeter Ujfalusi 
1426748d055SPeter Ujfalusi static const struct snd_soc_dapm_widget j721e_ivi_codec_b_dapm_widgets[] = {
1436748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI B Line Out 1", NULL),
1446748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI B Line Out 2", NULL),
1456748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI B Line Out 3", NULL),
1466748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI B Line Out 4", NULL),
1476748d055SPeter Ujfalusi 	SND_SOC_DAPM_MIC("IVI B Stereo Mic 1", NULL),
1486748d055SPeter Ujfalusi 	SND_SOC_DAPM_MIC("IVI B Stereo Mic 2", NULL),
1496748d055SPeter Ujfalusi 	SND_SOC_DAPM_LINE("IVI B Line In", NULL),
1506748d055SPeter Ujfalusi };
1516748d055SPeter Ujfalusi 
1526748d055SPeter Ujfalusi static const struct snd_soc_dapm_route j721e_codec_b_dapm_routes[] = {
1536748d055SPeter Ujfalusi 	{"IVI B Line Out 1", NULL, "codec-b AOUT1L"},
1546748d055SPeter Ujfalusi 	{"IVI B Line Out 1", NULL, "codec-b AOUT1R"},
1556748d055SPeter Ujfalusi 	{"IVI B Line Out 2", NULL, "codec-b AOUT2L"},
1566748d055SPeter Ujfalusi 	{"IVI B Line Out 2", NULL, "codec-b AOUT2R"},
1576748d055SPeter Ujfalusi 	{"IVI B Line Out 3", NULL, "codec-b AOUT3L"},
1586748d055SPeter Ujfalusi 	{"IVI B Line Out 3", NULL, "codec-b AOUT3R"},
1596748d055SPeter Ujfalusi 	{"IVI B Line Out 4", NULL, "codec-b AOUT4L"},
1606748d055SPeter Ujfalusi 	{"IVI B Line Out 4", NULL, "codec-b AOUT4R"},
1616748d055SPeter Ujfalusi 
1626748d055SPeter Ujfalusi 	{"codec-b AIN1L", NULL, "IVI B Stereo Mic 1"},
1636748d055SPeter Ujfalusi 	{"codec-b AIN1R", NULL, "IVI B Stereo Mic 1"},
1646748d055SPeter Ujfalusi 	{"codec-b AIN2L", NULL, "IVI B Stereo Mic 2"},
1656748d055SPeter Ujfalusi 	{"codec-b AIN2R", NULL, "IVI B Stereo Mic 2"},
1666748d055SPeter Ujfalusi 	{"codec-b AIN3L", NULL, "IVI B Line In"},
1676748d055SPeter Ujfalusi 	{"codec-b AIN3R", NULL, "IVI B Line In"},
1686748d055SPeter Ujfalusi };
1696748d055SPeter Ujfalusi 
1706748d055SPeter Ujfalusi static int j721e_configure_refclk(struct j721e_priv *priv,
1716748d055SPeter Ujfalusi 				  unsigned int audio_domain, unsigned int rate)
1726748d055SPeter Ujfalusi {
1736748d055SPeter Ujfalusi 	struct j721e_audio_domain *domain = &priv->audio_domains[audio_domain];
1746748d055SPeter Ujfalusi 	unsigned int scki;
1756748d055SPeter Ujfalusi 	int ret = -EINVAL;
1766748d055SPeter Ujfalusi 	int i, clk_id;
1776748d055SPeter Ujfalusi 
1786748d055SPeter Ujfalusi 	if (!(rate % 8000) && priv->pll_rates[J721E_CLK_PARENT_48000])
1796748d055SPeter Ujfalusi 		clk_id = J721E_CLK_PARENT_48000;
1806748d055SPeter Ujfalusi 	else if (!(rate % 11025) && priv->pll_rates[J721E_CLK_PARENT_44100])
1816748d055SPeter Ujfalusi 		clk_id = J721E_CLK_PARENT_44100;
1826748d055SPeter Ujfalusi 	else
1836748d055SPeter Ujfalusi 		return ret;
1846748d055SPeter Ujfalusi 
1856748d055SPeter Ujfalusi 	for (i = 0; i < ARRAY_SIZE(ratios_for_pcm3168a); i++) {
1866748d055SPeter Ujfalusi 		scki = ratios_for_pcm3168a[i] * rate;
1876748d055SPeter Ujfalusi 
1886748d055SPeter Ujfalusi 		if (priv->pll_rates[clk_id] / scki <= J721E_MAX_CLK_HSDIV) {
1896748d055SPeter Ujfalusi 			ret = 0;
1906748d055SPeter Ujfalusi 			break;
1916748d055SPeter Ujfalusi 		}
1926748d055SPeter Ujfalusi 	}
1936748d055SPeter Ujfalusi 
1946748d055SPeter Ujfalusi 	if (ret) {
1956748d055SPeter Ujfalusi 		dev_err(priv->dev, "No valid clock configuration for %u Hz\n",
1966748d055SPeter Ujfalusi 			rate);
1976748d055SPeter Ujfalusi 		return ret;
1986748d055SPeter Ujfalusi 	}
1996748d055SPeter Ujfalusi 
2006748d055SPeter Ujfalusi 	if (priv->hsdiv_rates[domain->parent_clk_id] != scki) {
2016748d055SPeter Ujfalusi 		dev_dbg(priv->dev,
2026748d055SPeter Ujfalusi 			"%s configuration for %u Hz: %s, %dxFS (SCKI: %u Hz)\n",
2036748d055SPeter Ujfalusi 			audio_domain == J721E_AUDIO_DOMAIN_CPB ? "CPB" : "IVI",
2046748d055SPeter Ujfalusi 			rate,
2056748d055SPeter Ujfalusi 			clk_id == J721E_CLK_PARENT_48000 ? "PLL4" : "PLL15",
2066748d055SPeter Ujfalusi 			ratios_for_pcm3168a[i], scki);
2076748d055SPeter Ujfalusi 
2086748d055SPeter Ujfalusi 		if (domain->parent_clk_id != clk_id) {
2096748d055SPeter Ujfalusi 			ret = clk_set_parent(domain->codec.target,
2106748d055SPeter Ujfalusi 					     domain->codec.parent[clk_id]);
2116748d055SPeter Ujfalusi 			if (ret)
2126748d055SPeter Ujfalusi 				return ret;
2136748d055SPeter Ujfalusi 
2146748d055SPeter Ujfalusi 			ret = clk_set_parent(domain->mcasp.target,
2156748d055SPeter Ujfalusi 					     domain->mcasp.parent[clk_id]);
2166748d055SPeter Ujfalusi 			if (ret)
2176748d055SPeter Ujfalusi 				return ret;
2186748d055SPeter Ujfalusi 
2196748d055SPeter Ujfalusi 			domain->parent_clk_id = clk_id;
2206748d055SPeter Ujfalusi 		}
2216748d055SPeter Ujfalusi 
2226748d055SPeter Ujfalusi 		ret = clk_set_rate(domain->codec.target, scki);
2236748d055SPeter Ujfalusi 		if (ret) {
2246748d055SPeter Ujfalusi 			dev_err(priv->dev, "codec set rate failed for %u Hz\n",
2256748d055SPeter Ujfalusi 				scki);
2266748d055SPeter Ujfalusi 			return ret;
2276748d055SPeter Ujfalusi 		}
2286748d055SPeter Ujfalusi 
2296748d055SPeter Ujfalusi 		ret = clk_set_rate(domain->mcasp.target, scki);
2306748d055SPeter Ujfalusi 		if (!ret) {
2316748d055SPeter Ujfalusi 			priv->hsdiv_rates[domain->parent_clk_id] = scki;
2326748d055SPeter Ujfalusi 		} else {
2336748d055SPeter Ujfalusi 			dev_err(priv->dev, "mcasp set rate failed for %u Hz\n",
2346748d055SPeter Ujfalusi 				scki);
2356748d055SPeter Ujfalusi 			return ret;
2366748d055SPeter Ujfalusi 		}
2376748d055SPeter Ujfalusi 	}
2386748d055SPeter Ujfalusi 
2396748d055SPeter Ujfalusi 	return ret;
2406748d055SPeter Ujfalusi }
2416748d055SPeter Ujfalusi 
2426748d055SPeter Ujfalusi static int j721e_rule_rate(struct snd_pcm_hw_params *params,
2436748d055SPeter Ujfalusi 			   struct snd_pcm_hw_rule *rule)
2446748d055SPeter Ujfalusi {
2456748d055SPeter Ujfalusi 	struct snd_interval *t = rule->private;
2466748d055SPeter Ujfalusi 
2476748d055SPeter Ujfalusi 	return snd_interval_refine(hw_param_interval(params, rule->var), t);
2486748d055SPeter Ujfalusi }
2496748d055SPeter Ujfalusi 
2506748d055SPeter Ujfalusi static int j721e_audio_startup(struct snd_pcm_substream *substream)
2516748d055SPeter Ujfalusi {
2526748d055SPeter Ujfalusi 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2536748d055SPeter Ujfalusi 	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);
2546748d055SPeter Ujfalusi 	unsigned int domain_id = rtd->dai_link->id;
2556748d055SPeter Ujfalusi 	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
2566748d055SPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
2576748d055SPeter Ujfalusi 	struct snd_soc_dai *codec_dai;
2586748d055SPeter Ujfalusi 	unsigned int active_rate;
2596748d055SPeter Ujfalusi 	int ret = 0;
2606748d055SPeter Ujfalusi 	int i;
2616748d055SPeter Ujfalusi 
2626748d055SPeter Ujfalusi 	mutex_lock(&priv->mutex);
2636748d055SPeter Ujfalusi 
2646748d055SPeter Ujfalusi 	domain->active++;
2656748d055SPeter Ujfalusi 
2666748d055SPeter Ujfalusi 	if (priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].rate)
2676748d055SPeter Ujfalusi 		active_rate = priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].rate;
2686748d055SPeter Ujfalusi 	else
2696748d055SPeter Ujfalusi 		active_rate = priv->audio_domains[J721E_AUDIO_DOMAIN_IVI].rate;
2706748d055SPeter Ujfalusi 
2716748d055SPeter Ujfalusi 	if (active_rate)
2726748d055SPeter Ujfalusi 		ret = snd_pcm_hw_constraint_single(substream->runtime,
2736748d055SPeter Ujfalusi 						   SNDRV_PCM_HW_PARAM_RATE,
2746748d055SPeter Ujfalusi 						   active_rate);
2756748d055SPeter Ujfalusi 	else
2766748d055SPeter Ujfalusi 		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
2776748d055SPeter Ujfalusi 					  SNDRV_PCM_HW_PARAM_RATE,
2786748d055SPeter Ujfalusi 					  j721e_rule_rate, &priv->rate_range,
2796748d055SPeter Ujfalusi 					  SNDRV_PCM_HW_PARAM_RATE, -1);
2806748d055SPeter Ujfalusi 
2816748d055SPeter Ujfalusi 	mutex_unlock(&priv->mutex);
2826748d055SPeter Ujfalusi 
2836748d055SPeter Ujfalusi 	if (ret)
2846748d055SPeter Ujfalusi 		return ret;
2856748d055SPeter Ujfalusi 
2866748d055SPeter Ujfalusi 	/* Reset TDM slots to 32 */
2876748d055SPeter Ujfalusi 	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);
2886748d055SPeter Ujfalusi 	if (ret && ret != -ENOTSUPP)
2896748d055SPeter Ujfalusi 		return ret;
2906748d055SPeter Ujfalusi 
2916748d055SPeter Ujfalusi 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
2926748d055SPeter Ujfalusi 		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32);
2936748d055SPeter Ujfalusi 		if (ret && ret != -ENOTSUPP)
2946748d055SPeter Ujfalusi 			return ret;
2956748d055SPeter Ujfalusi 	}
2966748d055SPeter Ujfalusi 
2976748d055SPeter Ujfalusi 	return 0;
2986748d055SPeter Ujfalusi }
2996748d055SPeter Ujfalusi 
3006748d055SPeter Ujfalusi static int j721e_audio_hw_params(struct snd_pcm_substream *substream,
3016748d055SPeter Ujfalusi 				 struct snd_pcm_hw_params *params)
3026748d055SPeter Ujfalusi {
3036748d055SPeter Ujfalusi 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
3046748d055SPeter Ujfalusi 	struct snd_soc_card *card = rtd->card;
3056748d055SPeter Ujfalusi 	struct j721e_priv *priv = snd_soc_card_get_drvdata(card);
3066748d055SPeter Ujfalusi 	unsigned int domain_id = rtd->dai_link->id;
3076748d055SPeter Ujfalusi 	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
3086748d055SPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
3096748d055SPeter Ujfalusi 	struct snd_soc_dai *codec_dai;
3106748d055SPeter Ujfalusi 	unsigned int sysclk_rate;
3116748d055SPeter Ujfalusi 	int slot_width = 32;
3126748d055SPeter Ujfalusi 	int ret;
3136748d055SPeter Ujfalusi 	int i;
3146748d055SPeter Ujfalusi 
3156748d055SPeter Ujfalusi 	mutex_lock(&priv->mutex);
3166748d055SPeter Ujfalusi 
3176748d055SPeter Ujfalusi 	if (domain->rate && domain->rate != params_rate(params)) {
3186748d055SPeter Ujfalusi 		ret = -EINVAL;
3196748d055SPeter Ujfalusi 		goto out;
3206748d055SPeter Ujfalusi 	}
3216748d055SPeter Ujfalusi 
3226748d055SPeter Ujfalusi 	if (params_width(params) == 16)
3236748d055SPeter Ujfalusi 		slot_width = 16;
3246748d055SPeter Ujfalusi 
3256748d055SPeter Ujfalusi 	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, slot_width);
3266748d055SPeter Ujfalusi 	if (ret && ret != -ENOTSUPP)
3276748d055SPeter Ujfalusi 		goto out;
3286748d055SPeter Ujfalusi 
3296748d055SPeter Ujfalusi 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
3306748d055SPeter Ujfalusi 		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2,
3316748d055SPeter Ujfalusi 					       slot_width);
3326748d055SPeter Ujfalusi 		if (ret && ret != -ENOTSUPP)
333*59b44649SWei Yongjun 			goto out;
3346748d055SPeter Ujfalusi 	}
3356748d055SPeter Ujfalusi 
3366748d055SPeter Ujfalusi 	ret = j721e_configure_refclk(priv, domain_id, params_rate(params));
3376748d055SPeter Ujfalusi 	if (ret)
3386748d055SPeter Ujfalusi 		goto out;
3396748d055SPeter Ujfalusi 
3406748d055SPeter Ujfalusi 	sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id];
3416748d055SPeter Ujfalusi 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
3426748d055SPeter Ujfalusi 		ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate,
3436748d055SPeter Ujfalusi 					     SND_SOC_CLOCK_IN);
3446748d055SPeter Ujfalusi 		if (ret && ret != -ENOTSUPP) {
3456748d055SPeter Ujfalusi 			dev_err(priv->dev,
3466748d055SPeter Ujfalusi 				"codec set_sysclk failed for %u Hz\n",
3476748d055SPeter Ujfalusi 				sysclk_rate);
3486748d055SPeter Ujfalusi 			goto out;
3496748d055SPeter Ujfalusi 		}
3506748d055SPeter Ujfalusi 	}
3516748d055SPeter Ujfalusi 
3526748d055SPeter Ujfalusi 	ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK,
3536748d055SPeter Ujfalusi 				     sysclk_rate, SND_SOC_CLOCK_IN);
3546748d055SPeter Ujfalusi 
3556748d055SPeter Ujfalusi 	if (ret && ret != -ENOTSUPP) {
3566748d055SPeter Ujfalusi 		dev_err(priv->dev, "mcasp set_sysclk failed for %u Hz\n",
3576748d055SPeter Ujfalusi 			sysclk_rate);
3586748d055SPeter Ujfalusi 	} else {
3596748d055SPeter Ujfalusi 		domain->rate = params_rate(params);
3606748d055SPeter Ujfalusi 		ret = 0;
3616748d055SPeter Ujfalusi 	}
3626748d055SPeter Ujfalusi 
3636748d055SPeter Ujfalusi out:
3646748d055SPeter Ujfalusi 	mutex_unlock(&priv->mutex);
3656748d055SPeter Ujfalusi 	return ret;
3666748d055SPeter Ujfalusi }
3676748d055SPeter Ujfalusi 
3686748d055SPeter Ujfalusi static void j721e_audio_shutdown(struct snd_pcm_substream *substream)
3696748d055SPeter Ujfalusi {
3706748d055SPeter Ujfalusi 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
3716748d055SPeter Ujfalusi 	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);
3726748d055SPeter Ujfalusi 	unsigned int domain_id = rtd->dai_link->id;
3736748d055SPeter Ujfalusi 	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
3746748d055SPeter Ujfalusi 
3756748d055SPeter Ujfalusi 	mutex_lock(&priv->mutex);
3766748d055SPeter Ujfalusi 
3776748d055SPeter Ujfalusi 	domain->active--;
3786748d055SPeter Ujfalusi 	if (!domain->active) {
3796748d055SPeter Ujfalusi 		domain->rate = 0;
3806748d055SPeter Ujfalusi 		domain->active_link = 0;
3816748d055SPeter Ujfalusi 	}
3826748d055SPeter Ujfalusi 
3836748d055SPeter Ujfalusi 	mutex_unlock(&priv->mutex);
3846748d055SPeter Ujfalusi }
3856748d055SPeter Ujfalusi 
3866748d055SPeter Ujfalusi static const struct snd_soc_ops j721e_audio_ops = {
3876748d055SPeter Ujfalusi 	.startup = j721e_audio_startup,
3886748d055SPeter Ujfalusi 	.hw_params = j721e_audio_hw_params,
3896748d055SPeter Ujfalusi 	.shutdown = j721e_audio_shutdown,
3906748d055SPeter Ujfalusi };
3916748d055SPeter Ujfalusi 
3926748d055SPeter Ujfalusi static int j721e_audio_init(struct snd_soc_pcm_runtime *rtd)
3936748d055SPeter Ujfalusi {
3946748d055SPeter Ujfalusi 	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);
3956748d055SPeter Ujfalusi 	unsigned int domain_id = rtd->dai_link->id;
3966748d055SPeter Ujfalusi 	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
3976748d055SPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
3986748d055SPeter Ujfalusi 	struct snd_soc_dai *codec_dai;
3996748d055SPeter Ujfalusi 	unsigned int sysclk_rate;
4006748d055SPeter Ujfalusi 	int i, ret;
4016748d055SPeter Ujfalusi 
4026748d055SPeter Ujfalusi 	/* Set up initial clock configuration */
4036748d055SPeter Ujfalusi 	ret = j721e_configure_refclk(priv, domain_id, 48000);
4046748d055SPeter Ujfalusi 	if (ret)
4056748d055SPeter Ujfalusi 		return ret;
4066748d055SPeter Ujfalusi 
4076748d055SPeter Ujfalusi 	sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id];
4086748d055SPeter Ujfalusi 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
4096748d055SPeter Ujfalusi 		ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate,
4106748d055SPeter Ujfalusi 					     SND_SOC_CLOCK_IN);
4116748d055SPeter Ujfalusi 		if (ret && ret != -ENOTSUPP)
4126748d055SPeter Ujfalusi 			return ret;
4136748d055SPeter Ujfalusi 	}
4146748d055SPeter Ujfalusi 
4156748d055SPeter Ujfalusi 	ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK,
4166748d055SPeter Ujfalusi 				     sysclk_rate, SND_SOC_CLOCK_IN);
4176748d055SPeter Ujfalusi 	if (ret && ret != -ENOTSUPP)
4186748d055SPeter Ujfalusi 		return ret;
4196748d055SPeter Ujfalusi 
4206748d055SPeter Ujfalusi 	/* Set initial tdm slots */
4216748d055SPeter Ujfalusi 	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);
4226748d055SPeter Ujfalusi 	if (ret && ret != -ENOTSUPP)
4236748d055SPeter Ujfalusi 		return ret;
4246748d055SPeter Ujfalusi 
4256748d055SPeter Ujfalusi 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
4266748d055SPeter Ujfalusi 		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32);
4276748d055SPeter Ujfalusi 		if (ret && ret != -ENOTSUPP)
4286748d055SPeter Ujfalusi 			return ret;
4296748d055SPeter Ujfalusi 	}
4306748d055SPeter Ujfalusi 
4316748d055SPeter Ujfalusi 	return 0;
4326748d055SPeter Ujfalusi }
4336748d055SPeter Ujfalusi 
4346748d055SPeter Ujfalusi static int j721e_audio_init_ivi(struct snd_soc_pcm_runtime *rtd)
4356748d055SPeter Ujfalusi {
4366748d055SPeter Ujfalusi 	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
4376748d055SPeter Ujfalusi 
4386748d055SPeter Ujfalusi 	snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_a_dapm_widgets,
4396748d055SPeter Ujfalusi 				  ARRAY_SIZE(j721e_ivi_codec_a_dapm_widgets));
4406748d055SPeter Ujfalusi 	snd_soc_dapm_add_routes(dapm, j721e_codec_a_dapm_routes,
4416748d055SPeter Ujfalusi 				ARRAY_SIZE(j721e_codec_a_dapm_routes));
4426748d055SPeter Ujfalusi 	snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_b_dapm_widgets,
4436748d055SPeter Ujfalusi 				  ARRAY_SIZE(j721e_ivi_codec_b_dapm_widgets));
4446748d055SPeter Ujfalusi 	snd_soc_dapm_add_routes(dapm, j721e_codec_b_dapm_routes,
4456748d055SPeter Ujfalusi 				ARRAY_SIZE(j721e_codec_b_dapm_routes));
4466748d055SPeter Ujfalusi 
4476748d055SPeter Ujfalusi 	return j721e_audio_init(rtd);
4486748d055SPeter Ujfalusi }
4496748d055SPeter Ujfalusi 
4506748d055SPeter Ujfalusi static int j721e_get_clocks(struct device *dev,
4516748d055SPeter Ujfalusi 			    struct j721e_audio_clocks *clocks, char *prefix)
4526748d055SPeter Ujfalusi {
4536748d055SPeter Ujfalusi 	struct clk *parent;
4546748d055SPeter Ujfalusi 	char *clk_name;
4556748d055SPeter Ujfalusi 	int ret;
4566748d055SPeter Ujfalusi 
4576748d055SPeter Ujfalusi 	clocks->target = devm_clk_get(dev, prefix);
4586748d055SPeter Ujfalusi 	if (IS_ERR(clocks->target)) {
4596748d055SPeter Ujfalusi 		ret = PTR_ERR(clocks->target);
4606748d055SPeter Ujfalusi 		if (ret != -EPROBE_DEFER)
4616748d055SPeter Ujfalusi 			dev_err(dev, "failed to acquire %s: %d\n",
4626748d055SPeter Ujfalusi 				prefix, ret);
4636748d055SPeter Ujfalusi 		return ret;
4646748d055SPeter Ujfalusi 	}
4656748d055SPeter Ujfalusi 
4666748d055SPeter Ujfalusi 	clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix);
4676748d055SPeter Ujfalusi 	if (clk_name) {
4686748d055SPeter Ujfalusi 		parent = devm_clk_get(dev, clk_name);
4696748d055SPeter Ujfalusi 		kfree(clk_name);
4706748d055SPeter Ujfalusi 		if (IS_ERR(parent)) {
4716748d055SPeter Ujfalusi 			ret = PTR_ERR(parent);
4726748d055SPeter Ujfalusi 			if (ret == -EPROBE_DEFER)
4736748d055SPeter Ujfalusi 				return ret;
4746748d055SPeter Ujfalusi 
4756748d055SPeter Ujfalusi 			dev_dbg(dev, "no 48KHz parent for %s: %d\n", prefix, ret);
4766748d055SPeter Ujfalusi 			parent = NULL;
4776748d055SPeter Ujfalusi 		}
4786748d055SPeter Ujfalusi 		clocks->parent[J721E_CLK_PARENT_48000] = parent;
4796748d055SPeter Ujfalusi 	} else {
4806748d055SPeter Ujfalusi 		return -ENOMEM;
4816748d055SPeter Ujfalusi 	}
4826748d055SPeter Ujfalusi 
4836748d055SPeter Ujfalusi 	clk_name = kasprintf(GFP_KERNEL, "%s-44100", prefix);
4846748d055SPeter Ujfalusi 	if (clk_name) {
4856748d055SPeter Ujfalusi 		parent = devm_clk_get(dev, clk_name);
4866748d055SPeter Ujfalusi 		kfree(clk_name);
4876748d055SPeter Ujfalusi 		if (IS_ERR(parent)) {
4886748d055SPeter Ujfalusi 			ret = PTR_ERR(parent);
4896748d055SPeter Ujfalusi 			if (ret == -EPROBE_DEFER)
4906748d055SPeter Ujfalusi 				return ret;
4916748d055SPeter Ujfalusi 
4926748d055SPeter Ujfalusi 			dev_dbg(dev, "no 44.1KHz parent for %s: %d\n", prefix, ret);
4936748d055SPeter Ujfalusi 			parent = NULL;
4946748d055SPeter Ujfalusi 		}
4956748d055SPeter Ujfalusi 		clocks->parent[J721E_CLK_PARENT_44100] = parent;
4966748d055SPeter Ujfalusi 	} else {
4976748d055SPeter Ujfalusi 		return -ENOMEM;
4986748d055SPeter Ujfalusi 	}
4996748d055SPeter Ujfalusi 
5006748d055SPeter Ujfalusi 	if (!clocks->parent[J721E_CLK_PARENT_44100] &&
5016748d055SPeter Ujfalusi 	    !clocks->parent[J721E_CLK_PARENT_48000]) {
5026748d055SPeter Ujfalusi 		dev_err(dev, "At least one parent clock is needed for %s\n",
5036748d055SPeter Ujfalusi 			prefix);
5046748d055SPeter Ujfalusi 		return -EINVAL;
5056748d055SPeter Ujfalusi 	}
5066748d055SPeter Ujfalusi 
5076748d055SPeter Ujfalusi 	return 0;
5086748d055SPeter Ujfalusi }
5096748d055SPeter Ujfalusi 
5106748d055SPeter Ujfalusi static const struct j721e_audio_match_data j721e_cpb_data = {
5116748d055SPeter Ujfalusi 	.board_type = J721E_BOARD_CPB,
5126748d055SPeter Ujfalusi 	.num_links = 2, /* CPB pcm3168a */
5136748d055SPeter Ujfalusi 	.pll_rates = {
5146748d055SPeter Ujfalusi 		[J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */
5156748d055SPeter Ujfalusi 		[J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */
5166748d055SPeter Ujfalusi 	},
5176748d055SPeter Ujfalusi };
5186748d055SPeter Ujfalusi 
5196748d055SPeter Ujfalusi static const struct j721e_audio_match_data j721e_cpb_ivi_data = {
5206748d055SPeter Ujfalusi 	.board_type = J721E_BOARD_CPB_IVI,
5216748d055SPeter Ujfalusi 	.num_links = 4, /* CPB pcm3168a + 2x pcm3168a on IVI */
5226748d055SPeter Ujfalusi 	.pll_rates = {
5236748d055SPeter Ujfalusi 		[J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */
5246748d055SPeter Ujfalusi 		[J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */
5256748d055SPeter Ujfalusi 	},
5266748d055SPeter Ujfalusi };
5276748d055SPeter Ujfalusi 
5286748d055SPeter Ujfalusi static const struct of_device_id j721e_audio_of_match[] = {
5296748d055SPeter Ujfalusi 	{
5306748d055SPeter Ujfalusi 		.compatible = "ti,j721e-cpb-audio",
5316748d055SPeter Ujfalusi 		.data = &j721e_cpb_data,
5326748d055SPeter Ujfalusi 	}, {
5336748d055SPeter Ujfalusi 		.compatible = "ti,j721e-cpb-ivi-audio",
5346748d055SPeter Ujfalusi 		.data = &j721e_cpb_ivi_data,
5356748d055SPeter Ujfalusi 	},
5366748d055SPeter Ujfalusi 	{ },
5376748d055SPeter Ujfalusi };
5386748d055SPeter Ujfalusi MODULE_DEVICE_TABLE(of, j721e_audio_of_match);
5396748d055SPeter Ujfalusi 
5406748d055SPeter Ujfalusi static int j721e_calculate_rate_range(struct j721e_priv *priv)
5416748d055SPeter Ujfalusi {
5426748d055SPeter Ujfalusi 	const struct j721e_audio_match_data *match_data = priv->match_data;
5436748d055SPeter Ujfalusi 	struct j721e_audio_clocks *domain_clocks;
5446748d055SPeter Ujfalusi 	unsigned int min_rate, max_rate, pll_rate;
5456748d055SPeter Ujfalusi 	struct clk *pll;
5466748d055SPeter Ujfalusi 
5476748d055SPeter Ujfalusi 	domain_clocks = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].mcasp;
5486748d055SPeter Ujfalusi 
5496748d055SPeter Ujfalusi 	pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_44100]);
5506748d055SPeter Ujfalusi 	if (IS_ERR_OR_NULL(pll)) {
5516748d055SPeter Ujfalusi 		priv->pll_rates[J721E_CLK_PARENT_44100] =
5526748d055SPeter Ujfalusi 				match_data->pll_rates[J721E_CLK_PARENT_44100];
5536748d055SPeter Ujfalusi 	} else {
5546748d055SPeter Ujfalusi 		priv->pll_rates[J721E_CLK_PARENT_44100] = clk_get_rate(pll);
5556748d055SPeter Ujfalusi 		clk_put(pll);
5566748d055SPeter Ujfalusi 	}
5576748d055SPeter Ujfalusi 
5586748d055SPeter Ujfalusi 	pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_48000]);
5596748d055SPeter Ujfalusi 	if (IS_ERR_OR_NULL(pll)) {
5606748d055SPeter Ujfalusi 		priv->pll_rates[J721E_CLK_PARENT_48000] =
5616748d055SPeter Ujfalusi 				match_data->pll_rates[J721E_CLK_PARENT_48000];
5626748d055SPeter Ujfalusi 	} else {
5636748d055SPeter Ujfalusi 		priv->pll_rates[J721E_CLK_PARENT_48000] = clk_get_rate(pll);
5646748d055SPeter Ujfalusi 		clk_put(pll);
5656748d055SPeter Ujfalusi 	}
5666748d055SPeter Ujfalusi 
5676748d055SPeter Ujfalusi 	if (!priv->pll_rates[J721E_CLK_PARENT_44100] &&
5686748d055SPeter Ujfalusi 	    !priv->pll_rates[J721E_CLK_PARENT_48000]) {
5696748d055SPeter Ujfalusi 		dev_err(priv->dev, "At least one PLL is needed\n");
5706748d055SPeter Ujfalusi 		return -EINVAL;
5716748d055SPeter Ujfalusi 	}
5726748d055SPeter Ujfalusi 
5736748d055SPeter Ujfalusi 	if (priv->pll_rates[J721E_CLK_PARENT_44100])
5746748d055SPeter Ujfalusi 		pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100];
5756748d055SPeter Ujfalusi 	else
5766748d055SPeter Ujfalusi 		pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000];
5776748d055SPeter Ujfalusi 
5786748d055SPeter Ujfalusi 	min_rate = pll_rate / J721E_MAX_CLK_HSDIV;
5796748d055SPeter Ujfalusi 	min_rate /= ratios_for_pcm3168a[ARRAY_SIZE(ratios_for_pcm3168a) - 1];
5806748d055SPeter Ujfalusi 
5816748d055SPeter Ujfalusi 	if (priv->pll_rates[J721E_CLK_PARENT_48000])
5826748d055SPeter Ujfalusi 		pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000];
5836748d055SPeter Ujfalusi 	else
5846748d055SPeter Ujfalusi 		pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100];
5856748d055SPeter Ujfalusi 
5866748d055SPeter Ujfalusi 	if (pll_rate > PCM1368A_MAX_SYSCLK)
5876748d055SPeter Ujfalusi 		pll_rate = PCM1368A_MAX_SYSCLK;
5886748d055SPeter Ujfalusi 
5896748d055SPeter Ujfalusi 	max_rate = pll_rate / ratios_for_pcm3168a[0];
5906748d055SPeter Ujfalusi 
5916748d055SPeter Ujfalusi 	snd_interval_any(&priv->rate_range);
5926748d055SPeter Ujfalusi 	priv->rate_range.min = min_rate;
5936748d055SPeter Ujfalusi 	priv->rate_range.max = max_rate;
5946748d055SPeter Ujfalusi 
5956748d055SPeter Ujfalusi 	return 0;
5966748d055SPeter Ujfalusi }
5976748d055SPeter Ujfalusi 
5986748d055SPeter Ujfalusi static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
5996748d055SPeter Ujfalusi 			       int *conf_idx)
6006748d055SPeter Ujfalusi {
6016748d055SPeter Ujfalusi 	struct device_node *node = priv->dev->of_node;
6026748d055SPeter Ujfalusi 	struct snd_soc_dai_link_component *compnent;
6036748d055SPeter Ujfalusi 	struct device_node *dai_node, *codec_node;
6046748d055SPeter Ujfalusi 	struct j721e_audio_domain *domain;
6056748d055SPeter Ujfalusi 	int comp_count, comp_idx;
6066748d055SPeter Ujfalusi 	int ret;
6076748d055SPeter Ujfalusi 
6086748d055SPeter Ujfalusi 	dai_node = of_parse_phandle(node, "ti,cpb-mcasp", 0);
6096748d055SPeter Ujfalusi 	if (!dai_node) {
6106748d055SPeter Ujfalusi 		dev_err(priv->dev, "CPB McASP node is not provided\n");
6116748d055SPeter Ujfalusi 		return -EINVAL;
6126748d055SPeter Ujfalusi 	}
6136748d055SPeter Ujfalusi 
6146748d055SPeter Ujfalusi 	codec_node = of_parse_phandle(node, "ti,cpb-codec", 0);
6156748d055SPeter Ujfalusi 	if (!codec_node) {
6166748d055SPeter Ujfalusi 		dev_err(priv->dev, "CPB codec node is not provided\n");
6176748d055SPeter Ujfalusi 		return -EINVAL;
6186748d055SPeter Ujfalusi 	}
6196748d055SPeter Ujfalusi 
6206748d055SPeter Ujfalusi 	domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB];
6216748d055SPeter Ujfalusi 	ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki");
6226748d055SPeter Ujfalusi 	if (ret)
6236748d055SPeter Ujfalusi 		return ret;
6246748d055SPeter Ujfalusi 
6256748d055SPeter Ujfalusi 	ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk");
6266748d055SPeter Ujfalusi 	if (ret)
6276748d055SPeter Ujfalusi 		return ret;
6286748d055SPeter Ujfalusi 
6296748d055SPeter Ujfalusi 	/*
6306748d055SPeter Ujfalusi 	 * Common Processor Board, two links
6316748d055SPeter Ujfalusi 	 * Link 1: McASP10 -> pcm3168a_1 DAC
6326748d055SPeter Ujfalusi 	 * Link 2: McASP10 <- pcm3168a_1 ADC
6336748d055SPeter Ujfalusi 	 */
6346748d055SPeter Ujfalusi 	comp_count = 6;
6356748d055SPeter Ujfalusi 	compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
6366748d055SPeter Ujfalusi 				GFP_KERNEL);
6376748d055SPeter Ujfalusi 	if (!compnent)
6386748d055SPeter Ujfalusi 		return -ENOMEM;
6396748d055SPeter Ujfalusi 
6406748d055SPeter Ujfalusi 	comp_idx = 0;
6416748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
6426748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_cpus = 1;
6436748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs = &compnent[comp_idx++];
6446748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_codecs = 1;
6456748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];
6466748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_platforms = 1;
6476748d055SPeter Ujfalusi 
6486748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].name = "CPB PCM3168A Playback";
6496748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog";
6506748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus->of_node = dai_node;
6516748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms->of_node = dai_node;
6526748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs->of_node = codec_node;
6536748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-dac";
6546748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].playback_only = 1;
6556748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB;
6566748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;
6576748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].init = j721e_audio_init;
6586748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].ops = &j721e_audio_ops;
6596748d055SPeter Ujfalusi 	(*link_idx)++;
6606748d055SPeter Ujfalusi 
6616748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
6626748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_cpus = 1;
6636748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs = &compnent[comp_idx++];
6646748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_codecs = 1;
6656748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];
6666748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_platforms = 1;
6676748d055SPeter Ujfalusi 
6686748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].name = "CPB PCM3168A Capture";
6696748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog";
6706748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus->of_node = dai_node;
6716748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms->of_node = dai_node;
6726748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs->of_node = codec_node;
6736748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-adc";
6746748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].capture_only = 1;
6756748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB;
6766748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;
6776748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].init = j721e_audio_init;
6786748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].ops = &j721e_audio_ops;
6796748d055SPeter Ujfalusi 	(*link_idx)++;
6806748d055SPeter Ujfalusi 
6816748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].dlc.of_node = codec_node;
6826748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].name_prefix = "codec-1";
6836748d055SPeter Ujfalusi 	(*conf_idx)++;
6846748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].dlc.of_node = dai_node;
6856748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].name_prefix = "McASP10";
6866748d055SPeter Ujfalusi 	(*conf_idx)++;
6876748d055SPeter Ujfalusi 
6886748d055SPeter Ujfalusi 	return 0;
6896748d055SPeter Ujfalusi }
6906748d055SPeter Ujfalusi 
6916748d055SPeter Ujfalusi static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx,
6926748d055SPeter Ujfalusi 			       int *conf_idx)
6936748d055SPeter Ujfalusi {
6946748d055SPeter Ujfalusi 	struct device_node *node = priv->dev->of_node;
6956748d055SPeter Ujfalusi 	struct snd_soc_dai_link_component *compnent;
6966748d055SPeter Ujfalusi 	struct device_node *dai_node, *codeca_node, *codecb_node;
6976748d055SPeter Ujfalusi 	struct j721e_audio_domain *domain;
6986748d055SPeter Ujfalusi 	int comp_count, comp_idx;
6996748d055SPeter Ujfalusi 	int ret;
7006748d055SPeter Ujfalusi 
7016748d055SPeter Ujfalusi 	if (priv->match_data->board_type != J721E_BOARD_CPB_IVI)
7026748d055SPeter Ujfalusi 		return 0;
7036748d055SPeter Ujfalusi 
7046748d055SPeter Ujfalusi 	dai_node = of_parse_phandle(node, "ti,ivi-mcasp", 0);
7056748d055SPeter Ujfalusi 	if (!dai_node) {
7066748d055SPeter Ujfalusi 		dev_err(priv->dev, "IVI McASP node is not provided\n");
7076748d055SPeter Ujfalusi 		return -EINVAL;
7086748d055SPeter Ujfalusi 	}
7096748d055SPeter Ujfalusi 
7106748d055SPeter Ujfalusi 	codeca_node = of_parse_phandle(node, "ti,ivi-codec-a", 0);
7116748d055SPeter Ujfalusi 	if (!codeca_node) {
7126748d055SPeter Ujfalusi 		dev_err(priv->dev, "IVI codec-a node is not provided\n");
7136748d055SPeter Ujfalusi 		return -EINVAL;
7146748d055SPeter Ujfalusi 	}
7156748d055SPeter Ujfalusi 
7166748d055SPeter Ujfalusi 	codecb_node = of_parse_phandle(node, "ti,ivi-codec-b", 0);
7176748d055SPeter Ujfalusi 	if (!codecb_node) {
7186748d055SPeter Ujfalusi 		dev_warn(priv->dev, "IVI codec-b node is not provided\n");
7196748d055SPeter Ujfalusi 		return 0;
7206748d055SPeter Ujfalusi 	}
7216748d055SPeter Ujfalusi 
7226748d055SPeter Ujfalusi 	domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_IVI];
7236748d055SPeter Ujfalusi 	ret = j721e_get_clocks(priv->dev, &domain->codec, "ivi-codec-scki");
7246748d055SPeter Ujfalusi 	if (ret)
7256748d055SPeter Ujfalusi 		return ret;
7266748d055SPeter Ujfalusi 
7276748d055SPeter Ujfalusi 	ret = j721e_get_clocks(priv->dev, &domain->mcasp, "ivi-mcasp-auxclk");
7286748d055SPeter Ujfalusi 	if (ret)
7296748d055SPeter Ujfalusi 		return ret;
7306748d055SPeter Ujfalusi 
7316748d055SPeter Ujfalusi 	/*
7326748d055SPeter Ujfalusi 	 * IVI extension, two links
7336748d055SPeter Ujfalusi 	 * Link 1: McASP0 -> pcm3168a_a DAC
7346748d055SPeter Ujfalusi 	 *		  \> pcm3168a_b DAC
7356748d055SPeter Ujfalusi 	 * Link 2: McASP0 <- pcm3168a_a ADC
7366748d055SPeter Ujfalusi 	 *		   \ pcm3168a_b ADC
7376748d055SPeter Ujfalusi 	 */
7386748d055SPeter Ujfalusi 	comp_count = 8;
7396748d055SPeter Ujfalusi 	compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),
7406748d055SPeter Ujfalusi 				GFP_KERNEL);
7416748d055SPeter Ujfalusi 	if (!compnent)
7426748d055SPeter Ujfalusi 		return -ENOMEM;
7436748d055SPeter Ujfalusi 
7446748d055SPeter Ujfalusi 	comp_idx = 0;
7456748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
7466748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_cpus = 1;
7476748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];
7486748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_platforms = 1;
7496748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs = &compnent[comp_idx];
7506748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_codecs = 2;
7516748d055SPeter Ujfalusi 	comp_idx += 2;
7526748d055SPeter Ujfalusi 
7536748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Playback";
7546748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog";
7556748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus->of_node = dai_node;
7566748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms->of_node = dai_node;
7576748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[0].of_node = codeca_node;
7586748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-dac";
7596748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[1].of_node = codecb_node;
7606748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-dac";
7616748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].playback_only = 1;
7626748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI;
7636748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;
7646748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].init = j721e_audio_init_ivi;
7656748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].ops = &j721e_audio_ops;
7666748d055SPeter Ujfalusi 	(*link_idx)++;
7676748d055SPeter Ujfalusi 
7686748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
7696748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_cpus = 1;
7706748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];
7716748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_platforms = 1;
7726748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs = &compnent[comp_idx];
7736748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].num_codecs = 2;
7746748d055SPeter Ujfalusi 
7756748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Capture";
7766748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog";
7776748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].cpus->of_node = dai_node;
7786748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].platforms->of_node = dai_node;
7796748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[0].of_node = codeca_node;
7806748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-adc";
7816748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[1].of_node = codecb_node;
7826748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-adc";
7836748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].capture_only = 1;
7846748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI;
7856748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;
7866748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].init = j721e_audio_init;
7876748d055SPeter Ujfalusi 	priv->dai_links[*link_idx].ops = &j721e_audio_ops;
7886748d055SPeter Ujfalusi 	(*link_idx)++;
7896748d055SPeter Ujfalusi 
7906748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].dlc.of_node = codeca_node;
7916748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].name_prefix = "codec-a";
7926748d055SPeter Ujfalusi 	(*conf_idx)++;
7936748d055SPeter Ujfalusi 
7946748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].dlc.of_node = codecb_node;
7956748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].name_prefix = "codec-b";
7966748d055SPeter Ujfalusi 	(*conf_idx)++;
7976748d055SPeter Ujfalusi 
7986748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].dlc.of_node = dai_node;
7996748d055SPeter Ujfalusi 	priv->codec_conf[*conf_idx].name_prefix = "McASP0";
8006748d055SPeter Ujfalusi 	(*conf_idx)++;
8016748d055SPeter Ujfalusi 
8026748d055SPeter Ujfalusi 	return 0;
8036748d055SPeter Ujfalusi }
8046748d055SPeter Ujfalusi 
8056748d055SPeter Ujfalusi static int j721e_soc_probe(struct platform_device *pdev)
8066748d055SPeter Ujfalusi {
8076748d055SPeter Ujfalusi 	struct device_node *node = pdev->dev.of_node;
8086748d055SPeter Ujfalusi 	struct snd_soc_card *card;
8096748d055SPeter Ujfalusi 	const struct of_device_id *match;
8106748d055SPeter Ujfalusi 	struct j721e_priv *priv;
8116748d055SPeter Ujfalusi 	int link_cnt, conf_cnt, ret;
8126748d055SPeter Ujfalusi 
8136748d055SPeter Ujfalusi 	if (!node) {
8146748d055SPeter Ujfalusi 		dev_err(&pdev->dev, "of node is missing.\n");
8156748d055SPeter Ujfalusi 		return -ENODEV;
8166748d055SPeter Ujfalusi 	}
8176748d055SPeter Ujfalusi 
8186748d055SPeter Ujfalusi 	match = of_match_node(j721e_audio_of_match, node);
8196748d055SPeter Ujfalusi 	if (!match) {
8206748d055SPeter Ujfalusi 		dev_err(&pdev->dev, "No compatible match found\n");
8216748d055SPeter Ujfalusi 		return -ENODEV;
8226748d055SPeter Ujfalusi 	}
8236748d055SPeter Ujfalusi 
8246748d055SPeter Ujfalusi 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
8256748d055SPeter Ujfalusi 	if (!priv)
8266748d055SPeter Ujfalusi 		return -ENOMEM;
8276748d055SPeter Ujfalusi 
8286748d055SPeter Ujfalusi 	priv->match_data = match->data;
8296748d055SPeter Ujfalusi 
8306748d055SPeter Ujfalusi 	priv->dai_links = devm_kcalloc(&pdev->dev, priv->match_data->num_links,
8316748d055SPeter Ujfalusi 				       sizeof(*priv->dai_links), GFP_KERNEL);
8326748d055SPeter Ujfalusi 	if (!priv->dai_links)
8336748d055SPeter Ujfalusi 		return -ENOMEM;
8346748d055SPeter Ujfalusi 
8356748d055SPeter Ujfalusi 	priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].parent_clk_id = -1;
8366748d055SPeter Ujfalusi 	priv->audio_domains[J721E_AUDIO_DOMAIN_IVI].parent_clk_id = -1;
8376748d055SPeter Ujfalusi 	priv->dev = &pdev->dev;
8386748d055SPeter Ujfalusi 	card = &priv->card;
8396748d055SPeter Ujfalusi 	card->dev = &pdev->dev;
8406748d055SPeter Ujfalusi 	card->owner = THIS_MODULE;
8416748d055SPeter Ujfalusi 	card->dapm_widgets = j721e_cpb_dapm_widgets;
8426748d055SPeter Ujfalusi 	card->num_dapm_widgets = ARRAY_SIZE(j721e_cpb_dapm_widgets);
8436748d055SPeter Ujfalusi 	card->dapm_routes = j721e_cpb_dapm_routes;
8446748d055SPeter Ujfalusi 	card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes);
8456748d055SPeter Ujfalusi 	card->fully_routed = 1;
8466748d055SPeter Ujfalusi 
8476748d055SPeter Ujfalusi 	if (snd_soc_of_parse_card_name(card, "model")) {
8486748d055SPeter Ujfalusi 		dev_err(&pdev->dev, "Card name is not provided\n");
8496748d055SPeter Ujfalusi 		return -ENODEV;
8506748d055SPeter Ujfalusi 	}
8516748d055SPeter Ujfalusi 
8526748d055SPeter Ujfalusi 	link_cnt = 0;
8536748d055SPeter Ujfalusi 	conf_cnt = 0;
8546748d055SPeter Ujfalusi 	ret = j721e_soc_probe_cpb(priv, &link_cnt, &conf_cnt);
8556748d055SPeter Ujfalusi 	if (ret)
8566748d055SPeter Ujfalusi 		return ret;
8576748d055SPeter Ujfalusi 
8586748d055SPeter Ujfalusi 	ret = j721e_soc_probe_ivi(priv, &link_cnt, &conf_cnt);
8596748d055SPeter Ujfalusi 	if (ret)
8606748d055SPeter Ujfalusi 		return ret;
8616748d055SPeter Ujfalusi 
8626748d055SPeter Ujfalusi 	card->dai_link = priv->dai_links;
8636748d055SPeter Ujfalusi 	card->num_links = link_cnt;
8646748d055SPeter Ujfalusi 
8656748d055SPeter Ujfalusi 	card->codec_conf = priv->codec_conf;
8666748d055SPeter Ujfalusi 	card->num_configs = conf_cnt;
8676748d055SPeter Ujfalusi 
8686748d055SPeter Ujfalusi 	ret = j721e_calculate_rate_range(priv);
8696748d055SPeter Ujfalusi 	if (ret)
8706748d055SPeter Ujfalusi 		return ret;
8716748d055SPeter Ujfalusi 
8726748d055SPeter Ujfalusi 	snd_soc_card_set_drvdata(card, priv);
8736748d055SPeter Ujfalusi 
8746748d055SPeter Ujfalusi 	mutex_init(&priv->mutex);
8756748d055SPeter Ujfalusi 	ret = devm_snd_soc_register_card(&pdev->dev, card);
8766748d055SPeter Ujfalusi 	if (ret)
8776748d055SPeter Ujfalusi 		dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
8786748d055SPeter Ujfalusi 			ret);
8796748d055SPeter Ujfalusi 
8806748d055SPeter Ujfalusi 	return ret;
8816748d055SPeter Ujfalusi }
8826748d055SPeter Ujfalusi 
8836748d055SPeter Ujfalusi static struct platform_driver j721e_soc_driver = {
8846748d055SPeter Ujfalusi 	.driver = {
8856748d055SPeter Ujfalusi 		.name = "j721e-audio",
8866748d055SPeter Ujfalusi 		.pm = &snd_soc_pm_ops,
8876748d055SPeter Ujfalusi 		.of_match_table = of_match_ptr(j721e_audio_of_match),
8886748d055SPeter Ujfalusi 	},
8896748d055SPeter Ujfalusi 	.probe = j721e_soc_probe,
8906748d055SPeter Ujfalusi };
8916748d055SPeter Ujfalusi 
8926748d055SPeter Ujfalusi module_platform_driver(j721e_soc_driver);
8936748d055SPeter Ujfalusi 
8946748d055SPeter Ujfalusi MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
8956748d055SPeter Ujfalusi MODULE_DESCRIPTION("ASoC machine driver for j721e Common Processor Board");
8966748d055SPeter Ujfalusi MODULE_LICENSE("GPL v2");
897