1*0ed275efSAlexander Shiyan /* 2*0ed275efSAlexander Shiyan * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface 3*0ed275efSAlexander Shiyan * 4*0ed275efSAlexander Shiyan * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> 5*0ed275efSAlexander Shiyan * Copyright (C) 2006 Applied Data Systems 6*0ed275efSAlexander Shiyan * 7*0ed275efSAlexander Shiyan * Rewritten for the SoC audio subsystem (Based on PXA2xx code): 8*0ed275efSAlexander Shiyan * Copyright (c) 2008 Ryan Mallon 9*0ed275efSAlexander Shiyan * 10*0ed275efSAlexander Shiyan * This program is free software; you can redistribute it and/or modify 11*0ed275efSAlexander Shiyan * it under the terms of the GNU General Public License version 2 as 12*0ed275efSAlexander Shiyan * published by the Free Software Foundation. 13*0ed275efSAlexander Shiyan */ 14*0ed275efSAlexander Shiyan 15*0ed275efSAlexander Shiyan #include <linux/module.h> 16*0ed275efSAlexander Shiyan #include <linux/init.h> 17*0ed275efSAlexander Shiyan #include <linux/device.h> 18*0ed275efSAlexander Shiyan #include <linux/slab.h> 19*0ed275efSAlexander Shiyan #include <linux/dmaengine.h> 20*0ed275efSAlexander Shiyan #include <linux/dma-mapping.h> 21*0ed275efSAlexander Shiyan 22*0ed275efSAlexander Shiyan #include <sound/core.h> 23*0ed275efSAlexander Shiyan #include <sound/pcm.h> 24*0ed275efSAlexander Shiyan #include <sound/pcm_params.h> 25*0ed275efSAlexander Shiyan #include <sound/soc.h> 26*0ed275efSAlexander Shiyan #include <sound/dmaengine_pcm.h> 27*0ed275efSAlexander Shiyan 28*0ed275efSAlexander Shiyan #include <mach/dma.h> 29*0ed275efSAlexander Shiyan #include <mach/hardware.h> 30*0ed275efSAlexander Shiyan #include <mach/ep93xx-regs.h> 31*0ed275efSAlexander Shiyan 32*0ed275efSAlexander Shiyan #include "ep93xx-pcm.h" 33*0ed275efSAlexander Shiyan 34*0ed275efSAlexander Shiyan static const struct snd_pcm_hardware ep93xx_pcm_hardware = { 35*0ed275efSAlexander Shiyan .info = (SNDRV_PCM_INFO_MMAP | 36*0ed275efSAlexander Shiyan SNDRV_PCM_INFO_MMAP_VALID | 37*0ed275efSAlexander Shiyan SNDRV_PCM_INFO_INTERLEAVED | 38*0ed275efSAlexander Shiyan SNDRV_PCM_INFO_BLOCK_TRANSFER), 39*0ed275efSAlexander Shiyan 40*0ed275efSAlexander Shiyan .rates = SNDRV_PCM_RATE_8000_192000, 41*0ed275efSAlexander Shiyan .rate_min = SNDRV_PCM_RATE_8000, 42*0ed275efSAlexander Shiyan .rate_max = SNDRV_PCM_RATE_192000, 43*0ed275efSAlexander Shiyan 44*0ed275efSAlexander Shiyan .formats = (SNDRV_PCM_FMTBIT_S16_LE | 45*0ed275efSAlexander Shiyan SNDRV_PCM_FMTBIT_S24_LE | 46*0ed275efSAlexander Shiyan SNDRV_PCM_FMTBIT_S32_LE), 47*0ed275efSAlexander Shiyan 48*0ed275efSAlexander Shiyan .buffer_bytes_max = 131072, 49*0ed275efSAlexander Shiyan .period_bytes_min = 32, 50*0ed275efSAlexander Shiyan .period_bytes_max = 32768, 51*0ed275efSAlexander Shiyan .periods_min = 1, 52*0ed275efSAlexander Shiyan .periods_max = 32, 53*0ed275efSAlexander Shiyan .fifo_size = 32, 54*0ed275efSAlexander Shiyan }; 55*0ed275efSAlexander Shiyan 56*0ed275efSAlexander Shiyan static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) 57*0ed275efSAlexander Shiyan { 58*0ed275efSAlexander Shiyan struct ep93xx_dma_data *data = filter_param; 59*0ed275efSAlexander Shiyan 60*0ed275efSAlexander Shiyan if (data->direction == ep93xx_dma_chan_direction(chan)) { 61*0ed275efSAlexander Shiyan chan->private = data; 62*0ed275efSAlexander Shiyan return true; 63*0ed275efSAlexander Shiyan } 64*0ed275efSAlexander Shiyan 65*0ed275efSAlexander Shiyan return false; 66*0ed275efSAlexander Shiyan } 67*0ed275efSAlexander Shiyan 68*0ed275efSAlexander Shiyan static int ep93xx_pcm_open(struct snd_pcm_substream *substream) 69*0ed275efSAlexander Shiyan { 70*0ed275efSAlexander Shiyan struct snd_soc_pcm_runtime *rtd = substream->private_data; 71*0ed275efSAlexander Shiyan struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 72*0ed275efSAlexander Shiyan struct ep93xx_pcm_dma_params *dma_params; 73*0ed275efSAlexander Shiyan struct ep93xx_dma_data *dma_data; 74*0ed275efSAlexander Shiyan int ret; 75*0ed275efSAlexander Shiyan 76*0ed275efSAlexander Shiyan snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); 77*0ed275efSAlexander Shiyan 78*0ed275efSAlexander Shiyan dma_data = kmalloc(sizeof(*dma_data), GFP_KERNEL); 79*0ed275efSAlexander Shiyan if (!dma_data) 80*0ed275efSAlexander Shiyan return -ENOMEM; 81*0ed275efSAlexander Shiyan 82*0ed275efSAlexander Shiyan dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream); 83*0ed275efSAlexander Shiyan dma_data->port = dma_params->dma_port; 84*0ed275efSAlexander Shiyan dma_data->name = dma_params->name; 85*0ed275efSAlexander Shiyan dma_data->direction = snd_pcm_substream_to_dma_direction(substream); 86*0ed275efSAlexander Shiyan 87*0ed275efSAlexander Shiyan ret = snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, dma_data); 88*0ed275efSAlexander Shiyan if (ret) { 89*0ed275efSAlexander Shiyan kfree(dma_data); 90*0ed275efSAlexander Shiyan return ret; 91*0ed275efSAlexander Shiyan } 92*0ed275efSAlexander Shiyan 93*0ed275efSAlexander Shiyan snd_dmaengine_pcm_set_data(substream, dma_data); 94*0ed275efSAlexander Shiyan 95*0ed275efSAlexander Shiyan return 0; 96*0ed275efSAlexander Shiyan } 97*0ed275efSAlexander Shiyan 98*0ed275efSAlexander Shiyan static int ep93xx_pcm_close(struct snd_pcm_substream *substream) 99*0ed275efSAlexander Shiyan { 100*0ed275efSAlexander Shiyan struct dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); 101*0ed275efSAlexander Shiyan 102*0ed275efSAlexander Shiyan snd_dmaengine_pcm_close(substream); 103*0ed275efSAlexander Shiyan kfree(dma_data); 104*0ed275efSAlexander Shiyan return 0; 105*0ed275efSAlexander Shiyan } 106*0ed275efSAlexander Shiyan 107*0ed275efSAlexander Shiyan static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, 108*0ed275efSAlexander Shiyan struct snd_pcm_hw_params *params) 109*0ed275efSAlexander Shiyan { 110*0ed275efSAlexander Shiyan snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 111*0ed275efSAlexander Shiyan 112*0ed275efSAlexander Shiyan return 0; 113*0ed275efSAlexander Shiyan } 114*0ed275efSAlexander Shiyan 115*0ed275efSAlexander Shiyan static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) 116*0ed275efSAlexander Shiyan { 117*0ed275efSAlexander Shiyan snd_pcm_set_runtime_buffer(substream, NULL); 118*0ed275efSAlexander Shiyan return 0; 119*0ed275efSAlexander Shiyan } 120*0ed275efSAlexander Shiyan 121*0ed275efSAlexander Shiyan static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream, 122*0ed275efSAlexander Shiyan struct vm_area_struct *vma) 123*0ed275efSAlexander Shiyan { 124*0ed275efSAlexander Shiyan struct snd_pcm_runtime *runtime = substream->runtime; 125*0ed275efSAlexander Shiyan 126*0ed275efSAlexander Shiyan return dma_mmap_writecombine(substream->pcm->card->dev, vma, 127*0ed275efSAlexander Shiyan runtime->dma_area, 128*0ed275efSAlexander Shiyan runtime->dma_addr, 129*0ed275efSAlexander Shiyan runtime->dma_bytes); 130*0ed275efSAlexander Shiyan } 131*0ed275efSAlexander Shiyan 132*0ed275efSAlexander Shiyan static struct snd_pcm_ops ep93xx_pcm_ops = { 133*0ed275efSAlexander Shiyan .open = ep93xx_pcm_open, 134*0ed275efSAlexander Shiyan .close = ep93xx_pcm_close, 135*0ed275efSAlexander Shiyan .ioctl = snd_pcm_lib_ioctl, 136*0ed275efSAlexander Shiyan .hw_params = ep93xx_pcm_hw_params, 137*0ed275efSAlexander Shiyan .hw_free = ep93xx_pcm_hw_free, 138*0ed275efSAlexander Shiyan .trigger = snd_dmaengine_pcm_trigger, 139*0ed275efSAlexander Shiyan .pointer = snd_dmaengine_pcm_pointer_no_residue, 140*0ed275efSAlexander Shiyan .mmap = ep93xx_pcm_mmap, 141*0ed275efSAlexander Shiyan }; 142*0ed275efSAlexander Shiyan 143*0ed275efSAlexander Shiyan static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 144*0ed275efSAlexander Shiyan { 145*0ed275efSAlexander Shiyan struct snd_pcm_substream *substream = pcm->streams[stream].substream; 146*0ed275efSAlexander Shiyan struct snd_dma_buffer *buf = &substream->dma_buffer; 147*0ed275efSAlexander Shiyan size_t size = ep93xx_pcm_hardware.buffer_bytes_max; 148*0ed275efSAlexander Shiyan 149*0ed275efSAlexander Shiyan buf->dev.type = SNDRV_DMA_TYPE_DEV; 150*0ed275efSAlexander Shiyan buf->dev.dev = pcm->card->dev; 151*0ed275efSAlexander Shiyan buf->private_data = NULL; 152*0ed275efSAlexander Shiyan buf->area = dma_alloc_writecombine(pcm->card->dev, size, 153*0ed275efSAlexander Shiyan &buf->addr, GFP_KERNEL); 154*0ed275efSAlexander Shiyan buf->bytes = size; 155*0ed275efSAlexander Shiyan 156*0ed275efSAlexander Shiyan return (buf->area == NULL) ? -ENOMEM : 0; 157*0ed275efSAlexander Shiyan } 158*0ed275efSAlexander Shiyan 159*0ed275efSAlexander Shiyan static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 160*0ed275efSAlexander Shiyan { 161*0ed275efSAlexander Shiyan struct snd_pcm_substream *substream; 162*0ed275efSAlexander Shiyan struct snd_dma_buffer *buf; 163*0ed275efSAlexander Shiyan int stream; 164*0ed275efSAlexander Shiyan 165*0ed275efSAlexander Shiyan for (stream = 0; stream < 2; stream++) { 166*0ed275efSAlexander Shiyan substream = pcm->streams[stream].substream; 167*0ed275efSAlexander Shiyan if (!substream) 168*0ed275efSAlexander Shiyan continue; 169*0ed275efSAlexander Shiyan 170*0ed275efSAlexander Shiyan buf = &substream->dma_buffer; 171*0ed275efSAlexander Shiyan if (!buf->area) 172*0ed275efSAlexander Shiyan continue; 173*0ed275efSAlexander Shiyan 174*0ed275efSAlexander Shiyan dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, 175*0ed275efSAlexander Shiyan buf->addr); 176*0ed275efSAlexander Shiyan buf->area = NULL; 177*0ed275efSAlexander Shiyan } 178*0ed275efSAlexander Shiyan } 179*0ed275efSAlexander Shiyan 180*0ed275efSAlexander Shiyan static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32); 181*0ed275efSAlexander Shiyan 182*0ed275efSAlexander Shiyan static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd) 183*0ed275efSAlexander Shiyan { 184*0ed275efSAlexander Shiyan struct snd_card *card = rtd->card->snd_card; 185*0ed275efSAlexander Shiyan struct snd_pcm *pcm = rtd->pcm; 186*0ed275efSAlexander Shiyan int ret = 0; 187*0ed275efSAlexander Shiyan 188*0ed275efSAlexander Shiyan if (!card->dev->dma_mask) 189*0ed275efSAlexander Shiyan card->dev->dma_mask = &ep93xx_pcm_dmamask; 190*0ed275efSAlexander Shiyan if (!card->dev->coherent_dma_mask) 191*0ed275efSAlexander Shiyan card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 192*0ed275efSAlexander Shiyan 193*0ed275efSAlexander Shiyan if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 194*0ed275efSAlexander Shiyan ret = ep93xx_pcm_preallocate_dma_buffer(pcm, 195*0ed275efSAlexander Shiyan SNDRV_PCM_STREAM_PLAYBACK); 196*0ed275efSAlexander Shiyan if (ret) 197*0ed275efSAlexander Shiyan return ret; 198*0ed275efSAlexander Shiyan } 199*0ed275efSAlexander Shiyan 200*0ed275efSAlexander Shiyan if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 201*0ed275efSAlexander Shiyan ret = ep93xx_pcm_preallocate_dma_buffer(pcm, 202*0ed275efSAlexander Shiyan SNDRV_PCM_STREAM_CAPTURE); 203*0ed275efSAlexander Shiyan if (ret) 204*0ed275efSAlexander Shiyan return ret; 205*0ed275efSAlexander Shiyan } 206*0ed275efSAlexander Shiyan 207*0ed275efSAlexander Shiyan return 0; 208*0ed275efSAlexander Shiyan } 209*0ed275efSAlexander Shiyan 210*0ed275efSAlexander Shiyan static struct snd_soc_platform_driver ep93xx_soc_platform = { 211*0ed275efSAlexander Shiyan .ops = &ep93xx_pcm_ops, 212*0ed275efSAlexander Shiyan .pcm_new = &ep93xx_pcm_new, 213*0ed275efSAlexander Shiyan .pcm_free = &ep93xx_pcm_free_dma_buffers, 214*0ed275efSAlexander Shiyan }; 215*0ed275efSAlexander Shiyan 216*0ed275efSAlexander Shiyan static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev) 217*0ed275efSAlexander Shiyan { 218*0ed275efSAlexander Shiyan return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform); 219*0ed275efSAlexander Shiyan } 220*0ed275efSAlexander Shiyan 221*0ed275efSAlexander Shiyan static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev) 222*0ed275efSAlexander Shiyan { 223*0ed275efSAlexander Shiyan snd_soc_unregister_platform(&pdev->dev); 224*0ed275efSAlexander Shiyan return 0; 225*0ed275efSAlexander Shiyan } 226*0ed275efSAlexander Shiyan 227*0ed275efSAlexander Shiyan static struct platform_driver ep93xx_pcm_driver = { 228*0ed275efSAlexander Shiyan .driver = { 229*0ed275efSAlexander Shiyan .name = "ep93xx-pcm-audio", 230*0ed275efSAlexander Shiyan .owner = THIS_MODULE, 231*0ed275efSAlexander Shiyan }, 232*0ed275efSAlexander Shiyan 233*0ed275efSAlexander Shiyan .probe = ep93xx_soc_platform_probe, 234*0ed275efSAlexander Shiyan .remove = __devexit_p(ep93xx_soc_platform_remove), 235*0ed275efSAlexander Shiyan }; 236*0ed275efSAlexander Shiyan 237*0ed275efSAlexander Shiyan module_platform_driver(ep93xx_pcm_driver); 238*0ed275efSAlexander Shiyan 239*0ed275efSAlexander Shiyan MODULE_AUTHOR("Ryan Mallon"); 240*0ed275efSAlexander Shiyan MODULE_DESCRIPTION("EP93xx ALSA PCM interface"); 241*0ed275efSAlexander Shiyan MODULE_LICENSE("GPL"); 242*0ed275efSAlexander Shiyan MODULE_ALIAS("platform:ep93xx-pcm-audio"); 243