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_pcm_runtime *rtd; 62 struct snd_soc_dai *codec_dai; 63 struct snd_soc_codec *codec; 64 struct bells_drvdata *bells = card->drvdata; 65 int ret; 66 67 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); 68 codec_dai = rtd->codec_dai; 69 codec = codec_dai->codec; 70 71 if (dapm->dev != codec_dai->dev) 72 return 0; 73 74 switch (level) { 75 case SND_SOC_BIAS_PREPARE: 76 if (dapm->bias_level != SND_SOC_BIAS_STANDBY) 77 break; 78 79 ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 80 ARIZONA_FLL_SRC_MCLK1, 81 MCLK_RATE, 82 bells->sysclk_rate); 83 if (ret < 0) 84 pr_err("Failed to start FLL: %d\n", ret); 85 86 if (bells->asyncclk_rate) { 87 ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 88 ARIZONA_FLL_SRC_AIF2BCLK, 89 BCLK2_RATE, 90 bells->asyncclk_rate); 91 if (ret < 0) 92 pr_err("Failed to start FLL: %d\n", ret); 93 } 94 break; 95 96 default: 97 break; 98 } 99 100 return 0; 101 } 102 103 static int bells_set_bias_level_post(struct snd_soc_card *card, 104 struct snd_soc_dapm_context *dapm, 105 enum snd_soc_bias_level level) 106 { 107 struct snd_soc_pcm_runtime *rtd; 108 struct snd_soc_dai *codec_dai; 109 struct snd_soc_codec *codec; 110 struct bells_drvdata *bells = card->drvdata; 111 int ret; 112 113 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); 114 codec_dai = rtd->codec_dai; 115 codec = codec_dai->codec; 116 117 if (dapm->dev != codec_dai->dev) 118 return 0; 119 120 switch (level) { 121 case SND_SOC_BIAS_STANDBY: 122 ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0); 123 if (ret < 0) { 124 pr_err("Failed to stop FLL: %d\n", ret); 125 return ret; 126 } 127 128 if (bells->asyncclk_rate) { 129 ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 130 0, 0, 0); 131 if (ret < 0) { 132 pr_err("Failed to stop FLL: %d\n", ret); 133 return ret; 134 } 135 } 136 break; 137 138 default: 139 break; 140 } 141 142 dapm->bias_level = level; 143 144 return 0; 145 } 146 147 static int bells_late_probe(struct snd_soc_card *card) 148 { 149 struct bells_drvdata *bells = card->drvdata; 150 struct snd_soc_pcm_runtime *rtd; 151 struct snd_soc_codec *wm0010; 152 struct snd_soc_codec *codec; 153 struct snd_soc_dai *aif1_dai; 154 struct snd_soc_dai *aif2_dai; 155 struct snd_soc_dai *aif3_dai; 156 struct snd_soc_dai *wm9081_dai; 157 int ret; 158 159 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name); 160 wm0010 = rtd->codec; 161 162 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); 163 codec = rtd->codec; 164 aif1_dai = rtd->codec_dai; 165 166 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, 167 ARIZONA_CLK_SRC_FLL1, 168 bells->sysclk_rate, 169 SND_SOC_CLOCK_IN); 170 if (ret != 0) { 171 dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); 172 return ret; 173 } 174 175 ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0); 176 if (ret != 0) { 177 dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret); 178 return ret; 179 } 180 181 ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); 182 if (ret != 0) 183 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 184 185 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0, 186 SYS_MCLK_RATE, SND_SOC_CLOCK_OUT); 187 if (ret != 0) 188 dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret); 189 190 if (card->num_rtd == DAI_CODEC_CP) 191 return 0; 192 193 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK, 194 ARIZONA_CLK_SRC_FLL2, 195 bells->asyncclk_rate, 196 SND_SOC_CLOCK_IN); 197 if (ret != 0) { 198 dev_err(codec->dev, "Failed to set ASYNCCLK: %d\n", ret); 199 return ret; 200 } 201 202 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_CP].name); 203 aif2_dai = rtd->cpu_dai; 204 205 ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); 206 if (ret != 0) { 207 dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret); 208 return ret; 209 } 210 211 if (card->num_rtd == DAI_CODEC_SUB) 212 return 0; 213 214 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_SUB].name); 215 aif3_dai = rtd->cpu_dai; 216 wm9081_dai = rtd->codec_dai; 217 218 ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0); 219 if (ret != 0) { 220 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 221 return ret; 222 } 223 224 ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK, 225 0, SYS_MCLK_RATE, 0); 226 if (ret != 0) { 227 dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret); 228 return ret; 229 } 230 231 return 0; 232 } 233 234 static const struct snd_soc_pcm_stream baseband_params = { 235 .formats = SNDRV_PCM_FMTBIT_S32_LE, 236 .rate_min = 8000, 237 .rate_max = 8000, 238 .channels_min = 2, 239 .channels_max = 2, 240 }; 241 242 static const struct snd_soc_pcm_stream sub_params = { 243 .formats = SNDRV_PCM_FMTBIT_S32_LE, 244 .rate_min = SYS_AUDIO_RATE, 245 .rate_max = SYS_AUDIO_RATE, 246 .channels_min = 2, 247 .channels_max = 2, 248 }; 249 250 static struct snd_soc_dai_link bells_dai_wm2200[] = { 251 { 252 .name = "CPU-DSP", 253 .stream_name = "CPU-DSP", 254 .cpu_dai_name = "samsung-i2s.0", 255 .codec_dai_name = "wm0010-sdi1", 256 .platform_name = "samsung-i2s.0", 257 .codec_name = "spi0.0", 258 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 259 | SND_SOC_DAIFMT_CBM_CFM, 260 }, 261 { 262 .name = "DSP-CODEC", 263 .stream_name = "DSP-CODEC", 264 .cpu_dai_name = "wm0010-sdi2", 265 .codec_dai_name = "wm2200", 266 .codec_name = "wm2200.1-003a", 267 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 268 | SND_SOC_DAIFMT_CBM_CFM, 269 .params = &sub_params, 270 .ignore_suspend = 1, 271 }, 272 }; 273 274 static struct snd_soc_dai_link bells_dai_wm5102[] = { 275 { 276 .name = "CPU-DSP", 277 .stream_name = "CPU-DSP", 278 .cpu_dai_name = "samsung-i2s.0", 279 .codec_dai_name = "wm0010-sdi1", 280 .platform_name = "samsung-i2s.0", 281 .codec_name = "spi0.0", 282 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 283 | SND_SOC_DAIFMT_CBM_CFM, 284 }, 285 { 286 .name = "DSP-CODEC", 287 .stream_name = "DSP-CODEC", 288 .cpu_dai_name = "wm0010-sdi2", 289 .codec_dai_name = "wm5102-aif1", 290 .codec_name = "wm5102-codec", 291 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 292 | SND_SOC_DAIFMT_CBM_CFM, 293 .params = &sub_params, 294 .ignore_suspend = 1, 295 }, 296 { 297 .name = "Baseband", 298 .stream_name = "Baseband", 299 .cpu_dai_name = "wm5102-aif2", 300 .codec_dai_name = "wm1250-ev1", 301 .codec_name = "wm1250-ev1.1-0027", 302 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 303 | SND_SOC_DAIFMT_CBM_CFM, 304 .ignore_suspend = 1, 305 .params = &baseband_params, 306 }, 307 { 308 .name = "Sub", 309 .stream_name = "Sub", 310 .cpu_dai_name = "wm5102-aif3", 311 .codec_dai_name = "wm9081-hifi", 312 .codec_name = "wm9081.1-006c", 313 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 314 | SND_SOC_DAIFMT_CBS_CFS, 315 .ignore_suspend = 1, 316 .params = &sub_params, 317 }, 318 }; 319 320 static struct snd_soc_dai_link bells_dai_wm5110[] = { 321 { 322 .name = "CPU-DSP", 323 .stream_name = "CPU-DSP", 324 .cpu_dai_name = "samsung-i2s.0", 325 .codec_dai_name = "wm0010-sdi1", 326 .platform_name = "samsung-i2s.0", 327 .codec_name = "spi0.0", 328 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 329 | SND_SOC_DAIFMT_CBM_CFM, 330 }, 331 { 332 .name = "DSP-CODEC", 333 .stream_name = "DSP-CODEC", 334 .cpu_dai_name = "wm0010-sdi2", 335 .codec_dai_name = "wm5110-aif1", 336 .codec_name = "wm5110-codec", 337 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 338 | SND_SOC_DAIFMT_CBM_CFM, 339 .params = &sub_params, 340 .ignore_suspend = 1, 341 }, 342 { 343 .name = "Baseband", 344 .stream_name = "Baseband", 345 .cpu_dai_name = "wm5110-aif2", 346 .codec_dai_name = "wm1250-ev1", 347 .codec_name = "wm1250-ev1.1-0027", 348 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 349 | SND_SOC_DAIFMT_CBM_CFM, 350 .ignore_suspend = 1, 351 .params = &baseband_params, 352 }, 353 { 354 .name = "Sub", 355 .stream_name = "Sub", 356 .cpu_dai_name = "wm5110-aif3", 357 .codec_dai_name = "wm9081-hifi", 358 .codec_name = "wm9081.1-006c", 359 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 360 | SND_SOC_DAIFMT_CBS_CFS, 361 .ignore_suspend = 1, 362 .params = &sub_params, 363 }, 364 }; 365 366 static struct snd_soc_codec_conf bells_codec_conf[] = { 367 { 368 .dev_name = "wm9081.1-006c", 369 .name_prefix = "Sub", 370 }, 371 }; 372 373 static struct snd_soc_dapm_widget bells_widgets[] = { 374 SND_SOC_DAPM_MIC("DMIC", NULL), 375 }; 376 377 static struct snd_soc_dapm_route bells_routes[] = { 378 { "Sub CLK_SYS", NULL, "OPCLK" }, 379 { "CLKIN", NULL, "OPCLK" }, 380 381 { "DMIC", NULL, "MICBIAS2" }, 382 { "IN2L", NULL, "DMIC" }, 383 { "IN2R", NULL, "DMIC" }, 384 }; 385 386 static struct snd_soc_card bells_cards[] = { 387 { 388 .name = "Bells WM2200", 389 .owner = THIS_MODULE, 390 .dai_link = bells_dai_wm2200, 391 .num_links = ARRAY_SIZE(bells_dai_wm2200), 392 .codec_conf = bells_codec_conf, 393 .num_configs = ARRAY_SIZE(bells_codec_conf), 394 395 .late_probe = bells_late_probe, 396 397 .dapm_widgets = bells_widgets, 398 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 399 .dapm_routes = bells_routes, 400 .num_dapm_routes = ARRAY_SIZE(bells_routes), 401 402 .set_bias_level = bells_set_bias_level, 403 .set_bias_level_post = bells_set_bias_level_post, 404 405 .drvdata = &wm2200_drvdata, 406 }, 407 { 408 .name = "Bells WM5102", 409 .owner = THIS_MODULE, 410 .dai_link = bells_dai_wm5102, 411 .num_links = ARRAY_SIZE(bells_dai_wm5102), 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 = &wm5102_drvdata, 426 }, 427 { 428 .name = "Bells WM5110", 429 .owner = THIS_MODULE, 430 .dai_link = bells_dai_wm5110, 431 .num_links = ARRAY_SIZE(bells_dai_wm5110), 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 = &wm5110_drvdata, 446 }, 447 }; 448 449 450 static int bells_probe(struct platform_device *pdev) 451 { 452 int ret; 453 454 bells_cards[pdev->id].dev = &pdev->dev; 455 456 ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]); 457 if (ret) 458 dev_err(&pdev->dev, 459 "snd_soc_register_card(%s) failed: %d\n", 460 bells_cards[pdev->id].name, ret); 461 462 return ret; 463 } 464 465 static struct platform_driver bells_driver = { 466 .driver = { 467 .name = "bells", 468 .pm = &snd_soc_pm_ops, 469 }, 470 .probe = bells_probe, 471 }; 472 473 module_platform_driver(bells_driver); 474 475 MODULE_DESCRIPTION("Bells audio support"); 476 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 477 MODULE_LICENSE("GPL"); 478 MODULE_ALIAS("platform:bells"); 479