simple-scu-card.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /*
  2. * ASoC simple SCU sound card support
  3. *
  4. * Copyright (C) 2015 Renesas Solutions Corp.
  5. * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  6. *
  7. * based on ${LINUX}/sound/soc/generic/simple-card.c
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2 as
  11. * published by the Free Software Foundation.
  12. */
  13. #include <linux/clk.h>
  14. #include <linux/device.h>
  15. #include <linux/module.h>
  16. #include <linux/of.h>
  17. #include <linux/of_device.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/string.h>
  20. #include <sound/jack.h>
  21. #include <sound/soc.h>
  22. #include <sound/soc-dai.h>
  23. #include <sound/simple_card_utils.h>
  24. struct asoc_simple_card_priv {
  25. struct snd_soc_card snd_card;
  26. struct snd_soc_codec_conf codec_conf;
  27. struct asoc_simple_dai *dai_props;
  28. struct snd_soc_dai_link *dai_link;
  29. u32 convert_rate;
  30. u32 convert_channels;
  31. };
  32. #define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
  33. #define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
  34. #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
  35. #define DAI "sound-dai"
  36. #define CELL "#sound-dai-cells"
  37. #define PREFIX "simple-audio-card,"
  38. static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
  39. {
  40. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  41. struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
  42. struct asoc_simple_dai *dai_props =
  43. simple_priv_to_props(priv, rtd->num);
  44. return clk_prepare_enable(dai_props->clk);
  45. }
  46. static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
  47. {
  48. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  49. struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
  50. struct asoc_simple_dai *dai_props =
  51. simple_priv_to_props(priv, rtd->num);
  52. clk_disable_unprepare(dai_props->clk);
  53. }
  54. static struct snd_soc_ops asoc_simple_card_ops = {
  55. .startup = asoc_simple_card_startup,
  56. .shutdown = asoc_simple_card_shutdown,
  57. };
  58. static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
  59. {
  60. struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
  61. struct snd_soc_dai *dai;
  62. struct snd_soc_dai_link *dai_link;
  63. struct asoc_simple_dai *dai_props;
  64. int num = rtd->num;
  65. dai_link = simple_priv_to_link(priv, num);
  66. dai_props = simple_priv_to_props(priv, num);
  67. dai = dai_link->dynamic ?
  68. rtd->cpu_dai :
  69. rtd->codec_dai;
  70. return asoc_simple_card_init_dai(dai, dai_props);
  71. }
  72. static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
  73. struct snd_pcm_hw_params *params)
  74. {
  75. struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
  76. struct snd_interval *rate = hw_param_interval(params,
  77. SNDRV_PCM_HW_PARAM_RATE);
  78. struct snd_interval *channels = hw_param_interval(params,
  79. SNDRV_PCM_HW_PARAM_CHANNELS);
  80. if (priv->convert_rate)
  81. rate->min =
  82. rate->max = priv->convert_rate;
  83. if (priv->convert_channels)
  84. channels->min =
  85. channels->max = priv->convert_channels;
  86. return 0;
  87. }
  88. static int asoc_simple_card_parse_links(struct device_node *np,
  89. struct asoc_simple_card_priv *priv,
  90. unsigned int daifmt,
  91. int idx, bool is_fe)
  92. {
  93. struct device *dev = simple_priv_to_dev(priv);
  94. struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
  95. struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx);
  96. int ret;
  97. if (is_fe) {
  98. int is_single_links = 0;
  99. /* BE is dummy */
  100. dai_link->codec_of_node = NULL;
  101. dai_link->codec_dai_name = "snd-soc-dummy-dai";
  102. dai_link->codec_name = "snd-soc-dummy";
  103. /* FE settings */
  104. dai_link->dynamic = 1;
  105. dai_link->dpcm_merged_format = 1;
  106. ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
  107. &is_single_links);
  108. if (ret)
  109. return ret;
  110. ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props);
  111. if (ret < 0)
  112. return ret;
  113. ret = asoc_simple_card_set_dailink_name(dev, dai_link,
  114. "fe.%s",
  115. dai_link->cpu_dai_name);
  116. if (ret < 0)
  117. return ret;
  118. asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
  119. } else {
  120. /* FE is dummy */
  121. dai_link->cpu_of_node = NULL;
  122. dai_link->cpu_dai_name = "snd-soc-dummy-dai";
  123. dai_link->cpu_name = "snd-soc-dummy";
  124. /* BE settings */
  125. dai_link->no_pcm = 1;
  126. dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup;
  127. ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
  128. if (ret < 0)
  129. return ret;
  130. ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props);
  131. if (ret < 0)
  132. return ret;
  133. ret = asoc_simple_card_set_dailink_name(dev, dai_link,
  134. "be.%s",
  135. dai_link->codec_dai_name);
  136. if (ret < 0)
  137. return ret;
  138. snd_soc_of_parse_audio_prefix(&priv->snd_card,
  139. &priv->codec_conf,
  140. dai_link->codec_of_node,
  141. PREFIX "prefix");
  142. }
  143. ret = snd_soc_of_parse_tdm_slot(np,
  144. &dai_props->tx_slot_mask,
  145. &dai_props->rx_slot_mask,
  146. &dai_props->slots,
  147. &dai_props->slot_width);
  148. if (ret)
  149. return ret;
  150. ret = asoc_simple_card_canonicalize_dailink(dai_link);
  151. if (ret < 0)
  152. return ret;
  153. dai_link->dai_fmt = daifmt;
  154. dai_link->dpcm_playback = 1;
  155. dai_link->dpcm_capture = 1;
  156. dai_link->ops = &asoc_simple_card_ops;
  157. dai_link->init = asoc_simple_card_dai_init;
  158. dev_dbg(dev, "\t%s / %04x / %d\n",
  159. dai_link->name,
  160. dai_link->dai_fmt,
  161. dai_props->sysclk);
  162. return 0;
  163. }
  164. static int asoc_simple_card_dai_link_of(struct device_node *node,
  165. struct asoc_simple_card_priv *priv)
  166. {
  167. struct device *dev = simple_priv_to_dev(priv);
  168. struct device_node *np;
  169. unsigned int daifmt = 0;
  170. int ret, i;
  171. bool is_fe;
  172. /* find 1st codec */
  173. np = of_get_child_by_name(node, PREFIX "codec");
  174. if (!np)
  175. return -ENODEV;
  176. ret = asoc_simple_card_parse_daifmt(dev, node, np,
  177. PREFIX, &daifmt);
  178. if (ret < 0)
  179. return ret;
  180. i = 0;
  181. for_each_child_of_node(node, np) {
  182. is_fe = false;
  183. if (strcmp(np->name, PREFIX "cpu") == 0)
  184. is_fe = true;
  185. ret = asoc_simple_card_parse_links(np, priv, daifmt, i, is_fe);
  186. if (ret < 0)
  187. return ret;
  188. i++;
  189. }
  190. return 0;
  191. }
  192. static int asoc_simple_card_parse_of(struct device_node *node,
  193. struct asoc_simple_card_priv *priv,
  194. struct device *dev)
  195. {
  196. struct asoc_simple_dai *props;
  197. struct snd_soc_dai_link *links;
  198. int ret;
  199. int num;
  200. if (!node)
  201. return -EINVAL;
  202. num = of_get_child_count(node);
  203. props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL);
  204. links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL);
  205. if (!props || !links)
  206. return -ENOMEM;
  207. priv->dai_props = props;
  208. priv->dai_link = links;
  209. /* Init snd_soc_card */
  210. priv->snd_card.owner = THIS_MODULE;
  211. priv->snd_card.dev = dev;
  212. priv->snd_card.dai_link = priv->dai_link;
  213. priv->snd_card.num_links = num;
  214. priv->snd_card.codec_conf = &priv->codec_conf;
  215. priv->snd_card.num_configs = 1;
  216. ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing");
  217. if (ret < 0)
  218. return ret;
  219. /* sampling rate convert */
  220. of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate);
  221. /* channels transfer */
  222. of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels);
  223. ret = asoc_simple_card_dai_link_of(node, priv);
  224. if (ret < 0)
  225. return ret;
  226. ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
  227. if (ret < 0)
  228. return ret;
  229. dev_dbg(dev, "New card: %s\n",
  230. priv->snd_card.name ? priv->snd_card.name : "");
  231. dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
  232. dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
  233. return 0;
  234. }
  235. static int asoc_simple_card_probe(struct platform_device *pdev)
  236. {
  237. struct asoc_simple_card_priv *priv;
  238. struct device_node *np = pdev->dev.of_node;
  239. struct device *dev = &pdev->dev;
  240. int ret;
  241. /* Allocate the private data */
  242. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  243. if (!priv)
  244. return -ENOMEM;
  245. ret = asoc_simple_card_parse_of(np, priv, dev);
  246. if (ret < 0) {
  247. if (ret != -EPROBE_DEFER)
  248. dev_err(dev, "parse error %d\n", ret);
  249. goto err;
  250. }
  251. snd_soc_card_set_drvdata(&priv->snd_card, priv);
  252. ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
  253. if (ret >= 0)
  254. return ret;
  255. err:
  256. asoc_simple_card_clean_reference(&priv->snd_card);
  257. return ret;
  258. }
  259. static int asoc_simple_card_remove(struct platform_device *pdev)
  260. {
  261. struct snd_soc_card *card = platform_get_drvdata(pdev);
  262. return asoc_simple_card_clean_reference(card);
  263. }
  264. static const struct of_device_id asoc_simple_of_match[] = {
  265. { .compatible = "renesas,rsrc-card", },
  266. { .compatible = "simple-scu-audio-card", },
  267. {},
  268. };
  269. MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
  270. static struct platform_driver asoc_simple_card = {
  271. .driver = {
  272. .name = "simple-scu-audio-card",
  273. .of_match_table = asoc_simple_of_match,
  274. },
  275. .probe = asoc_simple_card_probe,
  276. .remove = asoc_simple_card_remove,
  277. };
  278. module_platform_driver(asoc_simple_card);
  279. MODULE_ALIAS("platform:asoc-simple-scu-card");
  280. MODULE_LICENSE("GPL v2");
  281. MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
  282. MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");