nuc900-pcm.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. struct snd_pcm_runtime *runtime = substream->runtime;
  40. struct nuc900_audio *nuc900_audio = runtime->private_data;
  41. unsigned long flags;
  42. int ret = 0;
  43. ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
  44. if (ret < 0)
  45. return ret;
  46. spin_lock_irqsave(&nuc900_audio->lock, flags);
  47. nuc900_audio->substream = substream;
  48. nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr;
  49. nuc900_audio->buffersize[substream->stream] =
  50. params_buffer_bytes(params);
  51. spin_unlock_irqrestore(&nuc900_audio->lock, flags);
  52. return ret;
  53. }
  54. static void nuc900_update_dma_register(struct snd_pcm_substream *substream,
  55. dma_addr_t dma_addr, size_t count)
  56. {
  57. struct snd_pcm_runtime *runtime = substream->runtime;
  58. struct nuc900_audio *nuc900_audio = runtime->private_data;
  59. void __iomem *mmio_addr, *mmio_len;
  60. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  61. mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
  62. mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
  63. } else {
  64. mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
  65. mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
  66. }
  67. AUDIO_WRITE(mmio_addr, dma_addr);
  68. AUDIO_WRITE(mmio_len, count);
  69. }
  70. static void nuc900_dma_start(struct snd_pcm_substream *substream)
  71. {
  72. struct snd_pcm_runtime *runtime = substream->runtime;
  73. struct nuc900_audio *nuc900_audio = runtime->private_data;
  74. unsigned long val;
  75. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  76. val |= (T_DMA_IRQ | R_DMA_IRQ);
  77. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
  78. }
  79. static void nuc900_dma_stop(struct snd_pcm_substream *substream)
  80. {
  81. struct snd_pcm_runtime *runtime = substream->runtime;
  82. struct nuc900_audio *nuc900_audio = runtime->private_data;
  83. unsigned long val;
  84. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  85. val &= ~(T_DMA_IRQ | R_DMA_IRQ);
  86. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
  87. }
  88. static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
  89. {
  90. struct snd_pcm_substream *substream = dev_id;
  91. struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
  92. unsigned long val;
  93. spin_lock(&nuc900_audio->lock);
  94. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  95. if (val & R_DMA_IRQ) {
  96. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
  97. val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
  98. if (val & R_DMA_MIDDLE_IRQ) {
  99. val |= R_DMA_MIDDLE_IRQ;
  100. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
  101. }
  102. if (val & R_DMA_END_IRQ) {
  103. val |= R_DMA_END_IRQ;
  104. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
  105. }
  106. } else if (val & T_DMA_IRQ) {
  107. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
  108. val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
  109. if (val & P_DMA_MIDDLE_IRQ) {
  110. val |= P_DMA_MIDDLE_IRQ;
  111. AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
  112. }
  113. if (val & P_DMA_END_IRQ) {
  114. val |= P_DMA_END_IRQ;
  115. AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
  116. }
  117. } else {
  118. dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
  119. spin_unlock(&nuc900_audio->lock);
  120. return IRQ_HANDLED;
  121. }
  122. spin_unlock(&nuc900_audio->lock);
  123. snd_pcm_period_elapsed(substream);
  124. return IRQ_HANDLED;
  125. }
  126. static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
  127. {
  128. snd_pcm_lib_free_pages(substream);
  129. return 0;
  130. }
  131. static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
  132. {
  133. struct snd_pcm_runtime *runtime = substream->runtime;
  134. struct nuc900_audio *nuc900_audio = runtime->private_data;
  135. unsigned long flags, val;
  136. int ret = 0;
  137. spin_lock_irqsave(&nuc900_audio->lock, flags);
  138. nuc900_update_dma_register(substream,
  139. nuc900_audio->dma_addr[substream->stream],
  140. nuc900_audio->buffersize[substream->stream]);
  141. val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
  142. switch (runtime->channels) {
  143. case 1:
  144. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  145. val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
  146. val |= PLAY_RIGHT_CHNNEL;
  147. } else {
  148. val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
  149. val |= RECORD_RIGHT_CHNNEL;
  150. }
  151. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
  152. break;
  153. case 2:
  154. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  155. val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
  156. else
  157. val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
  158. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
  159. break;
  160. default:
  161. ret = -EINVAL;
  162. }
  163. spin_unlock_irqrestore(&nuc900_audio->lock, flags);
  164. return ret;
  165. }
  166. static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
  167. {
  168. int ret = 0;
  169. switch (cmd) {
  170. case SNDRV_PCM_TRIGGER_START:
  171. case SNDRV_PCM_TRIGGER_RESUME:
  172. nuc900_dma_start(substream);
  173. break;
  174. case SNDRV_PCM_TRIGGER_STOP:
  175. case SNDRV_PCM_TRIGGER_SUSPEND:
  176. nuc900_dma_stop(substream);
  177. break;
  178. default:
  179. ret = -EINVAL;
  180. break;
  181. }
  182. return ret;
  183. }
  184. static int nuc900_dma_getposition(struct snd_pcm_substream *substream,
  185. dma_addr_t *src, dma_addr_t *dst)
  186. {
  187. struct snd_pcm_runtime *runtime = substream->runtime;
  188. struct nuc900_audio *nuc900_audio = runtime->private_data;
  189. if (src != NULL)
  190. *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
  191. if (dst != NULL)
  192. *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
  193. return 0;
  194. }
  195. static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
  196. {
  197. struct snd_pcm_runtime *runtime = substream->runtime;
  198. dma_addr_t src, dst;
  199. unsigned long res;
  200. nuc900_dma_getposition(substream, &src, &dst);
  201. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  202. res = dst - runtime->dma_addr;
  203. else
  204. res = src - runtime->dma_addr;
  205. return bytes_to_frames(substream->runtime, res);
  206. }
  207. static int nuc900_dma_open(struct snd_pcm_substream *substream)
  208. {
  209. struct snd_pcm_runtime *runtime = substream->runtime;
  210. struct nuc900_audio *nuc900_audio;
  211. snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
  212. nuc900_audio = nuc900_ac97_data;
  213. if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
  214. 0, "nuc900-dma", substream))
  215. return -EBUSY;
  216. runtime->private_data = nuc900_audio;
  217. return 0;
  218. }
  219. static int nuc900_dma_close(struct snd_pcm_substream *substream)
  220. {
  221. struct snd_pcm_runtime *runtime = substream->runtime;
  222. struct nuc900_audio *nuc900_audio = runtime->private_data;
  223. free_irq(nuc900_audio->irq_num, substream);
  224. return 0;
  225. }
  226. static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
  227. struct vm_area_struct *vma)
  228. {
  229. struct snd_pcm_runtime *runtime = substream->runtime;
  230. return dma_mmap_writecombine(substream->pcm->card->dev, vma,
  231. runtime->dma_area,
  232. runtime->dma_addr,
  233. runtime->dma_bytes);
  234. }
  235. static struct snd_pcm_ops nuc900_dma_ops = {
  236. .open = nuc900_dma_open,
  237. .close = nuc900_dma_close,
  238. .ioctl = snd_pcm_lib_ioctl,
  239. .hw_params = nuc900_dma_hw_params,
  240. .hw_free = nuc900_dma_hw_free,
  241. .prepare = nuc900_dma_prepare,
  242. .trigger = nuc900_dma_trigger,
  243. .pointer = nuc900_dma_pointer,
  244. .mmap = nuc900_dma_mmap,
  245. };
  246. static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
  247. {
  248. snd_pcm_lib_preallocate_free_for_all(pcm);
  249. }
  250. static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
  251. {
  252. struct snd_card *card = rtd->card->snd_card;
  253. struct snd_pcm *pcm = rtd->pcm;
  254. int ret;
  255. ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
  256. if (ret)
  257. return ret;
  258. snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
  259. card->dev, 4 * 1024, (4 * 1024) - 1);
  260. return 0;
  261. }
  262. static struct snd_soc_platform_driver nuc900_soc_platform = {
  263. .ops = &nuc900_dma_ops,
  264. .pcm_new = nuc900_dma_new,
  265. .pcm_free = nuc900_dma_free_dma_buffers,
  266. };
  267. static int nuc900_soc_platform_probe(struct platform_device *pdev)
  268. {
  269. return snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
  270. }
  271. static int nuc900_soc_platform_remove(struct platform_device *pdev)
  272. {
  273. snd_soc_unregister_platform(&pdev->dev);
  274. return 0;
  275. }
  276. static struct platform_driver nuc900_pcm_driver = {
  277. .driver = {
  278. .name = "nuc900-pcm-audio",
  279. .owner = THIS_MODULE,
  280. },
  281. .probe = nuc900_soc_platform_probe,
  282. .remove = nuc900_soc_platform_remove,
  283. };
  284. module_platform_driver(nuc900_pcm_driver);
  285. MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
  286. MODULE_DESCRIPTION("nuc900 Audio DMA module");
  287. MODULE_LICENSE("GPL");