678 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.4//5// Authors: Inha Song <ideal.song@samsung.com>6// Sylwester Nawrocki <s.nawrocki@samsung.com>7 8#include <linux/clk.h>9#include <linux/gpio.h>10#include <linux/gpio/consumer.h>11#include <linux/module.h>12#include <linux/of.h>13#include <sound/pcm_params.h>14#include <sound/soc.h>15 16#include "i2s.h"17#include "../codecs/wm5110.h"18 19/*20 * The source clock is XCLKOUT with its mux set to the external fixed rate21 * oscillator (XXTI).22 */23#define MCLK_RATE 24000000U24 25#define TM2_DAI_AIF1 026#define TM2_DAI_AIF2 127 28struct tm2_machine_priv {29 struct snd_soc_component *component;30 unsigned int sysclk_rate;31 struct gpio_desc *gpio_mic_bias;32};33 34static int tm2_start_sysclk(struct snd_soc_card *card)35{36 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);37 struct snd_soc_component *component = priv->component;38 int ret;39 40 ret = snd_soc_component_set_pll(component, WM5110_FLL1_REFCLK,41 ARIZONA_FLL_SRC_MCLK1,42 MCLK_RATE,43 priv->sysclk_rate);44 if (ret < 0) {45 dev_err(component->dev, "Failed to set FLL1 source: %d\n", ret);46 return ret;47 }48 49 ret = snd_soc_component_set_pll(component, WM5110_FLL1,50 ARIZONA_FLL_SRC_MCLK1,51 MCLK_RATE,52 priv->sysclk_rate);53 if (ret < 0) {54 dev_err(component->dev, "Failed to start FLL1: %d\n", ret);55 return ret;56 }57 58 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,59 ARIZONA_CLK_SRC_FLL1,60 priv->sysclk_rate,61 SND_SOC_CLOCK_IN);62 if (ret < 0) {63 dev_err(component->dev, "Failed to set SYSCLK source: %d\n", ret);64 return ret;65 }66 67 return 0;68}69 70static int tm2_stop_sysclk(struct snd_soc_card *card)71{72 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);73 struct snd_soc_component *component = priv->component;74 int ret;75 76 ret = snd_soc_component_set_pll(component, WM5110_FLL1, 0, 0, 0);77 if (ret < 0) {78 dev_err(component->dev, "Failed to stop FLL1: %d\n", ret);79 return ret;80 }81 82 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,83 ARIZONA_CLK_SRC_FLL1, 0, 0);84 if (ret < 0) {85 dev_err(component->dev, "Failed to stop SYSCLK: %d\n", ret);86 return ret;87 }88 89 return 0;90}91 92static int tm2_aif1_hw_params(struct snd_pcm_substream *substream,93 struct snd_pcm_hw_params *params)94{95 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);96 struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;97 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card);98 99 switch (params_rate(params)) {100 case 4000:101 case 8000:102 case 12000:103 case 16000:104 case 24000:105 case 32000:106 case 48000:107 case 96000:108 case 192000:109 /* Highest possible SYSCLK frequency: 147.456MHz */110 priv->sysclk_rate = 147456000U;111 break;112 case 11025:113 case 22050:114 case 44100:115 case 88200:116 case 176400:117 /* Highest possible SYSCLK frequency: 135.4752 MHz */118 priv->sysclk_rate = 135475200U;119 break;120 default:121 dev_err(component->dev, "Not supported sample rate: %d\n",122 params_rate(params));123 return -EINVAL;124 }125 126 return tm2_start_sysclk(rtd->card);127}128 129static const struct snd_soc_ops tm2_aif1_ops = {130 .hw_params = tm2_aif1_hw_params,131};132 133static int tm2_aif2_hw_params(struct snd_pcm_substream *substream,134 struct snd_pcm_hw_params *params)135{136 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);137 struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;138 unsigned int asyncclk_rate;139 int ret;140 141 switch (params_rate(params)) {142 case 8000:143 case 12000:144 case 16000:145 /* Highest possible ASYNCCLK frequency: 49.152MHz */146 asyncclk_rate = 49152000U;147 break;148 case 11025:149 /* Highest possible ASYNCCLK frequency: 45.1584 MHz */150 asyncclk_rate = 45158400U;151 break;152 default:153 dev_err(component->dev, "Not supported sample rate: %d\n",154 params_rate(params));155 return -EINVAL;156 }157 158 ret = snd_soc_component_set_pll(component, WM5110_FLL2_REFCLK,159 ARIZONA_FLL_SRC_MCLK1,160 MCLK_RATE,161 asyncclk_rate);162 if (ret < 0) {163 dev_err(component->dev, "Failed to set FLL2 source: %d\n", ret);164 return ret;165 }166 167 ret = snd_soc_component_set_pll(component, WM5110_FLL2,168 ARIZONA_FLL_SRC_MCLK1,169 MCLK_RATE,170 asyncclk_rate);171 if (ret < 0) {172 dev_err(component->dev, "Failed to start FLL2: %d\n", ret);173 return ret;174 }175 176 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK,177 ARIZONA_CLK_SRC_FLL2,178 asyncclk_rate,179 SND_SOC_CLOCK_IN);180 if (ret < 0) {181 dev_err(component->dev, "Failed to set ASYNCCLK source: %d\n", ret);182 return ret;183 }184 185 return 0;186}187 188static int tm2_aif2_hw_free(struct snd_pcm_substream *substream)189{190 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);191 struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;192 int ret;193 194 /* disable FLL2 */195 ret = snd_soc_component_set_pll(component, WM5110_FLL2, ARIZONA_FLL_SRC_MCLK1,196 0, 0);197 if (ret < 0)198 dev_err(component->dev, "Failed to stop FLL2: %d\n", ret);199 200 return ret;201}202 203static const struct snd_soc_ops tm2_aif2_ops = {204 .hw_params = tm2_aif2_hw_params,205 .hw_free = tm2_aif2_hw_free,206};207 208static int tm2_hdmi_hw_params(struct snd_pcm_substream *substream,209 struct snd_pcm_hw_params *params)210{211 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);212 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);213 unsigned int bfs;214 int bitwidth, ret;215 216 bitwidth = snd_pcm_format_width(params_format(params));217 if (bitwidth < 0) {218 dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth);219 return bitwidth;220 }221 222 switch (bitwidth) {223 case 48:224 bfs = 64;225 break;226 case 16:227 bfs = 32;228 break;229 default:230 dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth);231 return -EINVAL;232 }233 234 switch (params_rate(params)) {235 case 48000:236 case 96000:237 case 192000:238 break;239 default:240 dev_err(rtd->card->dev, "Unsupported sample rate: %d\n",241 params_rate(params));242 return -EINVAL;243 }244 245 ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK,246 0, SAMSUNG_I2S_OPCLK_PCLK);247 if (ret < 0)248 return ret;249 250 ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs);251 if (ret < 0)252 return ret;253 254 return 0;255}256 257static const struct snd_soc_ops tm2_hdmi_ops = {258 .hw_params = tm2_hdmi_hw_params,259};260 261static int tm2_mic_bias(struct snd_soc_dapm_widget *w,262 struct snd_kcontrol *kcontrol, int event)263{264 struct snd_soc_card *card = w->dapm->card;265 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);266 267 switch (event) {268 case SND_SOC_DAPM_PRE_PMU:269 gpiod_set_value_cansleep(priv->gpio_mic_bias, 1);270 break;271 case SND_SOC_DAPM_POST_PMD:272 gpiod_set_value_cansleep(priv->gpio_mic_bias, 0);273 break;274 }275 276 return 0;277}278 279static int tm2_set_bias_level(struct snd_soc_card *card,280 struct snd_soc_dapm_context *dapm,281 enum snd_soc_bias_level level)282{283 struct snd_soc_pcm_runtime *rtd;284 285 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);286 287 if (dapm->dev != snd_soc_rtd_to_codec(rtd, 0)->dev)288 return 0;289 290 switch (level) {291 case SND_SOC_BIAS_STANDBY:292 if (card->dapm.bias_level == SND_SOC_BIAS_OFF)293 tm2_start_sysclk(card);294 break;295 case SND_SOC_BIAS_OFF:296 tm2_stop_sysclk(card);297 break;298 default:299 break;300 }301 302 return 0;303}304 305static struct snd_soc_aux_dev tm2_speaker_amp_dev;306 307static int tm2_late_probe(struct snd_soc_card *card)308{309 struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);310 unsigned int ch_map[] = { 0, 1 };311 struct snd_soc_dai *amp_pdm_dai;312 struct snd_soc_pcm_runtime *rtd;313 struct snd_soc_dai *aif1_dai;314 struct snd_soc_dai *aif2_dai;315 int ret;316 317 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF1]);318 aif1_dai = snd_soc_rtd_to_codec(rtd, 0);319 priv->component = snd_soc_rtd_to_codec(rtd, 0)->component;320 321 ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);322 if (ret < 0) {323 dev_err(aif1_dai->dev, "Failed to set SYSCLK: %d\n", ret);324 return ret;325 }326 327 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF2]);328 aif2_dai = snd_soc_rtd_to_codec(rtd, 0);329 330 ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);331 if (ret < 0) {332 dev_err(aif2_dai->dev, "Failed to set ASYNCCLK: %d\n", ret);333 return ret;334 }335 336 amp_pdm_dai = snd_soc_find_dai(&tm2_speaker_amp_dev.dlc);337 if (!amp_pdm_dai)338 return -ENODEV;339 340 /* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */341 ret = snd_soc_dai_set_channel_map(amp_pdm_dai, ARRAY_SIZE(ch_map),342 ch_map, 0, NULL);343 if (ret < 0)344 return ret;345 346 ret = snd_soc_dai_set_tdm_slot(amp_pdm_dai, 0x3, 0x0, 2, 16);347 if (ret < 0)348 return ret;349 350 return 0;351}352 353static const struct snd_kcontrol_new tm2_controls[] = {354 SOC_DAPM_PIN_SWITCH("HP"),355 SOC_DAPM_PIN_SWITCH("SPK"),356 SOC_DAPM_PIN_SWITCH("RCV"),357 SOC_DAPM_PIN_SWITCH("VPS"),358 SOC_DAPM_PIN_SWITCH("HDMI"),359 360 SOC_DAPM_PIN_SWITCH("Main Mic"),361 SOC_DAPM_PIN_SWITCH("Sub Mic"),362 SOC_DAPM_PIN_SWITCH("Third Mic"),363 364 SOC_DAPM_PIN_SWITCH("Headset Mic"),365};366 367static const struct snd_soc_dapm_widget tm2_dapm_widgets[] = {368 SND_SOC_DAPM_HP("HP", NULL),369 SND_SOC_DAPM_SPK("SPK", NULL),370 SND_SOC_DAPM_SPK("RCV", NULL),371 SND_SOC_DAPM_LINE("VPS", NULL),372 SND_SOC_DAPM_LINE("HDMI", NULL),373 374 SND_SOC_DAPM_MIC("Main Mic", tm2_mic_bias),375 SND_SOC_DAPM_MIC("Sub Mic", NULL),376 SND_SOC_DAPM_MIC("Third Mic", NULL),377 378 SND_SOC_DAPM_MIC("Headset Mic", NULL),379};380 381static const struct snd_soc_component_driver tm2_component = {382 .name = "tm2-audio",383};384 385static struct snd_soc_dai_driver tm2_ext_dai[] = {386 {387 .name = "Voice call",388 .playback = {389 .channels_min = 1,390 .channels_max = 4,391 .rate_min = 8000,392 .rate_max = 48000,393 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |394 SNDRV_PCM_RATE_48000),395 .formats = SNDRV_PCM_FMTBIT_S16_LE,396 },397 .capture = {398 .channels_min = 1,399 .channels_max = 4,400 .rate_min = 8000,401 .rate_max = 48000,402 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |403 SNDRV_PCM_RATE_48000),404 .formats = SNDRV_PCM_FMTBIT_S16_LE,405 },406 },407 {408 .name = "Bluetooth",409 .playback = {410 .channels_min = 1,411 .channels_max = 4,412 .rate_min = 8000,413 .rate_max = 16000,414 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),415 .formats = SNDRV_PCM_FMTBIT_S16_LE,416 },417 .capture = {418 .channels_min = 1,419 .channels_max = 2,420 .rate_min = 8000,421 .rate_max = 16000,422 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),423 .formats = SNDRV_PCM_FMTBIT_S16_LE,424 },425 },426};427 428SND_SOC_DAILINK_DEFS(aif1,429 DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),430 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif1")),431 DAILINK_COMP_ARRAY(COMP_EMPTY()));432 433SND_SOC_DAILINK_DEFS(voice,434 DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),435 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif2")),436 DAILINK_COMP_ARRAY(COMP_EMPTY()));437 438SND_SOC_DAILINK_DEFS(bt,439 DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),440 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm5110-aif3")),441 DAILINK_COMP_ARRAY(COMP_EMPTY()));442 443SND_SOC_DAILINK_DEFS(hdmi,444 DAILINK_COMP_ARRAY(COMP_EMPTY()),445 DAILINK_COMP_ARRAY(COMP_EMPTY()),446 DAILINK_COMP_ARRAY(COMP_EMPTY()));447 448static struct snd_soc_dai_link tm2_dai_links[] = {449 {450 .name = "WM5110 AIF1",451 .stream_name = "HiFi Primary",452 .ops = &tm2_aif1_ops,453 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |454 SND_SOC_DAIFMT_CBM_CFM,455 SND_SOC_DAILINK_REG(aif1),456 }, {457 .name = "WM5110 Voice",458 .stream_name = "Voice call",459 .ops = &tm2_aif2_ops,460 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |461 SND_SOC_DAIFMT_CBM_CFM,462 .ignore_suspend = 1,463 SND_SOC_DAILINK_REG(voice),464 }, {465 .name = "WM5110 BT",466 .stream_name = "Bluetooth",467 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |468 SND_SOC_DAIFMT_CBM_CFM,469 .ignore_suspend = 1,470 SND_SOC_DAILINK_REG(bt),471 }, {472 .name = "HDMI",473 .stream_name = "i2s1",474 .ops = &tm2_hdmi_ops,475 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |476 SND_SOC_DAIFMT_CBS_CFS,477 SND_SOC_DAILINK_REG(hdmi),478 }479};480 481static struct snd_soc_card tm2_card = {482 .owner = THIS_MODULE,483 484 .dai_link = tm2_dai_links,485 .controls = tm2_controls,486 .num_controls = ARRAY_SIZE(tm2_controls),487 .dapm_widgets = tm2_dapm_widgets,488 .num_dapm_widgets = ARRAY_SIZE(tm2_dapm_widgets),489 .aux_dev = &tm2_speaker_amp_dev,490 .num_aux_devs = 1,491 492 .late_probe = tm2_late_probe,493 .set_bias_level = tm2_set_bias_level,494};495 496static int tm2_probe(struct platform_device *pdev)497{498 struct device_node *cpu_dai_node[2] = {};499 struct device_node *codec_dai_node[2] = {};500 const char *cells_name = NULL;501 struct device *dev = &pdev->dev;502 struct snd_soc_card *card = &tm2_card;503 struct tm2_machine_priv *priv;504 struct snd_soc_dai_link *dai_link;505 int num_codecs, ret, i;506 507 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);508 if (!priv)509 return -ENOMEM;510 511 snd_soc_card_set_drvdata(card, priv);512 card->dev = dev;513 514 priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", GPIOD_OUT_HIGH);515 if (IS_ERR(priv->gpio_mic_bias)) {516 dev_err(dev, "Failed to get mic bias gpio\n");517 return PTR_ERR(priv->gpio_mic_bias);518 }519 520 ret = snd_soc_of_parse_card_name(card, "model");521 if (ret < 0) {522 dev_err(dev, "Card name is not specified\n");523 return ret;524 }525 526 ret = snd_soc_of_parse_audio_routing(card, "audio-routing");527 if (ret < 0) {528 /* Backwards compatible way */529 ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");530 if (ret < 0) {531 dev_err(dev, "Audio routing is not specified or invalid\n");532 return ret;533 }534 }535 536 card->aux_dev[0].dlc.of_node = of_parse_phandle(dev->of_node,537 "audio-amplifier", 0);538 if (!card->aux_dev[0].dlc.of_node) {539 dev_err(dev, "audio-amplifier property invalid or missing\n");540 return -EINVAL;541 }542 543 num_codecs = of_count_phandle_with_args(dev->of_node, "audio-codec",544 NULL);545 546 /* Skip the HDMI link if not specified in DT */547 if (num_codecs > 1) {548 card->num_links = ARRAY_SIZE(tm2_dai_links);549 cells_name = "#sound-dai-cells";550 } else {551 card->num_links = ARRAY_SIZE(tm2_dai_links) - 1;552 }553 554 for (i = 0; i < num_codecs; i++) {555 struct of_phandle_args args;556 557 ret = of_parse_phandle_with_args(dev->of_node, "i2s-controller",558 cells_name, i, &args);559 if (ret) {560 dev_err(dev, "i2s-controller property parse error: %d\n", i);561 ret = -EINVAL;562 goto dai_node_put;563 }564 cpu_dai_node[i] = args.np;565 566 codec_dai_node[i] = of_parse_phandle(dev->of_node,567 "audio-codec", i);568 if (!codec_dai_node[i]) {569 dev_err(dev, "audio-codec property parse error\n");570 ret = -EINVAL;571 goto dai_node_put;572 }573 }574 575 /* Initialize WM5110 - I2S and HDMI - I2S1 DAI links */576 for_each_card_prelinks(card, i, dai_link) {577 unsigned int dai_index = 0; /* WM5110 */578 579 dai_link->cpus->name = NULL;580 dai_link->platforms->name = NULL;581 582 if (num_codecs > 1 && i == card->num_links - 1)583 dai_index = 1; /* HDMI */584 585 dai_link->codecs->of_node = codec_dai_node[dai_index];586 dai_link->cpus->of_node = cpu_dai_node[dai_index];587 dai_link->platforms->of_node = cpu_dai_node[dai_index];588 }589 590 if (num_codecs > 1) {591 struct of_phandle_args args;592 593 /* HDMI DAI link (I2S1) */594 i = card->num_links - 1;595 596 ret = of_parse_phandle_with_fixed_args(dev->of_node,597 "audio-codec", 0, 1, &args);598 if (ret) {599 dev_err(dev, "audio-codec property parse error\n");600 goto dai_node_put;601 }602 603 ret = snd_soc_get_dai_name(&args, &card->dai_link[i].codecs->dai_name);604 if (ret) {605 dev_err(dev, "Unable to get codec_dai_name\n");606 goto dai_node_put;607 }608 }609 610 ret = devm_snd_soc_register_component(dev, &tm2_component,611 tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai));612 if (ret < 0) {613 dev_err(dev, "Failed to register component: %d\n", ret);614 goto dai_node_put;615 }616 617 ret = devm_snd_soc_register_card(dev, card);618 if (ret < 0) {619 dev_err_probe(dev, ret, "Failed to register card\n");620 goto dai_node_put;621 }622 623dai_node_put:624 for (i = 0; i < num_codecs; i++) {625 of_node_put(codec_dai_node[i]);626 of_node_put(cpu_dai_node[i]);627 }628 629 of_node_put(card->aux_dev[0].dlc.of_node);630 631 return ret;632}633 634static int tm2_pm_prepare(struct device *dev)635{636 struct snd_soc_card *card = dev_get_drvdata(dev);637 638 return tm2_stop_sysclk(card);639}640 641static void tm2_pm_complete(struct device *dev)642{643 struct snd_soc_card *card = dev_get_drvdata(dev);644 645 tm2_start_sysclk(card);646}647 648static const struct dev_pm_ops tm2_pm_ops = {649 .prepare = tm2_pm_prepare,650 .suspend = snd_soc_suspend,651 .resume = snd_soc_resume,652 .complete = tm2_pm_complete,653 .freeze = snd_soc_suspend,654 .thaw = snd_soc_resume,655 .poweroff = snd_soc_poweroff,656 .restore = snd_soc_resume,657};658 659static const struct of_device_id tm2_of_match[] = {660 { .compatible = "samsung,tm2-audio" },661 { },662};663MODULE_DEVICE_TABLE(of, tm2_of_match);664 665static struct platform_driver tm2_driver = {666 .driver = {667 .name = "tm2-audio",668 .pm = &tm2_pm_ops,669 .of_match_table = tm2_of_match,670 },671 .probe = tm2_probe,672};673module_platform_driver(tm2_driver);674 675MODULE_AUTHOR("Inha Song <ideal.song@samsung.com>");676MODULE_DESCRIPTION("ALSA SoC Exynos TM2 Audio Support");677MODULE_LICENSE("GPL v2");678