1 /* 2 * ASoC simple sound card support 3 * 4 * Copyright (C) 2012 Renesas Solutions Corp. 5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 #include <linux/clk.h> 12 #include <linux/device.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/string.h> 17 #include <sound/simple_card.h> 18 #include <sound/soc-dai.h> 19 #include <sound/soc.h> 20 21 struct simple_card_data { 22 struct snd_soc_card snd_card; 23 struct simple_dai_props { 24 struct asoc_simple_dai cpu_dai; 25 struct asoc_simple_dai codec_dai; 26 } *dai_props; 27 unsigned int mclk_fs; 28 struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ 29 }; 30 31 static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, 32 struct snd_pcm_hw_params *params) 33 { 34 struct snd_soc_pcm_runtime *rtd = substream->private_data; 35 struct snd_soc_dai *codec_dai = rtd->codec_dai; 36 struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 37 unsigned int mclk; 38 int ret = 0; 39 40 if (priv->mclk_fs) { 41 mclk = params_rate(params) * priv->mclk_fs; 42 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 43 SND_SOC_CLOCK_IN); 44 } 45 46 return ret; 47 } 48 49 static struct snd_soc_ops asoc_simple_card_ops = { 50 .hw_params = asoc_simple_card_hw_params, 51 }; 52 53 static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, 54 struct asoc_simple_dai *set) 55 { 56 int ret; 57 58 if (set->fmt) { 59 ret = snd_soc_dai_set_fmt(dai, set->fmt); 60 if (ret && ret != -ENOTSUPP) { 61 dev_err(dai->dev, "simple-card: set_fmt error\n"); 62 goto err; 63 } 64 } 65 66 if (set->sysclk) { 67 ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); 68 if (ret && ret != -ENOTSUPP) { 69 dev_err(dai->dev, "simple-card: set_sysclk error\n"); 70 goto err; 71 } 72 } 73 74 if (set->slots) { 75 ret = snd_soc_dai_set_tdm_slot(dai, 0, 0, 76 set->slots, 77 set->slot_width); 78 if (ret && ret != -ENOTSUPP) { 79 dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); 80 goto err; 81 } 82 } 83 84 ret = 0; 85 86 err: 87 return ret; 88 } 89 90 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) 91 { 92 struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); 93 struct snd_soc_dai *codec = rtd->codec_dai; 94 struct snd_soc_dai *cpu = rtd->cpu_dai; 95 struct simple_dai_props *dai_props; 96 int num, ret; 97 98 num = rtd - rtd->card->rtd; 99 dai_props = &priv->dai_props[num]; 100 ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai); 101 if (ret < 0) 102 return ret; 103 104 ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai); 105 if (ret < 0) 106 return ret; 107 108 return 0; 109 } 110 111 static int 112 asoc_simple_card_sub_parse_of(struct device_node *np, 113 struct asoc_simple_dai *dai, 114 const struct device_node **p_node, 115 const char **name) 116 { 117 struct device_node *node; 118 struct clk *clk; 119 int ret; 120 121 /* 122 * get node via "sound-dai = <&phandle port>" 123 * it will be used as xxx_of_node on soc_bind_dai_link() 124 */ 125 node = of_parse_phandle(np, "sound-dai", 0); 126 if (!node) 127 return -ENODEV; 128 *p_node = node; 129 130 /* get dai->name */ 131 ret = snd_soc_of_get_dai_name(np, name); 132 if (ret < 0) 133 return ret; 134 135 /* parse TDM slot */ 136 ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); 137 if (ret) 138 return ret; 139 140 /* 141 * dai->sysclk come from 142 * "clocks = <&xxx>" (if system has common clock) 143 * or "system-clock-frequency = <xxx>" 144 * or device's module clock. 145 */ 146 if (of_property_read_bool(np, "clocks")) { 147 clk = of_clk_get(np, 0); 148 if (IS_ERR(clk)) { 149 ret = PTR_ERR(clk); 150 return ret; 151 } 152 153 dai->sysclk = clk_get_rate(clk); 154 } else if (of_property_read_bool(np, "system-clock-frequency")) { 155 of_property_read_u32(np, 156 "system-clock-frequency", 157 &dai->sysclk); 158 } else { 159 clk = of_clk_get(node, 0); 160 if (!IS_ERR(clk)) 161 dai->sysclk = clk_get_rate(clk); 162 } 163 164 return 0; 165 } 166 167 static int simple_card_dai_link_of(struct device_node *node, 168 struct device *dev, 169 struct snd_soc_dai_link *dai_link, 170 struct simple_dai_props *dai_props, 171 bool is_top_level_node) 172 { 173 struct device_node *np = NULL; 174 struct device_node *bitclkmaster = NULL; 175 struct device_node *framemaster = NULL; 176 unsigned int daifmt; 177 char *name; 178 char prop[128]; 179 char *prefix = ""; 180 int ret; 181 182 if (is_top_level_node) 183 prefix = "simple-audio-card,"; 184 185 daifmt = snd_soc_of_parse_daifmt(node, prefix, 186 &bitclkmaster, &framemaster); 187 daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; 188 189 snprintf(prop, sizeof(prop), "%scpu", prefix); 190 np = of_get_child_by_name(node, prop); 191 if (!np) { 192 ret = -EINVAL; 193 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 194 goto dai_link_of_err; 195 } 196 197 ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai, 198 &dai_link->cpu_of_node, 199 &dai_link->cpu_dai_name); 200 if (ret < 0) 201 goto dai_link_of_err; 202 203 dai_props->cpu_dai.fmt = daifmt; 204 switch (((np == bitclkmaster) << 4) | (np == framemaster)) { 205 case 0x11: 206 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; 207 break; 208 case 0x10: 209 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; 210 break; 211 case 0x01: 212 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; 213 break; 214 default: 215 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; 216 break; 217 } 218 219 of_node_put(np); 220 snprintf(prop, sizeof(prop), "%scodec", prefix); 221 np = of_get_child_by_name(node, prop); 222 if (!np) { 223 ret = -EINVAL; 224 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 225 goto dai_link_of_err; 226 } 227 228 ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai, 229 &dai_link->codec_of_node, 230 &dai_link->codec_dai_name); 231 if (ret < 0) 232 goto dai_link_of_err; 233 234 if (strlen(prefix) && !bitclkmaster && !framemaster) { 235 /* No dai-link level and master setting was not found from 236 sound node level, revert back to legacy DT parsing and 237 take the settings from codec node. */ 238 dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n", 239 __func__); 240 dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt = 241 snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) | 242 (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); 243 } else { 244 dai_props->codec_dai.fmt = daifmt; 245 switch (((np == bitclkmaster) << 4) | (np == framemaster)) { 246 case 0x11: 247 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; 248 break; 249 case 0x10: 250 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; 251 break; 252 case 0x01: 253 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; 254 break; 255 default: 256 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; 257 break; 258 } 259 } 260 261 if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { 262 ret = -EINVAL; 263 goto dai_link_of_err; 264 } 265 266 /* simple-card assumes platform == cpu */ 267 dai_link->platform_of_node = dai_link->cpu_of_node; 268 269 /* Link name is created from CPU/CODEC dai name */ 270 name = devm_kzalloc(dev, 271 strlen(dai_link->cpu_dai_name) + 272 strlen(dai_link->codec_dai_name) + 2, 273 GFP_KERNEL); 274 sprintf(name, "%s-%s", dai_link->cpu_dai_name, 275 dai_link->codec_dai_name); 276 dai_link->name = dai_link->stream_name = name; 277 dai_link->ops = &asoc_simple_card_ops; 278 279 dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); 280 dev_dbg(dev, "\tcpu : %s / %04x / %d\n", 281 dai_link->cpu_dai_name, 282 dai_props->cpu_dai.fmt, 283 dai_props->cpu_dai.sysclk); 284 dev_dbg(dev, "\tcodec : %s / %04x / %d\n", 285 dai_link->codec_dai_name, 286 dai_props->codec_dai.fmt, 287 dai_props->codec_dai.sysclk); 288 289 dai_link_of_err: 290 if (np) 291 of_node_put(np); 292 if (bitclkmaster) 293 of_node_put(bitclkmaster); 294 if (framemaster) 295 of_node_put(framemaster); 296 return ret; 297 } 298 299 static int asoc_simple_card_parse_of(struct device_node *node, 300 struct simple_card_data *priv, 301 struct device *dev, 302 int multi) 303 { 304 struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; 305 struct simple_dai_props *dai_props = priv->dai_props; 306 int ret; 307 308 /* parsing the card name from DT */ 309 snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name"); 310 311 /* off-codec widgets */ 312 if (of_property_read_bool(node, "simple-audio-card,widgets")) { 313 ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, 314 "simple-audio-card,widgets"); 315 if (ret) 316 return ret; 317 } 318 319 /* DAPM routes */ 320 if (of_property_read_bool(node, "simple-audio-card,routing")) { 321 ret = snd_soc_of_parse_audio_routing(&priv->snd_card, 322 "simple-audio-card,routing"); 323 if (ret) 324 return ret; 325 } 326 327 /* Factor to mclk, used in hw_params() */ 328 of_property_read_u32(node, "simple-audio-card,mclk-fs", 329 &priv->mclk_fs); 330 331 dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ? 332 priv->snd_card.name : ""); 333 334 if (multi) { 335 struct device_node *np = NULL; 336 int i; 337 for (i = 0; (np = of_get_next_child(node, np)); i++) { 338 dev_dbg(dev, "\tlink %d:\n", i); 339 ret = simple_card_dai_link_of(np, dev, dai_link + i, 340 dai_props + i, false); 341 if (ret < 0) { 342 of_node_put(np); 343 return ret; 344 } 345 } 346 } else { 347 ret = simple_card_dai_link_of(node, dev, dai_link, dai_props, 348 true); 349 if (ret < 0) 350 return ret; 351 } 352 353 if (!priv->snd_card.name) 354 priv->snd_card.name = priv->snd_card.dai_link->name; 355 356 return 0; 357 } 358 359 /* update the reference count of the devices nodes at end of probe */ 360 static int asoc_simple_card_unref(struct platform_device *pdev) 361 { 362 struct snd_soc_card *card = platform_get_drvdata(pdev); 363 struct snd_soc_dai_link *dai_link; 364 struct device_node *np; 365 int num_links; 366 367 for (num_links = 0, dai_link = card->dai_link; 368 num_links < card->num_links; 369 num_links++, dai_link++) { 370 np = (struct device_node *) dai_link->cpu_of_node; 371 if (np) 372 of_node_put(np); 373 np = (struct device_node *) dai_link->codec_of_node; 374 if (np) 375 of_node_put(np); 376 } 377 return 0; 378 } 379 380 static int asoc_simple_card_probe(struct platform_device *pdev) 381 { 382 struct simple_card_data *priv; 383 struct snd_soc_dai_link *dai_link; 384 struct device_node *np = pdev->dev.of_node; 385 struct device *dev = &pdev->dev; 386 int num_links, multi, ret; 387 388 /* get the number of DAI links */ 389 if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) { 390 num_links = of_get_child_count(np); 391 multi = 1; 392 } else { 393 num_links = 1; 394 multi = 0; 395 } 396 397 /* allocate the private data and the DAI link array */ 398 priv = devm_kzalloc(dev, 399 sizeof(*priv) + sizeof(*dai_link) * num_links, 400 GFP_KERNEL); 401 if (!priv) 402 return -ENOMEM; 403 404 /* 405 * init snd_soc_card 406 */ 407 priv->snd_card.owner = THIS_MODULE; 408 priv->snd_card.dev = dev; 409 dai_link = priv->dai_link; 410 priv->snd_card.dai_link = dai_link; 411 priv->snd_card.num_links = num_links; 412 413 /* get room for the other properties */ 414 priv->dai_props = devm_kzalloc(dev, 415 sizeof(*priv->dai_props) * num_links, 416 GFP_KERNEL); 417 if (!priv->dai_props) 418 return -ENOMEM; 419 420 if (np && of_device_is_available(np)) { 421 422 ret = asoc_simple_card_parse_of(np, priv, dev, multi); 423 if (ret < 0) { 424 if (ret != -EPROBE_DEFER) 425 dev_err(dev, "parse error %d\n", ret); 426 goto err; 427 } 428 429 /* 430 * soc_bind_dai_link() will check cpu name 431 * after of_node matching if dai_link has cpu_dai_name. 432 * but, it will never match if name was created by fmt_single_name() 433 * remove cpu_dai_name to escape name matching. 434 * see 435 * fmt_single_name() 436 * fmt_multiple_name() 437 */ 438 if (num_links == 1) 439 dai_link->cpu_dai_name = NULL; 440 441 } else { 442 struct asoc_simple_card_info *cinfo; 443 444 cinfo = dev->platform_data; 445 if (!cinfo) { 446 dev_err(dev, "no info for asoc-simple-card\n"); 447 return -EINVAL; 448 } 449 450 if (!cinfo->name || 451 !cinfo->codec_dai.name || 452 !cinfo->codec || 453 !cinfo->platform || 454 !cinfo->cpu_dai.name) { 455 dev_err(dev, "insufficient asoc_simple_card_info settings\n"); 456 return -EINVAL; 457 } 458 459 priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; 460 dai_link->name = cinfo->name; 461 dai_link->stream_name = cinfo->name; 462 dai_link->platform_name = cinfo->platform; 463 dai_link->codec_name = cinfo->codec; 464 dai_link->cpu_dai_name = cinfo->cpu_dai.name; 465 dai_link->codec_dai_name = cinfo->codec_dai.name; 466 memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, 467 sizeof(priv->dai_props->cpu_dai)); 468 memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, 469 sizeof(priv->dai_props->codec_dai)); 470 471 priv->dai_props->cpu_dai.fmt |= cinfo->daifmt; 472 priv->dai_props->codec_dai.fmt |= cinfo->daifmt; 473 } 474 475 /* 476 * init snd_soc_dai_link 477 */ 478 dai_link->init = asoc_simple_card_dai_init; 479 480 snd_soc_card_set_drvdata(&priv->snd_card, priv); 481 482 ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); 483 484 err: 485 asoc_simple_card_unref(pdev); 486 return ret; 487 } 488 489 static const struct of_device_id asoc_simple_of_match[] = { 490 { .compatible = "simple-audio-card", }, 491 {}, 492 }; 493 MODULE_DEVICE_TABLE(of, asoc_simple_of_match); 494 495 static struct platform_driver asoc_simple_card = { 496 .driver = { 497 .name = "asoc-simple-card", 498 .owner = THIS_MODULE, 499 .of_match_table = asoc_simple_of_match, 500 }, 501 .probe = asoc_simple_card_probe, 502 }; 503 504 module_platform_driver(asoc_simple_card); 505 506 MODULE_ALIAS("platform:asoc-simple-card"); 507 MODULE_LICENSE("GPL"); 508 MODULE_DESCRIPTION("ASoC Simple Sound Card"); 509 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 510