aio-dma.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // Socionext UniPhier AIO DMA driver.
  4. //
  5. // Copyright (c) 2016-2018 Socionext Inc.
  6. #include <linux/dma-mapping.h>
  7. #include <linux/errno.h>
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <sound/core.h>
  11. #include <sound/pcm.h>
  12. #include <sound/soc.h>
  13. #include "aio.h"
  14. static struct snd_pcm_hardware uniphier_aiodma_hw = {
  15. .info = SNDRV_PCM_INFO_MMAP |
  16. SNDRV_PCM_INFO_MMAP_VALID |
  17. SNDRV_PCM_INFO_INTERLEAVED,
  18. .period_bytes_min = 256,
  19. .period_bytes_max = 4096,
  20. .periods_min = 4,
  21. .periods_max = 1024,
  22. .buffer_bytes_max = 128 * 1024,
  23. };
  24. static void aiodma_pcm_irq(struct uniphier_aio_sub *sub)
  25. {
  26. struct snd_pcm_runtime *runtime = sub->substream->runtime;
  27. int bytes = runtime->period_size *
  28. runtime->channels * samples_to_bytes(runtime, 1);
  29. int ret;
  30. spin_lock(&sub->lock);
  31. ret = aiodma_rb_set_threshold(sub, runtime->dma_bytes,
  32. sub->threshold + bytes);
  33. if (!ret)
  34. sub->threshold += bytes;
  35. aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
  36. aiodma_rb_clear_irq(sub);
  37. spin_unlock(&sub->lock);
  38. snd_pcm_period_elapsed(sub->substream);
  39. }
  40. static void aiodma_compr_irq(struct uniphier_aio_sub *sub)
  41. {
  42. struct snd_compr_runtime *runtime = sub->cstream->runtime;
  43. int bytes = runtime->fragment_size;
  44. int ret;
  45. spin_lock(&sub->lock);
  46. ret = aiodma_rb_set_threshold(sub, sub->compr_bytes,
  47. sub->threshold + bytes);
  48. if (!ret)
  49. sub->threshold += bytes;
  50. aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
  51. aiodma_rb_clear_irq(sub);
  52. spin_unlock(&sub->lock);
  53. snd_compr_fragment_elapsed(sub->cstream);
  54. }
  55. static irqreturn_t aiodma_irq(int irq, void *p)
  56. {
  57. struct platform_device *pdev = p;
  58. struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
  59. irqreturn_t ret = IRQ_NONE;
  60. int i, j;
  61. for (i = 0; i < chip->num_aios; i++) {
  62. struct uniphier_aio *aio = &chip->aios[i];
  63. for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
  64. struct uniphier_aio_sub *sub = &aio->sub[j];
  65. /* Skip channel that does not trigger */
  66. if (!sub->running || !aiodma_rb_is_irq(sub))
  67. continue;
  68. if (sub->substream)
  69. aiodma_pcm_irq(sub);
  70. if (sub->cstream)
  71. aiodma_compr_irq(sub);
  72. ret = IRQ_HANDLED;
  73. }
  74. }
  75. return ret;
  76. }
  77. static int uniphier_aiodma_open(struct snd_pcm_substream *substream)
  78. {
  79. struct snd_pcm_runtime *runtime = substream->runtime;
  80. snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw);
  81. return snd_pcm_hw_constraint_step(runtime, 0,
  82. SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256);
  83. }
  84. static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream,
  85. struct snd_pcm_hw_params *params)
  86. {
  87. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  88. substream->runtime->dma_bytes = params_buffer_bytes(params);
  89. return 0;
  90. }
  91. static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream)
  92. {
  93. snd_pcm_set_runtime_buffer(substream, NULL);
  94. substream->runtime->dma_bytes = 0;
  95. return 0;
  96. }
  97. static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream)
  98. {
  99. struct snd_pcm_runtime *runtime = substream->runtime;
  100. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  101. struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
  102. struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
  103. int bytes = runtime->period_size *
  104. runtime->channels * samples_to_bytes(runtime, 1);
  105. unsigned long flags;
  106. int ret;
  107. ret = aiodma_ch_set_param(sub);
  108. if (ret)
  109. return ret;
  110. spin_lock_irqsave(&sub->lock, flags);
  111. ret = aiodma_rb_set_buffer(sub, runtime->dma_addr,
  112. runtime->dma_addr + runtime->dma_bytes,
  113. bytes);
  114. spin_unlock_irqrestore(&sub->lock, flags);
  115. if (ret)
  116. return ret;
  117. return 0;
  118. }
  119. static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd)
  120. {
  121. struct snd_pcm_runtime *runtime = substream->runtime;
  122. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  123. struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
  124. struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
  125. struct device *dev = &aio->chip->pdev->dev;
  126. int bytes = runtime->period_size *
  127. runtime->channels * samples_to_bytes(runtime, 1);
  128. unsigned long flags;
  129. spin_lock_irqsave(&sub->lock, flags);
  130. switch (cmd) {
  131. case SNDRV_PCM_TRIGGER_START:
  132. aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes,
  133. bytes);
  134. aiodma_ch_set_enable(sub, 1);
  135. sub->running = 1;
  136. break;
  137. case SNDRV_PCM_TRIGGER_STOP:
  138. sub->running = 0;
  139. aiodma_ch_set_enable(sub, 0);
  140. break;
  141. default:
  142. dev_warn(dev, "Unknown trigger(%d) ignored\n", cmd);
  143. break;
  144. }
  145. spin_unlock_irqrestore(&sub->lock, flags);
  146. return 0;
  147. }
  148. static snd_pcm_uframes_t uniphier_aiodma_pointer(
  149. struct snd_pcm_substream *substream)
  150. {
  151. struct snd_pcm_runtime *runtime = substream->runtime;
  152. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  153. struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
  154. struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
  155. int bytes = runtime->period_size *
  156. runtime->channels * samples_to_bytes(runtime, 1);
  157. unsigned long flags;
  158. snd_pcm_uframes_t pos;
  159. spin_lock_irqsave(&sub->lock, flags);
  160. aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
  161. if (sub->swm->dir == PORT_DIR_OUTPUT)
  162. pos = bytes_to_frames(runtime, sub->rd_offs);
  163. else
  164. pos = bytes_to_frames(runtime, sub->wr_offs);
  165. spin_unlock_irqrestore(&sub->lock, flags);
  166. return pos;
  167. }
  168. static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream,
  169. struct vm_area_struct *vma)
  170. {
  171. vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
  172. return remap_pfn_range(vma, vma->vm_start,
  173. substream->dma_buffer.addr >> PAGE_SHIFT,
  174. vma->vm_end - vma->vm_start, vma->vm_page_prot);
  175. }
  176. static const struct snd_pcm_ops uniphier_aiodma_ops = {
  177. .open = uniphier_aiodma_open,
  178. .ioctl = snd_pcm_lib_ioctl,
  179. .hw_params = uniphier_aiodma_hw_params,
  180. .hw_free = uniphier_aiodma_hw_free,
  181. .prepare = uniphier_aiodma_prepare,
  182. .trigger = uniphier_aiodma_trigger,
  183. .pointer = uniphier_aiodma_pointer,
  184. .mmap = uniphier_aiodma_mmap,
  185. };
  186. static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd)
  187. {
  188. struct device *dev = rtd->card->snd_card->dev;
  189. struct snd_pcm *pcm = rtd->pcm;
  190. int ret;
  191. ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
  192. if (ret)
  193. return ret;
  194. return snd_pcm_lib_preallocate_pages_for_all(pcm,
  195. SNDRV_DMA_TYPE_DEV, dev,
  196. uniphier_aiodma_hw.buffer_bytes_max,
  197. uniphier_aiodma_hw.buffer_bytes_max);
  198. }
  199. static void uniphier_aiodma_free(struct snd_pcm *pcm)
  200. {
  201. snd_pcm_lib_preallocate_free_for_all(pcm);
  202. }
  203. static const struct snd_soc_component_driver uniphier_soc_platform = {
  204. .pcm_new = uniphier_aiodma_new,
  205. .pcm_free = uniphier_aiodma_free,
  206. .ops = &uniphier_aiodma_ops,
  207. .compr_ops = &uniphier_aio_compr_ops,
  208. };
  209. static const struct regmap_config aiodma_regmap_config = {
  210. .reg_bits = 32,
  211. .reg_stride = 4,
  212. .val_bits = 32,
  213. .max_register = 0x7fffc,
  214. .cache_type = REGCACHE_NONE,
  215. };
  216. /**
  217. * uniphier_aiodma_soc_register_platform - register the AIO DMA
  218. * @pdev: the platform device
  219. *
  220. * Register and setup the DMA of AIO to transfer the sound data to device.
  221. * This function need to call once at driver startup and need NOT to call
  222. * unregister function.
  223. *
  224. * Return: Zero if successful, otherwise a negative value on error.
  225. */
  226. int uniphier_aiodma_soc_register_platform(struct platform_device *pdev)
  227. {
  228. struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
  229. struct device *dev = &pdev->dev;
  230. struct resource *res;
  231. void __iomem *preg;
  232. int irq, ret;
  233. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  234. preg = devm_ioremap_resource(dev, res);
  235. if (IS_ERR(preg))
  236. return PTR_ERR(preg);
  237. chip->regmap = devm_regmap_init_mmio(dev, preg,
  238. &aiodma_regmap_config);
  239. if (IS_ERR(chip->regmap))
  240. return PTR_ERR(chip->regmap);
  241. irq = platform_get_irq(pdev, 0);
  242. if (irq < 0) {
  243. dev_err(dev, "Could not get irq.\n");
  244. return irq;
  245. }
  246. ret = devm_request_irq(dev, irq, aiodma_irq,
  247. IRQF_SHARED, dev_name(dev), pdev);
  248. if (ret)
  249. return ret;
  250. return devm_snd_soc_register_component(dev, &uniphier_soc_platform,
  251. NULL, 0);
  252. }
  253. EXPORT_SYMBOL_GPL(uniphier_aiodma_soc_register_platform);