162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com 462306a36Sopenharmony_ci * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <sound/core.h> 1362306a36Sopenharmony_ci#include <sound/pcm.h> 1462306a36Sopenharmony_ci#include <sound/pcm_params.h> 1562306a36Sopenharmony_ci#include <sound/soc.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "davinci-mcasp.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Maximum number of configuration entries for prefixes: 2162306a36Sopenharmony_ci * CPB: 2 (mcasp10 + codec) 2262306a36Sopenharmony_ci * IVI: 3 (mcasp0 + 2x codec) 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define J721E_CODEC_CONF_COUNT 5 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cienum j721e_audio_domain_id { 2762306a36Sopenharmony_ci J721E_AUDIO_DOMAIN_CPB = 0, 2862306a36Sopenharmony_ci J721E_AUDIO_DOMAIN_IVI, 2962306a36Sopenharmony_ci J721E_AUDIO_DOMAIN_LAST, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define J721E_CLK_PARENT_48000 0 3362306a36Sopenharmony_ci#define J721E_CLK_PARENT_44100 1 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define J721E_MAX_CLK_HSDIV 128 3662306a36Sopenharmony_ci#define PCM1368A_MAX_SYSCLK 36864000 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define J721E_DAI_FMT (SND_SOC_DAIFMT_RIGHT_J | \ 3962306a36Sopenharmony_ci SND_SOC_DAIFMT_NB_NF | \ 4062306a36Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cienum j721e_board_type { 4362306a36Sopenharmony_ci J721E_BOARD_CPB = 1, 4462306a36Sopenharmony_ci J721E_BOARD_CPB_IVI, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct j721e_audio_match_data { 4862306a36Sopenharmony_ci enum j721e_board_type board_type; 4962306a36Sopenharmony_ci int num_links; 5062306a36Sopenharmony_ci unsigned int pll_rates[2]; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic unsigned int ratios_for_pcm3168a[] = { 5462306a36Sopenharmony_ci 256, 5562306a36Sopenharmony_ci 512, 5662306a36Sopenharmony_ci 768, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct j721e_audio_clocks { 6062306a36Sopenharmony_ci struct clk *target; 6162306a36Sopenharmony_ci struct clk *parent[2]; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct j721e_audio_domain { 6562306a36Sopenharmony_ci struct j721e_audio_clocks codec; 6662306a36Sopenharmony_ci struct j721e_audio_clocks mcasp; 6762306a36Sopenharmony_ci int parent_clk_id; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci int active; 7062306a36Sopenharmony_ci unsigned int active_link; 7162306a36Sopenharmony_ci unsigned int rate; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct j721e_priv { 7562306a36Sopenharmony_ci struct device *dev; 7662306a36Sopenharmony_ci struct snd_soc_card card; 7762306a36Sopenharmony_ci struct snd_soc_dai_link *dai_links; 7862306a36Sopenharmony_ci struct snd_soc_codec_conf codec_conf[J721E_CODEC_CONF_COUNT]; 7962306a36Sopenharmony_ci struct snd_interval rate_range; 8062306a36Sopenharmony_ci const struct j721e_audio_match_data *match_data; 8162306a36Sopenharmony_ci u32 pll_rates[2]; 8262306a36Sopenharmony_ci unsigned int hsdiv_rates[2]; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci struct j721e_audio_domain audio_domains[J721E_AUDIO_DOMAIN_LAST]; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci struct mutex mutex; 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget j721e_cpb_dapm_widgets[] = { 9062306a36Sopenharmony_ci SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL), 9162306a36Sopenharmony_ci SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL), 9262306a36Sopenharmony_ci SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL), 9362306a36Sopenharmony_ci SND_SOC_DAPM_LINE("CPB Line Out", NULL), 9462306a36Sopenharmony_ci SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL), 9562306a36Sopenharmony_ci SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL), 9662306a36Sopenharmony_ci SND_SOC_DAPM_LINE("CPB Line In", NULL), 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic const struct snd_soc_dapm_route j721e_cpb_dapm_routes[] = { 10062306a36Sopenharmony_ci {"CPB Stereo HP 1", NULL, "codec-1 AOUT1L"}, 10162306a36Sopenharmony_ci {"CPB Stereo HP 1", NULL, "codec-1 AOUT1R"}, 10262306a36Sopenharmony_ci {"CPB Stereo HP 2", NULL, "codec-1 AOUT2L"}, 10362306a36Sopenharmony_ci {"CPB Stereo HP 2", NULL, "codec-1 AOUT2R"}, 10462306a36Sopenharmony_ci {"CPB Stereo HP 3", NULL, "codec-1 AOUT3L"}, 10562306a36Sopenharmony_ci {"CPB Stereo HP 3", NULL, "codec-1 AOUT3R"}, 10662306a36Sopenharmony_ci {"CPB Line Out", NULL, "codec-1 AOUT4L"}, 10762306a36Sopenharmony_ci {"CPB Line Out", NULL, "codec-1 AOUT4R"}, 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci {"codec-1 AIN1L", NULL, "CPB Stereo Mic 1"}, 11062306a36Sopenharmony_ci {"codec-1 AIN1R", NULL, "CPB Stereo Mic 1"}, 11162306a36Sopenharmony_ci {"codec-1 AIN2L", NULL, "CPB Stereo Mic 2"}, 11262306a36Sopenharmony_ci {"codec-1 AIN2R", NULL, "CPB Stereo Mic 2"}, 11362306a36Sopenharmony_ci {"codec-1 AIN3L", NULL, "CPB Line In"}, 11462306a36Sopenharmony_ci {"codec-1 AIN3R", NULL, "CPB Line In"}, 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget j721e_ivi_codec_a_dapm_widgets[] = { 11862306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI A Line Out 1", NULL), 11962306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI A Line Out 2", NULL), 12062306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI A Line Out 3", NULL), 12162306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI A Line Out 4", NULL), 12262306a36Sopenharmony_ci SND_SOC_DAPM_MIC("IVI A Stereo Mic 1", NULL), 12362306a36Sopenharmony_ci SND_SOC_DAPM_MIC("IVI A Stereo Mic 2", NULL), 12462306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI A Line In", NULL), 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct snd_soc_dapm_route j721e_codec_a_dapm_routes[] = { 12862306a36Sopenharmony_ci {"IVI A Line Out 1", NULL, "codec-a AOUT1L"}, 12962306a36Sopenharmony_ci {"IVI A Line Out 1", NULL, "codec-a AOUT1R"}, 13062306a36Sopenharmony_ci {"IVI A Line Out 2", NULL, "codec-a AOUT2L"}, 13162306a36Sopenharmony_ci {"IVI A Line Out 2", NULL, "codec-a AOUT2R"}, 13262306a36Sopenharmony_ci {"IVI A Line Out 3", NULL, "codec-a AOUT3L"}, 13362306a36Sopenharmony_ci {"IVI A Line Out 3", NULL, "codec-a AOUT3R"}, 13462306a36Sopenharmony_ci {"IVI A Line Out 4", NULL, "codec-a AOUT4L"}, 13562306a36Sopenharmony_ci {"IVI A Line Out 4", NULL, "codec-a AOUT4R"}, 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci {"codec-a AIN1L", NULL, "IVI A Stereo Mic 1"}, 13862306a36Sopenharmony_ci {"codec-a AIN1R", NULL, "IVI A Stereo Mic 1"}, 13962306a36Sopenharmony_ci {"codec-a AIN2L", NULL, "IVI A Stereo Mic 2"}, 14062306a36Sopenharmony_ci {"codec-a AIN2R", NULL, "IVI A Stereo Mic 2"}, 14162306a36Sopenharmony_ci {"codec-a AIN3L", NULL, "IVI A Line In"}, 14262306a36Sopenharmony_ci {"codec-a AIN3R", NULL, "IVI A Line In"}, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget j721e_ivi_codec_b_dapm_widgets[] = { 14662306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI B Line Out 1", NULL), 14762306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI B Line Out 2", NULL), 14862306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI B Line Out 3", NULL), 14962306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI B Line Out 4", NULL), 15062306a36Sopenharmony_ci SND_SOC_DAPM_MIC("IVI B Stereo Mic 1", NULL), 15162306a36Sopenharmony_ci SND_SOC_DAPM_MIC("IVI B Stereo Mic 2", NULL), 15262306a36Sopenharmony_ci SND_SOC_DAPM_LINE("IVI B Line In", NULL), 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const struct snd_soc_dapm_route j721e_codec_b_dapm_routes[] = { 15662306a36Sopenharmony_ci {"IVI B Line Out 1", NULL, "codec-b AOUT1L"}, 15762306a36Sopenharmony_ci {"IVI B Line Out 1", NULL, "codec-b AOUT1R"}, 15862306a36Sopenharmony_ci {"IVI B Line Out 2", NULL, "codec-b AOUT2L"}, 15962306a36Sopenharmony_ci {"IVI B Line Out 2", NULL, "codec-b AOUT2R"}, 16062306a36Sopenharmony_ci {"IVI B Line Out 3", NULL, "codec-b AOUT3L"}, 16162306a36Sopenharmony_ci {"IVI B Line Out 3", NULL, "codec-b AOUT3R"}, 16262306a36Sopenharmony_ci {"IVI B Line Out 4", NULL, "codec-b AOUT4L"}, 16362306a36Sopenharmony_ci {"IVI B Line Out 4", NULL, "codec-b AOUT4R"}, 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci {"codec-b AIN1L", NULL, "IVI B Stereo Mic 1"}, 16662306a36Sopenharmony_ci {"codec-b AIN1R", NULL, "IVI B Stereo Mic 1"}, 16762306a36Sopenharmony_ci {"codec-b AIN2L", NULL, "IVI B Stereo Mic 2"}, 16862306a36Sopenharmony_ci {"codec-b AIN2R", NULL, "IVI B Stereo Mic 2"}, 16962306a36Sopenharmony_ci {"codec-b AIN3L", NULL, "IVI B Line In"}, 17062306a36Sopenharmony_ci {"codec-b AIN3R", NULL, "IVI B Line In"}, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int j721e_configure_refclk(struct j721e_priv *priv, 17462306a36Sopenharmony_ci unsigned int audio_domain, unsigned int rate) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct j721e_audio_domain *domain = &priv->audio_domains[audio_domain]; 17762306a36Sopenharmony_ci unsigned int scki; 17862306a36Sopenharmony_ci int ret = -EINVAL; 17962306a36Sopenharmony_ci int i, clk_id; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!(rate % 8000) && priv->pll_rates[J721E_CLK_PARENT_48000]) 18262306a36Sopenharmony_ci clk_id = J721E_CLK_PARENT_48000; 18362306a36Sopenharmony_ci else if (!(rate % 11025) && priv->pll_rates[J721E_CLK_PARENT_44100]) 18462306a36Sopenharmony_ci clk_id = J721E_CLK_PARENT_44100; 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci return ret; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ratios_for_pcm3168a); i++) { 18962306a36Sopenharmony_ci scki = ratios_for_pcm3168a[i] * rate; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (priv->pll_rates[clk_id] / scki <= J721E_MAX_CLK_HSDIV) { 19262306a36Sopenharmony_ci ret = 0; 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (ret) { 19862306a36Sopenharmony_ci dev_err(priv->dev, "No valid clock configuration for %u Hz\n", 19962306a36Sopenharmony_ci rate); 20062306a36Sopenharmony_ci return ret; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (domain->parent_clk_id == -1 || priv->hsdiv_rates[domain->parent_clk_id] != scki) { 20462306a36Sopenharmony_ci dev_dbg(priv->dev, 20562306a36Sopenharmony_ci "domain%u configuration for %u Hz: %s, %dxFS (SCKI: %u Hz)\n", 20662306a36Sopenharmony_ci audio_domain, rate, 20762306a36Sopenharmony_ci clk_id == J721E_CLK_PARENT_48000 ? "PLL4" : "PLL15", 20862306a36Sopenharmony_ci ratios_for_pcm3168a[i], scki); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (domain->parent_clk_id != clk_id) { 21162306a36Sopenharmony_ci ret = clk_set_parent(domain->codec.target, 21262306a36Sopenharmony_ci domain->codec.parent[clk_id]); 21362306a36Sopenharmony_ci if (ret) 21462306a36Sopenharmony_ci return ret; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = clk_set_parent(domain->mcasp.target, 21762306a36Sopenharmony_ci domain->mcasp.parent[clk_id]); 21862306a36Sopenharmony_ci if (ret) 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci domain->parent_clk_id = clk_id; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ret = clk_set_rate(domain->codec.target, scki); 22562306a36Sopenharmony_ci if (ret) { 22662306a36Sopenharmony_ci dev_err(priv->dev, "codec set rate failed for %u Hz\n", 22762306a36Sopenharmony_ci scki); 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = clk_set_rate(domain->mcasp.target, scki); 23262306a36Sopenharmony_ci if (!ret) { 23362306a36Sopenharmony_ci priv->hsdiv_rates[domain->parent_clk_id] = scki; 23462306a36Sopenharmony_ci } else { 23562306a36Sopenharmony_ci dev_err(priv->dev, "mcasp set rate failed for %u Hz\n", 23662306a36Sopenharmony_ci scki); 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int j721e_rule_rate(struct snd_pcm_hw_params *params, 24562306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct snd_interval *t = rule->private; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, rule->var), t); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int j721e_audio_startup(struct snd_pcm_substream *substream) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 25562306a36Sopenharmony_ci struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card); 25662306a36Sopenharmony_ci unsigned int domain_id = rtd->dai_link->id; 25762306a36Sopenharmony_ci struct j721e_audio_domain *domain = &priv->audio_domains[domain_id]; 25862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 25962306a36Sopenharmony_ci struct snd_soc_dai *codec_dai; 26062306a36Sopenharmony_ci unsigned int active_rate; 26162306a36Sopenharmony_ci int ret = 0; 26262306a36Sopenharmony_ci int i; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci mutex_lock(&priv->mutex); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci domain->active++; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci for (i = 0; i < J721E_AUDIO_DOMAIN_LAST; i++) { 26962306a36Sopenharmony_ci active_rate = priv->audio_domains[i].rate; 27062306a36Sopenharmony_ci if (active_rate) 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (active_rate) 27562306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_single(substream->runtime, 27662306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 27762306a36Sopenharmony_ci active_rate); 27862306a36Sopenharmony_ci else 27962306a36Sopenharmony_ci ret = snd_pcm_hw_rule_add(substream->runtime, 0, 28062306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 28162306a36Sopenharmony_ci j721e_rule_rate, &priv->rate_range, 28262306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (ret) 28662306a36Sopenharmony_ci goto out; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Reset TDM slots to 32 */ 28962306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32); 29062306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 29162306a36Sopenharmony_ci goto out; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 29462306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32); 29562306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 29662306a36Sopenharmony_ci goto out; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (ret == -ENOTSUPP) 30062306a36Sopenharmony_ci ret = 0; 30162306a36Sopenharmony_ciout: 30262306a36Sopenharmony_ci if (ret) 30362306a36Sopenharmony_ci domain->active--; 30462306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int j721e_audio_hw_params(struct snd_pcm_substream *substream, 31062306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 31362306a36Sopenharmony_ci struct snd_soc_card *card = rtd->card; 31462306a36Sopenharmony_ci struct j721e_priv *priv = snd_soc_card_get_drvdata(card); 31562306a36Sopenharmony_ci unsigned int domain_id = rtd->dai_link->id; 31662306a36Sopenharmony_ci struct j721e_audio_domain *domain = &priv->audio_domains[domain_id]; 31762306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 31862306a36Sopenharmony_ci struct snd_soc_dai *codec_dai; 31962306a36Sopenharmony_ci unsigned int sysclk_rate; 32062306a36Sopenharmony_ci int slot_width = 32; 32162306a36Sopenharmony_ci int ret; 32262306a36Sopenharmony_ci int i; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci mutex_lock(&priv->mutex); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (domain->rate && domain->rate != params_rate(params)) { 32762306a36Sopenharmony_ci ret = -EINVAL; 32862306a36Sopenharmony_ci goto out; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (params_width(params) == 16) 33262306a36Sopenharmony_ci slot_width = 16; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, slot_width); 33562306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 33662306a36Sopenharmony_ci goto out; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 33962306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 34062306a36Sopenharmony_ci slot_width); 34162306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 34262306a36Sopenharmony_ci goto out; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = j721e_configure_refclk(priv, domain_id, params_rate(params)); 34662306a36Sopenharmony_ci if (ret) 34762306a36Sopenharmony_ci goto out; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id]; 35062306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 35162306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate, 35262306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 35362306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) { 35462306a36Sopenharmony_ci dev_err(priv->dev, 35562306a36Sopenharmony_ci "codec set_sysclk failed for %u Hz\n", 35662306a36Sopenharmony_ci sysclk_rate); 35762306a36Sopenharmony_ci goto out; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK, 36262306a36Sopenharmony_ci sysclk_rate, SND_SOC_CLOCK_IN); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) { 36562306a36Sopenharmony_ci dev_err(priv->dev, "mcasp set_sysclk failed for %u Hz\n", 36662306a36Sopenharmony_ci sysclk_rate); 36762306a36Sopenharmony_ci } else { 36862306a36Sopenharmony_ci domain->rate = params_rate(params); 36962306a36Sopenharmony_ci ret = 0; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ciout: 37362306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void j721e_audio_shutdown(struct snd_pcm_substream *substream) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 38062306a36Sopenharmony_ci struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card); 38162306a36Sopenharmony_ci unsigned int domain_id = rtd->dai_link->id; 38262306a36Sopenharmony_ci struct j721e_audio_domain *domain = &priv->audio_domains[domain_id]; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci mutex_lock(&priv->mutex); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci domain->active--; 38762306a36Sopenharmony_ci if (!domain->active) { 38862306a36Sopenharmony_ci domain->rate = 0; 38962306a36Sopenharmony_ci domain->active_link = 0; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic const struct snd_soc_ops j721e_audio_ops = { 39662306a36Sopenharmony_ci .startup = j721e_audio_startup, 39762306a36Sopenharmony_ci .hw_params = j721e_audio_hw_params, 39862306a36Sopenharmony_ci .shutdown = j721e_audio_shutdown, 39962306a36Sopenharmony_ci}; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int j721e_audio_init(struct snd_soc_pcm_runtime *rtd) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card); 40462306a36Sopenharmony_ci unsigned int domain_id = rtd->dai_link->id; 40562306a36Sopenharmony_ci struct j721e_audio_domain *domain = &priv->audio_domains[domain_id]; 40662306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 40762306a36Sopenharmony_ci struct snd_soc_dai *codec_dai; 40862306a36Sopenharmony_ci unsigned int sysclk_rate; 40962306a36Sopenharmony_ci int i, ret; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Set up initial clock configuration */ 41262306a36Sopenharmony_ci ret = j721e_configure_refclk(priv, domain_id, 48000); 41362306a36Sopenharmony_ci if (ret) 41462306a36Sopenharmony_ci return ret; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id]; 41762306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 41862306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate, 41962306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 42062306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK, 42562306a36Sopenharmony_ci sysclk_rate, SND_SOC_CLOCK_IN); 42662306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Set initial tdm slots */ 43062306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32); 43162306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 43562306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32); 43662306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 43762306a36Sopenharmony_ci return ret; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int j721e_audio_init_ivi(struct snd_soc_pcm_runtime *rtd) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = &rtd->card->dapm; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_a_dapm_widgets, 44862306a36Sopenharmony_ci ARRAY_SIZE(j721e_ivi_codec_a_dapm_widgets)); 44962306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, j721e_codec_a_dapm_routes, 45062306a36Sopenharmony_ci ARRAY_SIZE(j721e_codec_a_dapm_routes)); 45162306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_b_dapm_widgets, 45262306a36Sopenharmony_ci ARRAY_SIZE(j721e_ivi_codec_b_dapm_widgets)); 45362306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, j721e_codec_b_dapm_routes, 45462306a36Sopenharmony_ci ARRAY_SIZE(j721e_codec_b_dapm_routes)); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return j721e_audio_init(rtd); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int j721e_get_clocks(struct device *dev, 46062306a36Sopenharmony_ci struct j721e_audio_clocks *clocks, char *prefix) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct clk *parent; 46362306a36Sopenharmony_ci char *clk_name; 46462306a36Sopenharmony_ci int ret; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci clocks->target = devm_clk_get(dev, prefix); 46762306a36Sopenharmony_ci if (IS_ERR(clocks->target)) 46862306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(clocks->target), 46962306a36Sopenharmony_ci "failed to acquire %s\n", prefix); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix); 47262306a36Sopenharmony_ci if (clk_name) { 47362306a36Sopenharmony_ci parent = devm_clk_get(dev, clk_name); 47462306a36Sopenharmony_ci kfree(clk_name); 47562306a36Sopenharmony_ci if (IS_ERR(parent)) { 47662306a36Sopenharmony_ci ret = PTR_ERR(parent); 47762306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci dev_dbg(dev, "no 48KHz parent for %s: %d\n", prefix, ret); 48162306a36Sopenharmony_ci parent = NULL; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci clocks->parent[J721E_CLK_PARENT_48000] = parent; 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci return -ENOMEM; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci clk_name = kasprintf(GFP_KERNEL, "%s-44100", prefix); 48962306a36Sopenharmony_ci if (clk_name) { 49062306a36Sopenharmony_ci parent = devm_clk_get(dev, clk_name); 49162306a36Sopenharmony_ci kfree(clk_name); 49262306a36Sopenharmony_ci if (IS_ERR(parent)) { 49362306a36Sopenharmony_ci ret = PTR_ERR(parent); 49462306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 49562306a36Sopenharmony_ci return ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci dev_dbg(dev, "no 44.1KHz parent for %s: %d\n", prefix, ret); 49862306a36Sopenharmony_ci parent = NULL; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci clocks->parent[J721E_CLK_PARENT_44100] = parent; 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci return -ENOMEM; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!clocks->parent[J721E_CLK_PARENT_44100] && 50662306a36Sopenharmony_ci !clocks->parent[J721E_CLK_PARENT_48000]) { 50762306a36Sopenharmony_ci dev_err(dev, "At least one parent clock is needed for %s\n", 50862306a36Sopenharmony_ci prefix); 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic const struct j721e_audio_match_data j721e_cpb_data = { 51662306a36Sopenharmony_ci .board_type = J721E_BOARD_CPB, 51762306a36Sopenharmony_ci .num_links = 2, /* CPB pcm3168a */ 51862306a36Sopenharmony_ci .pll_rates = { 51962306a36Sopenharmony_ci [J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */ 52062306a36Sopenharmony_ci [J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */ 52162306a36Sopenharmony_ci }, 52262306a36Sopenharmony_ci}; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic const struct j721e_audio_match_data j721e_cpb_ivi_data = { 52562306a36Sopenharmony_ci .board_type = J721E_BOARD_CPB_IVI, 52662306a36Sopenharmony_ci .num_links = 4, /* CPB pcm3168a + 2x pcm3168a on IVI */ 52762306a36Sopenharmony_ci .pll_rates = { 52862306a36Sopenharmony_ci [J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */ 52962306a36Sopenharmony_ci [J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */ 53062306a36Sopenharmony_ci }, 53162306a36Sopenharmony_ci}; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic const struct j721e_audio_match_data j7200_cpb_data = { 53462306a36Sopenharmony_ci .board_type = J721E_BOARD_CPB, 53562306a36Sopenharmony_ci .num_links = 2, /* CPB pcm3168a */ 53662306a36Sopenharmony_ci .pll_rates = { 53762306a36Sopenharmony_ci [J721E_CLK_PARENT_48000] = 2359296000u, /* PLL4 */ 53862306a36Sopenharmony_ci }, 53962306a36Sopenharmony_ci}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic const struct of_device_id j721e_audio_of_match[] = { 54262306a36Sopenharmony_ci { 54362306a36Sopenharmony_ci .compatible = "ti,j721e-cpb-audio", 54462306a36Sopenharmony_ci .data = &j721e_cpb_data, 54562306a36Sopenharmony_ci }, { 54662306a36Sopenharmony_ci .compatible = "ti,j721e-cpb-ivi-audio", 54762306a36Sopenharmony_ci .data = &j721e_cpb_ivi_data, 54862306a36Sopenharmony_ci }, { 54962306a36Sopenharmony_ci .compatible = "ti,j7200-cpb-audio", 55062306a36Sopenharmony_ci .data = &j7200_cpb_data, 55162306a36Sopenharmony_ci }, 55262306a36Sopenharmony_ci { }, 55362306a36Sopenharmony_ci}; 55462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, j721e_audio_of_match); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic int j721e_calculate_rate_range(struct j721e_priv *priv) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci const struct j721e_audio_match_data *match_data = priv->match_data; 55962306a36Sopenharmony_ci struct j721e_audio_clocks *domain_clocks; 56062306a36Sopenharmony_ci unsigned int min_rate, max_rate, pll_rate; 56162306a36Sopenharmony_ci struct clk *pll; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci domain_clocks = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].mcasp; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_44100]); 56662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(pll)) { 56762306a36Sopenharmony_ci priv->pll_rates[J721E_CLK_PARENT_44100] = 56862306a36Sopenharmony_ci match_data->pll_rates[J721E_CLK_PARENT_44100]; 56962306a36Sopenharmony_ci } else { 57062306a36Sopenharmony_ci priv->pll_rates[J721E_CLK_PARENT_44100] = clk_get_rate(pll); 57162306a36Sopenharmony_ci clk_put(pll); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_48000]); 57562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(pll)) { 57662306a36Sopenharmony_ci priv->pll_rates[J721E_CLK_PARENT_48000] = 57762306a36Sopenharmony_ci match_data->pll_rates[J721E_CLK_PARENT_48000]; 57862306a36Sopenharmony_ci } else { 57962306a36Sopenharmony_ci priv->pll_rates[J721E_CLK_PARENT_48000] = clk_get_rate(pll); 58062306a36Sopenharmony_ci clk_put(pll); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (!priv->pll_rates[J721E_CLK_PARENT_44100] && 58462306a36Sopenharmony_ci !priv->pll_rates[J721E_CLK_PARENT_48000]) { 58562306a36Sopenharmony_ci dev_err(priv->dev, "At least one PLL is needed\n"); 58662306a36Sopenharmony_ci return -EINVAL; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (priv->pll_rates[J721E_CLK_PARENT_44100]) 59062306a36Sopenharmony_ci pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100]; 59162306a36Sopenharmony_ci else 59262306a36Sopenharmony_ci pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000]; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci min_rate = pll_rate / J721E_MAX_CLK_HSDIV; 59562306a36Sopenharmony_ci min_rate /= ratios_for_pcm3168a[ARRAY_SIZE(ratios_for_pcm3168a) - 1]; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (priv->pll_rates[J721E_CLK_PARENT_48000]) 59862306a36Sopenharmony_ci pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000]; 59962306a36Sopenharmony_ci else 60062306a36Sopenharmony_ci pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100]; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (pll_rate > PCM1368A_MAX_SYSCLK) 60362306a36Sopenharmony_ci pll_rate = PCM1368A_MAX_SYSCLK; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci max_rate = pll_rate / ratios_for_pcm3168a[0]; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci snd_interval_any(&priv->rate_range); 60862306a36Sopenharmony_ci priv->rate_range.min = min_rate; 60962306a36Sopenharmony_ci priv->rate_range.max = max_rate; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx, 61562306a36Sopenharmony_ci int *conf_idx) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct device_node *node = priv->dev->of_node; 61862306a36Sopenharmony_ci struct snd_soc_dai_link_component *compnent; 61962306a36Sopenharmony_ci struct device_node *dai_node, *codec_node; 62062306a36Sopenharmony_ci struct j721e_audio_domain *domain; 62162306a36Sopenharmony_ci int comp_count, comp_idx; 62262306a36Sopenharmony_ci int ret; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci dai_node = of_parse_phandle(node, "ti,cpb-mcasp", 0); 62562306a36Sopenharmony_ci if (!dai_node) { 62662306a36Sopenharmony_ci dev_err(priv->dev, "CPB McASP node is not provided\n"); 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci codec_node = of_parse_phandle(node, "ti,cpb-codec", 0); 63162306a36Sopenharmony_ci if (!codec_node) { 63262306a36Sopenharmony_ci dev_err(priv->dev, "CPB codec node is not provided\n"); 63362306a36Sopenharmony_ci ret = -EINVAL; 63462306a36Sopenharmony_ci goto put_dai_node; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB]; 63862306a36Sopenharmony_ci ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki"); 63962306a36Sopenharmony_ci if (ret) 64062306a36Sopenharmony_ci goto put_codec_node; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk"); 64362306a36Sopenharmony_ci if (ret) 64462306a36Sopenharmony_ci goto put_codec_node; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * Common Processor Board, two links 64862306a36Sopenharmony_ci * Link 1: McASP10 -> pcm3168a_1 DAC 64962306a36Sopenharmony_ci * Link 2: McASP10 <- pcm3168a_1 ADC 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci comp_count = 6; 65262306a36Sopenharmony_ci compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), 65362306a36Sopenharmony_ci GFP_KERNEL); 65462306a36Sopenharmony_ci if (!compnent) { 65562306a36Sopenharmony_ci ret = -ENOMEM; 65662306a36Sopenharmony_ci goto put_codec_node; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci comp_idx = 0; 66062306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 66162306a36Sopenharmony_ci priv->dai_links[*link_idx].num_cpus = 1; 66262306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs = &compnent[comp_idx++]; 66362306a36Sopenharmony_ci priv->dai_links[*link_idx].num_codecs = 1; 66462306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 66562306a36Sopenharmony_ci priv->dai_links[*link_idx].num_platforms = 1; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci priv->dai_links[*link_idx].name = "CPB PCM3168A Playback"; 66862306a36Sopenharmony_ci priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog"; 66962306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus->of_node = dai_node; 67062306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms->of_node = dai_node; 67162306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs->of_node = codec_node; 67262306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-dac"; 67362306a36Sopenharmony_ci priv->dai_links[*link_idx].playback_only = 1; 67462306a36Sopenharmony_ci priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB; 67562306a36Sopenharmony_ci priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 67662306a36Sopenharmony_ci priv->dai_links[*link_idx].init = j721e_audio_init; 67762306a36Sopenharmony_ci priv->dai_links[*link_idx].ops = &j721e_audio_ops; 67862306a36Sopenharmony_ci (*link_idx)++; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 68162306a36Sopenharmony_ci priv->dai_links[*link_idx].num_cpus = 1; 68262306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs = &compnent[comp_idx++]; 68362306a36Sopenharmony_ci priv->dai_links[*link_idx].num_codecs = 1; 68462306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 68562306a36Sopenharmony_ci priv->dai_links[*link_idx].num_platforms = 1; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci priv->dai_links[*link_idx].name = "CPB PCM3168A Capture"; 68862306a36Sopenharmony_ci priv->dai_links[*link_idx].stream_name = "CPB PCM3168A Analog"; 68962306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus->of_node = dai_node; 69062306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms->of_node = dai_node; 69162306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs->of_node = codec_node; 69262306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs->dai_name = "pcm3168a-adc"; 69362306a36Sopenharmony_ci priv->dai_links[*link_idx].capture_only = 1; 69462306a36Sopenharmony_ci priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB; 69562306a36Sopenharmony_ci priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 69662306a36Sopenharmony_ci priv->dai_links[*link_idx].init = j721e_audio_init; 69762306a36Sopenharmony_ci priv->dai_links[*link_idx].ops = &j721e_audio_ops; 69862306a36Sopenharmony_ci (*link_idx)++; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci priv->codec_conf[*conf_idx].dlc.of_node = codec_node; 70162306a36Sopenharmony_ci priv->codec_conf[*conf_idx].name_prefix = "codec-1"; 70262306a36Sopenharmony_ci (*conf_idx)++; 70362306a36Sopenharmony_ci priv->codec_conf[*conf_idx].dlc.of_node = dai_node; 70462306a36Sopenharmony_ci priv->codec_conf[*conf_idx].name_prefix = "McASP10"; 70562306a36Sopenharmony_ci (*conf_idx)++; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ciput_codec_node: 71062306a36Sopenharmony_ci of_node_put(codec_node); 71162306a36Sopenharmony_ciput_dai_node: 71262306a36Sopenharmony_ci of_node_put(dai_node); 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, 71762306a36Sopenharmony_ci int *conf_idx) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct device_node *node = priv->dev->of_node; 72062306a36Sopenharmony_ci struct snd_soc_dai_link_component *compnent; 72162306a36Sopenharmony_ci struct device_node *dai_node, *codeca_node, *codecb_node; 72262306a36Sopenharmony_ci struct j721e_audio_domain *domain; 72362306a36Sopenharmony_ci int comp_count, comp_idx; 72462306a36Sopenharmony_ci int ret; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (priv->match_data->board_type != J721E_BOARD_CPB_IVI) 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci dai_node = of_parse_phandle(node, "ti,ivi-mcasp", 0); 73062306a36Sopenharmony_ci if (!dai_node) { 73162306a36Sopenharmony_ci dev_err(priv->dev, "IVI McASP node is not provided\n"); 73262306a36Sopenharmony_ci return -EINVAL; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci codeca_node = of_parse_phandle(node, "ti,ivi-codec-a", 0); 73662306a36Sopenharmony_ci if (!codeca_node) { 73762306a36Sopenharmony_ci dev_err(priv->dev, "IVI codec-a node is not provided\n"); 73862306a36Sopenharmony_ci ret = -EINVAL; 73962306a36Sopenharmony_ci goto put_dai_node; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci codecb_node = of_parse_phandle(node, "ti,ivi-codec-b", 0); 74362306a36Sopenharmony_ci if (!codecb_node) { 74462306a36Sopenharmony_ci dev_warn(priv->dev, "IVI codec-b node is not provided\n"); 74562306a36Sopenharmony_ci ret = 0; 74662306a36Sopenharmony_ci goto put_codeca_node; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_IVI]; 75062306a36Sopenharmony_ci ret = j721e_get_clocks(priv->dev, &domain->codec, "ivi-codec-scki"); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci goto put_codecb_node; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ret = j721e_get_clocks(priv->dev, &domain->mcasp, "ivi-mcasp-auxclk"); 75562306a36Sopenharmony_ci if (ret) 75662306a36Sopenharmony_ci goto put_codecb_node; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* 75962306a36Sopenharmony_ci * IVI extension, two links 76062306a36Sopenharmony_ci * Link 1: McASP0 -> pcm3168a_a DAC 76162306a36Sopenharmony_ci * \> pcm3168a_b DAC 76262306a36Sopenharmony_ci * Link 2: McASP0 <- pcm3168a_a ADC 76362306a36Sopenharmony_ci * \ pcm3168a_b ADC 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci comp_count = 8; 76662306a36Sopenharmony_ci compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), 76762306a36Sopenharmony_ci GFP_KERNEL); 76862306a36Sopenharmony_ci if (!compnent) { 76962306a36Sopenharmony_ci ret = -ENOMEM; 77062306a36Sopenharmony_ci goto put_codecb_node; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci comp_idx = 0; 77462306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 77562306a36Sopenharmony_ci priv->dai_links[*link_idx].num_cpus = 1; 77662306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 77762306a36Sopenharmony_ci priv->dai_links[*link_idx].num_platforms = 1; 77862306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs = &compnent[comp_idx]; 77962306a36Sopenharmony_ci priv->dai_links[*link_idx].num_codecs = 2; 78062306a36Sopenharmony_ci comp_idx += 2; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Playback"; 78362306a36Sopenharmony_ci priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog"; 78462306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus->of_node = dai_node; 78562306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms->of_node = dai_node; 78662306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[0].of_node = codeca_node; 78762306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-dac"; 78862306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[1].of_node = codecb_node; 78962306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-dac"; 79062306a36Sopenharmony_ci priv->dai_links[*link_idx].playback_only = 1; 79162306a36Sopenharmony_ci priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI; 79262306a36Sopenharmony_ci priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 79362306a36Sopenharmony_ci priv->dai_links[*link_idx].init = j721e_audio_init_ivi; 79462306a36Sopenharmony_ci priv->dai_links[*link_idx].ops = &j721e_audio_ops; 79562306a36Sopenharmony_ci (*link_idx)++; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; 79862306a36Sopenharmony_ci priv->dai_links[*link_idx].num_cpus = 1; 79962306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms = &compnent[comp_idx++]; 80062306a36Sopenharmony_ci priv->dai_links[*link_idx].num_platforms = 1; 80162306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs = &compnent[comp_idx]; 80262306a36Sopenharmony_ci priv->dai_links[*link_idx].num_codecs = 2; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci priv->dai_links[*link_idx].name = "IVI 2xPCM3168A Capture"; 80562306a36Sopenharmony_ci priv->dai_links[*link_idx].stream_name = "IVI 2xPCM3168A Analog"; 80662306a36Sopenharmony_ci priv->dai_links[*link_idx].cpus->of_node = dai_node; 80762306a36Sopenharmony_ci priv->dai_links[*link_idx].platforms->of_node = dai_node; 80862306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[0].of_node = codeca_node; 80962306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[0].dai_name = "pcm3168a-adc"; 81062306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[1].of_node = codecb_node; 81162306a36Sopenharmony_ci priv->dai_links[*link_idx].codecs[1].dai_name = "pcm3168a-adc"; 81262306a36Sopenharmony_ci priv->dai_links[*link_idx].capture_only = 1; 81362306a36Sopenharmony_ci priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_IVI; 81462306a36Sopenharmony_ci priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT; 81562306a36Sopenharmony_ci priv->dai_links[*link_idx].init = j721e_audio_init; 81662306a36Sopenharmony_ci priv->dai_links[*link_idx].ops = &j721e_audio_ops; 81762306a36Sopenharmony_ci (*link_idx)++; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci priv->codec_conf[*conf_idx].dlc.of_node = codeca_node; 82062306a36Sopenharmony_ci priv->codec_conf[*conf_idx].name_prefix = "codec-a"; 82162306a36Sopenharmony_ci (*conf_idx)++; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci priv->codec_conf[*conf_idx].dlc.of_node = codecb_node; 82462306a36Sopenharmony_ci priv->codec_conf[*conf_idx].name_prefix = "codec-b"; 82562306a36Sopenharmony_ci (*conf_idx)++; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci priv->codec_conf[*conf_idx].dlc.of_node = dai_node; 82862306a36Sopenharmony_ci priv->codec_conf[*conf_idx].name_prefix = "McASP0"; 82962306a36Sopenharmony_ci (*conf_idx)++; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ciput_codecb_node: 83562306a36Sopenharmony_ci of_node_put(codecb_node); 83662306a36Sopenharmony_ciput_codeca_node: 83762306a36Sopenharmony_ci of_node_put(codeca_node); 83862306a36Sopenharmony_ciput_dai_node: 83962306a36Sopenharmony_ci of_node_put(dai_node); 84062306a36Sopenharmony_ci return ret; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int j721e_soc_probe(struct platform_device *pdev) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 84662306a36Sopenharmony_ci struct snd_soc_card *card; 84762306a36Sopenharmony_ci const struct of_device_id *match; 84862306a36Sopenharmony_ci struct j721e_priv *priv; 84962306a36Sopenharmony_ci int link_cnt, conf_cnt, ret, i; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (!node) { 85262306a36Sopenharmony_ci dev_err(&pdev->dev, "of node is missing.\n"); 85362306a36Sopenharmony_ci return -ENODEV; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci match = of_match_node(j721e_audio_of_match, node); 85762306a36Sopenharmony_ci if (!match) { 85862306a36Sopenharmony_ci dev_err(&pdev->dev, "No compatible match found\n"); 85962306a36Sopenharmony_ci return -ENODEV; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 86362306a36Sopenharmony_ci if (!priv) 86462306a36Sopenharmony_ci return -ENOMEM; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci priv->match_data = match->data; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci priv->dai_links = devm_kcalloc(&pdev->dev, priv->match_data->num_links, 86962306a36Sopenharmony_ci sizeof(*priv->dai_links), GFP_KERNEL); 87062306a36Sopenharmony_ci if (!priv->dai_links) 87162306a36Sopenharmony_ci return -ENOMEM; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci for (i = 0; i < J721E_AUDIO_DOMAIN_LAST; i++) 87462306a36Sopenharmony_ci priv->audio_domains[i].parent_clk_id = -1; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci priv->dev = &pdev->dev; 87762306a36Sopenharmony_ci card = &priv->card; 87862306a36Sopenharmony_ci card->dev = &pdev->dev; 87962306a36Sopenharmony_ci card->owner = THIS_MODULE; 88062306a36Sopenharmony_ci card->dapm_widgets = j721e_cpb_dapm_widgets; 88162306a36Sopenharmony_ci card->num_dapm_widgets = ARRAY_SIZE(j721e_cpb_dapm_widgets); 88262306a36Sopenharmony_ci card->dapm_routes = j721e_cpb_dapm_routes; 88362306a36Sopenharmony_ci card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); 88462306a36Sopenharmony_ci card->fully_routed = 1; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (snd_soc_of_parse_card_name(card, "model")) { 88762306a36Sopenharmony_ci dev_err(&pdev->dev, "Card name is not provided\n"); 88862306a36Sopenharmony_ci return -ENODEV; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci link_cnt = 0; 89262306a36Sopenharmony_ci conf_cnt = 0; 89362306a36Sopenharmony_ci ret = j721e_soc_probe_cpb(priv, &link_cnt, &conf_cnt); 89462306a36Sopenharmony_ci if (ret) 89562306a36Sopenharmony_ci return ret; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci ret = j721e_soc_probe_ivi(priv, &link_cnt, &conf_cnt); 89862306a36Sopenharmony_ci if (ret) 89962306a36Sopenharmony_ci return ret; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci card->dai_link = priv->dai_links; 90262306a36Sopenharmony_ci card->num_links = link_cnt; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci card->codec_conf = priv->codec_conf; 90562306a36Sopenharmony_ci card->num_configs = conf_cnt; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci ret = j721e_calculate_rate_range(priv); 90862306a36Sopenharmony_ci if (ret) 90962306a36Sopenharmony_ci return ret; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci snd_soc_card_set_drvdata(card, priv); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci mutex_init(&priv->mutex); 91462306a36Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, card); 91562306a36Sopenharmony_ci if (ret) 91662306a36Sopenharmony_ci dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", 91762306a36Sopenharmony_ci ret); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return ret; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic struct platform_driver j721e_soc_driver = { 92362306a36Sopenharmony_ci .driver = { 92462306a36Sopenharmony_ci .name = "j721e-audio", 92562306a36Sopenharmony_ci .pm = &snd_soc_pm_ops, 92662306a36Sopenharmony_ci .of_match_table = j721e_audio_of_match, 92762306a36Sopenharmony_ci }, 92862306a36Sopenharmony_ci .probe = j721e_soc_probe, 92962306a36Sopenharmony_ci}; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cimodule_platform_driver(j721e_soc_driver); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ciMODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 93462306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC machine driver for j721e Common Processor Board"); 93562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 936