nuc900-pcm.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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-auido.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, stype = SUBSTREAM_TYPE(substream);
  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[stype] = runtime->dma_addr;
  52. nuc900_audio->buffersize[stype] = params_buffer_bytes(params);
  53. spin_unlock_irqrestore(&nuc900_audio->lock, flags);
  54. return ret;
  55. }
  56. static void nuc900_update_dma_register(struct snd_pcm_substream *substream,
  57. dma_addr_t dma_addr, size_t count)
  58. {
  59. struct snd_pcm_runtime *runtime = substream->runtime;
  60. struct nuc900_audio *nuc900_audio = runtime->private_data;
  61. void __iomem *mmio_addr, *mmio_len;
  62. if (SUBSTREAM_TYPE(substream) == PCM_TX) {
  63. mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
  64. mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
  65. } else {
  66. mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
  67. mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
  68. }
  69. AUDIO_WRITE(mmio_addr, dma_addr);
  70. AUDIO_WRITE(mmio_len, count);
  71. }
  72. static void nuc900_dma_start(struct snd_pcm_substream *substream)
  73. {
  74. struct snd_pcm_runtime *runtime = substream->runtime;
  75. struct nuc900_audio *nuc900_audio = runtime->private_data;
  76. unsigned long val;
  77. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  78. val |= (T_DMA_IRQ | R_DMA_IRQ);
  79. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
  80. }
  81. static void nuc900_dma_stop(struct snd_pcm_substream *substream)
  82. {
  83. struct snd_pcm_runtime *runtime = substream->runtime;
  84. struct nuc900_audio *nuc900_audio = runtime->private_data;
  85. unsigned long val;
  86. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  87. val &= ~(T_DMA_IRQ | R_DMA_IRQ);
  88. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
  89. }
  90. static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
  91. {
  92. struct snd_pcm_substream *substream = dev_id;
  93. struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
  94. unsigned long val;
  95. spin_lock(&nuc900_audio->lock);
  96. val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
  97. if (val & R_DMA_IRQ) {
  98. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
  99. val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
  100. if (val & R_DMA_MIDDLE_IRQ) {
  101. val |= R_DMA_MIDDLE_IRQ;
  102. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
  103. }
  104. if (val & R_DMA_END_IRQ) {
  105. val |= R_DMA_END_IRQ;
  106. AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
  107. }
  108. } else if (val & T_DMA_IRQ) {
  109. AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
  110. val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
  111. if (val & P_DMA_MIDDLE_IRQ) {
  112. val |= P_DMA_MIDDLE_IRQ;
  113. AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
  114. }
  115. if (val & P_DMA_END_IRQ) {
  116. val |= P_DMA_END_IRQ;
  117. AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
  118. }
  119. } else {
  120. dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
  121. spin_unlock(&nuc900_audio->lock);
  122. return IRQ_HANDLED;
  123. }
  124. spin_unlock(&nuc900_audio->lock);
  125. snd_pcm_period_elapsed(substream);
  126. return IRQ_HANDLED;
  127. }
  128. static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
  129. {
  130. snd_pcm_lib_free_pages(substream);
  131. return 0;
  132. }
  133. static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
  134. {
  135. struct snd_pcm_runtime *runtime = substream->runtime;
  136. struct nuc900_audio *nuc900_audio = runtime->private_data;
  137. unsigned long flags, val, stype = SUBSTREAM_TYPE(substream);;
  138. spin_lock_irqsave(&nuc900_audio->lock, flags);
  139. nuc900_update_dma_register(substream,
  140. nuc900_audio->dma_addr[stype], nuc900_audio->buffersize[stype]);
  141. val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
  142. switch (runtime->channels) {
  143. case 1:
  144. if (PCM_TX == stype) {
  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 (PCM_TX == stype)
  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. return -EINVAL;
  162. }
  163. spin_unlock_irqrestore(&nuc900_audio->lock, flags);
  164. return 0;
  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. 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. IRQF_DISABLED, "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 u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
  251. static int nuc900_dma_new(struct snd_card *card,
  252. struct snd_soc_dai *dai, struct snd_pcm *pcm)
  253. {
  254. if (!card->dev->dma_mask)
  255. card->dev->dma_mask = &nuc900_pcm_dmamask;
  256. if (!card->dev->coherent_dma_mask)
  257. card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
  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. struct snd_soc_platform nuc900_soc_platform = {
  263. .name = "nuc900-dma",
  264. .pcm_ops = &nuc900_dma_ops,
  265. .pcm_new = nuc900_dma_new,
  266. .pcm_free = nuc900_dma_free_dma_buffers,
  267. }
  268. EXPORT_SYMBOL_GPL(nuc900_soc_platform);
  269. static int __init nuc900_soc_platform_init(void)
  270. {
  271. return snd_soc_register_platform(&nuc900_soc_platform);
  272. }
  273. static void __exit nuc900_soc_platform_exit(void)
  274. {
  275. snd_soc_unregister_platform(&nuc900_soc_platform);
  276. }
  277. module_init(nuc900_soc_platform_init);
  278. module_exit(nuc900_soc_platform_exit);
  279. MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
  280. MODULE_DESCRIPTION("nuc900 Audio DMA module");
  281. MODULE_LICENSE("GPL");