mmp-pcm.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 snd_dmaengine_dai_dma_data *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->addr;
  76. slave_config.dst_maxburst = 4;
  77. } else {
  78. slave_config.src_addr = dma_params->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. r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
  109. if (!r)
  110. return -EBUSY;
  111. snd_soc_set_runtime_hwparams(substream,
  112. &mmp_pcm_hardware[substream->stream]);
  113. dma_data.dma_res = r;
  114. dma_data.ssp_id = cpu_dai->id;
  115. return snd_dmaengine_pcm_open_request_chan(substream, filter,
  116. &dma_data);
  117. }
  118. static int mmp_pcm_mmap(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 struct snd_pcm_ops mmp_pcm_ops = {
  129. .open = mmp_pcm_open,
  130. .close = snd_dmaengine_pcm_close_release_chan,
  131. .ioctl = snd_pcm_lib_ioctl,
  132. .hw_params = mmp_pcm_hw_params,
  133. .trigger = snd_dmaengine_pcm_trigger,
  134. .pointer = snd_dmaengine_pcm_pointer,
  135. .mmap = mmp_pcm_mmap,
  136. };
  137. static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
  138. {
  139. struct snd_pcm_substream *substream;
  140. struct snd_dma_buffer *buf;
  141. int stream;
  142. struct gen_pool *gpool;
  143. gpool = sram_get_gpool("asram");
  144. if (!gpool)
  145. return;
  146. for (stream = 0; stream < 2; stream++) {
  147. size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
  148. substream = pcm->streams[stream].substream;
  149. if (!substream)
  150. continue;
  151. buf = &substream->dma_buffer;
  152. if (!buf->area)
  153. continue;
  154. gen_pool_free(gpool, (unsigned long)buf->area, size);
  155. buf->area = NULL;
  156. }
  157. return;
  158. }
  159. static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
  160. int stream)
  161. {
  162. struct snd_dma_buffer *buf = &substream->dma_buffer;
  163. size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
  164. struct gen_pool *gpool;
  165. buf->dev.type = SNDRV_DMA_TYPE_DEV;
  166. buf->dev.dev = substream->pcm->card->dev;
  167. buf->private_data = NULL;
  168. gpool = sram_get_gpool("asram");
  169. if (!gpool)
  170. return -ENOMEM;
  171. buf->area = (unsigned char *)gen_pool_alloc(gpool, size);
  172. if (!buf->area)
  173. return -ENOMEM;
  174. buf->addr = gen_pool_virt_to_phys(gpool, (unsigned long)buf->area);
  175. buf->bytes = size;
  176. return 0;
  177. }
  178. static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
  179. {
  180. struct snd_pcm_substream *substream;
  181. struct snd_pcm *pcm = rtd->pcm;
  182. int ret = 0, stream;
  183. for (stream = 0; stream < 2; stream++) {
  184. substream = pcm->streams[stream].substream;
  185. ret = mmp_pcm_preallocate_dma_buffer(substream, stream);
  186. if (ret)
  187. goto err;
  188. }
  189. return 0;
  190. err:
  191. mmp_pcm_free_dma_buffers(pcm);
  192. return ret;
  193. }
  194. static struct snd_soc_platform_driver mmp_soc_platform = {
  195. .ops = &mmp_pcm_ops,
  196. .pcm_new = mmp_pcm_new,
  197. .pcm_free = mmp_pcm_free_dma_buffers,
  198. };
  199. static int mmp_pcm_probe(struct platform_device *pdev)
  200. {
  201. struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
  202. if (pdata) {
  203. mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
  204. pdata->buffer_max_playback;
  205. mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
  206. pdata->period_max_playback;
  207. mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
  208. pdata->buffer_max_capture;
  209. mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
  210. pdata->period_max_capture;
  211. }
  212. return snd_soc_register_platform(&pdev->dev, &mmp_soc_platform);
  213. }
  214. static int mmp_pcm_remove(struct platform_device *pdev)
  215. {
  216. snd_soc_unregister_platform(&pdev->dev);
  217. return 0;
  218. }
  219. static struct platform_driver mmp_pcm_driver = {
  220. .driver = {
  221. .name = "mmp-pcm-audio",
  222. .owner = THIS_MODULE,
  223. },
  224. .probe = mmp_pcm_probe,
  225. .remove = mmp_pcm_remove,
  226. };
  227. module_platform_driver(mmp_pcm_driver);
  228. MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
  229. MODULE_DESCRIPTION("MMP Soc Audio DMA module");
  230. MODULE_LICENSE("GPL");