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