fsl_rpmsg.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // SPDX-License-Identifier: GPL-2.0+
  2. // Copyright 2018-2021 NXP
  3. #include <linux/clk.h>
  4. #include <linux/clk-provider.h>
  5. #include <linux/delay.h>
  6. #include <linux/dmaengine.h>
  7. #include <linux/module.h>
  8. #include <linux/of.h>
  9. #include <linux/pm_runtime.h>
  10. #include <linux/rpmsg.h>
  11. #include <linux/slab.h>
  12. #include <sound/core.h>
  13. #include <sound/dmaengine_pcm.h>
  14. #include <sound/pcm_params.h>
  15. #include "fsl_rpmsg.h"
  16. #include "imx-pcm.h"
  17. #define FSL_RPMSG_RATES (SNDRV_PCM_RATE_8000 | \
  18. SNDRV_PCM_RATE_16000 | \
  19. SNDRV_PCM_RATE_48000)
  20. #define FSL_RPMSG_FORMATS SNDRV_PCM_FMTBIT_S16_LE
  21. /* 192kHz/32bit/2ch/60s size is 0x574e00 */
  22. #define LPA_LARGE_BUFFER_SIZE (0x6000000)
  23. /* 16kHz/32bit/8ch/1s size is 0x7D000 */
  24. #define LPA_CAPTURE_BUFFER_SIZE (0x100000)
  25. static const unsigned int fsl_rpmsg_rates[] = {
  26. 8000, 11025, 16000, 22050, 44100,
  27. 32000, 48000, 96000, 88200, 176400, 192000,
  28. 352800, 384000, 705600, 768000, 1411200, 2822400,
  29. };
  30. static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints = {
  31. .count = ARRAY_SIZE(fsl_rpmsg_rates),
  32. .list = fsl_rpmsg_rates,
  33. };
  34. static int fsl_rpmsg_hw_params(struct snd_pcm_substream *substream,
  35. struct snd_pcm_hw_params *params,
  36. struct snd_soc_dai *dai)
  37. {
  38. struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
  39. struct clk *p = rpmsg->mclk, *pll = NULL, *npll = NULL;
  40. u64 rate = params_rate(params);
  41. int ret = 0;
  42. /* Get current pll parent */
  43. while (p && rpmsg->pll8k && rpmsg->pll11k) {
  44. struct clk *pp = clk_get_parent(p);
  45. if (clk_is_match(pp, rpmsg->pll8k) ||
  46. clk_is_match(pp, rpmsg->pll11k)) {
  47. pll = pp;
  48. break;
  49. }
  50. p = pp;
  51. }
  52. /* Switch to another pll parent if needed. */
  53. if (pll) {
  54. npll = (do_div(rate, 8000) ? rpmsg->pll11k : rpmsg->pll8k);
  55. if (!clk_is_match(pll, npll)) {
  56. ret = clk_set_parent(p, npll);
  57. if (ret < 0)
  58. dev_warn(dai->dev, "failed to set parent %s: %d\n",
  59. __clk_get_name(npll), ret);
  60. }
  61. }
  62. if (!(rpmsg->mclk_streams & BIT(substream->stream))) {
  63. ret = clk_prepare_enable(rpmsg->mclk);
  64. if (ret) {
  65. dev_err(dai->dev, "failed to enable mclk: %d\n", ret);
  66. return ret;
  67. }
  68. rpmsg->mclk_streams |= BIT(substream->stream);
  69. }
  70. return ret;
  71. }
  72. static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream,
  73. struct snd_soc_dai *dai)
  74. {
  75. struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
  76. if (rpmsg->mclk_streams & BIT(substream->stream)) {
  77. clk_disable_unprepare(rpmsg->mclk);
  78. rpmsg->mclk_streams &= ~BIT(substream->stream);
  79. }
  80. return 0;
  81. }
  82. static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
  83. struct snd_soc_dai *cpu_dai)
  84. {
  85. return snd_pcm_hw_constraint_list(substream->runtime, 0,
  86. SNDRV_PCM_HW_PARAM_RATE,
  87. &fsl_rpmsg_rate_constraints);
  88. }
  89. static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
  90. .startup = fsl_rpmsg_startup,
  91. .hw_params = fsl_rpmsg_hw_params,
  92. .hw_free = fsl_rpmsg_hw_free,
  93. };
  94. static struct snd_soc_dai_driver fsl_rpmsg_dai = {
  95. .playback = {
  96. .stream_name = "CPU-Playback",
  97. .channels_min = 2,
  98. .channels_max = 32,
  99. .rates = SNDRV_PCM_RATE_KNOT,
  100. .formats = FSL_RPMSG_FORMATS,
  101. },
  102. .capture = {
  103. .stream_name = "CPU-Capture",
  104. .channels_min = 2,
  105. .channels_max = 32,
  106. .rates = SNDRV_PCM_RATE_KNOT,
  107. .formats = FSL_RPMSG_FORMATS,
  108. },
  109. .symmetric_rate = 1,
  110. .symmetric_channels = 1,
  111. .symmetric_sample_bits = 1,
  112. .ops = &fsl_rpmsg_dai_ops,
  113. };
  114. static const struct snd_soc_component_driver fsl_component = {
  115. .name = "fsl-rpmsg",
  116. };
  117. static const struct fsl_rpmsg_soc_data imx7ulp_data = {
  118. .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
  119. SNDRV_PCM_RATE_48000,
  120. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  121. };
  122. static const struct fsl_rpmsg_soc_data imx8mm_data = {
  123. .rates = SNDRV_PCM_RATE_KNOT,
  124. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
  125. SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U8 |
  126. SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE,
  127. };
  128. static const struct fsl_rpmsg_soc_data imx8mn_data = {
  129. .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
  130. SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
  131. SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
  132. SNDRV_PCM_RATE_192000,
  133. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
  134. SNDRV_PCM_FMTBIT_S32_LE,
  135. };
  136. static const struct fsl_rpmsg_soc_data imx8mp_data = {
  137. .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
  138. SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
  139. SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
  140. SNDRV_PCM_RATE_192000,
  141. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
  142. SNDRV_PCM_FMTBIT_S32_LE,
  143. };
  144. static const struct fsl_rpmsg_soc_data imx93_data = {
  145. .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |
  146. SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
  147. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
  148. SNDRV_PCM_FMTBIT_S32_LE,
  149. };
  150. static const struct fsl_rpmsg_soc_data imx95_data = {
  151. .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |
  152. SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
  153. SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
  154. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
  155. SNDRV_PCM_FMTBIT_S32_LE,
  156. };
  157. static const struct of_device_id fsl_rpmsg_ids[] = {
  158. { .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data},
  159. { .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data},
  160. { .compatible = "fsl,imx8mn-rpmsg-audio", .data = &imx8mn_data},
  161. { .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data},
  162. { .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data},
  163. { .compatible = "fsl,imx93-rpmsg-audio", .data = &imx93_data},
  164. { .compatible = "fsl,imx95-rpmsg-audio", .data = &imx95_data},
  165. { /* sentinel */ }
  166. };
  167. MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
  168. static int fsl_rpmsg_probe(struct platform_device *pdev)
  169. {
  170. struct device_node *np = pdev->dev.of_node;
  171. struct snd_soc_dai_driver *dai_drv;
  172. const char *dai_name;
  173. struct fsl_rpmsg *rpmsg;
  174. int ret;
  175. dai_drv = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
  176. if (!dai_drv)
  177. return -ENOMEM;
  178. memcpy(dai_drv, &fsl_rpmsg_dai, sizeof(fsl_rpmsg_dai));
  179. rpmsg = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg), GFP_KERNEL);
  180. if (!rpmsg)
  181. return -ENOMEM;
  182. rpmsg->soc_data = of_device_get_match_data(&pdev->dev);
  183. if (rpmsg->soc_data) {
  184. dai_drv->playback.rates = rpmsg->soc_data->rates;
  185. dai_drv->capture.rates = rpmsg->soc_data->rates;
  186. dai_drv->playback.formats = rpmsg->soc_data->formats;
  187. dai_drv->capture.formats = rpmsg->soc_data->formats;
  188. }
  189. /* Use rpmsg channel name as cpu dai name */
  190. ret = of_property_read_string(np, "fsl,rpmsg-channel-name", &dai_name);
  191. if (ret) {
  192. if (ret == -EINVAL) {
  193. dai_name = "rpmsg-audio-channel";
  194. } else {
  195. dev_err(&pdev->dev, "Failed to get rpmsg channel name: %d!\n", ret);
  196. return ret;
  197. }
  198. }
  199. dai_drv->name = dai_name;
  200. /* Setup cpu dai for sound card that sits on rpmsg-micfil-channel */
  201. if (!strcmp(dai_name, "rpmsg-micfil-channel")) {
  202. dai_drv->capture.channels_min = 1;
  203. dai_drv->capture.channels_max = 8;
  204. dai_drv->capture.rates = SNDRV_PCM_RATE_8000_48000;
  205. dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S32_LE;
  206. if (of_device_is_compatible(np, "fsl,imx8mm-rpmsg-audio"))
  207. dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
  208. }
  209. if (of_property_read_bool(np, "fsl,enable-lpa")) {
  210. rpmsg->enable_lpa = 1;
  211. rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = LPA_LARGE_BUFFER_SIZE;
  212. rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = LPA_CAPTURE_BUFFER_SIZE;
  213. } else {
  214. rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = IMX_DEFAULT_DMABUF_SIZE;
  215. rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = IMX_DEFAULT_DMABUF_SIZE;
  216. }
  217. /* Get the optional clocks */
  218. rpmsg->ipg = devm_clk_get_optional(&pdev->dev, "ipg");
  219. if (IS_ERR(rpmsg->ipg))
  220. return PTR_ERR(rpmsg->ipg);
  221. rpmsg->mclk = devm_clk_get_optional(&pdev->dev, "mclk");
  222. if (IS_ERR(rpmsg->mclk))
  223. return PTR_ERR(rpmsg->mclk);
  224. rpmsg->dma = devm_clk_get_optional(&pdev->dev, "dma");
  225. if (IS_ERR(rpmsg->dma))
  226. return PTR_ERR(rpmsg->dma);
  227. rpmsg->pll8k = devm_clk_get_optional(&pdev->dev, "pll8k");
  228. if (IS_ERR(rpmsg->pll8k))
  229. return PTR_ERR(rpmsg->pll8k);
  230. rpmsg->pll11k = devm_clk_get_optional(&pdev->dev, "pll11k");
  231. if (IS_ERR(rpmsg->pll11k))
  232. return PTR_ERR(rpmsg->pll11k);
  233. platform_set_drvdata(pdev, rpmsg);
  234. pm_runtime_enable(&pdev->dev);
  235. ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
  236. dai_drv, 1);
  237. if (ret)
  238. goto err_pm_disable;
  239. return 0;
  240. err_pm_disable:
  241. pm_runtime_disable(&pdev->dev);
  242. return ret;
  243. }
  244. static void fsl_rpmsg_remove(struct platform_device *pdev)
  245. {
  246. struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev);
  247. pm_runtime_disable(&pdev->dev);
  248. if (rpmsg->card_pdev)
  249. platform_device_unregister(rpmsg->card_pdev);
  250. }
  251. static int fsl_rpmsg_runtime_resume(struct device *dev)
  252. {
  253. struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
  254. int ret;
  255. ret = clk_prepare_enable(rpmsg->ipg);
  256. if (ret) {
  257. dev_err(dev, "failed to enable ipg clock: %d\n", ret);
  258. goto ipg_err;
  259. }
  260. ret = clk_prepare_enable(rpmsg->dma);
  261. if (ret) {
  262. dev_err(dev, "Failed to enable dma clock %d\n", ret);
  263. goto dma_err;
  264. }
  265. return 0;
  266. dma_err:
  267. clk_disable_unprepare(rpmsg->ipg);
  268. ipg_err:
  269. return ret;
  270. }
  271. static int fsl_rpmsg_runtime_suspend(struct device *dev)
  272. {
  273. struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
  274. clk_disable_unprepare(rpmsg->dma);
  275. clk_disable_unprepare(rpmsg->ipg);
  276. return 0;
  277. }
  278. static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
  279. RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend, fsl_rpmsg_runtime_resume,
  280. NULL)
  281. };
  282. static struct platform_driver fsl_rpmsg_driver = {
  283. .probe = fsl_rpmsg_probe,
  284. .remove = fsl_rpmsg_remove,
  285. .driver = {
  286. .name = "fsl_rpmsg",
  287. .pm = pm_ptr(&fsl_rpmsg_pm_ops),
  288. .of_match_table = fsl_rpmsg_ids,
  289. },
  290. };
  291. module_platform_driver(fsl_rpmsg_driver);
  292. MODULE_DESCRIPTION("Freescale SoC Audio PRMSG CPU Interface");
  293. MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
  294. MODULE_ALIAS("platform:fsl_rpmsg");
  295. MODULE_LICENSE("GPL");