nuc900-pcm.c 9.2 KB

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