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 u32 val; 120 int ret; 121 122 /* 123 * get node via "sound-dai = <&phandle port>" 124 * it will be used as xxx_of_node on soc_bind_dai_link() 125 */ 126 node = of_parse_phandle(np, "sound-dai", 0); 127 if (!node) 128 return -ENODEV; 129 *p_node = node; 130 131 /* get dai->name */ 132 ret = snd_soc_of_get_dai_name(np, name); 133 if (ret < 0) 134 return ret; 135 136 /* parse TDM slot */ 137 ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); 138 if (ret) 139 return ret; 140 141 /* 142 * dai->sysclk come from 143 * "clocks = <&xxx>" (if system has common clock) 144 * or "system-clock-frequency = <xxx>" 145 * or device's module clock. 146 */ 147 if (of_property_read_bool(np, "clocks")) { 148 clk = of_clk_get(np, 0); 149 if (IS_ERR(clk)) { 150 ret = PTR_ERR(clk); 151 return ret; 152 } 153 154 dai->sysclk = clk_get_rate(clk); 155 } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { 156 dai->sysclk = val; 157 } else { 158 clk = of_clk_get(node, 0); 159 if (!IS_ERR(clk)) 160 dai->sysclk = clk_get_rate(clk); 161 } 162 163 return 0; 164 } 165 166 static int simple_card_dai_link_of(struct device_node *node, 167 struct device *dev, 168 struct snd_soc_dai_link *dai_link, 169 struct simple_dai_props *dai_props, 170 bool is_top_level_node) 171 { 172 struct device_node *np = NULL; 173 struct device_node *bitclkmaster = NULL; 174 struct device_node *framemaster = NULL; 175 unsigned int daifmt; 176 char *name; 177 char prop[128]; 178 char *prefix = ""; 179 int ret; 180 181 if (is_top_level_node) 182 prefix = "simple-audio-card,"; 183 184 daifmt = snd_soc_of_parse_daifmt(node, prefix, 185 &bitclkmaster, &framemaster); 186 daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; 187 188 snprintf(prop, sizeof(prop), "%scpu", prefix); 189 np = of_get_child_by_name(node, prop); 190 if (!np) { 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 ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai, 197 &dai_link->cpu_of_node, 198 &dai_link->cpu_dai_name); 199 if (ret < 0) 200 goto dai_link_of_err; 201 202 dai_props->cpu_dai.fmt = daifmt; 203 switch (((np == bitclkmaster) << 4) | (np == framemaster)) { 204 case 0x11: 205 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; 206 break; 207 case 0x10: 208 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; 209 break; 210 case 0x01: 211 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; 212 break; 213 default: 214 dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; 215 break; 216 } 217 218 of_node_put(np); 219 snprintf(prop, sizeof(prop), "%scodec", prefix); 220 np = of_get_child_by_name(node, prop); 221 if (!np) { 222 ret = -EINVAL; 223 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); 224 goto dai_link_of_err; 225 } 226 227 ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai, 228 &dai_link->codec_of_node, 229 &dai_link->codec_dai_name); 230 if (ret < 0) 231 goto dai_link_of_err; 232 233 if (strlen(prefix) && !bitclkmaster && !framemaster) { 234 /* No dai-link level and master setting was not found from 235 sound node level, revert back to legacy DT parsing and 236 take the settings from codec node. */ 237 dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n", 238 __func__); 239 dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt = 240 snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) | 241 (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); 242 } else { 243 dai_props->codec_dai.fmt = daifmt; 244 switch (((np == bitclkmaster) << 4) | (np == framemaster)) { 245 case 0x11: 246 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; 247 break; 248 case 0x10: 249 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; 250 break; 251 case 0x01: 252 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; 253 break; 254 default: 255 dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; 256 break; 257 } 258 } 259 260 if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { 261 ret = -EINVAL; 262 goto dai_link_of_err; 263 } 264 265 /* simple-card assumes platform == cpu */ 266 dai_link->platform_of_node = dai_link->cpu_of_node; 267 268 /* Link name is created from CPU/CODEC dai name */ 269 name = devm_kzalloc(dev, 270 strlen(dai_link->cpu_dai_name) + 271 strlen(dai_link->codec_dai_name) + 2, 272 GFP_KERNEL); 273 sprintf(name, "%s-%s", dai_link->cpu_dai_name, 274 dai_link->codec_dai_name); 275 dai_link->name = dai_link->stream_name = name; 276 dai_link->ops = &asoc_simple_card_ops; 277 278 dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); 279 dev_dbg(dev, "\tcpu : %s / %04x / %d\n", 280 dai_link->cpu_dai_name, 281 dai_props->cpu_dai.fmt, 282 dai_props->cpu_dai.sysclk); 283 dev_dbg(dev, "\tcodec : %s / %04x / %d\n", 284 dai_link->codec_dai_name, 285 dai_props->codec_dai.fmt, 286 dai_props->codec_dai.sysclk); 287 288 dai_link_of_err: 289 if (np) 290 of_node_put(np); 291 if (bitclkmaster) 292 of_node_put(bitclkmaster); 293 if (framemaster) 294 of_node_put(framemaster); 295 return ret; 296 } 297 298 static int asoc_simple_card_parse_of(struct device_node *node, 299 struct simple_card_data *priv, 300 struct device *dev, 301 int multi) 302 { 303 struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; 304 struct simple_dai_props *dai_props = priv->dai_props; 305 u32 val; 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 ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val); 329 if (ret == 0) 330 priv->mclk_fs = val; 331 332 dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ? 333 priv->snd_card.name : ""); 334 335 if (multi) { 336 struct device_node *np = NULL; 337 int i; 338 for (i = 0; (np = of_get_next_child(node, np)); i++) { 339 dev_dbg(dev, "\tlink %d:\n", i); 340 ret = simple_card_dai_link_of(np, dev, dai_link + i, 341 dai_props + i, false); 342 if (ret < 0) { 343 of_node_put(np); 344 return ret; 345 } 346 } 347 } else { 348 ret = simple_card_dai_link_of(node, dev, dai_link, dai_props, 349 true); 350 if (ret < 0) 351 return ret; 352 } 353 354 if (!priv->snd_card.name) 355 priv->snd_card.name = priv->snd_card.dai_link->name; 356 357 return 0; 358 } 359 360 /* update the reference count of the devices nodes at end of probe */ 361 static int asoc_simple_card_unref(struct platform_device *pdev) 362 { 363 struct snd_soc_card *card = platform_get_drvdata(pdev); 364 struct snd_soc_dai_link *dai_link; 365 struct device_node *np; 366 int num_links; 367 368 for (num_links = 0, dai_link = card->dai_link; 369 num_links < card->num_links; 370 num_links++, dai_link++) { 371 np = (struct device_node *) dai_link->cpu_of_node; 372 if (np) 373 of_node_put(np); 374 np = (struct device_node *) dai_link->codec_of_node; 375 if (np) 376 of_node_put(np); 377 } 378 return 0; 379 } 380 381 static int asoc_simple_card_probe(struct platform_device *pdev) 382 { 383 struct simple_card_data *priv; 384 struct snd_soc_dai_link *dai_link; 385 struct device_node *np = pdev->dev.of_node; 386 struct device *dev = &pdev->dev; 387 int num_links, multi, ret; 388 389 /* get the number of DAI links */ 390 if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) { 391 num_links = of_get_child_count(np); 392 multi = 1; 393 } else { 394 num_links = 1; 395 multi = 0; 396 } 397 398 /* allocate the private data and the DAI link array */ 399 priv = devm_kzalloc(dev, 400 sizeof(*priv) + sizeof(*dai_link) * num_links, 401 GFP_KERNEL); 402 if (!priv) 403 return -ENOMEM; 404 405 /* 406 * init snd_soc_card 407 */ 408 priv->snd_card.owner = THIS_MODULE; 409 priv->snd_card.dev = dev; 410 dai_link = priv->dai_link; 411 priv->snd_card.dai_link = dai_link; 412 priv->snd_card.num_links = num_links; 413 414 /* get room for the other properties */ 415 priv->dai_props = devm_kzalloc(dev, 416 sizeof(*priv->dai_props) * num_links, 417 GFP_KERNEL); 418 if (!priv->dai_props) 419 return -ENOMEM; 420 421 if (np && of_device_is_available(np)) { 422 423 ret = asoc_simple_card_parse_of(np, priv, dev, multi); 424 if (ret < 0) { 425 if (ret != -EPROBE_DEFER) 426 dev_err(dev, "parse error %d\n", ret); 427 goto err; 428 } 429 430 /* 431 * soc_bind_dai_link() will check cpu name 432 * after of_node matching if dai_link has cpu_dai_name. 433 * but, it will never match if name was created by fmt_single_name() 434 * remove cpu_dai_name to escape name matching. 435 * see 436 * fmt_single_name() 437 * fmt_multiple_name() 438 */ 439 if (num_links == 1) 440 dai_link->cpu_dai_name = NULL; 441 442 } else { 443 struct asoc_simple_card_info *cinfo; 444 445 cinfo = dev->platform_data; 446 if (!cinfo) { 447 dev_err(dev, "no info for asoc-simple-card\n"); 448 return -EINVAL; 449 } 450 451 if (!cinfo->name || 452 !cinfo->codec_dai.name || 453 !cinfo->codec || 454 !cinfo->platform || 455 !cinfo->cpu_dai.name) { 456 dev_err(dev, "insufficient asoc_simple_card_info settings\n"); 457 return -EINVAL; 458 } 459 460 priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; 461 dai_link->name = cinfo->name; 462 dai_link->stream_name = cinfo->name; 463 dai_link->platform_name = cinfo->platform; 464 dai_link->codec_name = cinfo->codec; 465 dai_link->cpu_dai_name = cinfo->cpu_dai.name; 466 dai_link->codec_dai_name = cinfo->codec_dai.name; 467 memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, 468 sizeof(priv->dai_props->cpu_dai)); 469 memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, 470 sizeof(priv->dai_props->codec_dai)); 471 472 priv->dai_props->cpu_dai.fmt |= cinfo->daifmt; 473 priv->dai_props->codec_dai.fmt |= cinfo->daifmt; 474 } 475 476 /* 477 * init snd_soc_dai_link 478 */ 479 dai_link->init = asoc_simple_card_dai_init; 480 481 snd_soc_card_set_drvdata(&priv->snd_card, priv); 482 483 ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); 484 485 err: 486 asoc_simple_card_unref(pdev); 487 return ret; 488 } 489 490 static const struct of_device_id asoc_simple_of_match[] = { 491 { .compatible = "simple-audio-card", }, 492 {}, 493 }; 494 MODULE_DEVICE_TABLE(of, asoc_simple_of_match); 495 496 static struct platform_driver asoc_simple_card = { 497 .driver = { 498 .name = "asoc-simple-card", 499 .owner = THIS_MODULE, 500 .of_match_table = asoc_simple_of_match, 501 }, 502 .probe = asoc_simple_card_probe, 503 }; 504 505 module_platform_driver(asoc_simple_card); 506 507 MODULE_ALIAS("platform:asoc-simple-card"); 508 MODULE_LICENSE("GPL"); 509 MODULE_DESCRIPTION("ASoC Simple Sound Card"); 510 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 511