1 /* 2 * omap-pcm.c -- ALSA PCM interface for the OMAP SoC 3 * 4 * Copyright (C) 2008 Nokia Corporation 5 * 6 * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> 7 * Peter Ujfalusi <peter.ujfalusi@ti.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 25 #include <linux/dma-mapping.h> 26 #include <linux/slab.h> 27 #include <linux/module.h> 28 #include <linux/omap-dma.h> 29 #include <sound/core.h> 30 #include <sound/pcm.h> 31 #include <sound/pcm_params.h> 32 #include <sound/dmaengine_pcm.h> 33 #include <sound/soc.h> 34 35 #ifdef CONFIG_ARCH_OMAP1 36 #define pcm_omap1510() cpu_is_omap1510() 37 #else 38 #define pcm_omap1510() 0 39 #endif 40 41 static const struct snd_pcm_hardware omap_pcm_hardware = { 42 .info = SNDRV_PCM_INFO_MMAP | 43 SNDRV_PCM_INFO_MMAP_VALID | 44 SNDRV_PCM_INFO_INTERLEAVED | 45 SNDRV_PCM_INFO_PAUSE | 46 SNDRV_PCM_INFO_RESUME | 47 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 48 .formats = SNDRV_PCM_FMTBIT_S16_LE | 49 SNDRV_PCM_FMTBIT_S32_LE, 50 .period_bytes_min = 32, 51 .period_bytes_max = 64 * 1024, 52 .periods_min = 2, 53 .periods_max = 255, 54 .buffer_bytes_max = 128 * 1024, 55 }; 56 57 /* this may get called several times by oss emulation */ 58 static int omap_pcm_hw_params(struct snd_pcm_substream *substream, 59 struct snd_pcm_hw_params *params) 60 { 61 struct snd_pcm_runtime *runtime = substream->runtime; 62 struct snd_soc_pcm_runtime *rtd = substream->private_data; 63 struct omap_pcm_dma_data *dma_data; 64 struct dma_slave_config config; 65 struct dma_chan *chan; 66 int err = 0; 67 68 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 69 70 /* return if this is a bufferless transfer e.g. 71 * codec <--> BT codec or GSM modem -- lg FIXME */ 72 if (!dma_data) 73 return 0; 74 75 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 76 runtime->dma_bytes = params_buffer_bytes(params); 77 78 chan = snd_dmaengine_pcm_get_chan(substream); 79 if (!chan) 80 return -EINVAL; 81 82 /* fills in addr_width and direction */ 83 err = snd_hwparams_to_dma_slave_config(substream, params, &config); 84 if (err) 85 return err; 86 87 snd_dmaengine_pcm_set_config_from_dai_data(substream, 88 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), 89 &config); 90 91 return dmaengine_slave_config(chan, &config); 92 } 93 94 static int omap_pcm_hw_free(struct snd_pcm_substream *substream) 95 { 96 snd_pcm_set_runtime_buffer(substream, NULL); 97 return 0; 98 } 99 100 static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) 101 { 102 snd_pcm_uframes_t offset; 103 104 if (pcm_omap1510()) 105 offset = snd_dmaengine_pcm_pointer_no_residue(substream); 106 else 107 offset = snd_dmaengine_pcm_pointer(substream); 108 109 return offset; 110 } 111 112 static int omap_pcm_open(struct snd_pcm_substream *substream) 113 { 114 struct snd_soc_pcm_runtime *rtd = substream->private_data; 115 struct snd_dmaengine_dai_dma_data *dma_data; 116 117 snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); 118 119 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 120 121 return snd_dmaengine_pcm_open_request_chan(substream, 122 omap_dma_filter_fn, 123 dma_data->filter_data); 124 } 125 126 static int omap_pcm_mmap(struct snd_pcm_substream *substream, 127 struct vm_area_struct *vma) 128 { 129 struct snd_pcm_runtime *runtime = substream->runtime; 130 131 return dma_mmap_writecombine(substream->pcm->card->dev, vma, 132 runtime->dma_area, 133 runtime->dma_addr, 134 runtime->dma_bytes); 135 } 136 137 static struct snd_pcm_ops omap_pcm_ops = { 138 .open = omap_pcm_open, 139 .close = snd_dmaengine_pcm_close_release_chan, 140 .ioctl = snd_pcm_lib_ioctl, 141 .hw_params = omap_pcm_hw_params, 142 .hw_free = omap_pcm_hw_free, 143 .trigger = snd_dmaengine_pcm_trigger, 144 .pointer = omap_pcm_pointer, 145 .mmap = omap_pcm_mmap, 146 }; 147 148 static u64 omap_pcm_dmamask = DMA_BIT_MASK(64); 149 150 static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, 151 int stream) 152 { 153 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 154 struct snd_dma_buffer *buf = &substream->dma_buffer; 155 size_t size = omap_pcm_hardware.buffer_bytes_max; 156 157 buf->dev.type = SNDRV_DMA_TYPE_DEV; 158 buf->dev.dev = pcm->card->dev; 159 buf->private_data = NULL; 160 buf->area = dma_alloc_writecombine(pcm->card->dev, size, 161 &buf->addr, GFP_KERNEL); 162 if (!buf->area) 163 return -ENOMEM; 164 165 buf->bytes = size; 166 return 0; 167 } 168 169 static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) 170 { 171 struct snd_pcm_substream *substream; 172 struct snd_dma_buffer *buf; 173 int stream; 174 175 for (stream = 0; stream < 2; stream++) { 176 substream = pcm->streams[stream].substream; 177 if (!substream) 178 continue; 179 180 buf = &substream->dma_buffer; 181 if (!buf->area) 182 continue; 183 184 dma_free_writecombine(pcm->card->dev, buf->bytes, 185 buf->area, buf->addr); 186 buf->area = NULL; 187 } 188 } 189 190 static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd) 191 { 192 struct snd_card *card = rtd->card->snd_card; 193 struct snd_pcm *pcm = rtd->pcm; 194 int ret = 0; 195 196 if (!card->dev->dma_mask) 197 card->dev->dma_mask = &omap_pcm_dmamask; 198 if (!card->dev->coherent_dma_mask) 199 card->dev->coherent_dma_mask = DMA_BIT_MASK(64); 200 201 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 202 ret = omap_pcm_preallocate_dma_buffer(pcm, 203 SNDRV_PCM_STREAM_PLAYBACK); 204 if (ret) 205 goto out; 206 } 207 208 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 209 ret = omap_pcm_preallocate_dma_buffer(pcm, 210 SNDRV_PCM_STREAM_CAPTURE); 211 if (ret) 212 goto out; 213 } 214 215 out: 216 /* free preallocated buffers in case of error */ 217 if (ret) 218 omap_pcm_free_dma_buffers(pcm); 219 220 return ret; 221 } 222 223 static struct snd_soc_platform_driver omap_soc_platform = { 224 .ops = &omap_pcm_ops, 225 .pcm_new = omap_pcm_new, 226 .pcm_free = omap_pcm_free_dma_buffers, 227 }; 228 229 static int omap_pcm_probe(struct platform_device *pdev) 230 { 231 return snd_soc_register_platform(&pdev->dev, 232 &omap_soc_platform); 233 } 234 235 static int omap_pcm_remove(struct platform_device *pdev) 236 { 237 snd_soc_unregister_platform(&pdev->dev); 238 return 0; 239 } 240 241 static struct platform_driver omap_pcm_driver = { 242 .driver = { 243 .name = "omap-pcm-audio", 244 .owner = THIS_MODULE, 245 }, 246 247 .probe = omap_pcm_probe, 248 .remove = omap_pcm_remove, 249 }; 250 251 module_platform_driver(omap_pcm_driver); 252 253 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>"); 254 MODULE_DESCRIPTION("OMAP PCM DMA module"); 255 MODULE_LICENSE("GPL"); 256 MODULE_ALIAS("platform:omap-pcm-audio"); 257
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.