smdk_spdif.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // SPDX-License-Identifier: GPL-2.0+
  2. //
  3. // smdk_spdif.c - S/PDIF audio for SMDK
  4. //
  5. // Copyright (C) 2010 Samsung Electronics Co., Ltd.
  6. #include <linux/clk.h>
  7. #include <linux/module.h>
  8. #include <sound/soc.h>
  9. #include "spdif.h"
  10. /* Audio clock settings are belonged to board specific part. Every
  11. * board can set audio source clock setting which is matched with H/W
  12. * like this function-'set_audio_clock_heirachy'.
  13. */
  14. static int set_audio_clock_heirachy(struct platform_device *pdev)
  15. {
  16. struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif;
  17. int ret = 0;
  18. fout_epll = clk_get(NULL, "fout_epll");
  19. if (IS_ERR(fout_epll)) {
  20. printk(KERN_WARNING "%s: Cannot find fout_epll.\n",
  21. __func__);
  22. return -EINVAL;
  23. }
  24. mout_epll = clk_get(NULL, "mout_epll");
  25. if (IS_ERR(mout_epll)) {
  26. printk(KERN_WARNING "%s: Cannot find mout_epll.\n",
  27. __func__);
  28. ret = -EINVAL;
  29. goto out1;
  30. }
  31. sclk_audio0 = clk_get(&pdev->dev, "sclk_audio");
  32. if (IS_ERR(sclk_audio0)) {
  33. printk(KERN_WARNING "%s: Cannot find sclk_audio.\n",
  34. __func__);
  35. ret = -EINVAL;
  36. goto out2;
  37. }
  38. sclk_spdif = clk_get(NULL, "sclk_spdif");
  39. if (IS_ERR(sclk_spdif)) {
  40. printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n",
  41. __func__);
  42. ret = -EINVAL;
  43. goto out3;
  44. }
  45. /* Set audio clock hierarchy for S/PDIF */
  46. clk_set_parent(mout_epll, fout_epll);
  47. clk_set_parent(sclk_audio0, mout_epll);
  48. clk_set_parent(sclk_spdif, sclk_audio0);
  49. clk_put(sclk_spdif);
  50. out3:
  51. clk_put(sclk_audio0);
  52. out2:
  53. clk_put(mout_epll);
  54. out1:
  55. clk_put(fout_epll);
  56. return ret;
  57. }
  58. /* We should haved to set clock directly on this part because of clock
  59. * scheme of Samsudng SoCs did not support to set rates from abstrct
  60. * clock of it's hierarchy.
  61. */
  62. static int set_audio_clock_rate(unsigned long epll_rate,
  63. unsigned long audio_rate)
  64. {
  65. struct clk *fout_epll, *sclk_spdif;
  66. fout_epll = clk_get(NULL, "fout_epll");
  67. if (IS_ERR(fout_epll)) {
  68. printk(KERN_ERR "%s: failed to get fout_epll\n", __func__);
  69. return -ENOENT;
  70. }
  71. clk_set_rate(fout_epll, epll_rate);
  72. clk_put(fout_epll);
  73. sclk_spdif = clk_get(NULL, "sclk_spdif");
  74. if (IS_ERR(sclk_spdif)) {
  75. printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__);
  76. return -ENOENT;
  77. }
  78. clk_set_rate(sclk_spdif, audio_rate);
  79. clk_put(sclk_spdif);
  80. return 0;
  81. }
  82. static int smdk_hw_params(struct snd_pcm_substream *substream,
  83. struct snd_pcm_hw_params *params)
  84. {
  85. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  86. struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
  87. unsigned long pll_out, rclk_rate;
  88. int ret, ratio;
  89. switch (params_rate(params)) {
  90. case 44100:
  91. pll_out = 45158400;
  92. break;
  93. case 32000:
  94. case 48000:
  95. case 96000:
  96. pll_out = 49152000;
  97. break;
  98. default:
  99. return -EINVAL;
  100. }
  101. /* Setting ratio to 512fs helps to use S/PDIF with HDMI without
  102. * modify S/PDIF ASoC machine driver.
  103. */
  104. ratio = 512;
  105. rclk_rate = params_rate(params) * ratio;
  106. /* Set audio source clock rates */
  107. ret = set_audio_clock_rate(pll_out, rclk_rate);
  108. if (ret < 0)
  109. return ret;
  110. /* Set S/PDIF uses internal source clock */
  111. ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
  112. rclk_rate, SND_SOC_CLOCK_IN);
  113. if (ret < 0)
  114. return ret;
  115. return ret;
  116. }
  117. static const struct snd_soc_ops smdk_spdif_ops = {
  118. .hw_params = smdk_hw_params,
  119. };
  120. SND_SOC_DAILINK_DEFS(spdif,
  121. DAILINK_COMP_ARRAY(COMP_CPU("samsung-spdif")),
  122. DAILINK_COMP_ARRAY(COMP_CODEC("spdif-dit", "dit-hifi")),
  123. DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-spdif")));
  124. static struct snd_soc_dai_link smdk_dai = {
  125. .name = "S/PDIF",
  126. .stream_name = "S/PDIF PCM Playback",
  127. .ops = &smdk_spdif_ops,
  128. SND_SOC_DAILINK_REG(spdif),
  129. };
  130. static struct snd_soc_card smdk = {
  131. .name = "SMDK-S/PDIF",
  132. .owner = THIS_MODULE,
  133. .dai_link = &smdk_dai,
  134. .num_links = 1,
  135. };
  136. static struct platform_device *smdk_snd_spdif_dit_device;
  137. static struct platform_device *smdk_snd_spdif_device;
  138. static int __init smdk_init(void)
  139. {
  140. int ret;
  141. smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);
  142. if (!smdk_snd_spdif_dit_device)
  143. return -ENOMEM;
  144. ret = platform_device_add(smdk_snd_spdif_dit_device);
  145. if (ret)
  146. goto err1;
  147. smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1);
  148. if (!smdk_snd_spdif_device) {
  149. ret = -ENOMEM;
  150. goto err2;
  151. }
  152. platform_set_drvdata(smdk_snd_spdif_device, &smdk);
  153. ret = platform_device_add(smdk_snd_spdif_device);
  154. if (ret)
  155. goto err3;
  156. /* Set audio clock hierarchy manually */
  157. ret = set_audio_clock_heirachy(smdk_snd_spdif_device);
  158. if (ret)
  159. goto err4;
  160. return 0;
  161. err4:
  162. platform_device_del(smdk_snd_spdif_device);
  163. err3:
  164. platform_device_put(smdk_snd_spdif_device);
  165. err2:
  166. platform_device_del(smdk_snd_spdif_dit_device);
  167. err1:
  168. platform_device_put(smdk_snd_spdif_dit_device);
  169. return ret;
  170. }
  171. static void __exit smdk_exit(void)
  172. {
  173. platform_device_unregister(smdk_snd_spdif_device);
  174. platform_device_unregister(smdk_snd_spdif_dit_device);
  175. }
  176. module_init(smdk_init);
  177. module_exit(smdk_exit);
  178. MODULE_AUTHOR("Seungwhan Youn, <[email protected]>");
  179. MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF");
  180. MODULE_LICENSE("GPL");