linux/sound/soc/samsung/littlemill.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// Littlemill audio support
   4//
   5// Copyright 2011 Wolfson Microelectronics
   6
   7#include <sound/soc.h>
   8#include <sound/soc-dapm.h>
   9#include <sound/jack.h>
  10#include <linux/gpio.h>
  11#include <linux/module.h>
  12
  13#include "../codecs/wm8994.h"
  14
  15static int sample_rate = 44100;
  16
  17static int littlemill_set_bias_level(struct snd_soc_card *card,
  18                                          struct snd_soc_dapm_context *dapm,
  19                                          enum snd_soc_bias_level level)
  20{
  21        struct snd_soc_pcm_runtime *rtd;
  22        struct snd_soc_dai *aif1_dai;
  23        int ret;
  24
  25        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
  26        aif1_dai = asoc_rtd_to_codec(rtd, 0);
  27
  28        if (dapm->dev != aif1_dai->dev)
  29                return 0;
  30
  31        switch (level) {
  32        case SND_SOC_BIAS_PREPARE:
  33                /*
  34                 * If we've not already clocked things via hw_params()
  35                 * then do so now, otherwise these are noops.
  36                 */
  37                if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
  38                        ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
  39                                                  WM8994_FLL_SRC_MCLK2, 32768,
  40                                                  sample_rate * 512);
  41                        if (ret < 0) {
  42                                pr_err("Failed to start FLL: %d\n", ret);
  43                                return ret;
  44                        }
  45
  46                        ret = snd_soc_dai_set_sysclk(aif1_dai,
  47                                                     WM8994_SYSCLK_FLL1,
  48                                                     sample_rate * 512,
  49                                                     SND_SOC_CLOCK_IN);
  50                        if (ret < 0) {
  51                                pr_err("Failed to set SYSCLK: %d\n", ret);
  52                                return ret;
  53                        }
  54                }
  55                break;
  56
  57        default:
  58                break;
  59        }
  60
  61        return 0;
  62}
  63
  64static int littlemill_set_bias_level_post(struct snd_soc_card *card,
  65                                               struct snd_soc_dapm_context *dapm,
  66                                               enum snd_soc_bias_level level)
  67{
  68        struct snd_soc_pcm_runtime *rtd;
  69        struct snd_soc_dai *aif1_dai;
  70        int ret;
  71
  72        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
  73        aif1_dai = asoc_rtd_to_codec(rtd, 0);
  74
  75        if (dapm->dev != aif1_dai->dev)
  76                return 0;
  77
  78        switch (level) {
  79        case SND_SOC_BIAS_STANDBY:
  80                ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
  81                                             32768, SND_SOC_CLOCK_IN);
  82                if (ret < 0) {
  83                        pr_err("Failed to switch away from FLL1: %d\n", ret);
  84                        return ret;
  85                }
  86
  87                ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
  88                                          0, 0, 0);
  89                if (ret < 0) {
  90                        pr_err("Failed to stop FLL1: %d\n", ret);
  91                        return ret;
  92                }
  93                break;
  94
  95        default:
  96                break;
  97        }
  98
  99        dapm->bias_level = level;
 100
 101        return 0;
 102}
 103
 104static int littlemill_hw_params(struct snd_pcm_substream *substream,
 105                                struct snd_pcm_hw_params *params)
 106{
 107        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 108        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 109        int ret;
 110
 111        sample_rate = params_rate(params);
 112
 113        ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
 114                                  WM8994_FLL_SRC_MCLK2, 32768,
 115                                  sample_rate * 512);
 116        if (ret < 0) {
 117                pr_err("Failed to start FLL: %d\n", ret);
 118                return ret;
 119        }
 120
 121        ret = snd_soc_dai_set_sysclk(codec_dai,
 122                                     WM8994_SYSCLK_FLL1,
 123                                     sample_rate * 512,
 124                                     SND_SOC_CLOCK_IN);
 125        if (ret < 0) {
 126                pr_err("Failed to set SYSCLK: %d\n", ret);
 127                return ret;
 128        }
 129
 130        return 0;
 131}
 132
 133static const struct snd_soc_ops littlemill_ops = {
 134        .hw_params = littlemill_hw_params,
 135};
 136
 137static const struct snd_soc_pcm_stream baseband_params = {
 138        .formats = SNDRV_PCM_FMTBIT_S32_LE,
 139        .rate_min = 8000,
 140        .rate_max = 8000,
 141        .channels_min = 2,
 142        .channels_max = 2,
 143};
 144
 145SND_SOC_DAILINK_DEFS(cpu,
 146        DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
 147        DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
 148        DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
 149
 150SND_SOC_DAILINK_DEFS(baseband,
 151        DAILINK_COMP_ARRAY(COMP_CPU("wm8994-aif2")),
 152        DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027",
 153                                      "wm1250-ev1")));
 154
 155static struct snd_soc_dai_link littlemill_dai[] = {
 156        {
 157                .name = "CPU",
 158                .stream_name = "CPU",
 159                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 160                                | SND_SOC_DAIFMT_CBM_CFM,
 161                .ops = &littlemill_ops,
 162                SND_SOC_DAILINK_REG(cpu),
 163        },
 164        {
 165                .name = "Baseband",
 166                .stream_name = "Baseband",
 167                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 168                                | SND_SOC_DAIFMT_CBM_CFM,
 169                .ignore_suspend = 1,
 170                .params = &baseband_params,
 171                SND_SOC_DAILINK_REG(baseband),
 172        },
 173};
 174
 175static int bbclk_ev(struct snd_soc_dapm_widget *w,
 176                    struct snd_kcontrol *kcontrol, int event)
 177{
 178        struct snd_soc_card *card = w->dapm->card;
 179        struct snd_soc_pcm_runtime *rtd;
 180        struct snd_soc_dai *aif2_dai;
 181        int ret;
 182
 183        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
 184        aif2_dai = asoc_rtd_to_cpu(rtd, 0);
 185
 186        switch (event) {
 187        case SND_SOC_DAPM_PRE_PMU:
 188                ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
 189                                          WM8994_FLL_SRC_BCLK, 64 * 8000,
 190                                          8000 * 256);
 191                if (ret < 0) {
 192                        pr_err("Failed to start FLL: %d\n", ret);
 193                        return ret;
 194                }
 195
 196                ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2,
 197                                             8000 * 256,
 198                                             SND_SOC_CLOCK_IN);
 199                if (ret < 0) {
 200                        pr_err("Failed to set SYSCLK: %d\n", ret);
 201                        return ret;
 202                }
 203                break;
 204        case SND_SOC_DAPM_POST_PMD:
 205                ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
 206                                             32768, SND_SOC_CLOCK_IN);
 207                if (ret < 0) {
 208                        pr_err("Failed to switch away from FLL2: %d\n", ret);
 209                        return ret;
 210                }
 211
 212                ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
 213                                          0, 0, 0);
 214                if (ret < 0) {
 215                        pr_err("Failed to stop FLL2: %d\n", ret);
 216                        return ret;
 217                }
 218                break;
 219        default:
 220                return -EINVAL;
 221        }
 222
 223        return 0;
 224}
 225
 226static const struct snd_kcontrol_new controls[] = {
 227        SOC_DAPM_PIN_SWITCH("WM1250 Input"),
 228        SOC_DAPM_PIN_SWITCH("WM1250 Output"),
 229};
 230
 231static struct snd_soc_dapm_widget widgets[] = {
 232        SND_SOC_DAPM_HP("Headphone", NULL),
 233
 234        SND_SOC_DAPM_MIC("AMIC", NULL),
 235        SND_SOC_DAPM_MIC("DMIC", NULL),
 236
 237        SND_SOC_DAPM_SUPPLY_S("Baseband Clock", -1, SND_SOC_NOPM, 0, 0,
 238                              bbclk_ev,
 239                              SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 240};
 241
 242static struct snd_soc_dapm_route audio_paths[] = {
 243        { "Headphone", NULL, "HPOUT1L" },
 244        { "Headphone", NULL, "HPOUT1R" },
 245
 246        { "AMIC", NULL, "MICBIAS1" },   /* Default for AMICBIAS jumper */
 247        { "IN1LN", NULL, "AMIC" },
 248
 249        { "DMIC", NULL, "MICBIAS2" },   /* Default for DMICBIAS jumper */
 250        { "DMIC1DAT", NULL, "DMIC" },
 251        { "DMIC2DAT", NULL, "DMIC" },
 252
 253        { "AIF2CLK", NULL, "Baseband Clock" },
 254};
 255
 256static struct snd_soc_jack littlemill_headset;
 257
 258static int littlemill_late_probe(struct snd_soc_card *card)
 259{
 260        struct snd_soc_pcm_runtime *rtd;
 261        struct snd_soc_component *component;
 262        struct snd_soc_dai *aif1_dai;
 263        struct snd_soc_dai *aif2_dai;
 264        int ret;
 265
 266        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
 267        component = asoc_rtd_to_codec(rtd, 0)->component;
 268        aif1_dai = asoc_rtd_to_codec(rtd, 0);
 269
 270        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
 271        aif2_dai = asoc_rtd_to_cpu(rtd, 0);
 272
 273        ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
 274                                     32768, SND_SOC_CLOCK_IN);
 275        if (ret < 0)
 276                return ret;
 277
 278        ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
 279                                     32768, SND_SOC_CLOCK_IN);
 280        if (ret < 0)
 281                return ret;
 282
 283        ret = snd_soc_card_jack_new(card, "Headset",
 284                                    SND_JACK_HEADSET | SND_JACK_MECHANICAL |
 285                                    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 286                                    SND_JACK_BTN_2 | SND_JACK_BTN_3 |
 287                                    SND_JACK_BTN_4 | SND_JACK_BTN_5,
 288                                    &littlemill_headset, NULL, 0);
 289        if (ret)
 290                return ret;
 291
 292        /* This will check device compatibility itself */
 293        wm8958_mic_detect(component, &littlemill_headset, NULL, NULL, NULL, NULL);
 294
 295        /* As will this */
 296        wm8994_mic_detect(component, &littlemill_headset, 1);
 297
 298        return 0;
 299}
 300
 301static struct snd_soc_card littlemill = {
 302        .name = "Littlemill",
 303        .owner = THIS_MODULE,
 304        .dai_link = littlemill_dai,
 305        .num_links = ARRAY_SIZE(littlemill_dai),
 306
 307        .set_bias_level = littlemill_set_bias_level,
 308        .set_bias_level_post = littlemill_set_bias_level_post,
 309
 310        .controls = controls,
 311        .num_controls = ARRAY_SIZE(controls),
 312        .dapm_widgets = widgets,
 313        .num_dapm_widgets = ARRAY_SIZE(widgets),
 314        .dapm_routes = audio_paths,
 315        .num_dapm_routes = ARRAY_SIZE(audio_paths),
 316
 317        .late_probe = littlemill_late_probe,
 318};
 319
 320static int littlemill_probe(struct platform_device *pdev)
 321{
 322        struct snd_soc_card *card = &littlemill;
 323        int ret;
 324
 325        card->dev = &pdev->dev;
 326
 327        ret = devm_snd_soc_register_card(&pdev->dev, card);
 328        if (ret && ret != -EPROBE_DEFER)
 329                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
 330                        ret);
 331
 332        return ret;
 333}
 334
 335static struct platform_driver littlemill_driver = {
 336        .driver = {
 337                .name = "littlemill",
 338                .pm = &snd_soc_pm_ops,
 339        },
 340        .probe = littlemill_probe,
 341};
 342
 343module_platform_driver(littlemill_driver);
 344
 345MODULE_DESCRIPTION("Littlemill audio support");
 346MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 347MODULE_LICENSE("GPL");
 348MODULE_ALIAS("platform:littlemill");
 349