1 /* 2 * tegra_pcm.c - Tegra PCM driver 3 * 4 * Author: Stephen Warren <swarren@nvidia.com> 5 * Copyright (C) 2010,2012 - NVIDIA, Inc. 6 * 7 * Based on code copyright/by: 8 * 9 * Copyright (c) 2009-2010, NVIDIA Corporation. 10 * Scott Peterson <speterson@nvidia.com> 11 * Vijay Mali <vmali@nvidia.com> 12 * 13 * Copyright (C) 2010 Google, Inc. 14 * Iliyan Malchev <malchev@google.com> 15 * 16 * This program is free software; you can redistribute it and/or 17 * modify it under the terms of the GNU General Public License 18 * version 2 as published by the Free Software Foundation. 19 * 20 * This program is distributed in the hope that it will be useful, but 21 * WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 * General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 28 * 02110-1301 USA 29 * 30 */ 31 32 #include <linux/dma-mapping.h> 33 #include <linux/module.h> 34 #include <linux/slab.h> 35 #include <sound/core.h> 36 #include <sound/pcm.h> 37 #include <sound/pcm_params.h> 38 #include <sound/soc.h> 39 #include <sound/dmaengine_pcm.h> 40 41 #include "tegra_pcm.h" 42 43 static const struct snd_pcm_hardware tegra_pcm_hardware = { 44 .info = SNDRV_PCM_INFO_MMAP | 45 SNDRV_PCM_INFO_MMAP_VALID | 46 SNDRV_PCM_INFO_INTERLEAVED, 47 .formats = SNDRV_PCM_FMTBIT_S16_LE, 48 .channels_min = 2, 49 .channels_max = 2, 50 .period_bytes_min = 1024, 51 .period_bytes_max = PAGE_SIZE, 52 .periods_min = 2, 53 .periods_max = 8, 54 .buffer_bytes_max = PAGE_SIZE * 8, 55 .fifo_size = 4, 56 }; 57 58 static int tegra_pcm_open(struct snd_pcm_substream *substream) 59 { 60 struct snd_soc_pcm_runtime *rtd = substream->private_data; 61 struct device *dev = rtd->platform->dev; 62 int ret; 63 64 /* Set HW params now that initialization is complete */ 65 snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); 66 67 ret = snd_dmaengine_pcm_open(substream, NULL, NULL); 68 if (ret) { 69 dev_err(dev, "dmaengine pcm open failed with err %d\n", ret); 70 return ret; 71 } 72 73 return 0; 74 } 75 76 static int tegra_pcm_close(struct snd_pcm_substream *substream) 77 { 78 snd_dmaengine_pcm_close(substream); 79 return 0; 80 } 81 82 static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, 83 struct snd_pcm_hw_params *params) 84 { 85 struct snd_soc_pcm_runtime *rtd = substream->private_data; 86 struct device *dev = rtd->platform->dev; 87 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 88 struct tegra_pcm_dma_params *dmap; 89 struct dma_slave_config slave_config; 90 int ret; 91 92 dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 93 94 ret = snd_hwparams_to_dma_slave_config(substream, params, 95 &slave_config); 96 if (ret) { 97 dev_err(dev, "hw params config failed with err %d\n", ret); 98 return ret; 99 } 100 101 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 102 slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 103 slave_config.dst_addr = dmap->addr; 104 slave_config.dst_maxburst = 4; 105 } else { 106 slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 107 slave_config.src_addr = dmap->addr; 108 slave_config.src_maxburst = 4; 109 } 110 slave_config.slave_id = dmap->req_sel; 111 112 ret = dmaengine_slave_config(chan, &slave_config); 113 if (ret < 0) { 114 dev_err(dev, "dma slave config failed with err %d\n", ret); 115 return ret; 116 } 117 118 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 119 return 0; 120 } 121 122 static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) 123 { 124 snd_pcm_set_runtime_buffer(substream, NULL); 125 return 0; 126 } 127 128 static int tegra_pcm_mmap(struct snd_pcm_substream *substream, 129 struct vm_area_struct *vma) 130 { 131 struct snd_pcm_runtime *runtime = substream->runtime; 132 133 return dma_mmap_writecombine(substream->pcm->card->dev, vma, 134 runtime->dma_area, 135 runtime->dma_addr, 136 runtime->dma_bytes); 137 } 138 139 static struct snd_pcm_ops tegra_pcm_ops = { 140 .open = tegra_pcm_open, 141 .close = tegra_pcm_close, 142 .ioctl = snd_pcm_lib_ioctl, 143 .hw_params = tegra_pcm_hw_params, 144 .hw_free = tegra_pcm_hw_free, 145 .trigger = snd_dmaengine_pcm_trigger, 146 .pointer = snd_dmaengine_pcm_pointer, 147 .mmap = tegra_pcm_mmap, 148 }; 149 150 static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 151 { 152 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 153 struct snd_dma_buffer *buf = &substream->dma_buffer; 154 size_t size = tegra_pcm_hardware.buffer_bytes_max; 155 156 buf->area = dma_alloc_writecombine(pcm->card->dev, size, 157 &buf->addr, GFP_KERNEL); 158 if (!buf->area) 159 return -ENOMEM; 160 161 buf->dev.type = SNDRV_DMA_TYPE_DEV; 162 buf->dev.dev = pcm->card->dev; 163 buf->private_data = NULL; 164 buf->bytes = size; 165 166 return 0; 167 } 168 169 static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) 170 { 171 struct snd_pcm_substream *substream; 172 struct snd_dma_buffer *buf; 173 174 substream = pcm->streams[stream].substream; 175 if (!substream) 176 return; 177 178 buf = &substream->dma_buffer; 179 if (!buf->area) 180 return; 181 182 dma_free_writecombine(pcm->card->dev, buf->bytes, 183 buf->area, buf->addr); 184 buf->area = NULL; 185 } 186 187 static u64 tegra_dma_mask = DMA_BIT_MASK(32); 188 189 static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd) 190 { 191 struct snd_card *card = rtd->card->snd_card; 192 struct snd_pcm *pcm = rtd->pcm; 193 int ret = 0; 194 195 if (!card->dev->dma_mask) 196 card->dev->dma_mask = &tegra_dma_mask; 197 if (!card->dev->coherent_dma_mask) 198 card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 199 200 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 201 ret = tegra_pcm_preallocate_dma_buffer(pcm, 202 SNDRV_PCM_STREAM_PLAYBACK); 203 if (ret) 204 goto err; 205 } 206 207 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 208 ret = tegra_pcm_preallocate_dma_buffer(pcm, 209 SNDRV_PCM_STREAM_CAPTURE); 210 if (ret) 211 goto err_free_play; 212 } 213 214 return 0; 215 216 err_free_play: 217 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); 218 err: 219 return ret; 220 } 221 222 static void tegra_pcm_free(struct snd_pcm *pcm) 223 { 224 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); 225 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); 226 } 227 228 static struct snd_soc_platform_driver tegra_pcm_platform = { 229 .ops = &tegra_pcm_ops, 230 .pcm_new = tegra_pcm_new, 231 .pcm_free = tegra_pcm_free, 232 }; 233 234 int tegra_pcm_platform_register(struct device *dev) 235 { 236 return snd_soc_register_platform(dev, &tegra_pcm_platform); 237 } 238 EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); 239 240 void tegra_pcm_platform_unregister(struct device *dev) 241 { 242 snd_soc_unregister_platform(dev); 243 } 244 EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister); 245 246 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 247 MODULE_DESCRIPTION("Tegra PCM ASoC driver"); 248 MODULE_LICENSE("GPL"); 249