nuc900-pcm.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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. spin_lock_irqsave(&nuc900_audio->lock, flags);
  47. ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
  48. if (ret < 0)
  49. return ret;
  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. spin_lock_irqsave(&nuc900_audio->lock, flags);
  140. nuc900_update_dma_register(substream,
  141. nuc900_audio->dma_addr[substream->stream],
  142. nuc900_audio->buffersize[substream->stream]);
  143. val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
  144. switch (runtime->channels) {
  145. case 1:
  146. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  147. val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
  148. val |= PLAY_RIGHT_CHNNEL;
  149. } else {
  150. val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
  151. val |= RECORD_RIGHT_CHNNEL;
  152. }
  153. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
  154. break;
  155. case 2:
  156. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  157. val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
  158. else
  159. val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
  160. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
  161. break;
  162. default:
  163. return -EINVAL;
  164. }
  165. spin_unlock_irqrestore(&nuc900_audio->lock, flags);
  166. return 0;
  167. }
  168. static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
  169. {
  170. int ret = 0;
  171. switch (cmd) {
  172. case SNDRV_PCM_TRIGGER_START:
  173. case SNDRV_PCM_TRIGGER_RESUME:
  174. nuc900_dma_start(substream);
  175. break;
  176. case SNDRV_PCM_TRIGGER_STOP:
  177. case SNDRV_PCM_TRIGGER_SUSPEND:
  178. nuc900_dma_stop(substream);
  179. break;
  180. default:
  181. ret = -EINVAL;
  182. break;
  183. }
  184. return ret;
  185. }
  186. int nuc900_dma_getposition(struct snd_pcm_substream *substream,
  187. dma_addr_t *src, dma_addr_t *dst)
  188. {
  189. struct snd_pcm_runtime *runtime = substream->runtime;
  190. struct nuc900_audio *nuc900_audio = runtime->private_data;
  191. if (src != NULL)
  192. *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
  193. if (dst != NULL)
  194. *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
  195. return 0;
  196. }
  197. static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
  198. {
  199. struct snd_pcm_runtime *runtime = substream->runtime;
  200. dma_addr_t src, dst;
  201. unsigned long res;
  202. nuc900_dma_getposition(substream, &src, &dst);
  203. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  204. res = dst - runtime->dma_addr;
  205. else
  206. res = src - runtime->dma_addr;
  207. return bytes_to_frames(substream->runtime, res);
  208. }
  209. static int nuc900_dma_open(struct snd_pcm_substream *substream)
  210. {
  211. struct snd_pcm_runtime *runtime = substream->runtime;
  212. struct nuc900_audio *nuc900_audio;
  213. snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
  214. nuc900_audio = nuc900_ac97_data;
  215. if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
  216. IRQF_DISABLED, "nuc900-dma", substream))
  217. return -EBUSY;
  218. runtime->private_data = nuc900_audio;
  219. return 0;
  220. }
  221. static int nuc900_dma_close(struct snd_pcm_substream *substream)
  222. {
  223. struct snd_pcm_runtime *runtime = substream->runtime;
  224. struct nuc900_audio *nuc900_audio = runtime->private_data;
  225. free_irq(nuc900_audio->irq_num, substream);
  226. return 0;
  227. }
  228. static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
  229. struct vm_area_struct *vma)
  230. {
  231. struct snd_pcm_runtime *runtime = substream->runtime;
  232. return dma_mmap_writecombine(substream->pcm->card->dev, vma,
  233. runtime->dma_area,
  234. runtime->dma_addr,
  235. runtime->dma_bytes);
  236. }
  237. static struct snd_pcm_ops nuc900_dma_ops = {
  238. .open = nuc900_dma_open,
  239. .close = nuc900_dma_close,
  240. .ioctl = snd_pcm_lib_ioctl,
  241. .hw_params = nuc900_dma_hw_params,
  242. .hw_free = nuc900_dma_hw_free,
  243. .prepare = nuc900_dma_prepare,
  244. .trigger = nuc900_dma_trigger,
  245. .pointer = nuc900_dma_pointer,
  246. .mmap = nuc900_dma_mmap,
  247. };
  248. static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
  249. {
  250. snd_pcm_lib_preallocate_free_for_all(pcm);
  251. }
  252. static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
  253. static int nuc900_dma_new(struct snd_card *card,
  254. struct snd_soc_dai *dai, struct snd_pcm *pcm)
  255. {
  256. if (!card->dev->dma_mask)
  257. card->dev->dma_mask = &nuc900_pcm_dmamask;
  258. if (!card->dev->coherent_dma_mask)
  259. card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
  260. snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
  261. card->dev, 4 * 1024, (4 * 1024) - 1);
  262. return 0;
  263. }
  264. struct snd_soc_platform nuc900_soc_platform = {
  265. .name = "nuc900-dma",
  266. .pcm_ops = &nuc900_dma_ops,
  267. .pcm_new = nuc900_dma_new,
  268. .pcm_free = nuc900_dma_free_dma_buffers,
  269. }
  270. EXPORT_SYMBOL_GPL(nuc900_soc_platform);
  271. static int __init nuc900_soc_platform_init(void)
  272. {
  273. return snd_soc_register_platform(&nuc900_soc_platform);
  274. }
  275. static void __exit nuc900_soc_platform_exit(void)
  276. {
  277. snd_soc_unregister_platform(&nuc900_soc_platform);
  278. }
  279. module_init(nuc900_soc_platform_init);
  280. module_exit(nuc900_soc_platform_exit);
  281. MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
  282. MODULE_DESCRIPTION("nuc900 Audio DMA module");
  283. MODULE_LICENSE("GPL");