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