mmp-pcm.c 6.2 KB

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