tegra_pcm.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*
  2. * tegra_pcm.c - Tegra PCM driver
  3. *
  4. * Author: Stephen Warren <swarren@nvidia.com>
  5. * Copyright (C) 2010,2012 - NVIDIA, Inc.
  6. *
  7. * Based on code copyright/by:
  8. *
  9. * Copyright (c) 2009-2010, NVIDIA Corporation.
  10. * Scott Peterson <speterson@nvidia.com>
  11. * Vijay Mali <vmali@nvidia.com>
  12. *
  13. * Copyright (C) 2010 Google, Inc.
  14. * Iliyan Malchev <malchev@google.com>
  15. *
  16. * This program is free software; you can redistribute it and/or
  17. * modify it under the terms of the GNU General Public License
  18. * version 2 as published by the Free Software Foundation.
  19. *
  20. * This program is distributed in the hope that it will be useful, but
  21. * WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  23. * General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program; if not, write to the Free Software
  27. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  28. * 02110-1301 USA
  29. *
  30. */
  31. #include <linux/dma-mapping.h>
  32. #include <linux/module.h>
  33. #include <linux/slab.h>
  34. #include <sound/core.h>
  35. #include <sound/pcm.h>
  36. #include <sound/pcm_params.h>
  37. #include <sound/soc.h>
  38. #include <sound/dmaengine_pcm.h>
  39. #include "tegra_pcm.h"
  40. static const struct snd_pcm_hardware tegra_pcm_hardware = {
  41. .info = SNDRV_PCM_INFO_MMAP |
  42. SNDRV_PCM_INFO_MMAP_VALID |
  43. SNDRV_PCM_INFO_INTERLEAVED,
  44. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  45. .channels_min = 2,
  46. .channels_max = 2,
  47. .period_bytes_min = 1024,
  48. .period_bytes_max = PAGE_SIZE,
  49. .periods_min = 2,
  50. .periods_max = 8,
  51. .buffer_bytes_max = PAGE_SIZE * 8,
  52. .fifo_size = 4,
  53. };
  54. static int tegra_pcm_open(struct snd_pcm_substream *substream)
  55. {
  56. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  57. struct device *dev = rtd->platform->dev;
  58. int ret;
  59. /* Set HW params now that initialization is complete */
  60. snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
  61. ret = snd_dmaengine_pcm_open(substream, NULL, NULL);
  62. if (ret) {
  63. dev_err(dev, "dmaengine pcm open failed with err %d\n", ret);
  64. return ret;
  65. }
  66. return 0;
  67. }
  68. static int tegra_pcm_close(struct snd_pcm_substream *substream)
  69. {
  70. snd_dmaengine_pcm_close(substream);
  71. return 0;
  72. }
  73. static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
  74. struct snd_pcm_hw_params *params)
  75. {
  76. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  77. struct device *dev = rtd->platform->dev;
  78. struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  79. struct tegra_pcm_dma_params *dmap;
  80. struct dma_slave_config slave_config;
  81. int ret;
  82. dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  83. ret = snd_hwparams_to_dma_slave_config(substream, params,
  84. &slave_config);
  85. if (ret) {
  86. dev_err(dev, "hw params config failed with err %d\n", ret);
  87. return ret;
  88. }
  89. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  90. slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
  91. slave_config.dst_addr = dmap->addr;
  92. slave_config.dst_maxburst = 4;
  93. } else {
  94. slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
  95. slave_config.src_addr = dmap->addr;
  96. slave_config.src_maxburst = 4;
  97. }
  98. slave_config.slave_id = dmap->req_sel;
  99. ret = dmaengine_slave_config(chan, &slave_config);
  100. if (ret < 0) {
  101. dev_err(dev, "dma slave config failed with err %d\n", ret);
  102. return ret;
  103. }
  104. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  105. return 0;
  106. }
  107. static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
  108. {
  109. snd_pcm_set_runtime_buffer(substream, NULL);
  110. return 0;
  111. }
  112. static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
  113. struct vm_area_struct *vma)
  114. {
  115. struct snd_pcm_runtime *runtime = substream->runtime;
  116. return dma_mmap_writecombine(substream->pcm->card->dev, vma,
  117. runtime->dma_area,
  118. runtime->dma_addr,
  119. runtime->dma_bytes);
  120. }
  121. static struct snd_pcm_ops tegra_pcm_ops = {
  122. .open = tegra_pcm_open,
  123. .close = tegra_pcm_close,
  124. .ioctl = snd_pcm_lib_ioctl,
  125. .hw_params = tegra_pcm_hw_params,
  126. .hw_free = tegra_pcm_hw_free,
  127. .trigger = snd_dmaengine_pcm_trigger,
  128. .pointer = snd_dmaengine_pcm_pointer,
  129. .mmap = tegra_pcm_mmap,
  130. };
  131. static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
  132. {
  133. struct snd_pcm_substream *substream = pcm->streams[stream].substream;
  134. struct snd_dma_buffer *buf = &substream->dma_buffer;
  135. size_t size = tegra_pcm_hardware.buffer_bytes_max;
  136. buf->area = dma_alloc_writecombine(pcm->card->dev, size,
  137. &buf->addr, GFP_KERNEL);
  138. if (!buf->area)
  139. return -ENOMEM;
  140. buf->dev.type = SNDRV_DMA_TYPE_DEV;
  141. buf->dev.dev = pcm->card->dev;
  142. buf->private_data = NULL;
  143. buf->bytes = size;
  144. return 0;
  145. }
  146. static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
  147. {
  148. struct snd_pcm_substream *substream;
  149. struct snd_dma_buffer *buf;
  150. substream = pcm->streams[stream].substream;
  151. if (!substream)
  152. return;
  153. buf = &substream->dma_buffer;
  154. if (!buf->area)
  155. return;
  156. dma_free_writecombine(pcm->card->dev, buf->bytes,
  157. buf->area, buf->addr);
  158. buf->area = NULL;
  159. }
  160. static u64 tegra_dma_mask = DMA_BIT_MASK(32);
  161. static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
  162. {
  163. struct snd_card *card = rtd->card->snd_card;
  164. struct snd_pcm *pcm = rtd->pcm;
  165. int ret = 0;
  166. if (!card->dev->dma_mask)
  167. card->dev->dma_mask = &tegra_dma_mask;
  168. if (!card->dev->coherent_dma_mask)
  169. card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
  170. if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
  171. ret = tegra_pcm_preallocate_dma_buffer(pcm,
  172. SNDRV_PCM_STREAM_PLAYBACK);
  173. if (ret)
  174. goto err;
  175. }
  176. if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
  177. ret = tegra_pcm_preallocate_dma_buffer(pcm,
  178. SNDRV_PCM_STREAM_CAPTURE);
  179. if (ret)
  180. goto err_free_play;
  181. }
  182. return 0;
  183. err_free_play:
  184. tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
  185. err:
  186. return ret;
  187. }
  188. static void tegra_pcm_free(struct snd_pcm *pcm)
  189. {
  190. tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
  191. tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
  192. }
  193. static struct snd_soc_platform_driver tegra_pcm_platform = {
  194. .ops = &tegra_pcm_ops,
  195. .pcm_new = tegra_pcm_new,
  196. .pcm_free = tegra_pcm_free,
  197. };
  198. int tegra_pcm_platform_register(struct device *dev)
  199. {
  200. return snd_soc_register_platform(dev, &tegra_pcm_platform);
  201. }
  202. EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
  203. void tegra_pcm_platform_unregister(struct device *dev)
  204. {
  205. snd_soc_unregister_platform(dev);
  206. }
  207. EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
  208. MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
  209. MODULE_DESCRIPTION("Tegra PCM ASoC driver");
  210. MODULE_LICENSE("GPL");