linux/sound/soc/samsung/tm2_wm5110.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.
   3 *
   4 * Authors: Inha Song <ideal.song@samsung.com>
   5 *          Sylwester Nawrocki <s.nawrocki@samsung.com>
   6 *
   7 * This program is free software; you can redistribute  it and/or modify it
   8 * under  the terms of  the GNU General  Public License as published by the
   9 * Free Software Foundation;  either version 2 of the  License, or (at your
  10 * option) any later version.
  11 */
  12
  13#include <linux/clk.h>
  14#include <linux/gpio.h>
  15#include <linux/gpio/consumer.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <sound/pcm_params.h>
  19#include <sound/soc.h>
  20
  21#include "i2s.h"
  22#include "../codecs/wm5110.h"
  23
  24/*
  25 * The source clock is XCLKOUT with its mux set to the external fixed rate
  26 * oscillator (XXTI).
  27 */
  28#define MCLK_RATE       24000000U
  29
  30#define TM2_DAI_AIF1    0
  31#define TM2_DAI_AIF2    1
  32
  33struct tm2_machine_priv {
  34        struct snd_soc_codec *codec;
  35        unsigned int sysclk_rate;
  36        struct gpio_desc *gpio_mic_bias;
  37};
  38
  39static int tm2_start_sysclk(struct snd_soc_card *card)
  40{
  41        struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
  42        struct snd_soc_codec *codec = priv->codec;
  43        int ret;
  44
  45        ret = snd_soc_codec_set_pll(codec, WM5110_FLL1_REFCLK,
  46                                    ARIZONA_FLL_SRC_MCLK1,
  47                                    MCLK_RATE,
  48                                    priv->sysclk_rate);
  49        if (ret < 0) {
  50                dev_err(codec->dev, "Failed to set FLL1 source: %d\n", ret);
  51                return ret;
  52        }
  53
  54        ret = snd_soc_codec_set_pll(codec, WM5110_FLL1,
  55                                    ARIZONA_FLL_SRC_MCLK1,
  56                                    MCLK_RATE,
  57                                    priv->sysclk_rate);
  58        if (ret < 0) {
  59                dev_err(codec->dev, "Failed to start FLL1: %d\n", ret);
  60                return ret;
  61        }
  62
  63        ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
  64                                       ARIZONA_CLK_SRC_FLL1,
  65                                       priv->sysclk_rate,
  66                                       SND_SOC_CLOCK_IN);
  67        if (ret < 0) {
  68                dev_err(codec->dev, "Failed to set SYSCLK source: %d\n", ret);
  69                return ret;
  70        }
  71
  72        return 0;
  73}
  74
  75static int tm2_stop_sysclk(struct snd_soc_card *card)
  76{
  77        struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
  78        struct snd_soc_codec *codec = priv->codec;
  79        int ret;
  80
  81        ret = snd_soc_codec_set_pll(codec, WM5110_FLL1, 0, 0, 0);
  82        if (ret < 0) {
  83                dev_err(codec->dev, "Failed to stop FLL1: %d\n", ret);
  84                return ret;
  85        }
  86
  87        ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
  88                                       ARIZONA_CLK_SRC_FLL1, 0, 0);
  89        if (ret < 0) {
  90                dev_err(codec->dev, "Failed to stop SYSCLK: %d\n", ret);
  91                return ret;
  92        }
  93
  94        return 0;
  95}
  96
  97static int tm2_aif1_hw_params(struct snd_pcm_substream *substream,
  98                                struct snd_pcm_hw_params *params)
  99{
 100        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 101        struct snd_soc_codec *codec = rtd->codec;
 102        struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card);
 103
 104        switch (params_rate(params)) {
 105        case 4000:
 106        case 8000:
 107        case 12000:
 108        case 16000:
 109        case 24000:
 110        case 32000:
 111        case 48000:
 112        case 96000:
 113        case 192000:
 114                /* Highest possible SYSCLK frequency: 147.456MHz */
 115                priv->sysclk_rate = 147456000U;
 116                break;
 117        case 11025:
 118        case 22050:
 119        case 44100:
 120        case 88200:
 121        case 176400:
 122                /* Highest possible SYSCLK frequency: 135.4752 MHz */
 123                priv->sysclk_rate = 135475200U;
 124                break;
 125        default:
 126                dev_err(codec->dev, "Not supported sample rate: %d\n",
 127                        params_rate(params));
 128                return -EINVAL;
 129        }
 130
 131        return tm2_start_sysclk(rtd->card);
 132}
 133
 134static struct snd_soc_ops tm2_aif1_ops = {
 135        .hw_params = tm2_aif1_hw_params,
 136};
 137
 138static int tm2_aif2_hw_params(struct snd_pcm_substream *substream,
 139                                struct snd_pcm_hw_params *params)
 140{
 141        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 142        struct snd_soc_codec *codec = rtd->codec;
 143        unsigned int asyncclk_rate;
 144        int ret;
 145
 146        switch (params_rate(params)) {
 147        case 8000:
 148        case 12000:
 149        case 16000:
 150                /* Highest possible ASYNCCLK frequency: 49.152MHz */
 151                asyncclk_rate = 49152000U;
 152                break;
 153        case 11025:
 154                /* Highest possible ASYNCCLK frequency: 45.1584 MHz */
 155                asyncclk_rate = 45158400U;
 156                break;
 157        default:
 158                dev_err(codec->dev, "Not supported sample rate: %d\n",
 159                        params_rate(params));
 160                return -EINVAL;
 161        }
 162
 163        ret = snd_soc_codec_set_pll(codec, WM5110_FLL2_REFCLK,
 164                                    ARIZONA_FLL_SRC_MCLK1,
 165                                    MCLK_RATE,
 166                                    asyncclk_rate);
 167        if (ret < 0) {
 168                dev_err(codec->dev, "Failed to set FLL2 source: %d\n", ret);
 169                return ret;
 170        }
 171
 172        ret = snd_soc_codec_set_pll(codec, WM5110_FLL2,
 173                                    ARIZONA_FLL_SRC_MCLK1,
 174                                    MCLK_RATE,
 175                                    asyncclk_rate);
 176        if (ret < 0) {
 177                dev_err(codec->dev, "Failed to start FLL2: %d\n", ret);
 178                return ret;
 179        }
 180
 181        ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
 182                                       ARIZONA_CLK_SRC_FLL2,
 183                                       asyncclk_rate,
 184                                       SND_SOC_CLOCK_IN);
 185        if (ret < 0) {
 186                dev_err(codec->dev, "Failed to set ASYNCCLK source: %d\n", ret);
 187                return ret;
 188        }
 189
 190        return 0;
 191}
 192
 193static int tm2_aif2_hw_free(struct snd_pcm_substream *substream)
 194{
 195        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 196        struct snd_soc_codec *codec = rtd->codec;
 197        int ret;
 198
 199        /* disable FLL2 */
 200        ret = snd_soc_codec_set_pll(codec, WM5110_FLL2, ARIZONA_FLL_SRC_MCLK1,
 201                                    0, 0);
 202        if (ret < 0)
 203                dev_err(codec->dev, "Failed to stop FLL2: %d\n", ret);
 204
 205        return ret;
 206}
 207
 208static struct snd_soc_ops tm2_aif2_ops = {
 209        .hw_params = tm2_aif2_hw_params,
 210        .hw_free = tm2_aif2_hw_free,
 211};
 212
 213static int tm2_mic_bias(struct snd_soc_dapm_widget *w,
 214                                struct snd_kcontrol *kcontrol, int event)
 215{
 216        struct snd_soc_card *card = w->dapm->card;
 217        struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
 218
 219        switch (event) {
 220        case SND_SOC_DAPM_PRE_PMU:
 221                gpiod_set_value_cansleep(priv->gpio_mic_bias,  1);
 222                break;
 223        case SND_SOC_DAPM_POST_PMD:
 224                gpiod_set_value_cansleep(priv->gpio_mic_bias,  0);
 225                break;
 226        }
 227
 228        return 0;
 229}
 230
 231static int tm2_set_bias_level(struct snd_soc_card *card,
 232                                struct snd_soc_dapm_context *dapm,
 233                                enum snd_soc_bias_level level)
 234{
 235        struct snd_soc_pcm_runtime *rtd;
 236
 237        rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 238
 239        if (dapm->dev != rtd->codec_dai->dev)
 240                return 0;
 241
 242        switch (level) {
 243        case SND_SOC_BIAS_STANDBY:
 244                if (card->dapm.bias_level == SND_SOC_BIAS_OFF)
 245                        tm2_start_sysclk(card);
 246                break;
 247        case SND_SOC_BIAS_OFF:
 248                tm2_stop_sysclk(card);
 249                break;
 250        default:
 251                break;
 252        }
 253
 254        return 0;
 255}
 256
 257static struct snd_soc_aux_dev tm2_speaker_amp_dev;
 258
 259static int tm2_late_probe(struct snd_soc_card *card)
 260{
 261        struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
 262        struct snd_soc_dai_link_component dlc = { 0 };
 263        unsigned int ch_map[] = { 0, 1 };
 264        struct snd_soc_dai *amp_pdm_dai;
 265        struct snd_soc_pcm_runtime *rtd;
 266        struct snd_soc_dai *aif1_dai;
 267        struct snd_soc_dai *aif2_dai;
 268        int ret;
 269
 270        rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF1].name);
 271        aif1_dai = rtd->codec_dai;
 272        priv->codec = rtd->codec;
 273
 274        ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
 275        if (ret < 0) {
 276                dev_err(aif1_dai->dev, "Failed to set SYSCLK: %d\n", ret);
 277                return ret;
 278        }
 279
 280        rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF2].name);
 281        aif2_dai = rtd->codec_dai;
 282
 283        ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
 284        if (ret < 0) {
 285                dev_err(aif2_dai->dev, "Failed to set ASYNCCLK: %d\n", ret);
 286                return ret;
 287        }
 288
 289        dlc.of_node = tm2_speaker_amp_dev.codec_of_node;
 290        amp_pdm_dai = snd_soc_find_dai(&dlc);
 291        if (!amp_pdm_dai)
 292                return -ENODEV;
 293
 294        /* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */
 295        ret = snd_soc_dai_set_channel_map(amp_pdm_dai, ARRAY_SIZE(ch_map),
 296                                          ch_map, 0, NULL);
 297        if (ret < 0)
 298                return ret;
 299
 300        ret = snd_soc_dai_set_tdm_slot(amp_pdm_dai, 0x3, 0x0, 2, 16);
 301        if (ret < 0)
 302                return ret;
 303
 304        return 0;
 305}
 306
 307static const struct snd_kcontrol_new tm2_controls[] = {
 308        SOC_DAPM_PIN_SWITCH("HP"),
 309        SOC_DAPM_PIN_SWITCH("SPK"),
 310        SOC_DAPM_PIN_SWITCH("RCV"),
 311        SOC_DAPM_PIN_SWITCH("VPS"),
 312        SOC_DAPM_PIN_SWITCH("HDMI"),
 313
 314        SOC_DAPM_PIN_SWITCH("Main Mic"),
 315        SOC_DAPM_PIN_SWITCH("Sub Mic"),
 316        SOC_DAPM_PIN_SWITCH("Third Mic"),
 317
 318        SOC_DAPM_PIN_SWITCH("Headset Mic"),
 319};
 320
 321static const struct snd_soc_dapm_widget tm2_dapm_widgets[] = {
 322        SND_SOC_DAPM_HP("HP", NULL),
 323        SND_SOC_DAPM_SPK("SPK", NULL),
 324        SND_SOC_DAPM_SPK("RCV", NULL),
 325        SND_SOC_DAPM_LINE("VPS", NULL),
 326        SND_SOC_DAPM_LINE("HDMI", NULL),
 327
 328        SND_SOC_DAPM_MIC("Main Mic", tm2_mic_bias),
 329        SND_SOC_DAPM_MIC("Sub Mic", NULL),
 330        SND_SOC_DAPM_MIC("Third Mic", NULL),
 331
 332        SND_SOC_DAPM_MIC("Headset Mic", NULL),
 333};
 334
 335static const struct snd_soc_component_driver tm2_component = {
 336        .name   = "tm2-audio",
 337};
 338
 339static struct snd_soc_dai_driver tm2_ext_dai[] = {
 340        {
 341                .name = "Voice call",
 342                .playback = {
 343                        .channels_min = 1,
 344                        .channels_max = 4,
 345                        .rate_min = 8000,
 346                        .rate_max = 48000,
 347                        .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
 348                                        SNDRV_PCM_RATE_48000),
 349                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 350                },
 351                .capture = {
 352                        .channels_min = 1,
 353                        .channels_max = 4,
 354                        .rate_min = 8000,
 355                        .rate_max = 48000,
 356                        .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
 357                                        SNDRV_PCM_RATE_48000),
 358                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 359                },
 360        },
 361        {
 362                .name = "Bluetooth",
 363                .playback = {
 364                        .channels_min = 1,
 365                        .channels_max = 4,
 366                        .rate_min = 8000,
 367                        .rate_max = 16000,
 368                        .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
 369                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 370                },
 371                .capture = {
 372                        .channels_min = 1,
 373                        .channels_max = 2,
 374                        .rate_min = 8000,
 375                        .rate_max = 16000,
 376                        .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
 377                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 378                },
 379        },
 380};
 381
 382static struct snd_soc_dai_link tm2_dai_links[] = {
 383        {
 384                .name           = "WM5110 AIF1",
 385                .stream_name    = "HiFi Primary",
 386                .codec_dai_name = "wm5110-aif1",
 387                .ops            = &tm2_aif1_ops,
 388                .dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 389                                  SND_SOC_DAIFMT_CBM_CFM,
 390        }, {
 391                .name           = "WM5110 Voice",
 392                .stream_name    = "Voice call",
 393                .codec_dai_name = "wm5110-aif2",
 394                .ops            = &tm2_aif2_ops,
 395                .dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 396                                  SND_SOC_DAIFMT_CBM_CFM,
 397                .ignore_suspend = 1,
 398        }, {
 399                .name           = "WM5110 BT",
 400                .stream_name    = "Bluetooth",
 401                .codec_dai_name = "wm5110-aif3",
 402                .dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 403                                  SND_SOC_DAIFMT_CBM_CFM,
 404                .ignore_suspend = 1,
 405        }
 406};
 407
 408static struct snd_soc_card tm2_card = {
 409        .owner                  = THIS_MODULE,
 410
 411        .dai_link               = tm2_dai_links,
 412        .num_links              = ARRAY_SIZE(tm2_dai_links),
 413        .controls               = tm2_controls,
 414        .num_controls           = ARRAY_SIZE(tm2_controls),
 415        .dapm_widgets           = tm2_dapm_widgets,
 416        .num_dapm_widgets       = ARRAY_SIZE(tm2_dapm_widgets),
 417        .aux_dev                = &tm2_speaker_amp_dev,
 418        .num_aux_devs           = 1,
 419
 420        .late_probe             = tm2_late_probe,
 421        .set_bias_level         = tm2_set_bias_level,
 422};
 423
 424static int tm2_probe(struct platform_device *pdev)
 425{
 426        struct device *dev = &pdev->dev;
 427        struct snd_soc_card *card = &tm2_card;
 428        struct tm2_machine_priv *priv;
 429        struct device_node *cpu_dai_node, *codec_dai_node;
 430        int ret, i;
 431
 432        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 433        if (!priv)
 434                return -ENOMEM;
 435
 436        snd_soc_card_set_drvdata(card, priv);
 437        card->dev = dev;
 438
 439        priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias",
 440                                                GPIOF_OUT_INIT_LOW);
 441        if (IS_ERR(priv->gpio_mic_bias)) {
 442                dev_err(dev, "Failed to get mic bias gpio\n");
 443                return PTR_ERR(priv->gpio_mic_bias);
 444        }
 445
 446        ret = snd_soc_of_parse_card_name(card, "model");
 447        if (ret < 0) {
 448                dev_err(dev, "Card name is not specified\n");
 449                return ret;
 450        }
 451
 452        ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
 453        if (ret < 0) {
 454                dev_err(dev, "Audio routing is not specified or invalid\n");
 455                return ret;
 456        }
 457
 458        card->aux_dev[0].codec_of_node = of_parse_phandle(dev->of_node,
 459                                                        "audio-amplifier", 0);
 460        if (!card->aux_dev[0].codec_of_node) {
 461                dev_err(dev, "audio-amplifier property invalid or missing\n");
 462                return -EINVAL;
 463        }
 464
 465        cpu_dai_node = of_parse_phandle(dev->of_node, "i2s-controller", 0);
 466        if (!cpu_dai_node) {
 467                dev_err(dev, "i2s-controllers property invalid or missing\n");
 468                ret = -EINVAL;
 469                goto amp_node_put;
 470        }
 471
 472        codec_dai_node = of_parse_phandle(dev->of_node, "audio-codec", 0);
 473        if (!codec_dai_node) {
 474                dev_err(dev, "audio-codec property invalid or missing\n");
 475                ret = -EINVAL;
 476                goto cpu_dai_node_put;
 477        }
 478
 479        for (i = 0; i < card->num_links; i++) {
 480                card->dai_link[i].cpu_dai_name = NULL;
 481                card->dai_link[i].cpu_name = NULL;
 482                card->dai_link[i].platform_name = NULL;
 483                card->dai_link[i].codec_of_node = codec_dai_node;
 484                card->dai_link[i].cpu_of_node = cpu_dai_node;
 485                card->dai_link[i].platform_of_node = cpu_dai_node;
 486        }
 487
 488        ret = devm_snd_soc_register_component(dev, &tm2_component,
 489                                tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai));
 490        if (ret < 0) {
 491                dev_err(dev, "Failed to register component: %d\n", ret);
 492                goto codec_dai_node_put;
 493        }
 494
 495        ret = devm_snd_soc_register_card(dev, card);
 496        if (ret < 0) {
 497                dev_err(dev, "Failed to register card: %d\n", ret);
 498                goto codec_dai_node_put;
 499        }
 500
 501codec_dai_node_put:
 502        of_node_put(codec_dai_node);
 503cpu_dai_node_put:
 504        of_node_put(cpu_dai_node);
 505amp_node_put:
 506        of_node_put(card->aux_dev[0].codec_of_node);
 507        return ret;
 508}
 509
 510static int tm2_pm_prepare(struct device *dev)
 511{
 512        struct snd_soc_card *card = dev_get_drvdata(dev);
 513
 514        return tm2_stop_sysclk(card);
 515}
 516
 517static void tm2_pm_complete(struct device *dev)
 518{
 519        struct snd_soc_card *card = dev_get_drvdata(dev);
 520
 521        tm2_start_sysclk(card);
 522}
 523
 524static const struct dev_pm_ops tm2_pm_ops = {
 525        .prepare        = tm2_pm_prepare,
 526        .suspend        = snd_soc_suspend,
 527        .resume         = snd_soc_resume,
 528        .complete       = tm2_pm_complete,
 529        .freeze         = snd_soc_suspend,
 530        .thaw           = snd_soc_resume,
 531        .poweroff       = snd_soc_poweroff,
 532        .restore        = snd_soc_resume,
 533};
 534
 535static const struct of_device_id tm2_of_match[] = {
 536        { .compatible = "samsung,tm2-audio" },
 537        { },
 538};
 539MODULE_DEVICE_TABLE(of, tm2_of_match);
 540
 541static struct platform_driver tm2_driver = {
 542        .driver = {
 543                .name           = "tm2-audio",
 544                .pm             = &tm2_pm_ops,
 545                .of_match_table = tm2_of_match,
 546        },
 547        .probe  = tm2_probe,
 548};
 549module_platform_driver(tm2_driver);
 550
 551MODULE_AUTHOR("Inha Song <ideal.song@samsung.com>");
 552MODULE_DESCRIPTION("ALSA SoC Exynos TM2 Audio Support");
 553MODULE_LICENSE("GPL v2");
 554