omap-pcm.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * omap-pcm.c -- ALSA PCM interface for the OMAP SoC
  3. *
  4. * Copyright (C) 2008 Nokia Corporation
  5. *
  6. * Contact: Jarkko Nikula <jhnikula@gmail.com>
  7. * Peter Ujfalusi <peter.ujfalusi@nokia.com>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * version 2 as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21. * 02110-1301 USA
  22. *
  23. */
  24. #include <linux/dma-mapping.h>
  25. #include <sound/core.h>
  26. #include <sound/pcm.h>
  27. #include <sound/pcm_params.h>
  28. #include <sound/soc.h>
  29. #include <mach/dma.h>
  30. #include "omap-pcm.h"
  31. static const struct snd_pcm_hardware omap_pcm_hardware = {
  32. .info = SNDRV_PCM_INFO_MMAP |
  33. SNDRV_PCM_INFO_MMAP_VALID |
  34. SNDRV_PCM_INFO_INTERLEAVED |
  35. SNDRV_PCM_INFO_PAUSE |
  36. SNDRV_PCM_INFO_RESUME,
  37. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  38. .period_bytes_min = 32,
  39. .period_bytes_max = 64 * 1024,
  40. .periods_min = 2,
  41. .periods_max = 255,
  42. .buffer_bytes_max = 128 * 1024,
  43. };
  44. struct omap_runtime_data {
  45. spinlock_t lock;
  46. struct omap_pcm_dma_data *dma_data;
  47. int dma_ch;
  48. int period_index;
  49. };
  50. static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
  51. {
  52. struct snd_pcm_substream *substream = data;
  53. struct snd_pcm_runtime *runtime = substream->runtime;
  54. struct omap_runtime_data *prtd = runtime->private_data;
  55. unsigned long flags;
  56. if ((cpu_is_omap1510()) &&
  57. (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) {
  58. /*
  59. * OMAP1510 doesn't fully support DMA progress counter
  60. * and there is no software emulation implemented yet,
  61. * so have to maintain our own playback progress counter
  62. * that can be used by omap_pcm_pointer() instead.
  63. */
  64. spin_lock_irqsave(&prtd->lock, flags);
  65. if (prtd->period_index >= 0) {
  66. if (++prtd->period_index == runtime->periods) {
  67. prtd->period_index = 0;
  68. }
  69. }
  70. spin_unlock_irqrestore(&prtd->lock, flags);
  71. }
  72. snd_pcm_period_elapsed(substream);
  73. }
  74. /* this may get called several times by oss emulation */
  75. static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
  76. struct snd_pcm_hw_params *params)
  77. {
  78. struct snd_pcm_runtime *runtime = substream->runtime;
  79. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  80. struct omap_runtime_data *prtd = runtime->private_data;
  81. struct omap_pcm_dma_data *dma_data = rtd->dai->cpu_dai->dma_data;
  82. int err = 0;
  83. /* return if this is a bufferless transfer e.g.
  84. * codec <--> BT codec or GSM modem -- lg FIXME */
  85. if (!dma_data)
  86. return 0;
  87. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  88. runtime->dma_bytes = params_buffer_bytes(params);
  89. if (prtd->dma_data)
  90. return 0;
  91. prtd->dma_data = dma_data;
  92. err = omap_request_dma(dma_data->dma_req, dma_data->name,
  93. omap_pcm_dma_irq, substream, &prtd->dma_ch);
  94. if (!err) {
  95. /*
  96. * Link channel with itself so DMA doesn't need any
  97. * reprogramming while looping the buffer
  98. */
  99. omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
  100. }
  101. return err;
  102. }
  103. static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
  104. {
  105. struct snd_pcm_runtime *runtime = substream->runtime;
  106. struct omap_runtime_data *prtd = runtime->private_data;
  107. if (prtd->dma_data == NULL)
  108. return 0;
  109. omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
  110. omap_free_dma(prtd->dma_ch);
  111. prtd->dma_data = NULL;
  112. snd_pcm_set_runtime_buffer(substream, NULL);
  113. return 0;
  114. }
  115. static int omap_pcm_prepare(struct snd_pcm_substream *substream)
  116. {
  117. struct snd_pcm_runtime *runtime = substream->runtime;
  118. struct omap_runtime_data *prtd = runtime->private_data;
  119. struct omap_pcm_dma_data *dma_data = prtd->dma_data;
  120. struct omap_dma_channel_params dma_params;
  121. /* return if this is a bufferless transfer e.g.
  122. * codec <--> BT codec or GSM modem -- lg FIXME */
  123. if (!prtd->dma_data)
  124. return 0;
  125. memset(&dma_params, 0, sizeof(dma_params));
  126. /*
  127. * Note: Regardless of interface data formats supported by OMAP McBSP
  128. * or EAC blocks, internal representation is always fixed 16-bit/sample
  129. */
  130. dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
  131. dma_params.trigger = dma_data->dma_req;
  132. dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT;
  133. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  134. dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
  135. dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
  136. dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
  137. dma_params.src_start = runtime->dma_addr;
  138. dma_params.dst_start = dma_data->port_addr;
  139. dma_params.dst_port = OMAP_DMA_PORT_MPUI;
  140. } else {
  141. dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
  142. dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
  143. dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
  144. dma_params.src_start = dma_data->port_addr;
  145. dma_params.dst_start = runtime->dma_addr;
  146. dma_params.src_port = OMAP_DMA_PORT_MPUI;
  147. }
  148. /*
  149. * Set DMA transfer frame size equal to ALSA period size and frame
  150. * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
  151. * we can transfer the whole ALSA buffer with single DMA transfer but
  152. * still can get an interrupt at each period bounary
  153. */
  154. dma_params.elem_count = snd_pcm_lib_period_bytes(substream) / 2;
  155. dma_params.frame_count = runtime->periods;
  156. omap_set_dma_params(prtd->dma_ch, &dma_params);
  157. omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
  158. return 0;
  159. }
  160. static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  161. {
  162. struct snd_pcm_runtime *runtime = substream->runtime;
  163. struct omap_runtime_data *prtd = runtime->private_data;
  164. unsigned long flags;
  165. int ret = 0;
  166. spin_lock_irqsave(&prtd->lock, flags);
  167. switch (cmd) {
  168. case SNDRV_PCM_TRIGGER_START:
  169. case SNDRV_PCM_TRIGGER_RESUME:
  170. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  171. prtd->period_index = 0;
  172. omap_start_dma(prtd->dma_ch);
  173. break;
  174. case SNDRV_PCM_TRIGGER_STOP:
  175. case SNDRV_PCM_TRIGGER_SUSPEND:
  176. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  177. prtd->period_index = -1;
  178. omap_stop_dma(prtd->dma_ch);
  179. break;
  180. default:
  181. ret = -EINVAL;
  182. }
  183. spin_unlock_irqrestore(&prtd->lock, flags);
  184. return ret;
  185. }
  186. static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
  187. {
  188. struct snd_pcm_runtime *runtime = substream->runtime;
  189. struct omap_runtime_data *prtd = runtime->private_data;
  190. dma_addr_t ptr;
  191. snd_pcm_uframes_t offset;
  192. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  193. ptr = omap_get_dma_dst_pos(prtd->dma_ch);
  194. offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
  195. } else if (!(cpu_is_omap1510())) {
  196. ptr = omap_get_dma_src_pos(prtd->dma_ch);
  197. offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
  198. } else
  199. offset = prtd->period_index * runtime->period_size;
  200. if (offset >= runtime->buffer_size)
  201. offset = 0;
  202. return offset;
  203. }
  204. static int omap_pcm_open(struct snd_pcm_substream *substream)
  205. {
  206. struct snd_pcm_runtime *runtime = substream->runtime;
  207. struct omap_runtime_data *prtd;
  208. int ret;
  209. snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
  210. /* Ensure that buffer size is a multiple of period size */
  211. ret = snd_pcm_hw_constraint_integer(runtime,
  212. SNDRV_PCM_HW_PARAM_PERIODS);
  213. if (ret < 0)
  214. goto out;
  215. prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
  216. if (prtd == NULL) {
  217. ret = -ENOMEM;
  218. goto out;
  219. }
  220. spin_lock_init(&prtd->lock);
  221. runtime->private_data = prtd;
  222. out:
  223. return ret;
  224. }
  225. static int omap_pcm_close(struct snd_pcm_substream *substream)
  226. {
  227. struct snd_pcm_runtime *runtime = substream->runtime;
  228. kfree(runtime->private_data);
  229. return 0;
  230. }
  231. static int omap_pcm_mmap(struct snd_pcm_substream *substream,
  232. struct vm_area_struct *vma)
  233. {
  234. struct snd_pcm_runtime *runtime = substream->runtime;
  235. return dma_mmap_writecombine(substream->pcm->card->dev, vma,
  236. runtime->dma_area,
  237. runtime->dma_addr,
  238. runtime->dma_bytes);
  239. }
  240. static struct snd_pcm_ops omap_pcm_ops = {
  241. .open = omap_pcm_open,
  242. .close = omap_pcm_close,
  243. .ioctl = snd_pcm_lib_ioctl,
  244. .hw_params = omap_pcm_hw_params,
  245. .hw_free = omap_pcm_hw_free,
  246. .prepare = omap_pcm_prepare,
  247. .trigger = omap_pcm_trigger,
  248. .pointer = omap_pcm_pointer,
  249. .mmap = omap_pcm_mmap,
  250. };
  251. static u64 omap_pcm_dmamask = DMA_BIT_MASK(32);
  252. static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
  253. int stream)
  254. {
  255. struct snd_pcm_substream *substream = pcm->streams[stream].substream;
  256. struct snd_dma_buffer *buf = &substream->dma_buffer;
  257. size_t size = omap_pcm_hardware.buffer_bytes_max;
  258. buf->dev.type = SNDRV_DMA_TYPE_DEV;
  259. buf->dev.dev = pcm->card->dev;
  260. buf->private_data = NULL;
  261. buf->area = dma_alloc_writecombine(pcm->card->dev, size,
  262. &buf->addr, GFP_KERNEL);
  263. if (!buf->area)
  264. return -ENOMEM;
  265. buf->bytes = size;
  266. return 0;
  267. }
  268. static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
  269. {
  270. struct snd_pcm_substream *substream;
  271. struct snd_dma_buffer *buf;
  272. int stream;
  273. for (stream = 0; stream < 2; stream++) {
  274. substream = pcm->streams[stream].substream;
  275. if (!substream)
  276. continue;
  277. buf = &substream->dma_buffer;
  278. if (!buf->area)
  279. continue;
  280. dma_free_writecombine(pcm->card->dev, buf->bytes,
  281. buf->area, buf->addr);
  282. buf->area = NULL;
  283. }
  284. }
  285. static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
  286. struct snd_pcm *pcm)
  287. {
  288. int ret = 0;
  289. if (!card->dev->dma_mask)
  290. card->dev->dma_mask = &omap_pcm_dmamask;
  291. if (!card->dev->coherent_dma_mask)
  292. card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
  293. if (dai->playback.channels_min) {
  294. ret = omap_pcm_preallocate_dma_buffer(pcm,
  295. SNDRV_PCM_STREAM_PLAYBACK);
  296. if (ret)
  297. goto out;
  298. }
  299. if (dai->capture.channels_min) {
  300. ret = omap_pcm_preallocate_dma_buffer(pcm,
  301. SNDRV_PCM_STREAM_CAPTURE);
  302. if (ret)
  303. goto out;
  304. }
  305. out:
  306. return ret;
  307. }
  308. struct snd_soc_platform omap_soc_platform = {
  309. .name = "omap-pcm-audio",
  310. .pcm_ops = &omap_pcm_ops,
  311. .pcm_new = omap_pcm_new,
  312. .pcm_free = omap_pcm_free_dma_buffers,
  313. };
  314. EXPORT_SYMBOL_GPL(omap_soc_platform);
  315. static int __init omap_soc_platform_init(void)
  316. {
  317. return snd_soc_register_platform(&omap_soc_platform);
  318. }
  319. module_init(omap_soc_platform_init);
  320. static void __exit omap_soc_platform_exit(void)
  321. {
  322. snd_soc_unregister_platform(&omap_soc_platform);
  323. }
  324. module_exit(omap_soc_platform_exit);
  325. MODULE_AUTHOR("Jarkko Nikula <jhnikula@gmail.com>");
  326. MODULE_DESCRIPTION("OMAP PCM DMA module");
  327. MODULE_LICENSE("GPL");