1b545dd92SMark Brown /* 2b545dd92SMark Brown * Bells audio support 3b545dd92SMark Brown * 4b545dd92SMark Brown * Copyright 2012 Wolfson Microelectronics 5b545dd92SMark Brown * 6b545dd92SMark Brown * This program is free software; you can redistribute it and/or modify it 7b545dd92SMark Brown * under the terms of the GNU General Public License as published by the 8b545dd92SMark Brown * Free Software Foundation; either version 2 of the License, or (at your 9b545dd92SMark Brown * option) any later version. 10b545dd92SMark Brown */ 11b545dd92SMark Brown 12b545dd92SMark Brown #include <sound/soc.h> 13b545dd92SMark Brown #include <sound/soc-dapm.h> 14b545dd92SMark Brown #include <sound/jack.h> 15b545dd92SMark Brown #include <linux/gpio.h> 16b545dd92SMark Brown #include <linux/module.h> 17b545dd92SMark Brown 18b545dd92SMark Brown #include "../codecs/wm5102.h" 19b545dd92SMark Brown #include "../codecs/wm9081.h" 20b545dd92SMark Brown 21b545dd92SMark Brown /* 22b545dd92SMark Brown * 44.1kHz based clocks for the SYSCLK domain, use a very high clock 23b545dd92SMark Brown * to allow all the DSP functionality to be enabled if desired. 24b545dd92SMark Brown */ 25b545dd92SMark Brown #define SYSCLK_RATE (44100 * 1024) 26b545dd92SMark Brown 27b545dd92SMark Brown /* 48kHz based clocks for the ASYNC domain */ 28b545dd92SMark Brown #define ASYNCCLK_RATE (48000 * 512) 29b545dd92SMark Brown 30b545dd92SMark Brown /* BCLK2 is fixed at this currently */ 31b545dd92SMark Brown #define BCLK2_RATE (64 * 8000) 32b545dd92SMark Brown 33b545dd92SMark Brown /* 34b545dd92SMark Brown * Expect a 24.576MHz crystal if one is fitted (the driver will function 35b545dd92SMark Brown * if this is not fitted). 36b545dd92SMark Brown */ 37b545dd92SMark Brown #define MCLK_RATE 24576000 38b545dd92SMark Brown 39*344c5edeSMark Brown #define SYS_AUDIO_RATE 44100 40*344c5edeSMark Brown #define SYS_MCLK_RATE (SYS_AUDIO_RATE * 256) 41*344c5edeSMark Brown 42*344c5edeSMark Brown #define DAI_AP_DSP 0 43*344c5edeSMark Brown #define DAI_DSP_CODEC 1 44*344c5edeSMark Brown #define DAI_CODEC_CP 2 45*344c5edeSMark Brown #define DAI_CODEC_SUB 3 46b545dd92SMark Brown 47b545dd92SMark Brown static int bells_set_bias_level(struct snd_soc_card *card, 48b545dd92SMark Brown struct snd_soc_dapm_context *dapm, 49b545dd92SMark Brown enum snd_soc_bias_level level) 50b545dd92SMark Brown { 51*344c5edeSMark Brown struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai; 52b545dd92SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 53b545dd92SMark Brown int ret; 54b545dd92SMark Brown 55b545dd92SMark Brown if (dapm->dev != codec_dai->dev) 56b545dd92SMark Brown return 0; 57b545dd92SMark Brown 58b545dd92SMark Brown switch (level) { 59b545dd92SMark Brown case SND_SOC_BIAS_PREPARE: 60b545dd92SMark Brown if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { 61b545dd92SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 62b545dd92SMark Brown ARIZONA_FLL_SRC_MCLK1, 63b545dd92SMark Brown MCLK_RATE, 64b545dd92SMark Brown SYSCLK_RATE); 65b545dd92SMark Brown if (ret < 0) 66b545dd92SMark Brown pr_err("Failed to start FLL: %d\n", ret); 67b545dd92SMark Brown 68b545dd92SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 69b545dd92SMark Brown ARIZONA_FLL_SRC_AIF2BCLK, 70b545dd92SMark Brown BCLK2_RATE, 71b545dd92SMark Brown ASYNCCLK_RATE); 72b545dd92SMark Brown if (ret < 0) 73b545dd92SMark Brown pr_err("Failed to start FLL: %d\n", ret); 74b545dd92SMark Brown } 75b545dd92SMark Brown break; 76b545dd92SMark Brown 77b545dd92SMark Brown default: 78b545dd92SMark Brown break; 79b545dd92SMark Brown } 80b545dd92SMark Brown 81b545dd92SMark Brown return 0; 82b545dd92SMark Brown } 83b545dd92SMark Brown 84b545dd92SMark Brown static int bells_set_bias_level_post(struct snd_soc_card *card, 85b545dd92SMark Brown struct snd_soc_dapm_context *dapm, 86b545dd92SMark Brown enum snd_soc_bias_level level) 87b545dd92SMark Brown { 88*344c5edeSMark Brown struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai; 89b545dd92SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 90b545dd92SMark Brown int ret; 91b545dd92SMark Brown 92b545dd92SMark Brown if (dapm->dev != codec_dai->dev) 93b545dd92SMark Brown return 0; 94b545dd92SMark Brown 95b545dd92SMark Brown switch (level) { 96b545dd92SMark Brown case SND_SOC_BIAS_STANDBY: 97b545dd92SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0); 98b545dd92SMark Brown if (ret < 0) { 99b545dd92SMark Brown pr_err("Failed to stop FLL: %d\n", ret); 100b545dd92SMark Brown return ret; 101b545dd92SMark Brown } 102b545dd92SMark Brown 103b545dd92SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 0, 0, 0); 104b545dd92SMark Brown if (ret < 0) { 105b545dd92SMark Brown pr_err("Failed to stop FLL: %d\n", ret); 106b545dd92SMark Brown return ret; 107b545dd92SMark Brown } 108b545dd92SMark Brown break; 109b545dd92SMark Brown 110b545dd92SMark Brown default: 111b545dd92SMark Brown break; 112b545dd92SMark Brown } 113b545dd92SMark Brown 114b545dd92SMark Brown dapm->bias_level = level; 115b545dd92SMark Brown 116b545dd92SMark Brown return 0; 117b545dd92SMark Brown } 118b545dd92SMark Brown 119b545dd92SMark Brown static int bells_late_probe(struct snd_soc_card *card) 120b545dd92SMark Brown { 121*344c5edeSMark Brown struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec; 122*344c5edeSMark Brown struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec; 123*344c5edeSMark Brown struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai; 124*344c5edeSMark Brown struct snd_soc_dai *aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai; 125*344c5edeSMark Brown struct snd_soc_dai *aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai; 126*344c5edeSMark Brown struct snd_soc_dai *wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai; 127b545dd92SMark Brown int ret; 128b545dd92SMark Brown 129*344c5edeSMark Brown ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0); 130*344c5edeSMark Brown if (ret != 0) { 131*344c5edeSMark Brown dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret); 132*344c5edeSMark Brown return ret; 133*344c5edeSMark Brown } 134*344c5edeSMark Brown 135b545dd92SMark Brown ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); 136b545dd92SMark Brown if (ret != 0) { 137b545dd92SMark Brown dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 138b545dd92SMark Brown return ret; 139b545dd92SMark Brown } 140b545dd92SMark Brown 141b545dd92SMark Brown ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); 142b545dd92SMark Brown if (ret != 0) { 143b545dd92SMark Brown dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret); 144b545dd92SMark Brown return ret; 145b545dd92SMark Brown } 146b545dd92SMark Brown 147b545dd92SMark Brown ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0); 148b545dd92SMark Brown if (ret != 0) { 149b545dd92SMark Brown dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 150b545dd92SMark Brown return ret; 151b545dd92SMark Brown } 152b545dd92SMark Brown 153b545dd92SMark Brown ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, 154b545dd92SMark Brown ARIZONA_CLK_SRC_FLL1, SYSCLK_RATE, 155b545dd92SMark Brown SND_SOC_CLOCK_IN); 156b545dd92SMark Brown if (ret != 0) { 157b545dd92SMark Brown dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); 158b545dd92SMark Brown return ret; 159b545dd92SMark Brown } 160b545dd92SMark Brown 161b545dd92SMark Brown ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0, 162*344c5edeSMark Brown SYS_MCLK_RATE, SND_SOC_CLOCK_OUT); 163b545dd92SMark Brown if (ret != 0) { 164b545dd92SMark Brown dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret); 165b545dd92SMark Brown return ret; 166b545dd92SMark Brown } 167b545dd92SMark Brown 168b545dd92SMark Brown ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK, 169b545dd92SMark Brown ARIZONA_CLK_SRC_FLL2, ASYNCCLK_RATE, 170b545dd92SMark Brown SND_SOC_CLOCK_IN); 171b545dd92SMark Brown if (ret != 0) { 172b545dd92SMark Brown dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); 173b545dd92SMark Brown return ret; 174b545dd92SMark Brown } 175b545dd92SMark Brown 176b545dd92SMark Brown ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK, 177*344c5edeSMark Brown 0, SYS_MCLK_RATE, 0); 178b545dd92SMark Brown if (ret != 0) { 179b545dd92SMark Brown dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret); 180b545dd92SMark Brown return ret; 181b545dd92SMark Brown } 182b545dd92SMark Brown 183b545dd92SMark Brown return 0; 184b545dd92SMark Brown } 185b545dd92SMark Brown 186b545dd92SMark Brown static const struct snd_soc_pcm_stream baseband_params = { 187b545dd92SMark Brown .formats = SNDRV_PCM_FMTBIT_S32_LE, 188b545dd92SMark Brown .rate_min = 8000, 189b545dd92SMark Brown .rate_max = 8000, 190b545dd92SMark Brown .channels_min = 2, 191b545dd92SMark Brown .channels_max = 2, 192b545dd92SMark Brown }; 193b545dd92SMark Brown 194b545dd92SMark Brown static const struct snd_soc_pcm_stream sub_params = { 195b545dd92SMark Brown .formats = SNDRV_PCM_FMTBIT_S32_LE, 196*344c5edeSMark Brown .rate_min = SYS_AUDIO_RATE, 197*344c5edeSMark Brown .rate_max = SYS_AUDIO_RATE, 198b545dd92SMark Brown .channels_min = 2, 199b545dd92SMark Brown .channels_max = 2, 200b545dd92SMark Brown }; 201b545dd92SMark Brown 202b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5102[] = { 203b545dd92SMark Brown { 204*344c5edeSMark Brown .name = "CPU-DSP", 205*344c5edeSMark Brown .stream_name = "CPU-DSP", 206b545dd92SMark Brown .cpu_dai_name = "samsung-i2s.0", 207*344c5edeSMark Brown .codec_dai_name = "wm0010-sdi1", 208b545dd92SMark Brown .platform_name = "samsung-audio", 209*344c5edeSMark Brown .codec_name = "spi0.0", 210*344c5edeSMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 211*344c5edeSMark Brown | SND_SOC_DAIFMT_CBM_CFM, 212*344c5edeSMark Brown }, 213*344c5edeSMark Brown { 214*344c5edeSMark Brown .name = "DSP-CODEC", 215*344c5edeSMark Brown .stream_name = "DSP-CODEC", 216*344c5edeSMark Brown .cpu_dai_name = "wm0010-sdi2", 217*344c5edeSMark Brown .codec_dai_name = "wm5102-aif1", 218b545dd92SMark Brown .codec_name = "wm5102-codec", 219b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 220b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 221*344c5edeSMark Brown .params = &sub_params, 222*344c5edeSMark Brown .ignore_suspend = 1, 223b545dd92SMark Brown }, 224b545dd92SMark Brown { 225b545dd92SMark Brown .name = "Baseband", 226b545dd92SMark Brown .stream_name = "Baseband", 227b545dd92SMark Brown .cpu_dai_name = "wm5102-aif2", 228b545dd92SMark Brown .codec_dai_name = "wm1250-ev1", 229b545dd92SMark Brown .codec_name = "wm1250-ev1.1-0027", 230b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 231b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 232b545dd92SMark Brown .ignore_suspend = 1, 233b545dd92SMark Brown .params = &baseband_params, 234b545dd92SMark Brown }, 235b545dd92SMark Brown { 236b545dd92SMark Brown .name = "Sub", 237b545dd92SMark Brown .stream_name = "Sub", 238b545dd92SMark Brown .cpu_dai_name = "wm5102-aif3", 239b545dd92SMark Brown .codec_dai_name = "wm9081-hifi", 240b545dd92SMark Brown .codec_name = "wm9081.1-006c", 241b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 242b545dd92SMark Brown | SND_SOC_DAIFMT_CBS_CFS, 243b545dd92SMark Brown .ignore_suspend = 1, 244b545dd92SMark Brown .params = &sub_params, 245b545dd92SMark Brown }, 246b545dd92SMark Brown }; 247b545dd92SMark Brown 248b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5110[] = { 249b545dd92SMark Brown { 250*344c5edeSMark Brown .name = "CPU-DSP", 251*344c5edeSMark Brown .stream_name = "CPU-DSP", 252b545dd92SMark Brown .cpu_dai_name = "samsung-i2s.0", 253*344c5edeSMark Brown .codec_dai_name = "wm0010-sdi1", 254b545dd92SMark Brown .platform_name = "samsung-audio", 255*344c5edeSMark Brown .codec_name = "spi0.0", 256*344c5edeSMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 257*344c5edeSMark Brown | SND_SOC_DAIFMT_CBM_CFM, 258*344c5edeSMark Brown }, 259*344c5edeSMark Brown { 260*344c5edeSMark Brown .name = "DSP-CODEC", 261*344c5edeSMark Brown .stream_name = "DSP-CODEC", 262*344c5edeSMark Brown .cpu_dai_name = "wm0010-sdi2", 263*344c5edeSMark Brown .codec_dai_name = "wm5110-aif1", 264b545dd92SMark Brown .codec_name = "wm5110-codec", 265b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 266b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 267*344c5edeSMark Brown .params = &sub_params, 268*344c5edeSMark Brown .ignore_suspend = 1, 269b545dd92SMark Brown }, 270b545dd92SMark Brown { 271b545dd92SMark Brown .name = "Baseband", 272b545dd92SMark Brown .stream_name = "Baseband", 273b545dd92SMark Brown .cpu_dai_name = "wm5110-aif2", 274b545dd92SMark Brown .codec_dai_name = "wm1250-ev1", 275b545dd92SMark Brown .codec_name = "wm1250-ev1.1-0027", 276b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 277b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 278b545dd92SMark Brown .ignore_suspend = 1, 279b545dd92SMark Brown .params = &baseband_params, 280b545dd92SMark Brown }, 281b545dd92SMark Brown { 282b545dd92SMark Brown .name = "Sub", 283b545dd92SMark Brown .stream_name = "Sub", 284b545dd92SMark Brown .cpu_dai_name = "wm5102-aif3", 285b545dd92SMark Brown .codec_dai_name = "wm9081-hifi", 286b545dd92SMark Brown .codec_name = "wm9081.1-006c", 287b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 288b545dd92SMark Brown | SND_SOC_DAIFMT_CBS_CFS, 289b545dd92SMark Brown .ignore_suspend = 1, 290b545dd92SMark Brown .params = &sub_params, 291b545dd92SMark Brown }, 292b545dd92SMark Brown }; 293b545dd92SMark Brown 294b545dd92SMark Brown static struct snd_soc_codec_conf bells_codec_conf[] = { 295b545dd92SMark Brown { 296b545dd92SMark Brown .dev_name = "wm9081.1-006c", 297b545dd92SMark Brown .name_prefix = "Sub", 298b545dd92SMark Brown }, 299b545dd92SMark Brown }; 300b545dd92SMark Brown 301b545dd92SMark Brown static struct snd_soc_dapm_route bells_routes[] = { 302b545dd92SMark Brown { "Sub CLK_SYS", NULL, "OPCLK" }, 303b545dd92SMark Brown }; 304b545dd92SMark Brown 305b545dd92SMark Brown static struct snd_soc_card bells_cards[] = { 306b545dd92SMark Brown { 307b545dd92SMark Brown .name = "Bells WM5102", 308b545dd92SMark Brown .owner = THIS_MODULE, 309b545dd92SMark Brown .dai_link = bells_dai_wm5102, 310b545dd92SMark Brown .num_links = ARRAY_SIZE(bells_dai_wm5102), 311b545dd92SMark Brown .codec_conf = bells_codec_conf, 312b545dd92SMark Brown .num_configs = ARRAY_SIZE(bells_codec_conf), 313b545dd92SMark Brown 314b545dd92SMark Brown .late_probe = bells_late_probe, 315b545dd92SMark Brown 316b545dd92SMark Brown .dapm_routes = bells_routes, 317b545dd92SMark Brown .num_dapm_routes = ARRAY_SIZE(bells_routes), 318b545dd92SMark Brown 319b545dd92SMark Brown .set_bias_level = bells_set_bias_level, 320b545dd92SMark Brown .set_bias_level_post = bells_set_bias_level_post, 321b545dd92SMark Brown }, 322b545dd92SMark Brown { 323b545dd92SMark Brown .name = "Bells WM5110", 324b545dd92SMark Brown .owner = THIS_MODULE, 325b545dd92SMark Brown .dai_link = bells_dai_wm5110, 326b545dd92SMark Brown .num_links = ARRAY_SIZE(bells_dai_wm5110), 327b545dd92SMark Brown .codec_conf = bells_codec_conf, 328b545dd92SMark Brown .num_configs = ARRAY_SIZE(bells_codec_conf), 329b545dd92SMark Brown 330b545dd92SMark Brown .late_probe = bells_late_probe, 331b545dd92SMark Brown 332b545dd92SMark Brown .dapm_routes = bells_routes, 333b545dd92SMark Brown .num_dapm_routes = ARRAY_SIZE(bells_routes), 334b545dd92SMark Brown 335b545dd92SMark Brown .set_bias_level = bells_set_bias_level, 336b545dd92SMark Brown .set_bias_level_post = bells_set_bias_level_post, 337b545dd92SMark Brown }, 338b545dd92SMark Brown }; 339b545dd92SMark Brown 340b545dd92SMark Brown 341b545dd92SMark Brown static __devinit int bells_probe(struct platform_device *pdev) 342b545dd92SMark Brown { 343b545dd92SMark Brown int ret; 344b545dd92SMark Brown 345b545dd92SMark Brown bells_cards[pdev->id].dev = &pdev->dev; 346b545dd92SMark Brown 347b545dd92SMark Brown ret = snd_soc_register_card(&bells_cards[pdev->id]); 348b545dd92SMark Brown if (ret) { 349b545dd92SMark Brown dev_err(&pdev->dev, 350b545dd92SMark Brown "snd_soc_register_card(%s) failed: %d\n", 351b545dd92SMark Brown bells_cards[pdev->id].name, ret); 352b545dd92SMark Brown return ret; 353b545dd92SMark Brown } 354b545dd92SMark Brown 355b545dd92SMark Brown return 0; 356b545dd92SMark Brown } 357b545dd92SMark Brown 358b545dd92SMark Brown static int __devexit bells_remove(struct platform_device *pdev) 359b545dd92SMark Brown { 360b545dd92SMark Brown snd_soc_unregister_card(&bells_cards[pdev->id]); 361b545dd92SMark Brown 362b545dd92SMark Brown return 0; 363b545dd92SMark Brown } 364b545dd92SMark Brown 365b545dd92SMark Brown static struct platform_driver bells_driver = { 366b545dd92SMark Brown .driver = { 367b545dd92SMark Brown .name = "bells", 368b545dd92SMark Brown .owner = THIS_MODULE, 369b545dd92SMark Brown .pm = &snd_soc_pm_ops, 370b545dd92SMark Brown }, 371b545dd92SMark Brown .probe = bells_probe, 372b545dd92SMark Brown .remove = __devexit_p(bells_remove), 373b545dd92SMark Brown }; 374b545dd92SMark Brown 375b545dd92SMark Brown module_platform_driver(bells_driver); 376b545dd92SMark Brown 377b545dd92SMark Brown MODULE_DESCRIPTION("Bells audio support"); 378b545dd92SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 379b545dd92SMark Brown MODULE_LICENSE("GPL"); 380b545dd92SMark Brown MODULE_ALIAS("platform:bells"); 381