1 /* 2 * ALSA PCM interface for ST SPEAr Processors 3 * 4 * sound/soc/spear/spear_pcm.c 5 * 6 * Copyright (C) 2012 ST Microelectronics 7 * Rajeev Kumar<rajeev-dlh.kumar@st.com> 8 * 9 * This file is licensed under the terms of the GNU General Public 10 * License version 2. This program is licensed "as is" without any 11 * warranty of any kind, whether express or implied. 12 */ 13 14 #include <linux/module.h> 15 #include <linux/dmaengine.h> 16 #include <linux/dma-mapping.h> 17 #include <linux/init.h> 18 #include <linux/platform_device.h> 19 #include <linux/scatterlist.h> 20 #include <linux/slab.h> 21 #include <sound/core.h> 22 #include <sound/dmaengine_pcm.h> 23 #include <sound/pcm.h> 24 #include <sound/pcm_params.h> 25 #include <sound/soc.h> 26 #include <sound/spear_dma.h> 27 28 struct snd_pcm_hardware spear_pcm_hardware = { 29 .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | 30 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 31 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), 32 .buffer_bytes_max = 16 * 1024, /* max buffer size */ 33 .period_bytes_min = 2 * 1024, /* 1 msec data minimum period size */ 34 .period_bytes_max = 2 * 1024, /* maximum period size */ 35 .periods_min = 1, /* min # periods */ 36 .periods_max = 8, /* max # of periods */ 37 .fifo_size = 0, /* fifo size in bytes */ 38 }; 39 40 static int spear_pcm_hw_params(struct snd_pcm_substream *substream, 41 struct snd_pcm_hw_params *params) 42 { 43 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 44 45 return 0; 46 } 47 48 static int spear_pcm_hw_free(struct snd_pcm_substream *substream) 49 { 50 snd_pcm_set_runtime_buffer(substream, NULL); 51 52 return 0; 53 } 54 55 static int spear_pcm_open(struct snd_pcm_substream *substream) 56 { 57 struct snd_soc_pcm_runtime *rtd = substream->private_data; 58 59 struct spear_dma_data *dma_data = (struct spear_dma_data *) 60 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 61 int ret; 62 63 ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware); 64 if (ret) 65 return ret; 66 67 ret = snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data); 68 if (ret) 69 return ret; 70 71 snd_dmaengine_pcm_set_data(substream, dma_data); 72 73 return 0; 74 } 75 76 static int spear_pcm_close(struct snd_pcm_substream *substream) 77 { 78 79 snd_dmaengine_pcm_close(substream); 80 81 return 0; 82 } 83 84 static int spear_pcm_mmap(struct snd_pcm_substream *substream, 85 struct vm_area_struct *vma) 86 { 87 struct snd_pcm_runtime *runtime = substream->runtime; 88 89 return dma_mmap_writecombine(substream->pcm->card->dev, vma, 90 runtime->dma_area, runtime->dma_addr, 91 runtime->dma_bytes); 92 } 93 94 static struct snd_pcm_ops spear_pcm_ops = { 95 .open = spear_pcm_open, 96 .close = spear_pcm_close, 97 .ioctl = snd_pcm_lib_ioctl, 98 .hw_params = spear_pcm_hw_params, 99 .hw_free = spear_pcm_hw_free, 100 .trigger = snd_dmaengine_pcm_trigger, 101 .pointer = snd_dmaengine_pcm_pointer, 102 .mmap = spear_pcm_mmap, 103 }; 104 105 static int 106 spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, 107 size_t size) 108 { 109 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 110 struct snd_dma_buffer *buf = &substream->dma_buffer; 111 112 buf->dev.type = SNDRV_DMA_TYPE_DEV; 113 buf->dev.dev = pcm->card->dev; 114 buf->private_data = NULL; 115 116 buf->area = dma_alloc_writecombine(pcm->card->dev, size, 117 &buf->addr, GFP_KERNEL); 118 if (!buf->area) 119 return -ENOMEM; 120 121 dev_info(buf->dev.dev, 122 " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", 123 (void *)buf->area, (void *)buf->addr, size); 124 125 buf->bytes = size; 126 return 0; 127 } 128 129 static void spear_pcm_free(struct snd_pcm *pcm) 130 { 131 struct snd_pcm_substream *substream; 132 struct snd_dma_buffer *buf; 133 int stream; 134 135 for (stream = 0; stream < 2; stream++) { 136 substream = pcm->streams[stream].substream; 137 if (!substream) 138 continue; 139 140 buf = &substream->dma_buffer; 141 if (!buf && !buf->area) 142 continue; 143 144 dma_free_writecombine(pcm->card->dev, buf->bytes, 145 buf->area, buf->addr); 146 buf->area = NULL; 147 } 148 } 149 150 static u64 spear_pcm_dmamask = DMA_BIT_MASK(32); 151 152 static int spear_pcm_new(struct snd_card *card, 153 struct snd_soc_dai *dai, struct snd_pcm *pcm) 154 { 155 int ret; 156 157 if (!card->dev->dma_mask) 158 card->dev->dma_mask = &spear_pcm_dmamask; 159 if (!card->dev->coherent_dma_mask) 160 card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 161 162 if (dai->driver->playback.channels_min) { 163 ret = spear_pcm_preallocate_dma_buffer(pcm, 164 SNDRV_PCM_STREAM_PLAYBACK, 165 spear_pcm_hardware.buffer_bytes_max); 166 if (ret) 167 return ret; 168 } 169 170 if (dai->driver->capture.channels_min) { 171 ret = spear_pcm_preallocate_dma_buffer(pcm, 172 SNDRV_PCM_STREAM_CAPTURE, 173 spear_pcm_hardware.buffer_bytes_max); 174 if (ret) 175 return ret; 176 } 177 178 return 0; 179 } 180 181 struct snd_soc_platform_driver spear_soc_platform = { 182 .ops = &spear_pcm_ops, 183 .pcm_new = spear_pcm_new, 184 .pcm_free = spear_pcm_free, 185 }; 186 187 static int __devinit spear_soc_platform_probe(struct platform_device *pdev) 188 { 189 return snd_soc_register_platform(&pdev->dev, &spear_soc_platform); 190 } 191 192 static int __devexit spear_soc_platform_remove(struct platform_device *pdev) 193 { 194 snd_soc_unregister_platform(&pdev->dev); 195 196 return 0; 197 } 198 199 static struct platform_driver spear_pcm_driver = { 200 .driver = { 201 .name = "spear-pcm-audio", 202 .owner = THIS_MODULE, 203 }, 204 205 .probe = spear_soc_platform_probe, 206 .remove = __devexit_p(spear_soc_platform_remove), 207 }; 208 209 module_platform_driver(spear_pcm_driver); 210 211 MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); 212 MODULE_DESCRIPTION("SPEAr PCM DMA module"); 213 MODULE_LICENSE("GPL"); 214 MODULE_ALIAS("platform:spear-pcm-audio"); 215