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