bells.c (12744B)
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 33struct bells_drvdata { 34 int sysclk_rate; 35 int asyncclk_rate; 36}; 37 38static struct bells_drvdata wm2200_drvdata = { 39 .sysclk_rate = 22579200, 40}; 41 42static struct bells_drvdata wm5102_drvdata = { 43 .sysclk_rate = 45158400, 44 .asyncclk_rate = 49152000, 45}; 46 47static struct bells_drvdata wm5110_drvdata = { 48 .sysclk_rate = 135475200, 49 .asyncclk_rate = 147456000, 50}; 51 52static 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 98static 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 142static 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 229static 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 237static 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 245SND_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 250SND_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 254static 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 .params = &sub_params, 268 .ignore_suspend = 1, 269 SND_SOC_DAILINK_REG(wm2200_dsp_codec), 270 }, 271}; 272 273SND_SOC_DAILINK_DEFS(wm5102_cpu_dsp, 274 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 275 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")), 276 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 277 278SND_SOC_DAILINK_DEFS(wm5102_dsp_codec, 279 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")), 280 DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1"))); 281 282SND_SOC_DAILINK_DEFS(wm5102_baseband, 283 DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")), 284 DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1"))); 285 286SND_SOC_DAILINK_DEFS(wm5102_sub, 287 DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif3")), 288 DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi"))); 289 290static struct snd_soc_dai_link bells_dai_wm5102[] = { 291 { 292 .name = "CPU-DSP", 293 .stream_name = "CPU-DSP", 294 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 295 | SND_SOC_DAIFMT_CBM_CFM, 296 SND_SOC_DAILINK_REG(wm5102_cpu_dsp), 297 }, 298 { 299 .name = "DSP-CODEC", 300 .stream_name = "DSP-CODEC", 301 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 302 | SND_SOC_DAIFMT_CBM_CFM, 303 .params = &sub_params, 304 .ignore_suspend = 1, 305 SND_SOC_DAILINK_REG(wm5102_dsp_codec), 306 }, 307 { 308 .name = "Baseband", 309 .stream_name = "Baseband", 310 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 311 | SND_SOC_DAIFMT_CBM_CFM, 312 .ignore_suspend = 1, 313 .params = &baseband_params, 314 SND_SOC_DAILINK_REG(wm5102_baseband), 315 }, 316 { 317 .name = "Sub", 318 .stream_name = "Sub", 319 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 320 | SND_SOC_DAIFMT_CBS_CFS, 321 .ignore_suspend = 1, 322 .params = &sub_params, 323 SND_SOC_DAILINK_REG(wm5102_sub), 324 }, 325}; 326 327SND_SOC_DAILINK_DEFS(wm5110_cpu_dsp, 328 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 329 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")), 330 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 331 332SND_SOC_DAILINK_DEFS(wm5110_dsp_codec, 333 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")), 334 DAILINK_COMP_ARRAY(COMP_CODEC("wm5110-codec", "wm5110-aif1"))); 335 336SND_SOC_DAILINK_DEFS(wm5110_baseband, 337 DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif2")), 338 DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1"))); 339 340 341SND_SOC_DAILINK_DEFS(wm5110_sub, 342 DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif3")), 343 DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi"))); 344 345static struct snd_soc_dai_link bells_dai_wm5110[] = { 346 { 347 .name = "CPU-DSP", 348 .stream_name = "CPU-DSP", 349 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 350 | SND_SOC_DAIFMT_CBM_CFM, 351 SND_SOC_DAILINK_REG(wm5110_cpu_dsp), 352 }, 353 { 354 .name = "DSP-CODEC", 355 .stream_name = "DSP-CODEC", 356 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 357 | SND_SOC_DAIFMT_CBM_CFM, 358 .params = &sub_params, 359 .ignore_suspend = 1, 360 SND_SOC_DAILINK_REG(wm5110_dsp_codec), 361 }, 362 { 363 .name = "Baseband", 364 .stream_name = "Baseband", 365 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 366 | SND_SOC_DAIFMT_CBM_CFM, 367 .ignore_suspend = 1, 368 .params = &baseband_params, 369 SND_SOC_DAILINK_REG(wm5110_baseband), 370 }, 371 { 372 .name = "Sub", 373 .stream_name = "Sub", 374 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 375 | SND_SOC_DAIFMT_CBS_CFS, 376 .ignore_suspend = 1, 377 .params = &sub_params, 378 SND_SOC_DAILINK_REG(wm5110_sub), 379 }, 380}; 381 382static struct snd_soc_codec_conf bells_codec_conf[] = { 383 { 384 .dlc = COMP_CODEC_CONF("wm9081.1-006c"), 385 .name_prefix = "Sub", 386 }, 387}; 388 389static const struct snd_soc_dapm_widget bells_widgets[] = { 390 SND_SOC_DAPM_MIC("DMIC", NULL), 391}; 392 393static const struct snd_soc_dapm_route bells_routes[] = { 394 { "Sub CLK_SYS", NULL, "OPCLK" }, 395 { "CLKIN", NULL, "OPCLK" }, 396 397 { "DMIC", NULL, "MICBIAS2" }, 398 { "IN2L", NULL, "DMIC" }, 399 { "IN2R", NULL, "DMIC" }, 400}; 401 402static struct snd_soc_card bells_cards[] = { 403 { 404 .name = "Bells WM2200", 405 .owner = THIS_MODULE, 406 .dai_link = bells_dai_wm2200, 407 .num_links = ARRAY_SIZE(bells_dai_wm2200), 408 .codec_conf = bells_codec_conf, 409 .num_configs = ARRAY_SIZE(bells_codec_conf), 410 411 .late_probe = bells_late_probe, 412 413 .dapm_widgets = bells_widgets, 414 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 415 .dapm_routes = bells_routes, 416 .num_dapm_routes = ARRAY_SIZE(bells_routes), 417 418 .set_bias_level = bells_set_bias_level, 419 .set_bias_level_post = bells_set_bias_level_post, 420 421 .drvdata = &wm2200_drvdata, 422 }, 423 { 424 .name = "Bells WM5102", 425 .owner = THIS_MODULE, 426 .dai_link = bells_dai_wm5102, 427 .num_links = ARRAY_SIZE(bells_dai_wm5102), 428 .codec_conf = bells_codec_conf, 429 .num_configs = ARRAY_SIZE(bells_codec_conf), 430 431 .late_probe = bells_late_probe, 432 433 .dapm_widgets = bells_widgets, 434 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 435 .dapm_routes = bells_routes, 436 .num_dapm_routes = ARRAY_SIZE(bells_routes), 437 438 .set_bias_level = bells_set_bias_level, 439 .set_bias_level_post = bells_set_bias_level_post, 440 441 .drvdata = &wm5102_drvdata, 442 }, 443 { 444 .name = "Bells WM5110", 445 .owner = THIS_MODULE, 446 .dai_link = bells_dai_wm5110, 447 .num_links = ARRAY_SIZE(bells_dai_wm5110), 448 .codec_conf = bells_codec_conf, 449 .num_configs = ARRAY_SIZE(bells_codec_conf), 450 451 .late_probe = bells_late_probe, 452 453 .dapm_widgets = bells_widgets, 454 .num_dapm_widgets = ARRAY_SIZE(bells_widgets), 455 .dapm_routes = bells_routes, 456 .num_dapm_routes = ARRAY_SIZE(bells_routes), 457 458 .set_bias_level = bells_set_bias_level, 459 .set_bias_level_post = bells_set_bias_level_post, 460 461 .drvdata = &wm5110_drvdata, 462 }, 463}; 464 465static int bells_probe(struct platform_device *pdev) 466{ 467 int ret; 468 469 bells_cards[pdev->id].dev = &pdev->dev; 470 471 ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]); 472 if (ret) 473 dev_err(&pdev->dev, 474 "snd_soc_register_card(%s) failed: %d\n", 475 bells_cards[pdev->id].name, ret); 476 477 return ret; 478} 479 480static struct platform_driver bells_driver = { 481 .driver = { 482 .name = "bells", 483 .pm = &snd_soc_pm_ops, 484 }, 485 .probe = bells_probe, 486}; 487 488module_platform_driver(bells_driver); 489 490MODULE_DESCRIPTION("Bells audio support"); 491MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 492MODULE_LICENSE("GPL"); 493MODULE_ALIAS("platform:bells");