mmp-pcm.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. * linux/sound/soc/pxa/mmp-pcm.c
  3. *
  4. * Copyright (C) 2011 Marvell International Ltd.
  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; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. */
  12. #include <linux/module.h>
  13. #include <linux/init.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/slab.h>
  16. #include <linux/dma-mapping.h>
  17. #include <linux/dmaengine.h>
  18. #include <linux/platform_data/dma-mmp_tdma.h>
  19. #include <linux/platform_data/mmp_audio.h>
  20. #include <sound/pxa2xx-lib.h>
  21. #include <sound/core.h>
  22. #include <sound/pcm.h>
  23. #include <sound/pcm_params.h>
  24. #include <sound/soc.h>
  25. #include <sound/dmaengine_pcm.h>
  26. struct mmp_dma_data {
  27. int ssp_id;
  28. struct resource *dma_res;
  29. };
  30. #define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
  31. SNDRV_PCM_INFO_MMAP_VALID | \
  32. SNDRV_PCM_INFO_INTERLEAVED | \
  33. SNDRV_PCM_INFO_PAUSE | \
  34. SNDRV_PCM_INFO_RESUME)
  35. #define MMP_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
  36. SNDRV_PCM_FMTBIT_S24_LE | \
  37. SNDRV_PCM_FMTBIT_S32_LE)
  38. static struct snd_pcm_hardware mmp_pcm_hardware[] = {
  39. {
  40. .info = MMP_PCM_INFO,
  41. .formats = MMP_PCM_FORMATS,
  42. .period_bytes_min = 1024,
  43. .period_bytes_max = 2048,
  44. .periods_min = 2,
  45. .periods_max = 32,
  46. .buffer_bytes_max = 4096,
  47. .fifo_size = 32,
  48. },
  49. {
  50. .info = MMP_PCM_INFO,
  51. .formats = MMP_PCM_FORMATS,
  52. .period_bytes_min = 1024,
  53. .period_bytes_max = 2048,
  54. .periods_min = 2,
  55. .periods_max = 32,
  56. .buffer_bytes_max = 4096,
  57. .fifo_size = 32,
  58. },
  59. };
  60. static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
  61. struct snd_pcm_hw_params *params)
  62. {
  63. struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  64. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  65. struct pxa2xx_pcm_dma_params *dma_params;
  66. struct dma_slave_config slave_config;
  67. int ret;
  68. dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  69. if (!dma_params)
  70. return 0;
  71. ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
  72. if (ret)
  73. return ret;
  74. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  75. slave_config.dst_addr = dma_params->dev_addr;
  76. slave_config.dst_maxburst = 4;
  77. } else {
  78. slave_config.src_addr = dma_params->dev_addr;
  79. slave_config.src_maxburst = 4;
  80. }
  81. ret = dmaengine_slave_config(chan, &slave_config);
  82. if (ret)
  83. return ret;
  84. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  85. return 0;
  86. }
  87. static bool filter(struct dma_chan *chan, void *param)
  88. {
  89. struct mmp_dma_data *dma_data = param;
  90. bool found = false;
  91. char *devname;
  92. devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
  93. dma_data->ssp_id);
  94. if ((strcmp(dev_name(chan->device->dev), devname) == 0) &&
  95. (chan->chan_id == dma_data->dma_res->start)) {
  96. found = true;
  97. }
  98. kfree(devname);
  99. return found;
  100. }
  101. static int mmp_pcm_open(struct snd_pcm_substream *substream)
  102. {
  103. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  104. struct platform_device *pdev = to_platform_device(rtd->platform->dev);
  105. struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  106. struct mmp_dma_data *dma_data;
  107. struct resource *r;
  108. int ret;
  109. r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
  110. if (!r)
  111. return -EBUSY;
  112. snd_soc_set_runtime_hwparams(substream,
  113. &mmp_pcm_hardware[substream->stream]);
  114. dma_data = devm_kzalloc(&pdev->dev,
  115. sizeof(struct mmp_dma_data), GFP_KERNEL);
  116. if (dma_data == NULL)
  117. return -ENOMEM;
  118. dma_data->dma_res = r;
  119. dma_data->ssp_id = cpu_dai->id;
  120. ret = snd_dmaengine_pcm_open(substream, filter, dma_data);
  121. if (ret) {
  122. devm_kfree(&pdev->dev, dma_data);
  123. return ret;
  124. }
  125. snd_dmaengine_pcm_set_data(substream, dma_data);
  126. return 0;
  127. }
  128. static int mmp_pcm_close(struct snd_pcm_substream *substream)
  129. {
  130. struct mmp_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
  131. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  132. struct platform_device *pdev = to_platform_device(rtd->platform->dev);
  133. snd_dmaengine_pcm_close(substream);
  134. devm_kfree(&pdev->dev, dma_data);
  135. return 0;
  136. }
  137. static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
  138. struct vm_area_struct *vma)
  139. {
  140. struct snd_pcm_runtime *runtime = substream->runtime;
  141. unsigned long off = vma->vm_pgoff;
  142. vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
  143. return remap_pfn_range(vma, vma->vm_start,
  144. __phys_to_pfn(runtime->dma_addr) + off,
  145. vma->vm_end - vma->vm_start, vma->vm_page_prot);
  146. }
  147. struct snd_pcm_ops mmp_pcm_ops = {
  148. .open = mmp_pcm_open,
  149. .close = mmp_pcm_close,
  150. .ioctl = snd_pcm_lib_ioctl,
  151. .hw_params = mmp_pcm_hw_params,
  152. .trigger = snd_dmaengine_pcm_trigger,
  153. .pointer = snd_dmaengine_pcm_pointer,
  154. .mmap = mmp_pcm_mmap,
  155. };
  156. static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
  157. {
  158. struct snd_pcm_substream *substream;
  159. struct snd_dma_buffer *buf;
  160. int stream;
  161. struct gen_pool *gpool;
  162. gpool = sram_get_gpool("asram");
  163. if (!gpool)
  164. return;
  165. for (stream = 0; stream < 2; stream++) {
  166. size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
  167. substream = pcm->streams[stream].substream;
  168. if (!substream)
  169. continue;
  170. buf = &substream->dma_buffer;
  171. if (!buf->area)
  172. continue;
  173. gen_pool_free(gpool, (unsigned long)buf->area, size);
  174. buf->area = NULL;
  175. }
  176. return;
  177. }
  178. static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
  179. int stream)
  180. {
  181. struct snd_dma_buffer *buf = &substream->dma_buffer;
  182. size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
  183. struct gen_pool *gpool;
  184. buf->dev.type = SNDRV_DMA_TYPE_DEV;
  185. buf->dev.dev = substream->pcm->card->dev;
  186. buf->private_data = NULL;
  187. gpool = sram_get_gpool("asram");
  188. if (!gpool)
  189. return -ENOMEM;
  190. buf->area = (unsigned char *)gen_pool_alloc(gpool, size);
  191. if (!buf->area)
  192. return -ENOMEM;
  193. buf->addr = gen_pool_virt_to_phys(gpool, (unsigned long)buf->area);
  194. buf->bytes = size;
  195. return 0;
  196. }
  197. int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
  198. {
  199. struct snd_pcm_substream *substream;
  200. struct snd_pcm *pcm = rtd->pcm;
  201. int ret = 0, stream;
  202. for (stream = 0; stream < 2; stream++) {
  203. substream = pcm->streams[stream].substream;
  204. ret = mmp_pcm_preallocate_dma_buffer(substream, stream);
  205. if (ret)
  206. goto err;
  207. }
  208. return 0;
  209. err:
  210. mmp_pcm_free_dma_buffers(pcm);
  211. return ret;
  212. }
  213. struct snd_soc_platform_driver mmp_soc_platform = {
  214. .ops = &mmp_pcm_ops,
  215. .pcm_new = mmp_pcm_new,
  216. .pcm_free = mmp_pcm_free_dma_buffers,
  217. };
  218. static int mmp_pcm_probe(struct platform_device *pdev)
  219. {
  220. struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
  221. if (pdata) {
  222. mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
  223. pdata->buffer_max_playback;
  224. mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
  225. pdata->period_max_playback;
  226. mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
  227. pdata->buffer_max_capture;
  228. mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
  229. pdata->period_max_capture;
  230. }
  231. return snd_soc_register_platform(&pdev->dev, &mmp_soc_platform);
  232. }
  233. static int mmp_pcm_remove(struct platform_device *pdev)
  234. {
  235. snd_soc_unregister_platform(&pdev->dev);
  236. return 0;
  237. }
  238. static struct platform_driver mmp_pcm_driver = {
  239. .driver = {
  240. .name = "mmp-pcm-audio",
  241. .owner = THIS_MODULE,
  242. },
  243. .probe = mmp_pcm_probe,
  244. .remove = mmp_pcm_remove,
  245. };
  246. module_platform_driver(mmp_pcm_driver);
  247. MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
  248. MODULE_DESCRIPTION("MMP Soc Audio DMA module");
  249. MODULE_LICENSE("GPL");