1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // ASoC simple sound card support 4 // 5 // Copyright (C) 2012 Renesas Solutions Corp. 6 // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 7 8 #include <linux/clk.h> 9 #include <linux/device.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/platform_device.h> 13 #include <linux/string.h> 14 #include <sound/simple_card.h> 15 #include <sound/soc-dai.h> 16 #include <sound/soc.h> 17 18 struct simple_card_data { 19 struct snd_soc_card snd_card; 20 struct simple_dai_props { 21 struct asoc_simple_dai *cpu_dai; 22 struct asoc_simple_dai *codec_dai; 23 struct snd_soc_dai_link_component codecs; /* single codec */ 24 struct snd_soc_dai_link_component platform; 25 unsigned int mclk_fs; 26 } *dai_props; 27 unsigned int mclk_fs; 28 struct asoc_simple_jack hp_jack; 29 struct asoc_simple_jack mic_jack; 30 struct snd_soc_dai_link *dai_link; 31 struct asoc_simple_dai *dais; 32 }; 33 34 #define simple_priv_to_card(priv) (&(priv)->snd_card) 35 #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) 36 #define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev) 37 #define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i)) 38 39 #define DAI "sound-dai" 40 #define CELL "#sound-dai-cells" 41 #define PREFIX "simple-audio-card," 42 43 static int asoc_simple_card_startup(struct snd_pcm_substream *substream) 44 { 45 struct snd_soc_pcm_runtime *rtd = substream->private_data; 46 struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 47 struct simple_dai_props *dai_props = 48 simple_priv_to_props(priv, rtd->num); 49 int ret; 50 51 ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); 52 if (ret) 53 return ret; 54 55 ret = asoc_simple_card_clk_enable(dai_props->codec_dai); 56 if (ret) 57 asoc_simple_card_clk_disable(dai_props->cpu_dai); 58 59 return ret; 60 } 61 62 static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) 63 { 64 struct snd_soc_pcm_runtime *rtd = substream->private_data; 65 struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 66 struct simple_dai_props *dai_props = 67 simple_priv_to_props(priv, rtd->num); 68 69 asoc_simple_card_clk_disable(dai_props->cpu_dai); 70 71 asoc_simple_card_clk_disable(dai_props->codec_dai); 72 } 73 74 static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, 75 unsigned long rate) 76 { 77 if (!simple_dai->clk) 78 return 0; 79 80 if (clk_get_rate(simple_dai->clk) == rate) 81 return 0; 82 83 return clk_set_rate(simple_dai->clk, rate); 84 } 85 86 static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, 87 struct snd_pcm_hw_params *params) 88 { 89 struct snd_soc_pcm_runtime *rtd = substream->private_data; 90 struct snd_soc_dai *codec_dai = rtd->codec_dai; 91 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 92 struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 93 struct simple_dai_props *dai_props = 94 simple_priv_to_props(priv, rtd->num); 95 unsigned int mclk, mclk_fs = 0; 96 int ret = 0; 97 98 if (priv->mclk_fs) 99 mclk_fs = priv->mclk_fs; 100 else if (dai_props->mclk_fs) 101 mclk_fs = dai_props->mclk_fs; 102 103 if (mclk_fs) { 104 mclk = params_rate(params) * mclk_fs; 105 106 ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk); 107 if (ret < 0) 108 return ret; 109 110 ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk); 111 if (ret < 0) 112 return ret; 113 114 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 115 SND_SOC_CLOCK_IN); 116 if (ret && ret != -ENOTSUPP) 117 goto err; 118 119 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, 120 SND_SOC_CLOCK_OUT); 121 if (ret && ret != -ENOTSUPP) 122 goto err; 123 } 124 return 0; 125 err: 126 return ret; 127 } 128 129 static const struct snd_soc_ops asoc_simple_card_ops = { 130 .startup = asoc_simple_card_startup, 131 .shutdown = asoc_simple_card_shutdown, 132 .hw_params = asoc_simple_card_hw_params, 133 }; 134 135 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) 136 { 137 struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 138 struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); 139 int ret; 140 141 ret = asoc_simple_card_init_dai(rtd->codec_dai, 142 dai_props->codec_dai); 143 if (ret < 0) 144 return ret; 145 146 ret = asoc_simple_card_init_dai(rtd->cpu_dai, 147 dai_props->cpu_dai); 148 if (ret < 0) 149 return ret; 150 151 return 0; 152 } 153 154 static int asoc_simple_card_dai_link_of(struct device_node *node, 155 struct simple_card_data *priv, 156 int *dai_idx, int link_idx, 157 bool is_top_level_node) 158 { 159 struct device *dev = simple_priv_to_dev(priv); 160 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); 161 struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); 162 struct asoc_simple_dai *cpu_dai; 163 struct asoc_simple_dai *codec_dai; 164 struct device_node *cpu = NULL; 165 struct device_node *plat = NULL; 166 struct device_node *codec = NULL; 167 char prop[128]; 168 char *prefix = ""; 169 int ret, single_cpu; 170 171 /* For single DAI link & old style of DT node */ 172 if (is_top_level_node) 173 prefix = PREFIX; 174 175 snprintf(prop, sizeof(prop), "%scpu", prefix); 176 cpu = of_get_child_by_name(node, prop); 177 178 if (!cpu) { 179 ret = -EINVAL; 180 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 181 goto dai_link_of_err; 182 } 183 184 snprintf(prop, sizeof(prop), "%splat", prefix); 185 plat = of_get_child_by_name(node, prop); 186 187 snprintf(prop, sizeof(prop), "%scodec", prefix); 188 codec = of_get_child_by_name(node, prop); 189 190 if (!codec) { 191 ret = -EINVAL; 192 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 193 goto dai_link_of_err; 194 } 195 196 cpu_dai = 197 dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; 198 codec_dai = 199 dai_props->codec_dai = &priv->dais[(*dai_idx)++]; 200 201 ret = asoc_simple_card_parse_daifmt(dev, node, codec, 202 prefix, &dai_link->dai_fmt); 203 if (ret < 0) 204 goto dai_link_of_err; 205 206 of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); 207 208 ret = asoc_simple_card_parse_cpu(cpu, dai_link, 209 DAI, CELL, &single_cpu); 210 if (ret < 0) 211 goto dai_link_of_err; 212 213 ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL); 214 if (ret < 0) 215 goto dai_link_of_err; 216 217 ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL); 218 if (ret < 0) 219 goto dai_link_of_err; 220 221 ret = asoc_simple_card_of_parse_tdm(cpu, cpu_dai); 222 if (ret < 0) 223 goto dai_link_of_err; 224 225 ret = asoc_simple_card_of_parse_tdm(codec, codec_dai); 226 if (ret < 0) 227 goto dai_link_of_err; 228 229 ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); 230 if (ret < 0) 231 goto dai_link_of_err; 232 233 ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai); 234 if (ret < 0) 235 goto dai_link_of_err; 236 237 ret = asoc_simple_card_canonicalize_dailink(dai_link); 238 if (ret < 0) 239 goto dai_link_of_err; 240 241 ret = asoc_simple_card_set_dailink_name(dev, dai_link, 242 "%s-%s", 243 dai_link->cpu_dai_name, 244 dai_link->codecs->dai_name); 245 if (ret < 0) 246 goto dai_link_of_err; 247 248 dai_link->ops = &asoc_simple_card_ops; 249 dai_link->init = asoc_simple_card_dai_init; 250 251 asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); 252 253 dai_link_of_err: 254 of_node_put(cpu); 255 of_node_put(codec); 256 257 return ret; 258 } 259 260 static int asoc_simple_card_parse_aux_devs(struct device_node *node, 261 struct simple_card_data *priv) 262 { 263 struct device *dev = simple_priv_to_dev(priv); 264 struct device_node *aux_node; 265 struct snd_soc_card *card = simple_priv_to_card(priv); 266 int i, n, len; 267 268 if (!of_find_property(node, PREFIX "aux-devs", &len)) 269 return 0; /* Ok to have no aux-devs */ 270 271 n = len / sizeof(__be32); 272 if (n <= 0) 273 return -EINVAL; 274 275 card->aux_dev = devm_kcalloc(dev, 276 n, sizeof(*card->aux_dev), GFP_KERNEL); 277 if (!card->aux_dev) 278 return -ENOMEM; 279 280 for (i = 0; i < n; i++) { 281 aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); 282 if (!aux_node) 283 return -EINVAL; 284 card->aux_dev[i].codec_of_node = aux_node; 285 } 286 287 card->num_aux_devs = n; 288 return 0; 289 } 290 291 static int asoc_simple_card_parse_of(struct simple_card_data *priv) 292 { 293 struct device *dev = simple_priv_to_dev(priv); 294 struct snd_soc_card *card = simple_priv_to_card(priv); 295 struct device_node *dai_link; 296 struct device_node *node = dev->of_node; 297 int ret; 298 int link_idx, dai_idx; 299 300 if (!node) 301 return -EINVAL; 302 303 dai_link = of_get_child_by_name(node, PREFIX "dai-link"); 304 305 ret = asoc_simple_card_of_parse_widgets(card, PREFIX); 306 if (ret < 0) 307 goto card_parse_end; 308 309 ret = asoc_simple_card_of_parse_routing(card, PREFIX); 310 if (ret < 0) 311 goto card_parse_end; 312 313 /* Factor to mclk, used in hw_params() */ 314 of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); 315 316 /* Single/Muti DAI link(s) & New style of DT node */ 317 link_idx = 0; 318 dai_idx = 0; 319 if (dai_link) { 320 struct device_node *np = NULL; 321 322 for_each_child_of_node(node, np) { 323 dev_dbg(dev, "\tlink %d:\n", link_idx); 324 ret = asoc_simple_card_dai_link_of(np, priv, 325 &dai_idx, link_idx++, false); 326 if (ret < 0) { 327 of_node_put(np); 328 goto card_parse_end; 329 } 330 } 331 } else { 332 /* For single DAI link & old style of DT node */ 333 ret = asoc_simple_card_dai_link_of(node, priv, 334 &dai_idx, link_idx++, true); 335 if (ret < 0) 336 goto card_parse_end; 337 } 338 339 ret = asoc_simple_card_parse_card_name(card, PREFIX); 340 if (ret < 0) 341 goto card_parse_end; 342 343 ret = asoc_simple_card_parse_aux_devs(node, priv); 344 345 card_parse_end: 346 of_node_put(dai_link); 347 348 return ret; 349 } 350 351 static int asoc_simple_soc_card_probe(struct snd_soc_card *card) 352 { 353 struct simple_card_data *priv = snd_soc_card_get_drvdata(card); 354 int ret; 355 356 ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX); 357 if (ret < 0) 358 return ret; 359 360 ret = asoc_simple_card_init_mic(card, &priv->mic_jack, PREFIX); 361 if (ret < 0) 362 return ret; 363 364 return 0; 365 } 366 367 static int asoc_simple_card_probe(struct platform_device *pdev) 368 { 369 struct simple_card_data *priv; 370 struct snd_soc_dai_link *dai_link; 371 struct simple_dai_props *dai_props; 372 struct asoc_simple_dai *dais; 373 struct device *dev = &pdev->dev; 374 struct device_node *np = dev->of_node; 375 struct snd_soc_card *card; 376 int num, ret, i; 377 378 /* Get the number of DAI links */ 379 if (np && of_get_child_by_name(np, PREFIX "dai-link")) 380 num = of_get_child_count(np); 381 else 382 num = 1; 383 384 /* Allocate the private data and the DAI link array */ 385 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 386 if (!priv) 387 return -ENOMEM; 388 389 dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); 390 dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); 391 dais = devm_kcalloc(dev, num * 2, sizeof(*dais), GFP_KERNEL); 392 if (!dai_props || !dai_link || !dais) 393 return -ENOMEM; 394 395 /* 396 * Use snd_soc_dai_link_component instead of legacy style 397 * It is codec only. but cpu/platform will be supported in the future. 398 * see 399 * soc-core.c :: snd_soc_init_multicodec() 400 */ 401 for (i = 0; i < num; i++) { 402 dai_link[i].codecs = &dai_props[i].codecs; 403 dai_link[i].num_codecs = 1; 404 dai_link[i].platform = &dai_props[i].platform; 405 } 406 407 priv->dai_props = dai_props; 408 priv->dai_link = dai_link; 409 priv->dais = dais; 410 411 /* Init snd_soc_card */ 412 card = simple_priv_to_card(priv); 413 card->owner = THIS_MODULE; 414 card->dev = dev; 415 card->dai_link = priv->dai_link; 416 card->num_links = num; 417 card->probe = asoc_simple_soc_card_probe; 418 419 if (np && of_device_is_available(np)) { 420 421 ret = asoc_simple_card_parse_of(priv); 422 if (ret < 0) { 423 if (ret != -EPROBE_DEFER) 424 dev_err(dev, "parse error %d\n", ret); 425 goto err; 426 } 427 428 } else { 429 struct asoc_simple_card_info *cinfo; 430 struct snd_soc_dai_link_component *codecs; 431 struct snd_soc_dai_link_component *platform; 432 int dai_idx = 0; 433 434 cinfo = dev->platform_data; 435 if (!cinfo) { 436 dev_err(dev, "no info for asoc-simple-card\n"); 437 return -EINVAL; 438 } 439 440 if (!cinfo->name || 441 !cinfo->codec_dai.name || 442 !cinfo->codec || 443 !cinfo->platform || 444 !cinfo->cpu_dai.name) { 445 dev_err(dev, "insufficient asoc_simple_card_info settings\n"); 446 return -EINVAL; 447 } 448 449 dai_props->cpu_dai = &priv->dais[dai_idx++]; 450 dai_props->codec_dai = &priv->dais[dai_idx++]; 451 452 codecs = dai_link->codecs; 453 codecs->name = cinfo->codec; 454 codecs->dai_name = cinfo->codec_dai.name; 455 456 platform = dai_link->platform; 457 platform->name = cinfo->platform; 458 459 card->name = (cinfo->card) ? cinfo->card : cinfo->name; 460 dai_link->name = cinfo->name; 461 dai_link->stream_name = cinfo->name; 462 dai_link->cpu_dai_name = cinfo->cpu_dai.name; 463 dai_link->dai_fmt = cinfo->daifmt; 464 dai_link->init = asoc_simple_card_dai_init; 465 memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai, 466 sizeof(*priv->dai_props->cpu_dai)); 467 memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai, 468 sizeof(*priv->dai_props->codec_dai)); 469 } 470 471 snd_soc_card_set_drvdata(card, priv); 472 473 ret = devm_snd_soc_register_card(dev, card); 474 if (ret < 0) 475 goto err; 476 477 return 0; 478 err: 479 asoc_simple_card_clean_reference(card); 480 481 return ret; 482 } 483 484 static int asoc_simple_card_remove(struct platform_device *pdev) 485 { 486 struct snd_soc_card *card = platform_get_drvdata(pdev); 487 488 return asoc_simple_card_clean_reference(card); 489 } 490 491 static const struct of_device_id asoc_simple_of_match[] = { 492 { .compatible = "simple-audio-card", }, 493 {}, 494 }; 495 MODULE_DEVICE_TABLE(of, asoc_simple_of_match); 496 497 static struct platform_driver asoc_simple_card = { 498 .driver = { 499 .name = "asoc-simple-card", 500 .pm = &snd_soc_pm_ops, 501 .of_match_table = asoc_simple_of_match, 502 }, 503 .probe = asoc_simple_card_probe, 504 .remove = asoc_simple_card_remove, 505 }; 506 507 module_platform_driver(asoc_simple_card); 508 509 MODULE_ALIAS("platform:asoc-simple-card"); 510 MODULE_LICENSE("GPL v2"); 511 MODULE_DESCRIPTION("ASoC Simple Sound Card"); 512 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 513