s3c6410-pcmif.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * s3c6410-pcmif.c -- ALSA Soc Audio Layer
  3. *
  4. * (c) 2008 Samsung Electronics
  5. * Ryu Euiyoul <steven.ryu@samsung.com, ryu.real@gmail.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation; either version 2 of the License, or (at your
  10. * option) any later version.
  11. *
  12. */
  13. #include <linux/init.h>
  14. #include <linux/module.h>
  15. #include <linux/device.h>
  16. #include <linux/delay.h>
  17. #include <linux/interrupt.h>
  18. #include <linux/clk.h>
  19. #include <sound/driver.h>
  20. #include <sound/core.h>
  21. #include <sound/pcm.h>
  22. #include <sound/pcm_params.h>
  23. #include <sound/initval.h>
  24. #include <sound/soc.h>
  25. #include <asm/hardware.h>
  26. #include <asm/io.h>
  27. #include <asm/dma.h>
  28. #include <asm/arch/regs-pcm.h>
  29. #include <asm/arch/regs-gpio.h>
  30. #include <asm/arch/audio.h>
  31. #include <asm/arch/dma.h>
  32. #include <asm/arch/regs-s3c6410-clock.h>
  33. #include "s3c-pcm.h"
  34. #include "s3c6410-pcmif.h"
  35. #ifdef CONFIG_SND_DEBUG
  36. #define s3cdbg(x...) printk(x)
  37. #else
  38. #define s3cdbg(x...)
  39. #endif
  40. static struct s3c2410_dma_client s3c24xx_dma_client_out = {
  41. .name = "PCM Mono out"
  42. };
  43. static struct s3c2410_dma_client s3c24xx_dma_client_in = {
  44. .name = "PCM Mono in"
  45. };
  46. static struct s3c24xx_pcm_dma_params s3c_pcmif_dma_out = {
  47. .client = &s3c24xx_dma_client_out,
  48. .channel = DMACH_PCMIF_CH1_TX,
  49. .dma_addr = S3C6410_PA_PCM_PORT1 + S3C_PCM_TX_FIFO,
  50. .dma_size = 2, /* transfer width = half word(16bit) */
  51. };
  52. static struct s3c24xx_pcm_dma_params s3c_pcmif_dma_in = {
  53. .client = &s3c24xx_dma_client_in,
  54. .channel = DMACH_PCMIF_CH1_RX,
  55. .dma_addr = S3C6410_PA_PCM_PORT1 + S3C_PCM_RX_FIFO,
  56. .dma_size = 2, /* transfer width = half word(16bit) */
  57. };
  58. struct s3c_pcmif_info {
  59. void __iomem *regs;
  60. struct clk *pcmif_clk;
  61. };
  62. static struct s3c_pcmif_info s3c_pcmif;
  63. static int s3c_pcmif_hw_params(struct snd_pcm_substream *substream,
  64. struct snd_pcm_hw_params *params)
  65. {
  66. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  67. u32 pcm_ctl;
  68. s3cdbg("Entered %s\n", __FUNCTION__);
  69. s3c_gpio_cfgpin(S3C_GPH7, S3C_GPH7_I2S_V40_CDCLK);
  70. s3c_gpio_pullup(S3C_GPH7, 0x2);
  71. s3c_gpio_cfgpin(S3C_GPE0,S3C_GPE0_PCM_DCLK1);
  72. s3c_gpio_cfgpin(S3C_GPE1,S3C_GPE1_PCM_EXTCLK1);
  73. s3c_gpio_cfgpin(S3C_GPE2,S3C_GPE2_PCM_FSYNC1);
  74. s3c_gpio_cfgpin(S3C_GPE3,S3C_GPE3_PCM_SIN1);
  75. s3c_gpio_cfgpin(S3C_GPE4,S3C_GPE4_PCM_SOUT1);
  76. s3c_gpio_pullup(S3C_GPE0,0);
  77. s3c_gpio_pullup(S3C_GPE1,0);
  78. s3c_gpio_pullup(S3C_GPE2,0);
  79. s3c_gpio_pullup(S3C_GPE3,0);
  80. s3c_gpio_pullup(S3C_GPE4,0);
  81. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  82. rtd->dai->cpu_dai->dma_data = &s3c_pcmif_dma_out;
  83. } else {
  84. rtd->dai->cpu_dai->dma_data = &s3c_pcmif_dma_in;
  85. }
  86. pcm_ctl = readl(s3c_pcmif.regs + S3C_PCM_CTL);
  87. pcm_ctl |= S3C_PCM_CTL_TX_DMA_EN;
  88. pcm_ctl |= S3C_PCM_CTL_TXFIFO_EN;
  89. pcm_ctl |= S3C_PCM_CTL_TX_FIFO_DIPSTICK(0x8);
  90. pcm_ctl |= S3C_PCM_CTL_RX_DMA_EN;
  91. pcm_ctl |= S3C_PCM_CTL_RXFIFO_EN;
  92. pcm_ctl |= S3C_PCM_CTL_RX_FIFO_DIPSTICK(0x20);
  93. pcm_ctl |= S3C_PCM_CTL_PCM_ENABLE;
  94. writel(pcm_ctl, s3c_pcmif.regs + S3C_PCM_CTL);
  95. writel(S3C_PCM_TX_FIFO_DVALID, s3c_pcmif.regs + S3C_PCM_TX_FIFO);
  96. writel(S3C_PCM_RX_FIFO_DVALID, s3c_pcmif.regs + S3C_PCM_RX_FIFO);
  97. s3cdbg("s3c pcm ctl: 0x%08x\n", readl(s3c_pcmif.regs + S3C_PCM_CTL));
  98. return 0;
  99. }
  100. static void s3c_pcmif_shutdown(struct snd_pcm_substream *substream)
  101. {
  102. u32 pcm_ctl;
  103. s3cdbg("Entered %s\n", __FUNCTION__);
  104. pcm_ctl = readl(s3c_pcmif.regs + S3C_PCM_CTL);
  105. pcm_ctl &= ~S3C_PCM_CTL_PCM_ENABLE;
  106. writel(pcm_ctl, s3c_pcmif.regs + S3C_PCM_CTL);
  107. s3c_gpio_cfgpin(S3C_GPH7,0);
  108. s3c_gpio_cfgpin(S3C_GPE0,0);
  109. s3c_gpio_cfgpin(S3C_GPE1,0);
  110. s3c_gpio_cfgpin(S3C_GPE2,0);
  111. s3c_gpio_cfgpin(S3C_GPE3,0);
  112. s3c_gpio_cfgpin(S3C_GPE4,0);
  113. }
  114. /*
  115. * Set S3C24xx Clock dividers
  116. */
  117. static int s3c_pcmif_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
  118. int div_id, int fs)
  119. {
  120. u32 pcm_clk, pcmsource, sclk;
  121. u32 sync_div = 31; /* must grater than 16 */
  122. u32 sclk_div;
  123. unsigned long pclk;
  124. s3c_pcmif.pcmif_clk=clk_get(NULL, "pcmif_1");
  125. if (s3c_pcmif.pcmif_clk == NULL) {
  126. printk("failed to get pcmif\n");
  127. iounmap(s3c_pcmif.regs);
  128. return -ENODEV;
  129. }
  130. clk_enable(s3c_pcmif.pcmif_clk);
  131. pclk = clk_get_rate(s3c_pcmif.pcmif_clk);
  132. pcmsource = pclk/1000;
  133. sclk = fs * (sync_div +1)/1000;
  134. /* To reduce noise */
  135. //sclk_div = pcmsource /(2*sclk) - 1;
  136. sclk_div = pcmsource /(2*sclk) + 1;
  137. s3cdbg("fs = %d,pcmsource = %d,sclk = %d,sclk_div = %d\n",
  138. __FUNCTION__, fs, pcmsource, sclk, sclk_div);
  139. /* PCM clock control */
  140. pcm_clk = S3C_PCM_CTLCTL_SERCLK_EN; /* using pclk */
  141. pcm_clk |= S3C_PCM_CTLCTL_SERCLK_SEL; /* using pclk */
  142. pcm_clk |= S3C_PCM_CLKCTL_SCLK_DIV(sclk_div);
  143. pcm_clk |= S3C_PCM_CLKCTL_SYNC_DIV(sync_div);
  144. s3cdbg("pcm_clk = 0x%08x\n",pcm_clk);
  145. writel(pcm_clk, s3c_pcmif.regs + S3C_PCM_CLKCTL);
  146. return 0;
  147. }
  148. static int s3c_pcmif_probe(struct platform_device *pdev)
  149. {
  150. s3cdbg("Entered %s\n", __FUNCTION__);
  151. s3c_pcmif.regs = ioremap(S3C6410_PA_PCM_PORT1, 0x100);
  152. if (s3c_pcmif.regs == NULL)
  153. return -ENXIO;
  154. #if 0
  155. s3c_pcmif.pcmif_clk=clk_get(&pdev->dev, "pcmif");
  156. if (s3c_pcmif.pcmif_clk == NULL) {
  157. printk("failed to get pcmif\n");
  158. iounmap(s3c_pcmif.regs);
  159. return -ENODEV;
  160. }
  161. clk_enable(s3c_pcmif.pcmif_clk);
  162. #endif
  163. return 0;
  164. }
  165. #ifdef CONFIG_PM
  166. static int s3c_pcmif_suspend(struct platform_device *dev,
  167. struct snd_soc_cpu_dai *dai)
  168. {
  169. s3cdbg("Entered %s\n", __FUNCTION__);
  170. return 0;
  171. }
  172. static int s3c_pcmif_resume(struct platform_device *pdev,
  173. struct snd_soc_cpu_dai *dai)
  174. {
  175. s3cdbg("Entered %s\n", __FUNCTION__);
  176. return 0;
  177. }
  178. #else
  179. #define s3c_pcmif_suspend NULL
  180. #define s3c_pcmif_resume NULL
  181. #endif
  182. #define S3C_PCMIF_RATES \
  183. (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
  184. SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
  185. SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
  186. struct snd_soc_cpu_dai s3c_pcmif_dai = {
  187. .name = "s3c-pcmif",
  188. .id = 0,
  189. .type = SND_SOC_DAI_PCM,
  190. .probe = s3c_pcmif_probe,
  191. .suspend = s3c_pcmif_suspend,
  192. .resume = s3c_pcmif_resume,
  193. .playback = {
  194. .channels_min = 1,
  195. .channels_max = 2,
  196. .rates = S3C_PCMIF_RATES,
  197. .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,},
  198. .capture = {
  199. .channels_min = 1,
  200. .channels_max = 2,
  201. .rates = S3C_PCMIF_RATES,
  202. .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,},
  203. .ops = {
  204. .shutdown = s3c_pcmif_shutdown,
  205. .hw_params = s3c_pcmif_hw_params,},
  206. .dai_ops = {
  207. .set_clkdiv = s3c_pcmif_set_clkdiv,
  208. },
  209. };
  210. EXPORT_SYMBOL_GPL(s3c_pcmif_dai);
  211. /* Module information */
  212. MODULE_AUTHOR("Ryu Euiyoul");
  213. MODULE_DESCRIPTION("S3C PCM INTERFACE");
  214. MODULE_LICENSE("GPL");