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].name); 63 codec_dai = rtd->codec_dai; 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].name); 109 codec_dai = rtd->codec_dai; 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].name); 155 wm0010 = rtd->codec_dai->component; 156 157 rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); 158 component = rtd->codec_dai->component; 159 aif1_dai = rtd->codec_dai; 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].name); 198 aif2_dai = rtd->cpu_dai; 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].name); 210 aif3_dai = rtd->cpu_dai; 211 wm9081_dai = rtd->codec_dai; 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 static struct snd_soc_dai_link bells_dai_wm2200[] = { 246 { 247 .name = "CPU-DSP", 248 .stream_name = "CPU-DSP", 249 .cpu_dai_name = "samsung-i2s.0", 250 .codec_dai_name = "wm0010-sdi1", 251 .platform_name = "samsung-i2s.0", 252 .codec_name = "spi0.0", 253 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 254 | SND_SOC_DAIFMT_CBM_CFM, 255 }, 256 { 257 .name = "DSP-CODEC", 258 .stream_name = "DSP-CODEC", 259 .cpu_dai_name = "wm0010-sdi2", 260 .codec_dai_name = "wm2200", 261 .codec_name = "wm2200.1-003a", 262 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 263 | SND_SOC_DAIFMT_CBM_CFM, 264 .params = &sub_params, 265 .ignore_suspend = 1, 266 }, 267 }; 268 269 static struct snd_soc_dai_link bells_dai_wm5102[] = { 270 { 271 .name = "CPU-DSP", 272 .stream_name = "CPU-DSP", 273 .cpu_dai_name = "samsung-i2s.0", 274 .codec_dai_name = "wm0010-sdi1", 275 .platform_name = "samsung-i2s.0", 276 .codec_name = "spi0.0", 277 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 278 | SND_SOC_DAIFMT_CBM_CFM, 279 }, 280 { 281 .name = "DSP-CODEC", 282 .stream_name = "DSP-CODEC", 283 .cpu_dai_name = "wm0010-sdi2", 284 .codec_dai_name = "wm5102-aif1", 285 .codec_name = "wm5102-codec", 286 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 287 | SND_SOC_DAIFMT_CBM_CFM, 288 .params = &sub_params, 289 .ignore_suspend = 1, 290 }, 291 { 292 .name = "Baseband", 293 .stream_name = "Baseband", 294 .cpu_dai_name = "wm5102-aif2", 295 .codec_dai_name = "wm1250-ev1", 296 .codec_name = "wm1250-ev1.1-0027", 297 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 298 | SND_SOC_DAIFMT_CBM_CFM, 299 .ignore_suspend = 1, 300 .params = &baseband_params, 301 }, 302 { 303 .name = "Sub", 304 .stream_name = "Sub", 305 .cpu_dai_name = "wm5102-aif3", 306 .codec_dai_name = "wm9081-hifi", 307 .codec_name = "wm9081.1-006c", 308 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 309 | SND_SOC_DAIFMT_CBS_CFS, 310 .ignore_suspend = 1, 311 .params = &sub_params, 312 }, 313 }; 314 315 static struct snd_soc_dai_link bells_dai_wm5110[] = { 316 { 317 .name = "CPU-DSP", 318 .stream_name = "CPU-DSP", 319 .cpu_dai_name = "samsung-i2s.0", 320 .codec_dai_name = "wm0010-sdi1", 321 .platform_name = "samsung-i2s.0", 322 .codec_name = "spi0.0", 323 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 324 | SND_SOC_DAIFMT_CBM_CFM, 325 }, 326 { 327 .name = "DSP-CODEC", 328 .stream_name = "DSP-CODEC", 329 .cpu_dai_name = "wm0010-sdi2", 330 .codec_dai_name = "wm5110-aif1", 331 .codec_name = "wm5110-codec", 332 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 333 | SND_SOC_DAIFMT_CBM_CFM, 334 .params = &sub_params, 335 .ignore_suspend = 1, 336 }, 337 { 338 .name = "Baseband", 339 .stream_name = "Baseband", 340 .cpu_dai_name = "wm5110-aif2", 341 .codec_dai_name = "wm1250-ev1", 342 .codec_name = "wm1250-ev1.1-0027", 343 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 344 | SND_SOC_DAIFMT_CBM_CFM, 345 .ignore_suspend = 1, 346 .params = &baseband_params, 347 }, 348 { 349 .name = "Sub", 350 .stream_name = "Sub", 351 .cpu_dai_name = "wm5110-aif3", 352 .codec_dai_name = "wm9081-hifi", 353 .codec_name = "wm9081.1-006c", 354 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 355 | SND_SOC_DAIFMT_CBS_CFS, 356 .ignore_suspend = 1, 357 .params = &sub_params, 358 }, 359 }; 360 361 static struct snd_soc_codec_conf bells_codec_conf[] = { 362 { 363 .dev_name = "wm9081.1-006c", 364 .name_prefix = "Sub", 365 }, 366 }; 367 368 static struct snd_soc_dapm_widget bells_widgets[] = { 369 SND_SOC_DAPM_MIC("DMIC", NULL), 370 }; 371 372 static struct snd_soc_dapm_route bells_routes[] = { 373 { "Sub CLK_SYS", NULL, "OPCLK" }, 374 { "CLKIN", NULL, "OPCLK" }, 375 376 { "DMIC", NULL, "MICBIAS2" }, 377 { "IN2L", NULL, "DMIC" }, 378 { "IN2R", NULL, "DMIC" }, 379 }; 380 381 static struct snd_soc_card bells_cards[] = { 382 { 383 .name = "Bells WM2200", 384 .owner = THIS_MODULE, 385 .dai_link = bells_dai_wm2200, 386 .num_links = ARRAY_SIZE(bells_dai_wm2200), 387 .codec_conf = bells_codec_conf, 388 .num_configs = ARRAY_SIZE(bells_codec_conf), 389 390 .late_probe = bells_late_probe, 391 392 .dapm_widgets = bells_widgets, 393 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 394 .dapm_routes = bells_routes, 395 .num_dapm_routes = ARRAY_SIZE(bells_routes), 396 397 .set_bias_level = bells_set_bias_level, 398 .set_bias_level_post = bells_set_bias_level_post, 399 400 .drvdata = &wm2200_drvdata, 401 }, 402 { 403 .name = "Bells WM5102", 404 .owner = THIS_MODULE, 405 .dai_link = bells_dai_wm5102, 406 .num_links = ARRAY_SIZE(bells_dai_wm5102), 407 .codec_conf = bells_codec_conf, 408 .num_configs = ARRAY_SIZE(bells_codec_conf), 409 410 .late_probe = bells_late_probe, 411 412 .dapm_widgets = bells_widgets, 413 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 414 .dapm_routes = bells_routes, 415 .num_dapm_routes = ARRAY_SIZE(bells_routes), 416 417 .set_bias_level = bells_set_bias_level, 418 .set_bias_level_post = bells_set_bias_level_post, 419 420 .drvdata = &wm5102_drvdata, 421 }, 422 { 423 .name = "Bells WM5110", 424 .owner = THIS_MODULE, 425 .dai_link = bells_dai_wm5110, 426 .num_links = ARRAY_SIZE(bells_dai_wm5110), 427 .codec_conf = bells_codec_conf, 428 .num_configs = ARRAY_SIZE(bells_codec_conf), 429 430 .late_probe = bells_late_probe, 431 432 .dapm_widgets = bells_widgets, 433 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 434 .dapm_routes = bells_routes, 435 .num_dapm_routes = ARRAY_SIZE(bells_routes), 436 437 .set_bias_level = bells_set_bias_level, 438 .set_bias_level_post = bells_set_bias_level_post, 439 440 .drvdata = &wm5110_drvdata, 441 }, 442 }; 443 444 static int bells_probe(struct platform_device *pdev) 445 { 446 int ret; 447 448 bells_cards[pdev->id].dev = &pdev->dev; 449 450 ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]); 451 if (ret) 452 dev_err(&pdev->dev, 453 "snd_soc_register_card(%s) failed: %d\n", 454 bells_cards[pdev->id].name, ret); 455 456 return ret; 457 } 458 459 static struct platform_driver bells_driver = { 460 .driver = { 461 .name = "bells", 462 .pm = &snd_soc_pm_ops, 463 }, 464 .probe = bells_probe, 465 }; 466 467 module_platform_driver(bells_driver); 468 469 MODULE_DESCRIPTION("Bells audio support"); 470 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 471 MODULE_LICENSE("GPL"); 472 MODULE_ALIAS("platform:bells"); 473