mmp-pcm.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * linux/sound/soc/pxa/mmp-pcm.c
  4. *
  5. * Copyright (C) 2011 Marvell International Ltd.
  6. */
  7. #include <linux/module.h>
  8. #include <linux/init.h>
  9. #include <linux/platform_device.h>
  10. #include <linux/slab.h>
  11. #include <linux/dma-mapping.h>
  12. #include <linux/dmaengine.h>
  13. #include <linux/platform_data/dma-mmp_tdma.h>
  14. #include <linux/platform_data/mmp_audio.h>
  15. #include <sound/pxa2xx-lib.h>
  16. #include <sound/core.h>
  17. #include <sound/pcm.h>
  18. #include <sound/pcm_params.h>
  19. #include <sound/soc.h>
  20. #include <sound/dmaengine_pcm.h>
  21. #define DRV_NAME "mmp-pcm"
  22. struct mmp_dma_data {
  23. int ssp_id;
  24. struct resource *dma_res;
  25. };
  26. #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
  27. SNDRV_PCM_INFO_MMAP_VALID | \
  28. SNDRV_PCM_INFO_INTERLEAVED | \
  29. SNDRV_PCM_INFO_PAUSE | \
  30. SNDRV_PCM_INFO_RESUME | \
  31. SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
  32. static struct snd_pcm_hardware mmp_pcm_hardware[] = {
  33. {
  34. .info = MMP_PCM_INFO,
  35. .period_bytes_min = 1024,
  36. .period_bytes_max = 2048,
  37. .periods_min = 2,
  38. .periods_max = 32,
  39. .buffer_bytes_max = 4096,
  40. .fifo_size = 32,
  41. },
  42. {
  43. .info = MMP_PCM_INFO,
  44. .period_bytes_min = 1024,
  45. .period_bytes_max = 2048,
  46. .periods_min = 2,
  47. .periods_max = 32,
  48. .buffer_bytes_max = 4096,
  49. .fifo_size = 32,
  50. },
  51. };
  52. static int mmp_pcm_hw_params(struct snd_soc_component *component,
  53. struct snd_pcm_substream *substream,
  54. struct snd_pcm_hw_params *params)
  55. {
  56. struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  57. struct dma_slave_config slave_config;
  58. int ret;
  59. ret =
  60. snd_dmaengine_pcm_prepare_slave_config(substream, params,
  61. &slave_config);
  62. if (ret)
  63. return ret;
  64. ret = dmaengine_slave_config(chan, &slave_config);
  65. if (ret)
  66. return ret;
  67. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  68. return 0;
  69. }
  70. static int mmp_pcm_trigger(struct snd_soc_component *component,
  71. struct snd_pcm_substream *substream, int cmd)
  72. {
  73. return snd_dmaengine_pcm_trigger(substream, cmd);
  74. }
  75. static snd_pcm_uframes_t mmp_pcm_pointer(struct snd_soc_component *component,
  76. struct snd_pcm_substream *substream)
  77. {
  78. return snd_dmaengine_pcm_pointer(substream);
  79. }
  80. static bool filter(struct dma_chan *chan, void *param)
  81. {
  82. struct mmp_dma_data *dma_data = param;
  83. bool found = false;
  84. char *devname;
  85. devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
  86. dma_data->ssp_id);
  87. if (devname && (strcmp(dev_name(chan->device->dev), devname) == 0) &&
  88. (chan->chan_id == dma_data->dma_res->start)) {
  89. found = true;
  90. }
  91. kfree(devname);
  92. return found;
  93. }
  94. static int mmp_pcm_open(struct snd_soc_component *component,
  95. struct snd_pcm_substream *substream)
  96. {
  97. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  98. struct platform_device *pdev = to_platform_device(component->dev);
  99. struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
  100. struct mmp_dma_data dma_data;
  101. struct resource *r;
  102. r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
  103. if (!r)
  104. return -EBUSY;
  105. snd_soc_set_runtime_hwparams(substream,
  106. &mmp_pcm_hardware[substream->stream]);
  107. dma_data.dma_res = r;
  108. dma_data.ssp_id = cpu_dai->id;
  109. return snd_dmaengine_pcm_open_request_chan(substream, filter,
  110. &dma_data);
  111. }
  112. static int mmp_pcm_close(struct snd_soc_component *component,
  113. struct snd_pcm_substream *substream)
  114. {
  115. return snd_dmaengine_pcm_close_release_chan(substream);
  116. }
  117. static int mmp_pcm_mmap(struct snd_soc_component *component,
  118. struct snd_pcm_substream *substream,
  119. struct vm_area_struct *vma)
  120. {
  121. struct snd_pcm_runtime *runtime = substream->runtime;
  122. unsigned long off = vma->vm_pgoff;
  123. vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
  124. return remap_pfn_range(vma, vma->vm_start,
  125. __phys_to_pfn(runtime->dma_addr) + off,
  126. vma->vm_end - vma->vm_start, vma->vm_page_prot);
  127. }
  128. static void mmp_pcm_free_dma_buffers(struct snd_soc_component *component,
  129. struct snd_pcm *pcm)
  130. {
  131. struct snd_pcm_substream *substream;
  132. struct snd_dma_buffer *buf;
  133. int stream;
  134. struct gen_pool *gpool;
  135. gpool = sram_get_gpool("asram");
  136. if (!gpool)
  137. return;
  138. for (stream = 0; stream < 2; stream++) {
  139. size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
  140. substream = pcm->streams[stream].substream;
  141. if (!substream)
  142. continue;
  143. buf = &substream->dma_buffer;
  144. if (!buf->area)
  145. continue;
  146. gen_pool_free(gpool, (unsigned long)buf->area, size);
  147. buf->area = NULL;
  148. }
  149. }
  150. static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
  151. int stream)
  152. {
  153. struct snd_dma_buffer *buf = &substream->dma_buffer;
  154. size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
  155. struct gen_pool *gpool;
  156. buf->dev.type = SNDRV_DMA_TYPE_DEV;
  157. buf->dev.dev = substream->pcm->card->dev;
  158. buf->private_data = NULL;
  159. gpool = sram_get_gpool("asram");
  160. if (!gpool)
  161. return -ENOMEM;
  162. buf->area = gen_pool_dma_alloc(gpool, size, &buf->addr);
  163. if (!buf->area)
  164. return -ENOMEM;
  165. buf->bytes = size;
  166. return 0;
  167. }
  168. static int mmp_pcm_new(struct snd_soc_component *component,
  169. struct snd_soc_pcm_runtime *rtd)
  170. {
  171. struct snd_pcm_substream *substream;
  172. struct snd_pcm *pcm = rtd->pcm;
  173. int ret, stream;
  174. for (stream = 0; stream < 2; stream++) {
  175. substream = pcm->streams[stream].substream;
  176. ret = mmp_pcm_preallocate_dma_buffer(substream, stream);
  177. if (ret)
  178. goto err;
  179. }
  180. return 0;
  181. err:
  182. mmp_pcm_free_dma_buffers(component, pcm);
  183. return ret;
  184. }
  185. static const struct snd_soc_component_driver mmp_soc_component = {
  186. .name = DRV_NAME,
  187. .open = mmp_pcm_open,
  188. .close = mmp_pcm_close,
  189. .hw_params = mmp_pcm_hw_params,
  190. .trigger = mmp_pcm_trigger,
  191. .pointer = mmp_pcm_pointer,
  192. .mmap = mmp_pcm_mmap,
  193. .pcm_construct = mmp_pcm_new,
  194. .pcm_destruct = mmp_pcm_free_dma_buffers,
  195. };
  196. static int mmp_pcm_probe(struct platform_device *pdev)
  197. {
  198. struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
  199. if (pdata) {
  200. mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
  201. pdata->buffer_max_playback;
  202. mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
  203. pdata->period_max_playback;
  204. mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
  205. pdata->buffer_max_capture;
  206. mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
  207. pdata->period_max_capture;
  208. }
  209. return devm_snd_soc_register_component(&pdev->dev, &mmp_soc_component,
  210. NULL, 0);
  211. }
  212. static struct platform_driver mmp_pcm_driver = {
  213. .driver = {
  214. .name = "mmp-pcm-audio",
  215. },
  216. .probe = mmp_pcm_probe,
  217. };
  218. module_platform_driver(mmp_pcm_driver);
  219. MODULE_AUTHOR("Leo Yan <[email protected]>");
  220. MODULE_DESCRIPTION("MMP Soc Audio DMA module");
  221. MODULE_LICENSE("GPL");
  222. MODULE_ALIAS("platform:mmp-pcm-audio");