nuc900-pcm.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /*
  2. * Copyright (c) 2010 Nuvoton technology corporation.
  3. *
  4. * Wan ZongShun <mcuos.com@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation;version 2 of the License.
  9. *
  10. */
  11. #include <linux/module.h>
  12. #include <linux/init.h>
  13. #include <linux/io.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/slab.h>
  16. #include <linux/dma-mapping.h>
  17. #include <sound/core.h>
  18. #include <sound/pcm.h>
  19. #include <sound/pcm_params.h>
  20. #include <sound/soc.h>
  21. #include <mach/hardware.h>
  22. #include "nuc900-audio.h"
  23. static const struct snd_pcm_hardware nuc900_pcm_hardware = {
  24. .info = SNDRV_PCM_INFO_INTERLEAVED |
  25. SNDRV_PCM_INFO_BLOCK_TRANSFER |
  26. SNDRV_PCM_INFO_MMAP |
  27. SNDRV_PCM_INFO_MMAP_VALID |
  28. SNDRV_PCM_INFO_PAUSE |
  29. SNDRV_PCM_INFO_RESUME,
  30. .buffer_bytes_max = 4*1024,
  31. .period_bytes_min = 1*1024,
  32. .period_bytes_max = 4*1024,
  33. .periods_min = 1,
  34. .periods_max = 1024,
  35. };
  36. static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
  37. struct snd_pcm_hw_params *params)
  38. {
  39. return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
  40. }
  41. static void nuc900_update_dma_register(struct snd_pcm_substream *substream)
  42. {
  43. struct snd_pcm_runtime *runtime = substream->runtime;
  44. struct nuc900_audio *nuc900_audio = runtime->private_data;
  45. void __iomem *mmio_addr, *mmio_len;
  46. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  47. mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
  48. mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
  49. } else {
  50. mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
  51. mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
  52. }
  53. AUDIO_WRITE(mmio_addr, runtime->dma_addr);
  54. AUDIO_WRITE(mmio_len, runtime->dma_bytes);
  55. }
  56. static void nuc900_dma_start(struct snd_pcm_substream *substream)
  57. {
  58. struct snd_pcm_runtime *runtime = substream->runtime;
  59. struct nuc900_audio *nuc900_audio = runtime->private_data;
  60. unsigned long val;
  61. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  62. val |= (T_DMA_IRQ | R_DMA_IRQ);
  63. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
  64. }
  65. static void nuc900_dma_stop(struct snd_pcm_substream *substream)
  66. {
  67. struct snd_pcm_runtime *runtime = substream->runtime;
  68. struct nuc900_audio *nuc900_audio = runtime->private_data;
  69. unsigned long val;
  70. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  71. val &= ~(T_DMA_IRQ | R_DMA_IRQ);
  72. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
  73. }
  74. static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
  75. {
  76. struct snd_pcm_substream *substream = dev_id;
  77. struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
  78. unsigned long val;
  79. spin_lock(&nuc900_audio->lock);
  80. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  81. if (val & R_DMA_IRQ) {
  82. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
  83. val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
  84. if (val & R_DMA_MIDDLE_IRQ) {
  85. val |= R_DMA_MIDDLE_IRQ;
  86. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
  87. }
  88. if (val & R_DMA_END_IRQ) {
  89. val |= R_DMA_END_IRQ;
  90. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
  91. }
  92. } else if (val & T_DMA_IRQ) {
  93. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
  94. val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
  95. if (val & P_DMA_MIDDLE_IRQ) {
  96. val |= P_DMA_MIDDLE_IRQ;
  97. AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
  98. }
  99. if (val & P_DMA_END_IRQ) {
  100. val |= P_DMA_END_IRQ;
  101. AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
  102. }
  103. } else {
  104. dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
  105. spin_unlock(&nuc900_audio->lock);
  106. return IRQ_HANDLED;
  107. }
  108. spin_unlock(&nuc900_audio->lock);
  109. snd_pcm_period_elapsed(substream);
  110. return IRQ_HANDLED;
  111. }
  112. static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
  113. {
  114. snd_pcm_lib_free_pages(substream);
  115. return 0;
  116. }
  117. static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
  118. {
  119. struct snd_pcm_runtime *runtime = substream->runtime;
  120. struct nuc900_audio *nuc900_audio = runtime->private_data;
  121. unsigned long flags, val;
  122. int ret = 0;
  123. spin_lock_irqsave(&nuc900_audio->lock, flags);
  124. nuc900_update_dma_register(substream);
  125. val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
  126. switch (runtime->channels) {
  127. case 1:
  128. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  129. val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
  130. val |= PLAY_RIGHT_CHNNEL;
  131. } else {
  132. val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
  133. val |= RECORD_RIGHT_CHNNEL;
  134. }
  135. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
  136. break;
  137. case 2:
  138. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  139. val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
  140. else
  141. val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
  142. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
  143. break;
  144. default:
  145. ret = -EINVAL;
  146. }
  147. spin_unlock_irqrestore(&nuc900_audio->lock, flags);
  148. return ret;
  149. }
  150. static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
  151. {
  152. int ret = 0;
  153. switch (cmd) {
  154. case SNDRV_PCM_TRIGGER_START:
  155. case SNDRV_PCM_TRIGGER_RESUME:
  156. nuc900_dma_start(substream);
  157. break;
  158. case SNDRV_PCM_TRIGGER_STOP:
  159. case SNDRV_PCM_TRIGGER_SUSPEND:
  160. nuc900_dma_stop(substream);
  161. break;
  162. default:
  163. ret = -EINVAL;
  164. break;
  165. }
  166. return ret;
  167. }
  168. static int nuc900_dma_getposition(struct snd_pcm_substream *substream,
  169. dma_addr_t *src, dma_addr_t *dst)
  170. {
  171. struct snd_pcm_runtime *runtime = substream->runtime;
  172. struct nuc900_audio *nuc900_audio = runtime->private_data;
  173. if (src != NULL)
  174. *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
  175. if (dst != NULL)
  176. *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
  177. return 0;
  178. }
  179. static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
  180. {
  181. struct snd_pcm_runtime *runtime = substream->runtime;
  182. dma_addr_t src, dst;
  183. unsigned long res;
  184. nuc900_dma_getposition(substream, &src, &dst);
  185. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  186. res = dst - runtime->dma_addr;
  187. else
  188. res = src - runtime->dma_addr;
  189. return bytes_to_frames(substream->runtime, res);
  190. }
  191. static int nuc900_dma_open(struct snd_pcm_substream *substream)
  192. {
  193. struct snd_pcm_runtime *runtime = substream->runtime;
  194. struct nuc900_audio *nuc900_audio;
  195. snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
  196. nuc900_audio = nuc900_ac97_data;
  197. if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
  198. 0, "nuc900-dma", substream))
  199. return -EBUSY;
  200. runtime->private_data = nuc900_audio;
  201. return 0;
  202. }
  203. static int nuc900_dma_close(struct snd_pcm_substream *substream)
  204. {
  205. struct snd_pcm_runtime *runtime = substream->runtime;
  206. struct nuc900_audio *nuc900_audio = runtime->private_data;
  207. free_irq(nuc900_audio->irq_num, substream);
  208. return 0;
  209. }
  210. static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
  211. struct vm_area_struct *vma)
  212. {
  213. struct snd_pcm_runtime *runtime = substream->runtime;
  214. return dma_mmap_writecombine(substream->pcm->card->dev, vma,
  215. runtime->dma_area,
  216. runtime->dma_addr,
  217. runtime->dma_bytes);
  218. }
  219. static struct snd_pcm_ops nuc900_dma_ops = {
  220. .open = nuc900_dma_open,
  221. .close = nuc900_dma_close,
  222. .ioctl = snd_pcm_lib_ioctl,
  223. .hw_params = nuc900_dma_hw_params,
  224. .hw_free = nuc900_dma_hw_free,
  225. .prepare = nuc900_dma_prepare,
  226. .trigger = nuc900_dma_trigger,
  227. .pointer = nuc900_dma_pointer,
  228. .mmap = nuc900_dma_mmap,
  229. };
  230. static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
  231. {
  232. struct snd_card *card = rtd->card->snd_card;
  233. struct snd_pcm *pcm = rtd->pcm;
  234. int ret;
  235. ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
  236. if (ret)
  237. return ret;
  238. snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
  239. card->dev, 4 * 1024, (4 * 1024) - 1);
  240. return 0;
  241. }
  242. static struct snd_soc_platform_driver nuc900_soc_platform = {
  243. .ops = &nuc900_dma_ops,
  244. .pcm_new = nuc900_dma_new,
  245. };
  246. static int nuc900_soc_platform_probe(struct platform_device *pdev)
  247. {
  248. return devm_snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
  249. }
  250. static struct platform_driver nuc900_pcm_driver = {
  251. .driver = {
  252. .name = "nuc900-pcm-audio",
  253. },
  254. .probe = nuc900_soc_platform_probe,
  255. };
  256. module_platform_driver(nuc900_pcm_driver);
  257. MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
  258. MODULE_DESCRIPTION("nuc900 Audio DMA module");
  259. MODULE_LICENSE("GPL");