1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // Bells audio support 4 // 5 // Copyright 2012 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/wm5102.h" 14 #include "../codecs/wm9081.h" 15 16 /* BCLK2 is fixed at this currently */ 17 #define BCLK2_RATE (64 * 8000) 18 19 /* 20 * Expect a 24.576MHz crystal if one is fitted (the driver will function 21 * if this is not fitted). 22 */ 23 #define MCLK_RATE 24576000 24 25 #define SYS_AUDIO_RATE 44100 26 #define SYS_MCLK_RATE (SYS_AUDIO_RATE * 512) 27 28 #define DAI_AP_DSP 0 29 #define DAI_DSP_CODEC 1 30 #define DAI_CODEC_CP 2 31 #define DAI_CODEC_SUB 3 32 33 struct bells_drvdata { 34 int sysclk_rate; 35 int asyncclk_rate; 36 }; 37 38 static struct bells_drvdata wm2200_drvdata = { 39 .sysclk_rate = 22579200, 40 }; 41 42 static struct bells_drvdata wm5102_drvdata = { 43 .sysclk_rate = 45158400, 44 .asyncclk_rate = 49152000, 45 }; 46 47 static struct bells_drvdata wm5110_drvdata = { 48 .sysclk_rate = 135475200, 49 .asyncclk_rate = 147456000, 50 }; 51 52 static int bells_set_bias_level(struct snd_soc_card *card, 53 struct snd_soc_dapm_context *dapm, 54 enum snd_soc_bias_level level) 55 { 56 struct snd_soc_pcm_runtime *rtd; 57 struct snd_soc_dai *codec_dai; 58 struct snd_soc_component *component; 59 struct bells_drvdata *bells = card->drvdata; 60 int ret; 61 62 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); 63 codec_dai = asoc_rtd_to_codec(rtd, 0); 64 component = codec_dai->component; 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_component_set_pll(component, 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_component_set_pll(component, 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_pcm_runtime *rtd; 103 struct snd_soc_dai *codec_dai; 104 struct snd_soc_component *component; 105 struct bells_drvdata *bells = card->drvdata; 106 int ret; 107 108 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); 109 codec_dai = asoc_rtd_to_codec(rtd, 0); 110 component = codec_dai->component; 111 112 if (dapm->dev != codec_dai->dev) 113 return 0; 114 115 switch (level) { 116 case SND_SOC_BIAS_STANDBY: 117 ret = snd_soc_component_set_pll(component, WM5102_FLL1, 0, 0, 0); 118 if (ret < 0) { 119 pr_err("Failed to stop FLL: %d\n", ret); 120 return ret; 121 } 122 123 if (bells->asyncclk_rate) { 124 ret = snd_soc_component_set_pll(component, WM5102_FLL2, 125 0, 0, 0); 126 if (ret < 0) { 127 pr_err("Failed to stop FLL: %d\n", ret); 128 return ret; 129 } 130 } 131 break; 132 133 default: 134 break; 135 } 136 137 dapm->bias_level = level; 138 139 return 0; 140 } 141 142 static int bells_late_probe(struct snd_soc_card *card) 143 { 144 struct bells_drvdata *bells = card->drvdata; 145 struct snd_soc_pcm_runtime *rtd; 146 struct snd_soc_component *wm0010; 147 struct snd_soc_component *component; 148 struct snd_soc_dai *aif1_dai; 149 struct snd_soc_dai *aif2_dai; 150 struct snd_soc_dai *aif3_dai; 151 struct snd_soc_dai *wm9081_dai; 152 int ret; 153 154 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]); 155 wm0010 = asoc_rtd_to_codec(rtd, 0)->component; 156 157 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); 158 component = asoc_rtd_to_codec(rtd, 0)->component; 159 aif1_dai = asoc_rtd_to_codec(rtd, 0); 160 161 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK, 162 ARIZONA_CLK_SRC_FLL1, 163 bells->sysclk_rate, 164 SND_SOC_CLOCK_IN); 165 if (ret != 0) { 166 dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret); 167 return ret; 168 } 169 170 ret = snd_soc_component_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0); 171 if (ret != 0) { 172 dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret); 173 return ret; 174 } 175 176 ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); 177 if (ret != 0) 178 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 179 180 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_OPCLK, 0, 181 SYS_MCLK_RATE, SND_SOC_CLOCK_OUT); 182 if (ret != 0) 183 dev_err(component->dev, "Failed to set OPCLK: %d\n", ret); 184 185 if (card->num_rtd == DAI_CODEC_CP) 186 return 0; 187 188 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK, 189 ARIZONA_CLK_SRC_FLL2, 190 bells->asyncclk_rate, 191 SND_SOC_CLOCK_IN); 192 if (ret != 0) { 193 dev_err(component->dev, "Failed to set ASYNCCLK: %d\n", ret); 194 return ret; 195 } 196 197 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]); 198 aif2_dai = asoc_rtd_to_cpu(rtd, 0); 199 200 ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); 201 if (ret != 0) { 202 dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret); 203 return ret; 204 } 205 206 if (card->num_rtd == DAI_CODEC_SUB) 207 return 0; 208 209 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]); 210 aif3_dai = asoc_rtd_to_cpu(rtd, 0); 211 wm9081_dai = asoc_rtd_to_codec(rtd, 0); 212 213 ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0); 214 if (ret != 0) { 215 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 216 return ret; 217 } 218 219 ret = snd_soc_component_set_sysclk(wm9081_dai->component, WM9081_SYSCLK_MCLK, 220 0, SYS_MCLK_RATE, 0); 221 if (ret != 0) { 222 dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret); 223 return ret; 224 } 225 226 return 0; 227 } 228 229 static const struct snd_soc_pcm_stream baseband_params = { 230 .formats = SNDRV_PCM_FMTBIT_S32_LE, 231 .rate_min = 8000, 232 .rate_max = 8000, 233 .channels_min = 2, 234 .channels_max = 2, 235 }; 236 237 static const struct snd_soc_pcm_stream sub_params = { 238 .formats = SNDRV_PCM_FMTBIT_S32_LE, 239 .rate_min = SYS_AUDIO_RATE, 240 .rate_max = SYS_AUDIO_RATE, 241 .channels_min = 2, 242 .channels_max = 2, 243 }; 244 245 SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp, 246 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 247 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")), 248 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 249 250 SND_SOC_DAILINK_DEFS(wm2200_dsp_codec, 251 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")), 252 DAILINK_COMP_ARRAY(COMP_CODEC("wm2200.1-003a", "wm2200"))); 253 254 static struct snd_soc_dai_link bells_dai_wm2200[] = { 255 { 256 .name = "CPU-DSP", 257 .stream_name = "CPU-DSP", 258 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 259 | SND_SOC_DAIFMT_CBM_CFM, 260 SND_SOC_DAILINK_REG(wm2200_cpu_dsp), 261 }, 262 { 263 .name = "DSP-CODEC", 264 .stream_name = "DSP-CODEC", 265 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 266 | SND_SOC_DAIFMT_CBM_CFM, 267 .c2c_params = &sub_params, 268 .num_c2c_params = 1, 269 .ignore_suspend = 1, 270 SND_SOC_DAILINK_REG(wm2200_dsp_codec), 271 }, 272 }; 273 274 SND_SOC_DAILINK_DEFS(wm5102_cpu_dsp, 275 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 276 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")), 277 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 278 279 SND_SOC_DAILINK_DEFS(wm5102_dsp_codec, 280 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")), 281 DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1"))); 282 283 SND_SOC_DAILINK_DEFS(wm5102_baseband, 284 DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")), 285 DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1"))); 286 287 SND_SOC_DAILINK_DEFS(wm5102_sub, 288 DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif3")), 289 DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi"))); 290 291 static struct snd_soc_dai_link bells_dai_wm5102[] = { 292 { 293 .name = "CPU-DSP", 294 .stream_name = "CPU-DSP", 295 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 296 | SND_SOC_DAIFMT_CBM_CFM, 297 SND_SOC_DAILINK_REG(wm5102_cpu_dsp), 298 }, 299 { 300 .name = "DSP-CODEC", 301 .stream_name = "DSP-CODEC", 302 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 303 | SND_SOC_DAIFMT_CBM_CFM, 304 .c2c_params = &sub_params, 305 .num_c2c_params = 1, 306 .ignore_suspend = 1, 307 SND_SOC_DAILINK_REG(wm5102_dsp_codec), 308 }, 309 { 310 .name = "Baseband", 311 .stream_name = "Baseband", 312 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 313 | SND_SOC_DAIFMT_CBM_CFM, 314 .ignore_suspend = 1, 315 .c2c_params = &baseband_params, 316 .num_c2c_params = 1, 317 SND_SOC_DAILINK_REG(wm5102_baseband), 318 }, 319 { 320 .name = "Sub", 321 .stream_name = "Sub", 322 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 323 | SND_SOC_DAIFMT_CBS_CFS, 324 .ignore_suspend = 1, 325 .c2c_params = &sub_params, 326 .num_c2c_params = 1, 327 SND_SOC_DAILINK_REG(wm5102_sub), 328 }, 329 }; 330 331 SND_SOC_DAILINK_DEFS(wm5110_cpu_dsp, 332 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 333 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")), 334 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 335 336 SND_SOC_DAILINK_DEFS(wm5110_dsp_codec, 337 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")), 338 DAILINK_COMP_ARRAY(COMP_CODEC("wm5110-codec", "wm5110-aif1"))); 339 340 SND_SOC_DAILINK_DEFS(wm5110_baseband, 341 DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif2")), 342 DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1"))); 343 344 345 SND_SOC_DAILINK_DEFS(wm5110_sub, 346 DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif3")), 347 DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi"))); 348 349 static struct snd_soc_dai_link bells_dai_wm5110[] = { 350 { 351 .name = "CPU-DSP", 352 .stream_name = "CPU-DSP", 353 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 354 | SND_SOC_DAIFMT_CBM_CFM, 355 SND_SOC_DAILINK_REG(wm5110_cpu_dsp), 356 }, 357 { 358 .name = "DSP-CODEC", 359 .stream_name = "DSP-CODEC", 360 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 361 | SND_SOC_DAIFMT_CBM_CFM, 362 .c2c_params = &sub_params, 363 .num_c2c_params = 1, 364 .ignore_suspend = 1, 365 SND_SOC_DAILINK_REG(wm5110_dsp_codec), 366 }, 367 { 368 .name = "Baseband", 369 .stream_name = "Baseband", 370 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 371 | SND_SOC_DAIFMT_CBM_CFM, 372 .ignore_suspend = 1, 373 .c2c_params = &baseband_params, 374 .num_c2c_params = 1, 375 SND_SOC_DAILINK_REG(wm5110_baseband), 376 }, 377 { 378 .name = "Sub", 379 .stream_name = "Sub", 380 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 381 | SND_SOC_DAIFMT_CBS_CFS, 382 .ignore_suspend = 1, 383 .c2c_params = &sub_params, 384 .num_c2c_params = 1, 385 SND_SOC_DAILINK_REG(wm5110_sub), 386 }, 387 }; 388 389 static struct snd_soc_codec_conf bells_codec_conf[] = { 390 { 391 .dlc = COMP_CODEC_CONF("wm9081.1-006c"), 392 .name_prefix = "Sub", 393 }, 394 }; 395 396 static const struct snd_soc_dapm_widget bells_widgets[] = { 397 SND_SOC_DAPM_MIC("DMIC", NULL), 398 }; 399 400 static const struct snd_soc_dapm_route bells_routes[] = { 401 { "Sub CLK_SYS", NULL, "OPCLK" }, 402 { "CLKIN", NULL, "OPCLK" }, 403 404 { "DMIC", NULL, "MICBIAS2" }, 405 { "IN2L", NULL, "DMIC" }, 406 { "IN2R", NULL, "DMIC" }, 407 }; 408 409 static struct snd_soc_card bells_cards[] = { 410 { 411 .name = "Bells WM2200", 412 .owner = THIS_MODULE, 413 .dai_link = bells_dai_wm2200, 414 .num_links = ARRAY_SIZE(bells_dai_wm2200), 415 .codec_conf = bells_codec_conf, 416 .num_configs = ARRAY_SIZE(bells_codec_conf), 417 418 .late_probe = bells_late_probe, 419 420 .dapm_widgets = bells_widgets, 421 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 422 .dapm_routes = bells_routes, 423 .num_dapm_routes = ARRAY_SIZE(bells_routes), 424 425 .set_bias_level = bells_set_bias_level, 426 .set_bias_level_post = bells_set_bias_level_post, 427 428 .drvdata = &wm2200_drvdata, 429 }, 430 { 431 .name = "Bells WM5102", 432 .owner = THIS_MODULE, 433 .dai_link = bells_dai_wm5102, 434 .num_links = ARRAY_SIZE(bells_dai_wm5102), 435 .codec_conf = bells_codec_conf, 436 .num_configs = ARRAY_SIZE(bells_codec_conf), 437 438 .late_probe = bells_late_probe, 439 440 .dapm_widgets = bells_widgets, 441 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 442 .dapm_routes = bells_routes, 443 .num_dapm_routes = ARRAY_SIZE(bells_routes), 444 445 .set_bias_level = bells_set_bias_level, 446 .set_bias_level_post = bells_set_bias_level_post, 447 448 .drvdata = &wm5102_drvdata, 449 }, 450 { 451 .name = "Bells WM5110", 452 .owner = THIS_MODULE, 453 .dai_link = bells_dai_wm5110, 454 .num_links = ARRAY_SIZE(bells_dai_wm5110), 455 .codec_conf = bells_codec_conf, 456 .num_configs = ARRAY_SIZE(bells_codec_conf), 457 458 .late_probe = bells_late_probe, 459 460 .dapm_widgets = bells_widgets, 461 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 462 .dapm_routes = bells_routes, 463 .num_dapm_routes = ARRAY_SIZE(bells_routes), 464 465 .set_bias_level = bells_set_bias_level, 466 .set_bias_level_post = bells_set_bias_level_post, 467 468 .drvdata = &wm5110_drvdata, 469 }, 470 }; 471 472 static int bells_probe(struct platform_device *pdev) 473 { 474 int ret; 475 476 bells_cards[pdev->id].dev = &pdev->dev; 477 478 ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]); 479 if (ret) 480 dev_err(&pdev->dev, 481 "snd_soc_register_card(%s) failed: %d\n", 482 bells_cards[pdev->id].name, ret); 483 484 return ret; 485 } 486 487 static struct platform_driver bells_driver = { 488 .driver = { 489 .name = "bells", 490 .pm = &snd_soc_pm_ops, 491 }, 492 .probe = bells_probe, 493 }; 494 495 module_platform_driver(bells_driver); 496 497 MODULE_DESCRIPTION("Bells audio support"); 498 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 499 MODULE_LICENSE("GPL"); 500 MODULE_ALIAS("platform:bells"); 501