smdk_spdif.c 5.0 KB

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