1a6d77317SDmitry Baryshkov /* 2a6d77317SDmitry Baryshkov * This program is free software; you can redistribute it and/or modify 3a6d77317SDmitry Baryshkov * it under the terms of the GNU General Public License version 2 as 4a6d77317SDmitry Baryshkov * published by the Free Software Foundation. 5a6d77317SDmitry Baryshkov */ 6a6d77317SDmitry Baryshkov 7*5a0e3ad6STejun Heo #include <linux/slab.h> 8a6d77317SDmitry Baryshkov #include <linux/module.h> 9a6d77317SDmitry Baryshkov #include <linux/dma-mapping.h> 10a6d77317SDmitry Baryshkov 11a6d77317SDmitry Baryshkov #include <sound/core.h> 12a6d77317SDmitry Baryshkov #include <sound/pcm.h> 13a6d77317SDmitry Baryshkov #include <sound/pcm_params.h> 14a6d77317SDmitry Baryshkov #include <sound/pxa2xx-lib.h> 15a6d77317SDmitry Baryshkov 165742964eSEric Miao #include <mach/dma.h> 17a6d77317SDmitry Baryshkov 18a6d77317SDmitry Baryshkov #include "pxa2xx-pcm.h" 19a6d77317SDmitry Baryshkov 20a6d77317SDmitry Baryshkov static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { 21a6d77317SDmitry Baryshkov .info = SNDRV_PCM_INFO_MMAP | 22a6d77317SDmitry Baryshkov SNDRV_PCM_INFO_MMAP_VALID | 23a6d77317SDmitry Baryshkov SNDRV_PCM_INFO_INTERLEAVED | 24a6d77317SDmitry Baryshkov SNDRV_PCM_INFO_PAUSE | 25a6d77317SDmitry Baryshkov SNDRV_PCM_INFO_RESUME, 26a6d77317SDmitry Baryshkov .formats = SNDRV_PCM_FMTBIT_S16_LE | 27a6d77317SDmitry Baryshkov SNDRV_PCM_FMTBIT_S24_LE | 28a6d77317SDmitry Baryshkov SNDRV_PCM_FMTBIT_S32_LE, 29a6d77317SDmitry Baryshkov .period_bytes_min = 32, 30a6d77317SDmitry Baryshkov .period_bytes_max = 8192 - 32, 31a6d77317SDmitry Baryshkov .periods_min = 1, 32a6d77317SDmitry Baryshkov .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), 33a6d77317SDmitry Baryshkov .buffer_bytes_max = 128 * 1024, 34a6d77317SDmitry Baryshkov .fifo_size = 32, 35a6d77317SDmitry Baryshkov }; 36a6d77317SDmitry Baryshkov 37a6d77317SDmitry Baryshkov int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, 38a6d77317SDmitry Baryshkov struct snd_pcm_hw_params *params) 39a6d77317SDmitry Baryshkov { 40a6d77317SDmitry Baryshkov struct snd_pcm_runtime *runtime = substream->runtime; 41a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *rtd = runtime->private_data; 42a6d77317SDmitry Baryshkov size_t totsize = params_buffer_bytes(params); 43a6d77317SDmitry Baryshkov size_t period = params_period_bytes(params); 44a6d77317SDmitry Baryshkov pxa_dma_desc *dma_desc; 45a6d77317SDmitry Baryshkov dma_addr_t dma_buff_phys, next_desc_phys; 46a6d77317SDmitry Baryshkov 47a6d77317SDmitry Baryshkov snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 48a6d77317SDmitry Baryshkov runtime->dma_bytes = totsize; 49a6d77317SDmitry Baryshkov 50a6d77317SDmitry Baryshkov dma_desc = rtd->dma_desc_array; 51a6d77317SDmitry Baryshkov next_desc_phys = rtd->dma_desc_array_phys; 52a6d77317SDmitry Baryshkov dma_buff_phys = runtime->dma_addr; 53a6d77317SDmitry Baryshkov do { 54a6d77317SDmitry Baryshkov next_desc_phys += sizeof(pxa_dma_desc); 55a6d77317SDmitry Baryshkov dma_desc->ddadr = next_desc_phys; 56a6d77317SDmitry Baryshkov if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 57a6d77317SDmitry Baryshkov dma_desc->dsadr = dma_buff_phys; 58a6d77317SDmitry Baryshkov dma_desc->dtadr = rtd->params->dev_addr; 59a6d77317SDmitry Baryshkov } else { 60a6d77317SDmitry Baryshkov dma_desc->dsadr = rtd->params->dev_addr; 61a6d77317SDmitry Baryshkov dma_desc->dtadr = dma_buff_phys; 62a6d77317SDmitry Baryshkov } 63a6d77317SDmitry Baryshkov if (period > totsize) 64a6d77317SDmitry Baryshkov period = totsize; 65a6d77317SDmitry Baryshkov dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; 66a6d77317SDmitry Baryshkov dma_desc++; 67a6d77317SDmitry Baryshkov dma_buff_phys += period; 68a6d77317SDmitry Baryshkov } while (totsize -= period); 69a6d77317SDmitry Baryshkov dma_desc[-1].ddadr = rtd->dma_desc_array_phys; 70a6d77317SDmitry Baryshkov 71a6d77317SDmitry Baryshkov return 0; 72a6d77317SDmitry Baryshkov } 73a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); 74a6d77317SDmitry Baryshkov 75a6d77317SDmitry Baryshkov int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) 76a6d77317SDmitry Baryshkov { 77a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; 78a6d77317SDmitry Baryshkov 79b7d4de7fSDaniel Mack if (rtd && rtd->params && rtd->params->drcmr) 80a6d77317SDmitry Baryshkov *rtd->params->drcmr = 0; 81a6d77317SDmitry Baryshkov 82a6d77317SDmitry Baryshkov snd_pcm_set_runtime_buffer(substream, NULL); 83a6d77317SDmitry Baryshkov return 0; 84a6d77317SDmitry Baryshkov } 85a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); 86a6d77317SDmitry Baryshkov 87a6d77317SDmitry Baryshkov int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 88a6d77317SDmitry Baryshkov { 89a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; 90a6d77317SDmitry Baryshkov int ret = 0; 91a6d77317SDmitry Baryshkov 92a6d77317SDmitry Baryshkov switch (cmd) { 93a6d77317SDmitry Baryshkov case SNDRV_PCM_TRIGGER_START: 94a6d77317SDmitry Baryshkov DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; 95a6d77317SDmitry Baryshkov DCSR(prtd->dma_ch) = DCSR_RUN; 96a6d77317SDmitry Baryshkov break; 97a6d77317SDmitry Baryshkov 98a6d77317SDmitry Baryshkov case SNDRV_PCM_TRIGGER_STOP: 99a6d77317SDmitry Baryshkov case SNDRV_PCM_TRIGGER_SUSPEND: 100a6d77317SDmitry Baryshkov case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 101a6d77317SDmitry Baryshkov DCSR(prtd->dma_ch) &= ~DCSR_RUN; 102a6d77317SDmitry Baryshkov break; 103a6d77317SDmitry Baryshkov 104a6d77317SDmitry Baryshkov case SNDRV_PCM_TRIGGER_RESUME: 105a6d77317SDmitry Baryshkov DCSR(prtd->dma_ch) |= DCSR_RUN; 106a6d77317SDmitry Baryshkov break; 107a6d77317SDmitry Baryshkov case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 108a6d77317SDmitry Baryshkov DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; 109a6d77317SDmitry Baryshkov DCSR(prtd->dma_ch) |= DCSR_RUN; 110a6d77317SDmitry Baryshkov break; 111a6d77317SDmitry Baryshkov 112a6d77317SDmitry Baryshkov default: 113a6d77317SDmitry Baryshkov ret = -EINVAL; 114a6d77317SDmitry Baryshkov } 115a6d77317SDmitry Baryshkov 116a6d77317SDmitry Baryshkov return ret; 117a6d77317SDmitry Baryshkov } 118a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_trigger); 119a6d77317SDmitry Baryshkov 120a6d77317SDmitry Baryshkov snd_pcm_uframes_t 121a6d77317SDmitry Baryshkov pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 122a6d77317SDmitry Baryshkov { 123a6d77317SDmitry Baryshkov struct snd_pcm_runtime *runtime = substream->runtime; 124a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *prtd = runtime->private_data; 125a6d77317SDmitry Baryshkov 126a6d77317SDmitry Baryshkov dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 127a6d77317SDmitry Baryshkov DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); 128a6d77317SDmitry Baryshkov snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); 129a6d77317SDmitry Baryshkov 130a6d77317SDmitry Baryshkov if (x == runtime->buffer_size) 131a6d77317SDmitry Baryshkov x = 0; 132a6d77317SDmitry Baryshkov return x; 133a6d77317SDmitry Baryshkov } 134a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_pointer); 135a6d77317SDmitry Baryshkov 136a6d77317SDmitry Baryshkov int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 137a6d77317SDmitry Baryshkov { 138a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; 139a6d77317SDmitry Baryshkov 140f8bae4caSMark Brown if (!prtd || !prtd->params) 141f8bae4caSMark Brown return 0; 142f8bae4caSMark Brown 143a6d77317SDmitry Baryshkov DCSR(prtd->dma_ch) &= ~DCSR_RUN; 144a6d77317SDmitry Baryshkov DCSR(prtd->dma_ch) = 0; 145a6d77317SDmitry Baryshkov DCMD(prtd->dma_ch) = 0; 146a6d77317SDmitry Baryshkov *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; 147a6d77317SDmitry Baryshkov 148a6d77317SDmitry Baryshkov return 0; 149a6d77317SDmitry Baryshkov } 150a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_prepare); 151a6d77317SDmitry Baryshkov 152a6d77317SDmitry Baryshkov void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) 153a6d77317SDmitry Baryshkov { 154a6d77317SDmitry Baryshkov struct snd_pcm_substream *substream = dev_id; 155a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; 156a6d77317SDmitry Baryshkov int dcsr; 157a6d77317SDmitry Baryshkov 158a6d77317SDmitry Baryshkov dcsr = DCSR(dma_ch); 159a6d77317SDmitry Baryshkov DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; 160a6d77317SDmitry Baryshkov 161a6d77317SDmitry Baryshkov if (dcsr & DCSR_ENDINTR) { 162a6d77317SDmitry Baryshkov snd_pcm_period_elapsed(substream); 163a6d77317SDmitry Baryshkov } else { 164a6d77317SDmitry Baryshkov printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", 165a6d77317SDmitry Baryshkov rtd->params->name, dma_ch, dcsr); 166a6d77317SDmitry Baryshkov snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 167a6d77317SDmitry Baryshkov } 168a6d77317SDmitry Baryshkov } 169a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); 170a6d77317SDmitry Baryshkov 171a6d77317SDmitry Baryshkov int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) 172a6d77317SDmitry Baryshkov { 173a6d77317SDmitry Baryshkov struct snd_pcm_runtime *runtime = substream->runtime; 174a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *rtd; 175a6d77317SDmitry Baryshkov int ret; 176a6d77317SDmitry Baryshkov 177a6d77317SDmitry Baryshkov runtime->hw = pxa2xx_pcm_hardware; 178a6d77317SDmitry Baryshkov 179a6d77317SDmitry Baryshkov /* 180a6d77317SDmitry Baryshkov * For mysterious reasons (and despite what the manual says) 181a6d77317SDmitry Baryshkov * playback samples are lost if the DMA count is not a multiple 182a6d77317SDmitry Baryshkov * of the DMA burst size. Let's add a rule to enforce that. 183a6d77317SDmitry Baryshkov */ 184a6d77317SDmitry Baryshkov ret = snd_pcm_hw_constraint_step(runtime, 0, 185a6d77317SDmitry Baryshkov SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 186a6d77317SDmitry Baryshkov if (ret) 187a6d77317SDmitry Baryshkov goto out; 188a6d77317SDmitry Baryshkov 189a6d77317SDmitry Baryshkov ret = snd_pcm_hw_constraint_step(runtime, 0, 190a6d77317SDmitry Baryshkov SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 191a6d77317SDmitry Baryshkov if (ret) 192a6d77317SDmitry Baryshkov goto out; 193a6d77317SDmitry Baryshkov 194a6d77317SDmitry Baryshkov ret = snd_pcm_hw_constraint_integer(runtime, 195a6d77317SDmitry Baryshkov SNDRV_PCM_HW_PARAM_PERIODS); 196a6d77317SDmitry Baryshkov if (ret < 0) 197a6d77317SDmitry Baryshkov goto out; 198a6d77317SDmitry Baryshkov 199a6d77317SDmitry Baryshkov ret = -ENOMEM; 20013095c37SMark Brown rtd = kzalloc(sizeof(*rtd), GFP_KERNEL); 201a6d77317SDmitry Baryshkov if (!rtd) 202a6d77317SDmitry Baryshkov goto out; 203a6d77317SDmitry Baryshkov rtd->dma_desc_array = 204a6d77317SDmitry Baryshkov dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, 205a6d77317SDmitry Baryshkov &rtd->dma_desc_array_phys, GFP_KERNEL); 206a6d77317SDmitry Baryshkov if (!rtd->dma_desc_array) 207a6d77317SDmitry Baryshkov goto err1; 208a6d77317SDmitry Baryshkov 2098727b909SDaniel Mack rtd->dma_ch = -1; 210a6d77317SDmitry Baryshkov runtime->private_data = rtd; 211a6d77317SDmitry Baryshkov return 0; 212a6d77317SDmitry Baryshkov 213a6d77317SDmitry Baryshkov err1: 214a6d77317SDmitry Baryshkov kfree(rtd); 215a6d77317SDmitry Baryshkov out: 216a6d77317SDmitry Baryshkov return ret; 217a6d77317SDmitry Baryshkov } 218a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_open); 219a6d77317SDmitry Baryshkov 220a6d77317SDmitry Baryshkov int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) 221a6d77317SDmitry Baryshkov { 222a6d77317SDmitry Baryshkov struct snd_pcm_runtime *runtime = substream->runtime; 223a6d77317SDmitry Baryshkov struct pxa2xx_runtime_data *rtd = runtime->private_data; 224a6d77317SDmitry Baryshkov 225a6d77317SDmitry Baryshkov dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, 226a6d77317SDmitry Baryshkov rtd->dma_desc_array, rtd->dma_desc_array_phys); 227a6d77317SDmitry Baryshkov kfree(rtd); 228a6d77317SDmitry Baryshkov return 0; 229a6d77317SDmitry Baryshkov } 230a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_close); 231a6d77317SDmitry Baryshkov 232a6d77317SDmitry Baryshkov int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, 233a6d77317SDmitry Baryshkov struct vm_area_struct *vma) 234a6d77317SDmitry Baryshkov { 235a6d77317SDmitry Baryshkov struct snd_pcm_runtime *runtime = substream->runtime; 236a6d77317SDmitry Baryshkov return dma_mmap_writecombine(substream->pcm->card->dev, vma, 237a6d77317SDmitry Baryshkov runtime->dma_area, 238a6d77317SDmitry Baryshkov runtime->dma_addr, 239a6d77317SDmitry Baryshkov runtime->dma_bytes); 240a6d77317SDmitry Baryshkov } 241a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_mmap); 242a6d77317SDmitry Baryshkov 243a6d77317SDmitry Baryshkov int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 244a6d77317SDmitry Baryshkov { 245a6d77317SDmitry Baryshkov struct snd_pcm_substream *substream = pcm->streams[stream].substream; 246a6d77317SDmitry Baryshkov struct snd_dma_buffer *buf = &substream->dma_buffer; 247a6d77317SDmitry Baryshkov size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; 248a6d77317SDmitry Baryshkov buf->dev.type = SNDRV_DMA_TYPE_DEV; 249a6d77317SDmitry Baryshkov buf->dev.dev = pcm->card->dev; 250a6d77317SDmitry Baryshkov buf->private_data = NULL; 251a6d77317SDmitry Baryshkov buf->area = dma_alloc_writecombine(pcm->card->dev, size, 252a6d77317SDmitry Baryshkov &buf->addr, GFP_KERNEL); 253a6d77317SDmitry Baryshkov if (!buf->area) 254a6d77317SDmitry Baryshkov return -ENOMEM; 255a6d77317SDmitry Baryshkov buf->bytes = size; 256a6d77317SDmitry Baryshkov return 0; 257a6d77317SDmitry Baryshkov } 258a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 259a6d77317SDmitry Baryshkov 260a6d77317SDmitry Baryshkov void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 261a6d77317SDmitry Baryshkov { 262a6d77317SDmitry Baryshkov struct snd_pcm_substream *substream; 263a6d77317SDmitry Baryshkov struct snd_dma_buffer *buf; 264a6d77317SDmitry Baryshkov int stream; 265a6d77317SDmitry Baryshkov 266a6d77317SDmitry Baryshkov for (stream = 0; stream < 2; stream++) { 267a6d77317SDmitry Baryshkov substream = pcm->streams[stream].substream; 268a6d77317SDmitry Baryshkov if (!substream) 269a6d77317SDmitry Baryshkov continue; 270a6d77317SDmitry Baryshkov buf = &substream->dma_buffer; 271a6d77317SDmitry Baryshkov if (!buf->area) 272a6d77317SDmitry Baryshkov continue; 273a6d77317SDmitry Baryshkov dma_free_writecombine(pcm->card->dev, buf->bytes, 274a6d77317SDmitry Baryshkov buf->area, buf->addr); 275a6d77317SDmitry Baryshkov buf->area = NULL; 276a6d77317SDmitry Baryshkov } 277a6d77317SDmitry Baryshkov } 278a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); 279a6d77317SDmitry Baryshkov 280a6d77317SDmitry Baryshkov MODULE_AUTHOR("Nicolas Pitre"); 281a6d77317SDmitry Baryshkov MODULE_DESCRIPTION("Intel PXA2xx sound library"); 282a6d77317SDmitry Baryshkov MODULE_LICENSE("GPL"); 283