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