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 { 25202cde14aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 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 2826748d055SPeter Ujfalusi if (ret) 283*78d2a05eSPeter Ujfalusi goto out; 2846748d055SPeter Ujfalusi 2856748d055SPeter Ujfalusi /* Reset TDM slots to 32 */ 2866748d055SPeter Ujfalusi ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32); 2876748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 288*78d2a05eSPeter Ujfalusi goto out; 2896748d055SPeter Ujfalusi 2906748d055SPeter Ujfalusi for_each_rtd_codec_dais(rtd, i, codec_dai) { 2916748d055SPeter Ujfalusi ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32); 2926748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 293*78d2a05eSPeter Ujfalusi goto out; 2946748d055SPeter Ujfalusi } 2956748d055SPeter Ujfalusi 296*78d2a05eSPeter Ujfalusi if (ret == -ENOTSUPP) 297*78d2a05eSPeter Ujfalusi ret = 0; 298*78d2a05eSPeter Ujfalusi out: 299*78d2a05eSPeter Ujfalusi if (ret) 300*78d2a05eSPeter Ujfalusi domain->active--; 301*78d2a05eSPeter Ujfalusi mutex_unlock(&priv->mutex); 302*78d2a05eSPeter Ujfalusi 303*78d2a05eSPeter Ujfalusi return ret; 3046748d055SPeter Ujfalusi } 3056748d055SPeter Ujfalusi 3066748d055SPeter Ujfalusi static int j721e_audio_hw_params(struct snd_pcm_substream *substream, 3076748d055SPeter Ujfalusi struct snd_pcm_hw_params *params) 3086748d055SPeter Ujfalusi { 30902cde14aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 3106748d055SPeter Ujfalusi struct snd_soc_card *card = rtd->card; 3116748d055SPeter Ujfalusi struct j721e_priv *priv = snd_soc_card_get_drvdata(card); 3126748d055SPeter Ujfalusi unsigned int domain_id = rtd->dai_link->id; 3136748d055SPeter Ujfalusi struct j721e_audio_domain *domain = &priv->audio_domains[domain_id]; 3146748d055SPeter Ujfalusi struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 3156748d055SPeter Ujfalusi struct snd_soc_dai *codec_dai; 3166748d055SPeter Ujfalusi unsigned int sysclk_rate; 3176748d055SPeter Ujfalusi int slot_width = 32; 3186748d055SPeter Ujfalusi int ret; 3196748d055SPeter Ujfalusi int i; 3206748d055SPeter Ujfalusi 3216748d055SPeter Ujfalusi mutex_lock(&priv->mutex); 3226748d055SPeter Ujfalusi 3236748d055SPeter Ujfalusi if (domain->rate && domain->rate != params_rate(params)) { 3246748d055SPeter Ujfalusi ret = -EINVAL; 3256748d055SPeter Ujfalusi goto out; 3266748d055SPeter Ujfalusi } 3276748d055SPeter Ujfalusi 3286748d055SPeter Ujfalusi if (params_width(params) == 16) 3296748d055SPeter Ujfalusi slot_width = 16; 3306748d055SPeter Ujfalusi 3316748d055SPeter Ujfalusi ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, slot_width); 3326748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 3336748d055SPeter Ujfalusi goto out; 3346748d055SPeter Ujfalusi 3356748d055SPeter Ujfalusi for_each_rtd_codec_dais(rtd, i, codec_dai) { 3366748d055SPeter Ujfalusi ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 3376748d055SPeter Ujfalusi slot_width); 3386748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 33959b44649SWei Yongjun goto out; 3406748d055SPeter Ujfalusi } 3416748d055SPeter Ujfalusi 3426748d055SPeter Ujfalusi ret = j721e_configure_refclk(priv, domain_id, params_rate(params)); 3436748d055SPeter Ujfalusi if (ret) 3446748d055SPeter Ujfalusi goto out; 3456748d055SPeter Ujfalusi 3466748d055SPeter Ujfalusi sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id]; 3476748d055SPeter Ujfalusi for_each_rtd_codec_dais(rtd, i, codec_dai) { 3486748d055SPeter Ujfalusi ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate, 3496748d055SPeter Ujfalusi SND_SOC_CLOCK_IN); 3506748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) { 3516748d055SPeter Ujfalusi dev_err(priv->dev, 3526748d055SPeter Ujfalusi "codec set_sysclk failed for %u Hz\n", 3536748d055SPeter Ujfalusi sysclk_rate); 3546748d055SPeter Ujfalusi goto out; 3556748d055SPeter Ujfalusi } 3566748d055SPeter Ujfalusi } 3576748d055SPeter Ujfalusi 3586748d055SPeter Ujfalusi ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK, 3596748d055SPeter Ujfalusi sysclk_rate, SND_SOC_CLOCK_IN); 3606748d055SPeter Ujfalusi 3616748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) { 3626748d055SPeter Ujfalusi dev_err(priv->dev, "mcasp set_sysclk failed for %u Hz\n", 3636748d055SPeter Ujfalusi sysclk_rate); 3646748d055SPeter Ujfalusi } else { 3656748d055SPeter Ujfalusi domain->rate = params_rate(params); 3666748d055SPeter Ujfalusi ret = 0; 3676748d055SPeter Ujfalusi } 3686748d055SPeter Ujfalusi 3696748d055SPeter Ujfalusi out: 3706748d055SPeter Ujfalusi mutex_unlock(&priv->mutex); 3716748d055SPeter Ujfalusi return ret; 3726748d055SPeter Ujfalusi } 3736748d055SPeter Ujfalusi 3746748d055SPeter Ujfalusi static void j721e_audio_shutdown(struct snd_pcm_substream *substream) 3756748d055SPeter Ujfalusi { 37602cde14aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 3776748d055SPeter Ujfalusi struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card); 3786748d055SPeter Ujfalusi unsigned int domain_id = rtd->dai_link->id; 3796748d055SPeter Ujfalusi struct j721e_audio_domain *domain = &priv->audio_domains[domain_id]; 3806748d055SPeter Ujfalusi 3816748d055SPeter Ujfalusi mutex_lock(&priv->mutex); 3826748d055SPeter Ujfalusi 3836748d055SPeter Ujfalusi domain->active--; 3846748d055SPeter Ujfalusi if (!domain->active) { 3856748d055SPeter Ujfalusi domain->rate = 0; 3866748d055SPeter Ujfalusi domain->active_link = 0; 3876748d055SPeter Ujfalusi } 3886748d055SPeter Ujfalusi 3896748d055SPeter Ujfalusi mutex_unlock(&priv->mutex); 3906748d055SPeter Ujfalusi } 3916748d055SPeter Ujfalusi 3926748d055SPeter Ujfalusi static const struct snd_soc_ops j721e_audio_ops = { 3936748d055SPeter Ujfalusi .startup = j721e_audio_startup, 3946748d055SPeter Ujfalusi .hw_params = j721e_audio_hw_params, 3956748d055SPeter Ujfalusi .shutdown = j721e_audio_shutdown, 3966748d055SPeter Ujfalusi }; 3976748d055SPeter Ujfalusi 3986748d055SPeter Ujfalusi static int j721e_audio_init(struct snd_soc_pcm_runtime *rtd) 3996748d055SPeter Ujfalusi { 4006748d055SPeter Ujfalusi struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card); 4016748d055SPeter Ujfalusi unsigned int domain_id = rtd->dai_link->id; 4026748d055SPeter Ujfalusi struct j721e_audio_domain *domain = &priv->audio_domains[domain_id]; 4036748d055SPeter Ujfalusi struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 4046748d055SPeter Ujfalusi struct snd_soc_dai *codec_dai; 4056748d055SPeter Ujfalusi unsigned int sysclk_rate; 4066748d055SPeter Ujfalusi int i, ret; 4076748d055SPeter Ujfalusi 4086748d055SPeter Ujfalusi /* Set up initial clock configuration */ 4096748d055SPeter Ujfalusi ret = j721e_configure_refclk(priv, domain_id, 48000); 4106748d055SPeter Ujfalusi if (ret) 4116748d055SPeter Ujfalusi return ret; 4126748d055SPeter Ujfalusi 4136748d055SPeter Ujfalusi sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id]; 4146748d055SPeter Ujfalusi for_each_rtd_codec_dais(rtd, i, codec_dai) { 4156748d055SPeter Ujfalusi ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate, 4166748d055SPeter Ujfalusi SND_SOC_CLOCK_IN); 4176748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 4186748d055SPeter Ujfalusi return ret; 4196748d055SPeter Ujfalusi } 4206748d055SPeter Ujfalusi 4216748d055SPeter Ujfalusi ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK, 4226748d055SPeter Ujfalusi sysclk_rate, SND_SOC_CLOCK_IN); 4236748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 4246748d055SPeter Ujfalusi return ret; 4256748d055SPeter Ujfalusi 4266748d055SPeter Ujfalusi /* Set initial tdm slots */ 4276748d055SPeter Ujfalusi ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32); 4286748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 4296748d055SPeter Ujfalusi return ret; 4306748d055SPeter Ujfalusi 4316748d055SPeter Ujfalusi for_each_rtd_codec_dais(rtd, i, codec_dai) { 4326748d055SPeter Ujfalusi ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32); 4336748d055SPeter Ujfalusi if (ret && ret != -ENOTSUPP) 4346748d055SPeter Ujfalusi return ret; 4356748d055SPeter Ujfalusi } 4366748d055SPeter Ujfalusi 4376748d055SPeter Ujfalusi return 0; 4386748d055SPeter Ujfalusi } 4396748d055SPeter Ujfalusi 4406748d055SPeter Ujfalusi static int j721e_audio_init_ivi(struct snd_soc_pcm_runtime *rtd) 4416748d055SPeter Ujfalusi { 4426748d055SPeter Ujfalusi struct snd_soc_dapm_context *dapm = &rtd->card->dapm; 4436748d055SPeter Ujfalusi 4446748d055SPeter Ujfalusi snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_a_dapm_widgets, 4456748d055SPeter Ujfalusi ARRAY_SIZE(j721e_ivi_codec_a_dapm_widgets)); 4466748d055SPeter Ujfalusi snd_soc_dapm_add_routes(dapm, j721e_codec_a_dapm_routes, 4476748d055SPeter Ujfalusi ARRAY_SIZE(j721e_codec_a_dapm_routes)); 4486748d055SPeter Ujfalusi snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_b_dapm_widgets, 4496748d055SPeter Ujfalusi ARRAY_SIZE(j721e_ivi_codec_b_dapm_widgets)); 4506748d055SPeter Ujfalusi snd_soc_dapm_add_routes(dapm, j721e_codec_b_dapm_routes, 4516748d055SPeter Ujfalusi ARRAY_SIZE(j721e_codec_b_dapm_routes)); 4526748d055SPeter Ujfalusi 4536748d055SPeter Ujfalusi return j721e_audio_init(rtd); 4546748d055SPeter Ujfalusi } 4556748d055SPeter Ujfalusi 4566748d055SPeter Ujfalusi static int j721e_get_clocks(struct device *dev, 4576748d055SPeter Ujfalusi struct j721e_audio_clocks *clocks, char *prefix) 4586748d055SPeter Ujfalusi { 4596748d055SPeter Ujfalusi struct clk *parent; 4606748d055SPeter Ujfalusi char *clk_name; 4616748d055SPeter Ujfalusi int ret; 4626748d055SPeter Ujfalusi 4636748d055SPeter Ujfalusi clocks->target = devm_clk_get(dev, prefix); 4646748d055SPeter Ujfalusi if (IS_ERR(clocks->target)) { 4656748d055SPeter Ujfalusi ret = PTR_ERR(clocks->target); 4666748d055SPeter Ujfalusi if (ret != -EPROBE_DEFER) 4676748d055SPeter Ujfalusi dev_err(dev, "failed to acquire %s: %d\n", 4686748d055SPeter Ujfalusi prefix, ret); 4696748d055SPeter Ujfalusi return ret; 4706748d055SPeter Ujfalusi } 4716748d055SPeter Ujfalusi 4726748d055SPeter Ujfalusi clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix); 4736748d055SPeter Ujfalusi if (clk_name) { 4746748d055SPeter Ujfalusi parent = devm_clk_get(dev, clk_name); 4756748d055SPeter Ujfalusi kfree(clk_name); 4766748d055SPeter Ujfalusi if (IS_ERR(parent)) { 4776748d055SPeter Ujfalusi ret = PTR_ERR(parent); 4786748d055SPeter Ujfalusi if (ret == -EPROBE_DEFER) 4796748d055SPeter Ujfalusi return ret; 4806748d055SPeter Ujfalusi 4816748d055SPeter Ujfalusi dev_dbg(dev, "no 48KHz parent for %s: %d\n", prefix, ret); 4826748d055SPeter Ujfalusi parent = NULL; 4836748d055SPeter Ujfalusi } 4846748d055SPeter Ujfalusi clocks->parent[J721E_CLK_PARENT_48000] = parent; 4856748d055SPeter Ujfalusi } else { 4866748d055SPeter Ujfalusi return -ENOMEM; 4876748d055SPeter Ujfalusi } 4886748d055SPeter Ujfalusi 4896748d055SPeter Ujfalusi clk_name = kasprintf(GFP_KERNEL, "%s-44100", prefix); 4906748d055SPeter Ujfalusi if (clk_name) { 4916748d055SPeter Ujfalusi parent = devm_clk_get(dev, clk_name); 4926748d055SPeter Ujfalusi kfree(clk_name); 4936748d055SPeter Ujfalusi if (IS_ERR(parent)) { 4946748d055SPeter Ujfalusi ret = PTR_ERR(parent); 4956748d055SPeter Ujfalusi if (ret == -EPROBE_DEFER) 4966748d055SPeter Ujfalusi return ret; 4976748d055SPeter Ujfalusi 4986748d055SPeter Ujfalusi dev_dbg(dev, "no 44.1KHz parent for %s: %d\n", prefix, ret); 4996748d055SPeter Ujfalusi parent = NULL; 5006748d055SPeter Ujfalusi } 5016748d055SPeter Ujfalusi clocks->parent[J721E_CLK_PARENT_44100] = parent; 5026748d055SPeter Ujfalusi } else { 5036748d055SPeter Ujfalusi return -ENOMEM; 5046748d055SPeter Ujfalusi } 5056748d055SPeter Ujfalusi 5066748d055SPeter Ujfalusi if (!clocks->parent[J721E_CLK_PARENT_44100] && 5076748d055SPeter Ujfalusi !clocks->parent[J721E_CLK_PARENT_48000]) { 5086748d055SPeter Ujfalusi dev_err(dev, "At least one parent clock is needed for %s\n", 5096748d055SPeter Ujfalusi prefix); 5106748d055SPeter Ujfalusi return -EINVAL; 5116748d055SPeter Ujfalusi } 5126748d055SPeter Ujfalusi 5136748d055SPeter Ujfalusi return 0; 5146748d055SPeter Ujfalusi } 5156748d055SPeter Ujfalusi 5166748d055SPeter Ujfalusi static const struct j721e_audio_match_data j721e_cpb_data = { 5176748d055SPeter Ujfalusi .board_type = J721E_BOARD_CPB, 5186748d055SPeter Ujfalusi .num_links = 2, /* CPB pcm3168a */ 5196748d055SPeter Ujfalusi .pll_rates = { 5206748d055SPeter Ujfalusi [J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */ 5216748d055SPeter Ujfalusi [J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */ 5226748d055SPeter Ujfalusi }, 5236748d055SPeter Ujfalusi }; 5246748d055SPeter Ujfalusi 5256748d055SPeter Ujfalusi static const struct j721e_audio_match_data j721e_cpb_ivi_data = { 5266748d055SPeter Ujfalusi .board_type = J721E_BOARD_CPB_IVI, 5276748d055SPeter Ujfalusi .num_links = 4, /* CPB pcm3168a + 2x pcm3168a on IVI */ 5286748d055SPeter Ujfalusi .pll_rates = { 5296748d055SPeter Ujfalusi [J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */ 5306748d055SPeter Ujfalusi [J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */ 5316748d055SPeter Ujfalusi }, 5326748d055SPeter Ujfalusi }; 5336748d055SPeter Ujfalusi 53418c140f4SPeter Ujfalusi static const struct j721e_audio_match_data j7200_cpb_data = { 53518c140f4SPeter Ujfalusi .board_type = J721E_BOARD_CPB, 53618c140f4SPeter Ujfalusi .num_links = 2, /* CPB pcm3168a */ 53718c140f4SPeter Ujfalusi .pll_rates = { 53818c140f4SPeter Ujfalusi [J721E_CLK_PARENT_48000] = 2359296000u, /* PLL4 */ 53918c140f4SPeter Ujfalusi }, 54018c140f4SPeter Ujfalusi }; 54118c140f4SPeter Ujfalusi 5426748d055SPeter Ujfalusi static const struct of_device_id j721e_audio_of_match[] = { 5436748d055SPeter Ujfalusi { 5446748d055SPeter Ujfalusi .compatible = "ti,j721e-cpb-audio", 5456748d055SPeter Ujfalusi .data = &j721e_cpb_data, 5466748d055SPeter Ujfalusi }, { 5476748d055SPeter Ujfalusi .compatible = "ti,j721e-cpb-ivi-audio", 5486748d055SPeter Ujfalusi .data = &j721e_cpb_ivi_data, 54918c140f4SPeter Ujfalusi }, { 55018c140f4SPeter Ujfalusi .compatible = "ti,j7200-cpb-audio", 55118c140f4SPeter Ujfalusi .data = &j7200_cpb_data, 5526748d055SPeter Ujfalusi }, 5536748d055SPeter Ujfalusi { }, 5546748d055SPeter Ujfalusi }; 5556748d055SPeter Ujfalusi MODULE_DEVICE_TABLE(of, j721e_audio_of_match); 5566748d055SPeter Ujfalusi 5576748d055SPeter Ujfalusi static int j721e_calculate_rate_range(struct j721e_priv *priv) 5586748d055SPeter Ujfalusi { 5596748d055SPeter Ujfalusi const struct j721e_audio_match_data *match_data = priv->match_data; 5606748d055SPeter Ujfalusi struct j721e_audio_clocks *domain_clocks; 5616748d055SPeter Ujfalusi unsigned int min_rate, max_rate, pll_rate; 5626748d055SPeter Ujfalusi struct clk *pll; 5636748d055SPeter Ujfalusi 5646748d055SPeter Ujfalusi domain_clocks = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].mcasp; 5656748d055SPeter Ujfalusi 5666748d055SPeter Ujfalusi pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_44100]); 5676748d055SPeter Ujfalusi if (IS_ERR_OR_NULL(pll)) { 5686748d055SPeter Ujfalusi priv->pll_rates[J721E_CLK_PARENT_44100] = 5696748d055SPeter Ujfalusi match_data->pll_rates[J721E_CLK_PARENT_44100]; 5706748d055SPeter Ujfalusi } else { 5716748d055SPeter Ujfalusi priv->pll_rates[J721E_CLK_PARENT_44100] = clk_get_rate(pll); 5726748d055SPeter Ujfalusi clk_put(pll); 5736748d055SPeter Ujfalusi } 5746748d055SPeter Ujfalusi 5756748d055SPeter Ujfalusi pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_48000]); 5766748d055SPeter Ujfalusi if (IS_ERR_OR_NULL(pll)) { 5776748d055SPeter Ujfalusi priv->pll_rates[J721E_CLK_PARENT_48000] = 5786748d055SPeter Ujfalusi match_data->pll_rates[J721E_CLK_PARENT_48000]; 5796748d055SPeter Ujfalusi } else { 5806748d055SPeter Ujfalusi priv->pll_rates[J721E_CLK_PARENT_48000] = clk_get_rate(pll); 5816748d055SPeter Ujfalusi clk_put(pll); 5826748d055SPeter Ujfalusi } 5836748d055SPeter Ujfalusi 5846748d055SPeter Ujfalusi if (!priv->pll_rates[J721E_CLK_PARENT_44100] && 5856748d055SPeter Ujfalusi !priv->pll_rates[J721E_CLK_PARENT_48000]) { 5866748d055SPeter Ujfalusi dev_err(priv->dev, "At least one PLL is needed\n"); 5876748d055SPeter Ujfalusi return -EINVAL; 5886748d055SPeter Ujfalusi } 5896748d055SPeter Ujfalusi 5906748d055SPeter Ujfalusi if (priv->pll_rates[J721E_CLK_PARENT_44100]) 5916748d055SPeter Ujfalusi pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100]; 5926748d055SPeter Ujfalusi else 5936748d055SPeter Ujfalusi pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000]; 5946748d055SPeter Ujfalusi 5956748d055SPeter Ujfalusi min_rate = pll_rate / J721E_MAX_CLK_HSDIV; 5966748d055SPeter Ujfalusi min_rate /= ratios_for_pcm3168a[ARRAY_SIZE(ratios_for_pcm3168a) - 1]; 5976748d055SPeter Ujfalusi 5986748d055SPeter Ujfalusi if (priv->pll_rates[J721E_CLK_PARENT_48000]) 5996748d055SPeter Ujfalusi pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000]; 6006748d055SPeter Ujfalusi else 6016748d055SPeter Ujfalusi pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100]; 6026748d055SPeter Ujfalusi 6036748d055SPeter Ujfalusi if (pll_rate > PCM1368A_MAX_SYSCLK) 6046748d055SPeter Ujfalusi pll_rate = PCM1368A_MAX_SYSCLK; 6056748d055SPeter Ujfalusi 6066748d055SPeter Ujfalusi max_rate = pll_rate / ratios_for_pcm3168a[0]; 6076748d055SPeter Ujfalusi 6086748d055SPeter Ujfalusi snd_interval_any(&priv->rate_range); 6096748d055SPeter Ujfalusi priv->rate_range.min = min_rate; 6106748d055SPeter Ujfalusi priv->rate_range.max = max_rate; 6116748d055SPeter Ujfalusi 6126748d055SPeter Ujfalusi return 0; 6136748d055SPeter Ujfalusi } 6146748d055SPeter Ujfalusi 6156748d055SPeter Ujfalusi static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx, 6166748d055SPeter Ujfalusi int *conf_idx) 6176748d055SPeter Ujfalusi { 6186748d055SPeter Ujfalusi struct device_node *node = priv->dev->of_node; 6196748d055SPeter Ujfalusi struct snd_soc_dai_link_component *compnent; 6206748d055SPeter Ujfalusi struct device_node *dai_node, *codec_node; 6216748d055SPeter Ujfalusi struct j721e_audio_domain *domain; 6226748d055SPeter Ujfalusi int comp_count, comp_idx; 6236748d055SPeter Ujfalusi int ret; 6246748d055SPeter Ujfalusi 6256748d055SPeter Ujfalusi dai_node = of_parse_phandle(node, "ti,cpb-mcasp", 0); 6266748d055SPeter Ujfalusi if (!dai_node) { 6276748d055SPeter Ujfalusi dev_err(priv->dev, "CPB McASP node is not provided\n"); 6286748d055SPeter Ujfalusi return -EINVAL; 6296748d055SPeter Ujfalusi } 6306748d055SPeter Ujfalusi 6316748d055SPeter Ujfalusi codec_node = of_parse_phandle(node, "ti,cpb-codec", 0); 6326748d055SPeter Ujfalusi if (!codec_node) { 6336748d055SPeter Ujfalusi dev_err(priv->dev, "CPB codec node is not provided\n"); 6346748d055SPeter Ujfalusi return -EINVAL; 6356748d055SPeter Ujfalusi } 6366748d055SPeter Ujfalusi 6376748d055SPeter Ujfalusi domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB]; 6386748d055SPeter Ujfalusi ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki"); 6396748d055SPeter Ujfalusi if (ret) 6406748d055SPeter Ujfalusi return ret; 6416748d055SPeter Ujfalusi 6426748d055SPeter Ujfalusi ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk"); 6436748d055SPeter Ujfalusi if (ret) 6446748d055SPeter Ujfalusi return ret; 6456748d055SPeter Ujfalusi 6466748d055SPeter Ujfalusi /* 6476748d055SPeter Ujfalusi * Common Processor Board, two links 6486748d055SPeter Ujfalusi * Link 1: McASP10 -> pcm3168a_1 DAC 6496748d055SPeter Ujfalusi * Link 2: McASP10 <- pcm3168a_1 ADC 6506748d055SPeter Ujfalusi */ 6516748d055SPeter Ujfalusi comp_count = 6; 6526748d055SPeter Ujfalusi compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), 6536748d055SPeter Ujfalusi GFP_KERNEL); 6546748d055SPeter Ujfalusi if (!compnent) 6556748d055SPeter Ujfalusi return -ENOMEM; 6566748d055SPeter Ujfalusi 6576748d055SPeter Ujfalusi comp_idx = 0; 6586748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 6596748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_cpus = 1; 6606748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs = &compnent[comp_idx++]; 6616748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_codecs = 1; 6626748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 6636748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_platforms = 1; 6646748d055SPeter Ujfalusi 6656748d055SPeter Ujfalusi priv->dai_links[*link_idx].name = "CPB PCM3168A Playback"; 6666748d055SPeter Ujfalusi priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog"; 6676748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus->of_node = dai_node; 6686748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms->of_node = dai_node; 6696748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs->of_node = codec_node; 6706748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-dac"; 6716748d055SPeter Ujfalusi priv->dai_links[*link_idx].playback_only = 1; 6726748d055SPeter Ujfalusi priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB; 6736748d055SPeter Ujfalusi priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 6746748d055SPeter Ujfalusi priv->dai_links[*link_idx].init = j721e_audio_init; 6756748d055SPeter Ujfalusi priv->dai_links[*link_idx].ops = &j721e_audio_ops; 6766748d055SPeter Ujfalusi (*link_idx)++; 6776748d055SPeter Ujfalusi 6786748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 6796748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_cpus = 1; 6806748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs = &compnent[comp_idx++]; 6816748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_codecs = 1; 6826748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 6836748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_platforms = 1; 6846748d055SPeter Ujfalusi 6856748d055SPeter Ujfalusi priv->dai_links[*link_idx].name = "CPB PCM3168A Capture"; 6866748d055SPeter Ujfalusi priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog"; 6876748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus->of_node = dai_node; 6886748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms->of_node = dai_node; 6896748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs->of_node = codec_node; 6906748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-adc"; 6916748d055SPeter Ujfalusi priv->dai_links[*link_idx].capture_only = 1; 6926748d055SPeter Ujfalusi priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB; 6936748d055SPeter Ujfalusi priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 6946748d055SPeter Ujfalusi priv->dai_links[*link_idx].init = j721e_audio_init; 6956748d055SPeter Ujfalusi priv->dai_links[*link_idx].ops = &j721e_audio_ops; 6966748d055SPeter Ujfalusi (*link_idx)++; 6976748d055SPeter Ujfalusi 6986748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].dlc.of_node = codec_node; 6996748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].name_prefix = "codec-1"; 7006748d055SPeter Ujfalusi (*conf_idx)++; 7016748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].dlc.of_node = dai_node; 7026748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].name_prefix = "McASP10"; 7036748d055SPeter Ujfalusi (*conf_idx)++; 7046748d055SPeter Ujfalusi 7056748d055SPeter Ujfalusi return 0; 7066748d055SPeter Ujfalusi } 7076748d055SPeter Ujfalusi 7086748d055SPeter Ujfalusi static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, 7096748d055SPeter Ujfalusi int *conf_idx) 7106748d055SPeter Ujfalusi { 7116748d055SPeter Ujfalusi struct device_node *node = priv->dev->of_node; 7126748d055SPeter Ujfalusi struct snd_soc_dai_link_component *compnent; 7136748d055SPeter Ujfalusi struct device_node *dai_node, *codeca_node, *codecb_node; 7146748d055SPeter Ujfalusi struct j721e_audio_domain *domain; 7156748d055SPeter Ujfalusi int comp_count, comp_idx; 7166748d055SPeter Ujfalusi int ret; 7176748d055SPeter Ujfalusi 7186748d055SPeter Ujfalusi if (priv->match_data->board_type != J721E_BOARD_CPB_IVI) 7196748d055SPeter Ujfalusi return 0; 7206748d055SPeter Ujfalusi 7216748d055SPeter Ujfalusi dai_node = of_parse_phandle(node, "ti,ivi-mcasp", 0); 7226748d055SPeter Ujfalusi if (!dai_node) { 7236748d055SPeter Ujfalusi dev_err(priv->dev, "IVI McASP node is not provided\n"); 7246748d055SPeter Ujfalusi return -EINVAL; 7256748d055SPeter Ujfalusi } 7266748d055SPeter Ujfalusi 7276748d055SPeter Ujfalusi codeca_node = of_parse_phandle(node, "ti,ivi-codec-a", 0); 7286748d055SPeter Ujfalusi if (!codeca_node) { 7296748d055SPeter Ujfalusi dev_err(priv->dev, "IVI codec-a node is not provided\n"); 7306748d055SPeter Ujfalusi return -EINVAL; 7316748d055SPeter Ujfalusi } 7326748d055SPeter Ujfalusi 7336748d055SPeter Ujfalusi codecb_node = of_parse_phandle(node, "ti,ivi-codec-b", 0); 7346748d055SPeter Ujfalusi if (!codecb_node) { 7356748d055SPeter Ujfalusi dev_warn(priv->dev, "IVI codec-b node is not provided\n"); 7366748d055SPeter Ujfalusi return 0; 7376748d055SPeter Ujfalusi } 7386748d055SPeter Ujfalusi 7396748d055SPeter Ujfalusi domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_IVI]; 7406748d055SPeter Ujfalusi ret = j721e_get_clocks(priv->dev, &domain->codec, "ivi-codec-scki"); 7416748d055SPeter Ujfalusi if (ret) 7426748d055SPeter Ujfalusi return ret; 7436748d055SPeter Ujfalusi 7446748d055SPeter Ujfalusi ret = j721e_get_clocks(priv->dev, &domain->mcasp, "ivi-mcasp-auxclk"); 7456748d055SPeter Ujfalusi if (ret) 7466748d055SPeter Ujfalusi return ret; 7476748d055SPeter Ujfalusi 7486748d055SPeter Ujfalusi /* 7496748d055SPeter Ujfalusi * IVI extension, two links 7506748d055SPeter Ujfalusi * Link 1: McASP0 -> pcm3168a_a DAC 7516748d055SPeter Ujfalusi * \> pcm3168a_b DAC 7526748d055SPeter Ujfalusi * Link 2: McASP0 <- pcm3168a_a ADC 7536748d055SPeter Ujfalusi * \ pcm3168a_b ADC 7546748d055SPeter Ujfalusi */ 7556748d055SPeter Ujfalusi comp_count = 8; 7566748d055SPeter Ujfalusi compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), 7576748d055SPeter Ujfalusi GFP_KERNEL); 7586748d055SPeter Ujfalusi if (!compnent) 7596748d055SPeter Ujfalusi return -ENOMEM; 7606748d055SPeter Ujfalusi 7616748d055SPeter Ujfalusi comp_idx = 0; 7626748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 7636748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_cpus = 1; 7646748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 7656748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_platforms = 1; 7666748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs = &compnent[comp_idx]; 7676748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_codecs = 2; 7686748d055SPeter Ujfalusi comp_idx += 2; 7696748d055SPeter Ujfalusi 7706748d055SPeter Ujfalusi priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Playback"; 7716748d055SPeter Ujfalusi priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog"; 7726748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus->of_node = dai_node; 7736748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms->of_node = dai_node; 7746748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[0].of_node = codeca_node; 7756748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-dac"; 7766748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[1].of_node = codecb_node; 7776748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-dac"; 7786748d055SPeter Ujfalusi priv->dai_links[*link_idx].playback_only = 1; 7796748d055SPeter Ujfalusi priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI; 7806748d055SPeter Ujfalusi priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 7816748d055SPeter Ujfalusi priv->dai_links[*link_idx].init = j721e_audio_init_ivi; 7826748d055SPeter Ujfalusi priv->dai_links[*link_idx].ops = &j721e_audio_ops; 7836748d055SPeter Ujfalusi (*link_idx)++; 7846748d055SPeter Ujfalusi 7856748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 7866748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_cpus = 1; 7876748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 7886748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_platforms = 1; 7896748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs = &compnent[comp_idx]; 7906748d055SPeter Ujfalusi priv->dai_links[*link_idx].num_codecs = 2; 7916748d055SPeter Ujfalusi 7926748d055SPeter Ujfalusi priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Capture"; 7936748d055SPeter Ujfalusi priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog"; 7946748d055SPeter Ujfalusi priv->dai_links[*link_idx].cpus->of_node = dai_node; 7956748d055SPeter Ujfalusi priv->dai_links[*link_idx].platforms->of_node = dai_node; 7966748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[0].of_node = codeca_node; 7976748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-adc"; 7986748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[1].of_node = codecb_node; 7996748d055SPeter Ujfalusi priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-adc"; 8006748d055SPeter Ujfalusi priv->dai_links[*link_idx].capture_only = 1; 8016748d055SPeter Ujfalusi priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI; 8026748d055SPeter Ujfalusi priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 8036748d055SPeter Ujfalusi priv->dai_links[*link_idx].init = j721e_audio_init; 8046748d055SPeter Ujfalusi priv->dai_links[*link_idx].ops = &j721e_audio_ops; 8056748d055SPeter Ujfalusi (*link_idx)++; 8066748d055SPeter Ujfalusi 8076748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].dlc.of_node = codeca_node; 8086748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].name_prefix = "codec-a"; 8096748d055SPeter Ujfalusi (*conf_idx)++; 8106748d055SPeter Ujfalusi 8116748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].dlc.of_node = codecb_node; 8126748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].name_prefix = "codec-b"; 8136748d055SPeter Ujfalusi (*conf_idx)++; 8146748d055SPeter Ujfalusi 8156748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].dlc.of_node = dai_node; 8166748d055SPeter Ujfalusi priv->codec_conf[*conf_idx].name_prefix = "McASP0"; 8176748d055SPeter Ujfalusi (*conf_idx)++; 8186748d055SPeter Ujfalusi 8196748d055SPeter Ujfalusi return 0; 8206748d055SPeter Ujfalusi } 8216748d055SPeter Ujfalusi 8226748d055SPeter Ujfalusi static int j721e_soc_probe(struct platform_device *pdev) 8236748d055SPeter Ujfalusi { 8246748d055SPeter Ujfalusi struct device_node *node = pdev->dev.of_node; 8256748d055SPeter Ujfalusi struct snd_soc_card *card; 8266748d055SPeter Ujfalusi const struct of_device_id *match; 8276748d055SPeter Ujfalusi struct j721e_priv *priv; 8286748d055SPeter Ujfalusi int link_cnt, conf_cnt, ret; 8296748d055SPeter Ujfalusi 8306748d055SPeter Ujfalusi if (!node) { 8316748d055SPeter Ujfalusi dev_err(&pdev->dev, "of node is missing.\n"); 8326748d055SPeter Ujfalusi return -ENODEV; 8336748d055SPeter Ujfalusi } 8346748d055SPeter Ujfalusi 8356748d055SPeter Ujfalusi match = of_match_node(j721e_audio_of_match, node); 8366748d055SPeter Ujfalusi if (!match) { 8376748d055SPeter Ujfalusi dev_err(&pdev->dev, "No compatible match found\n"); 8386748d055SPeter Ujfalusi return -ENODEV; 8396748d055SPeter Ujfalusi } 8406748d055SPeter Ujfalusi 8416748d055SPeter Ujfalusi priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 8426748d055SPeter Ujfalusi if (!priv) 8436748d055SPeter Ujfalusi return -ENOMEM; 8446748d055SPeter Ujfalusi 8456748d055SPeter Ujfalusi priv->match_data = match->data; 8466748d055SPeter Ujfalusi 8476748d055SPeter Ujfalusi priv->dai_links = devm_kcalloc(&pdev->dev, priv->match_data->num_links, 8486748d055SPeter Ujfalusi sizeof(*priv->dai_links), GFP_KERNEL); 8496748d055SPeter Ujfalusi if (!priv->dai_links) 8506748d055SPeter Ujfalusi return -ENOMEM; 8516748d055SPeter Ujfalusi 8526748d055SPeter Ujfalusi priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].parent_clk_id = -1; 8536748d055SPeter Ujfalusi priv->audio_domains[J721E_AUDIO_DOMAIN_IVI].parent_clk_id = -1; 8546748d055SPeter Ujfalusi priv->dev = &pdev->dev; 8556748d055SPeter Ujfalusi card = &priv->card; 8566748d055SPeter Ujfalusi card->dev = &pdev->dev; 8576748d055SPeter Ujfalusi card->owner = THIS_MODULE; 8586748d055SPeter Ujfalusi card->dapm_widgets = j721e_cpb_dapm_widgets; 8596748d055SPeter Ujfalusi card->num_dapm_widgets = ARRAY_SIZE(j721e_cpb_dapm_widgets); 8606748d055SPeter Ujfalusi card->dapm_routes = j721e_cpb_dapm_routes; 8616748d055SPeter Ujfalusi card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); 8626748d055SPeter Ujfalusi card->fully_routed = 1; 8636748d055SPeter Ujfalusi 8646748d055SPeter Ujfalusi if (snd_soc_of_parse_card_name(card, "model")) { 8656748d055SPeter Ujfalusi dev_err(&pdev->dev, "Card name is not provided\n"); 8666748d055SPeter Ujfalusi return -ENODEV; 8676748d055SPeter Ujfalusi } 8686748d055SPeter Ujfalusi 8696748d055SPeter Ujfalusi link_cnt = 0; 8706748d055SPeter Ujfalusi conf_cnt = 0; 8716748d055SPeter Ujfalusi ret = j721e_soc_probe_cpb(priv, &link_cnt, &conf_cnt); 8726748d055SPeter Ujfalusi if (ret) 8736748d055SPeter Ujfalusi return ret; 8746748d055SPeter Ujfalusi 8756748d055SPeter Ujfalusi ret = j721e_soc_probe_ivi(priv, &link_cnt, &conf_cnt); 8766748d055SPeter Ujfalusi if (ret) 8776748d055SPeter Ujfalusi return ret; 8786748d055SPeter Ujfalusi 8796748d055SPeter Ujfalusi card->dai_link = priv->dai_links; 8806748d055SPeter Ujfalusi card->num_links = link_cnt; 8816748d055SPeter Ujfalusi 8826748d055SPeter Ujfalusi card->codec_conf = priv->codec_conf; 8836748d055SPeter Ujfalusi card->num_configs = conf_cnt; 8846748d055SPeter Ujfalusi 8856748d055SPeter Ujfalusi ret = j721e_calculate_rate_range(priv); 8866748d055SPeter Ujfalusi if (ret) 8876748d055SPeter Ujfalusi return ret; 8886748d055SPeter Ujfalusi 8896748d055SPeter Ujfalusi snd_soc_card_set_drvdata(card, priv); 8906748d055SPeter Ujfalusi 8916748d055SPeter Ujfalusi mutex_init(&priv->mutex); 8926748d055SPeter Ujfalusi ret = devm_snd_soc_register_card(&pdev->dev, card); 8936748d055SPeter Ujfalusi if (ret) 8946748d055SPeter Ujfalusi dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", 8956748d055SPeter Ujfalusi ret); 8966748d055SPeter Ujfalusi 8976748d055SPeter Ujfalusi return ret; 8986748d055SPeter Ujfalusi } 8996748d055SPeter Ujfalusi 9006748d055SPeter Ujfalusi static struct platform_driver j721e_soc_driver = { 9016748d055SPeter Ujfalusi .driver = { 9026748d055SPeter Ujfalusi .name = "j721e-audio", 9036748d055SPeter Ujfalusi .pm = &snd_soc_pm_ops, 9045ec3c854SPeter Ujfalusi .of_match_table = j721e_audio_of_match, 9056748d055SPeter Ujfalusi }, 9066748d055SPeter Ujfalusi .probe = j721e_soc_probe, 9076748d055SPeter Ujfalusi }; 9086748d055SPeter Ujfalusi 9096748d055SPeter Ujfalusi module_platform_driver(j721e_soc_driver); 9106748d055SPeter Ujfalusi 9116748d055SPeter Ujfalusi MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 9126748d055SPeter Ujfalusi MODULE_DESCRIPTION("ASoC machine driver for j721e Common Processor Board"); 9136748d055SPeter Ujfalusi MODULE_LICENSE("GPL v2"); 914