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