linux/sound/soc/samsung/bells.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// Bells audio support
   4//
   5// Copyright 2012 Wolfson Microelectronics
   6
   7#include <sound/soc.h>
   8#include <sound/soc-dapm.h>
   9#include <sound/jack.h>
  10#include <linux/module.h>
  11
  12#include "../codecs/wm5102.h"
  13#include "../codecs/wm9081.h"
  14
  15/* BCLK2 is fixed at this currently */
  16#define BCLK2_RATE (64 * 8000)
  17
  18/*
  19 * Expect a 24.576MHz crystal if one is fitted (the driver will function
  20 * if this is not fitted).
  21 */
  22#define MCLK_RATE 24576000
  23
  24#define SYS_AUDIO_RATE 44100
  25#define SYS_MCLK_RATE  (SYS_AUDIO_RATE * 512)
  26
  27#define DAI_AP_DSP    0
  28#define DAI_DSP_CODEC 1
  29#define DAI_CODEC_CP  2
  30#define DAI_CODEC_SUB 3
  31
  32struct bells_drvdata {
  33        int sysclk_rate;
  34        int asyncclk_rate;
  35};
  36
  37static struct bells_drvdata wm2200_drvdata = {
  38        .sysclk_rate = 22579200,
  39};
  40
  41static struct bells_drvdata wm5102_drvdata = {
  42        .sysclk_rate = 45158400,
  43        .asyncclk_rate = 49152000,
  44};
  45
  46static struct bells_drvdata wm5110_drvdata = {
  47        .sysclk_rate = 135475200,
  48        .asyncclk_rate = 147456000,
  49};
  50
  51static int bells_set_bias_level(struct snd_soc_card *card,
  52                                struct snd_soc_dapm_context *dapm,
  53                                enum snd_soc_bias_level level)
  54{
  55        struct snd_soc_pcm_runtime *rtd;
  56        struct snd_soc_dai *codec_dai;
  57        struct snd_soc_component *component;
  58        struct bells_drvdata *bells = card->drvdata;
  59        int ret;
  60
  61        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
  62        codec_dai = snd_soc_rtd_to_codec(rtd, 0);
  63        component = codec_dai->component;
  64
  65        if (dapm->dev != codec_dai->dev)
  66                return 0;
  67
  68        switch (level) {
  69        case SND_SOC_BIAS_PREPARE:
  70                if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
  71                        break;
  72
  73                ret = snd_soc_component_set_pll(component, WM5102_FLL1,
  74                                            ARIZONA_FLL_SRC_MCLK1,
  75                                            MCLK_RATE,
  76                                            bells->sysclk_rate);
  77                if (ret < 0)
  78                        pr_err("Failed to start FLL: %d\n", ret);
  79
  80                if (bells->asyncclk_rate) {
  81                        ret = snd_soc_component_set_pll(component, WM5102_FLL2,
  82                                                    ARIZONA_FLL_SRC_AIF2BCLK,
  83                                                    BCLK2_RATE,
  84                                                    bells->asyncclk_rate);
  85                        if (ret < 0)
  86                                pr_err("Failed to start FLL: %d\n", ret);
  87                }
  88                break;
  89
  90        default:
  91                break;
  92        }
  93
  94        return 0;
  95}
  96
  97static int bells_set_bias_level_post(struct snd_soc_card *card,
  98                                     struct snd_soc_dapm_context *dapm,
  99                                     enum snd_soc_bias_level level)
 100{
 101        struct snd_soc_pcm_runtime *rtd;
 102        struct snd_soc_dai *codec_dai;
 103        struct snd_soc_component *component;
 104        struct bells_drvdata *bells = card->drvdata;
 105        int ret;
 106
 107        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
 108        codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 109        component = codec_dai->component;
 110
 111        if (dapm->dev != codec_dai->dev)
 112                return 0;
 113
 114        switch (level) {
 115        case SND_SOC_BIAS_STANDBY:
 116                ret = snd_soc_component_set_pll(component, WM5102_FLL1, 0, 0, 0);
 117                if (ret < 0) {
 118                        pr_err("Failed to stop FLL: %d\n", ret);
 119                        return ret;
 120                }
 121
 122                if (bells->asyncclk_rate) {
 123                        ret = snd_soc_component_set_pll(component, WM5102_FLL2,
 124                                                    0, 0, 0);
 125                        if (ret < 0) {
 126                                pr_err("Failed to stop FLL: %d\n", ret);
 127                                return ret;
 128                        }
 129                }
 130                break;
 131
 132        default:
 133                break;
 134        }
 135
 136        return 0;
 137}
 138
 139static int bells_late_probe(struct snd_soc_card *card)
 140{
 141        struct bells_drvdata *bells = card->drvdata;
 142        struct snd_soc_pcm_runtime *rtd;
 143        struct snd_soc_component *wm0010;
 144        struct snd_soc_component *component;
 145        struct snd_soc_dai *aif1_dai;
 146        struct snd_soc_dai *aif2_dai;
 147        struct snd_soc_dai *aif3_dai;
 148        struct snd_soc_dai *wm9081_dai;
 149        int ret;
 150
 151        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]);
 152        wm0010 = snd_soc_rtd_to_codec(rtd, 0)->component;
 153
 154        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
 155        component = snd_soc_rtd_to_codec(rtd, 0)->component;
 156        aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
 157
 158        ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,
 159                                       ARIZONA_CLK_SRC_FLL1,
 160                                       bells->sysclk_rate,
 161                                       SND_SOC_CLOCK_IN);
 162        if (ret != 0) {
 163                dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);
 164                return ret;
 165        }
 166
 167        ret = snd_soc_component_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
 168        if (ret != 0) {
 169                dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
 170                return ret;
 171        }
 172
 173        ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
 174        if (ret != 0)
 175                dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
 176
 177        ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_OPCLK, 0,
 178                                       SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
 179        if (ret != 0)
 180                dev_err(component->dev, "Failed to set OPCLK: %d\n", ret);
 181
 182        if (card->num_rtd == DAI_CODEC_CP)
 183                return 0;
 184
 185        ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK,
 186                                       ARIZONA_CLK_SRC_FLL2,
 187                                       bells->asyncclk_rate,
 188                                       SND_SOC_CLOCK_IN);
 189        if (ret != 0) {
 190                dev_err(component->dev, "Failed to set ASYNCCLK: %d\n", ret);
 191                return ret;
 192        }
 193
 194        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]);
 195        aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);
 196
 197        ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
 198        if (ret != 0) {
 199                dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
 200                return ret;
 201        }
 202
 203        if (card->num_rtd == DAI_CODEC_SUB)
 204                return 0;
 205
 206        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]);
 207        aif3_dai = snd_soc_rtd_to_cpu(rtd, 0);
 208        wm9081_dai = snd_soc_rtd_to_codec(rtd, 0);
 209
 210        ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
 211        if (ret != 0) {
 212                dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
 213                return ret;
 214        }
 215
 216        ret = snd_soc_component_set_sysclk(wm9081_dai->component, WM9081_SYSCLK_MCLK,
 217                                       0, SYS_MCLK_RATE, 0);
 218        if (ret != 0) {
 219                dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
 220                return ret;
 221        }
 222
 223        return 0;
 224}
 225
 226static const struct snd_soc_pcm_stream baseband_params = {
 227        .formats = SNDRV_PCM_FMTBIT_S32_LE,
 228        .rate_min = 8000,
 229        .rate_max = 8000,
 230        .channels_min = 2,
 231        .channels_max = 2,
 232};
 233
 234static const struct snd_soc_pcm_stream sub_params = {
 235        .formats = SNDRV_PCM_FMTBIT_S32_LE,
 236        .rate_min = SYS_AUDIO_RATE,
 237        .rate_max = SYS_AUDIO_RATE,
 238        .channels_min = 2,
 239        .channels_max = 2,
 240};
 241
 242SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp,
 243        DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
 244        DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
 245        DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
 246
 247SND_SOC_DAILINK_DEFS(wm2200_dsp_codec,
 248        DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
 249        DAILINK_COMP_ARRAY(COMP_CODEC("wm2200.1-003a", "wm2200")));
 250
 251static struct snd_soc_dai_link bells_dai_wm2200[] = {
 252        {
 253                .name = "CPU-DSP",
 254                .stream_name = "CPU-DSP",
 255                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 256                                | SND_SOC_DAIFMT_CBP_CFP,
 257                SND_SOC_DAILINK_REG(wm2200_cpu_dsp),
 258        },
 259        {
 260                .name = "DSP-CODEC",
 261                .stream_name = "DSP-CODEC",
 262                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 263                                | SND_SOC_DAIFMT_CBP_CFP,
 264                .c2c_params = &sub_params,
 265                .num_c2c_params = 1,
 266                .ignore_suspend = 1,
 267                SND_SOC_DAILINK_REG(wm2200_dsp_codec),
 268        },
 269};
 270
 271SND_SOC_DAILINK_DEFS(wm5102_cpu_dsp,
 272        DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
 273        DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
 274        DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
 275
 276SND_SOC_DAILINK_DEFS(wm5102_dsp_codec,
 277        DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
 278        DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1")));
 279
 280SND_SOC_DAILINK_DEFS(wm5102_baseband,
 281        DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")),
 282        DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
 283
 284SND_SOC_DAILINK_DEFS(wm5102_sub,
 285        DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif3")),
 286        DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
 287
 288static struct snd_soc_dai_link bells_dai_wm5102[] = {
 289        {
 290                .name = "CPU-DSP",
 291                .stream_name = "CPU-DSP",
 292                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 293                                | SND_SOC_DAIFMT_CBP_CFP,
 294                SND_SOC_DAILINK_REG(wm5102_cpu_dsp),
 295        },
 296        {
 297                .name = "DSP-CODEC",
 298                .stream_name = "DSP-CODEC",
 299                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 300                                | SND_SOC_DAIFMT_CBP_CFP,
 301                .c2c_params = &sub_params,
 302                .num_c2c_params = 1,
 303                .ignore_suspend = 1,
 304                SND_SOC_DAILINK_REG(wm5102_dsp_codec),
 305        },
 306        {
 307                .name = "Baseband",
 308                .stream_name = "Baseband",
 309                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 310                                | SND_SOC_DAIFMT_CBP_CFP,
 311                .ignore_suspend = 1,
 312                .c2c_params = &baseband_params,
 313                .num_c2c_params = 1,
 314                SND_SOC_DAILINK_REG(wm5102_baseband),
 315        },
 316        {
 317                .name = "Sub",
 318                .stream_name = "Sub",
 319                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 320                                | SND_SOC_DAIFMT_CBC_CFC,
 321                .ignore_suspend = 1,
 322                .c2c_params = &sub_params,
 323                .num_c2c_params = 1,
 324                SND_SOC_DAILINK_REG(wm5102_sub),
 325        },
 326};
 327
 328SND_SOC_DAILINK_DEFS(wm5110_cpu_dsp,
 329        DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
 330        DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
 331        DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
 332
 333SND_SOC_DAILINK_DEFS(wm5110_dsp_codec,
 334        DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
 335        DAILINK_COMP_ARRAY(COMP_CODEC("wm5110-codec", "wm5110-aif1")));
 336
 337SND_SOC_DAILINK_DEFS(wm5110_baseband,
 338        DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif2")),
 339        DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
 340
 341
 342SND_SOC_DAILINK_DEFS(wm5110_sub,
 343        DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif3")),
 344        DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
 345
 346static struct snd_soc_dai_link bells_dai_wm5110[] = {
 347        {
 348                .name = "CPU-DSP",
 349                .stream_name = "CPU-DSP",
 350                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 351                                | SND_SOC_DAIFMT_CBP_CFP,
 352                SND_SOC_DAILINK_REG(wm5110_cpu_dsp),
 353        },
 354        {
 355                .name = "DSP-CODEC",
 356                .stream_name = "DSP-CODEC",
 357                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 358                                | SND_SOC_DAIFMT_CBP_CFP,
 359                .c2c_params = &sub_params,
 360                .num_c2c_params = 1,
 361                .ignore_suspend = 1,
 362                SND_SOC_DAILINK_REG(wm5110_dsp_codec),
 363        },
 364        {
 365                .name = "Baseband",
 366                .stream_name = "Baseband",
 367                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 368                                | SND_SOC_DAIFMT_CBP_CFP,
 369                .ignore_suspend = 1,
 370                .c2c_params = &baseband_params,
 371                .num_c2c_params = 1,
 372                SND_SOC_DAILINK_REG(wm5110_baseband),
 373        },
 374        {
 375                .name = "Sub",
 376                .stream_name = "Sub",
 377                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 378                                | SND_SOC_DAIFMT_CBC_CFC,
 379                .ignore_suspend = 1,
 380                .c2c_params = &sub_params,
 381                .num_c2c_params = 1,
 382                SND_SOC_DAILINK_REG(wm5110_sub),
 383        },
 384};
 385
 386static struct snd_soc_codec_conf bells_codec_conf[] = {
 387        {
 388                .dlc = COMP_CODEC_CONF("wm9081.1-006c"),
 389                .name_prefix = "Sub",
 390        },
 391};
 392
 393static const struct snd_soc_dapm_widget bells_widgets[] = {
 394        SND_SOC_DAPM_MIC("DMIC", NULL),
 395};
 396
 397static const struct snd_soc_dapm_route bells_routes[] = {
 398        { "Sub CLK_SYS", NULL, "OPCLK" },
 399        { "CLKIN", NULL, "OPCLK" },
 400
 401        { "DMIC", NULL, "MICBIAS2" },
 402        { "IN2L", NULL, "DMIC" },
 403        { "IN2R", NULL, "DMIC" },
 404};
 405
 406static struct snd_soc_card bells_cards[] = {
 407        {
 408                .name = "Bells WM2200",
 409                .owner = THIS_MODULE,
 410                .dai_link = bells_dai_wm2200,
 411                .num_links = ARRAY_SIZE(bells_dai_wm2200),
 412                .codec_conf = bells_codec_conf,
 413                .num_configs = ARRAY_SIZE(bells_codec_conf),
 414
 415                .late_probe = bells_late_probe,
 416
 417                .dapm_widgets = bells_widgets,
 418                .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
 419                .dapm_routes = bells_routes,
 420                .num_dapm_routes = ARRAY_SIZE(bells_routes),
 421
 422                .set_bias_level = bells_set_bias_level,
 423                .set_bias_level_post = bells_set_bias_level_post,
 424
 425                .drvdata = &wm2200_drvdata,
 426        },
 427        {
 428                .name = "Bells WM5102",
 429                .owner = THIS_MODULE,
 430                .dai_link = bells_dai_wm5102,
 431                .num_links = ARRAY_SIZE(bells_dai_wm5102),
 432                .codec_conf = bells_codec_conf,
 433                .num_configs = ARRAY_SIZE(bells_codec_conf),
 434
 435                .late_probe = bells_late_probe,
 436
 437                .dapm_widgets = bells_widgets,
 438                .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
 439                .dapm_routes = bells_routes,
 440                .num_dapm_routes = ARRAY_SIZE(bells_routes),
 441
 442                .set_bias_level = bells_set_bias_level,
 443                .set_bias_level_post = bells_set_bias_level_post,
 444
 445                .drvdata = &wm5102_drvdata,
 446        },
 447        {
 448                .name = "Bells WM5110",
 449                .owner = THIS_MODULE,
 450                .dai_link = bells_dai_wm5110,
 451                .num_links = ARRAY_SIZE(bells_dai_wm5110),
 452                .codec_conf = bells_codec_conf,
 453                .num_configs = ARRAY_SIZE(bells_codec_conf),
 454
 455                .late_probe = bells_late_probe,
 456
 457                .dapm_widgets = bells_widgets,
 458                .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
 459                .dapm_routes = bells_routes,
 460                .num_dapm_routes = ARRAY_SIZE(bells_routes),
 461
 462                .set_bias_level = bells_set_bias_level,
 463                .set_bias_level_post = bells_set_bias_level_post,
 464
 465                .drvdata = &wm5110_drvdata,
 466        },
 467};
 468
 469static int bells_probe(struct platform_device *pdev)
 470{
 471        int ret;
 472
 473        bells_cards[pdev->id].dev = &pdev->dev;
 474
 475        ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]);
 476        if (ret)
 477                dev_err(&pdev->dev,
 478                        "snd_soc_register_card(%s) failed: %d\n",
 479                        bells_cards[pdev->id].name, ret);
 480
 481        return ret;
 482}
 483
 484static struct platform_driver bells_driver = {
 485        .driver = {
 486                .name = "bells",
 487                .pm = &snd_soc_pm_ops,
 488        },
 489        .probe = bells_probe,
 490};
 491
 492module_platform_driver(bells_driver);
 493
 494MODULE_DESCRIPTION("Bells audio support");
 495MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 496MODULE_LICENSE("GPL");
 497MODULE_ALIAS("platform:bells");
 498