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 static 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 return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter, 68 dma_data); 69 } 70 71 static int spear_pcm_mmap(struct snd_pcm_substream *substream, 72 struct vm_area_struct *vma) 73 { 74 struct snd_pcm_runtime *runtime = substream->runtime; 75 76 return dma_mmap_writecombine(substream->pcm->card->dev, vma, 77 runtime->dma_area, runtime->dma_addr, 78 runtime->dma_bytes); 79 } 80 81 static struct snd_pcm_ops spear_pcm_ops = { 82 .open = spear_pcm_open, 83 .close = snd_dmaengine_pcm_close_release_chan, 84 .ioctl = snd_pcm_lib_ioctl, 85 .hw_params = spear_pcm_hw_params, 86 .hw_free = spear_pcm_hw_free, 87 .trigger = snd_dmaengine_pcm_trigger, 88 .pointer = snd_dmaengine_pcm_pointer, 89 .mmap = spear_pcm_mmap, 90 }; 91 92 static int 93 spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, 94 size_t size) 95 { 96 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 97 struct snd_dma_buffer *buf = &substream->dma_buffer; 98 99 buf->dev.type = SNDRV_DMA_TYPE_DEV; 100 buf->dev.dev = pcm->card->dev; 101 buf->private_data = NULL; 102 103 buf->area = dma_alloc_writecombine(pcm->card->dev, size, 104 &buf->addr, GFP_KERNEL); 105 if (!buf->area) 106 return -ENOMEM; 107 108 dev_info(buf->dev.dev, 109 " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", 110 (void *)buf->area, (void *)buf->addr, size); 111 112 buf->bytes = size; 113 return 0; 114 } 115 116 static void spear_pcm_free(struct snd_pcm *pcm) 117 { 118 struct snd_pcm_substream *substream; 119 struct snd_dma_buffer *buf; 120 int stream; 121 122 for (stream = 0; stream < 2; stream++) { 123 substream = pcm->streams[stream].substream; 124 if (!substream) 125 continue; 126 127 buf = &substream->dma_buffer; 128 if (!buf || !buf->area) 129 continue; 130 131 dma_free_writecombine(pcm->card->dev, buf->bytes, 132 buf->area, buf->addr); 133 buf->area = NULL; 134 } 135 } 136 137 static u64 spear_pcm_dmamask = DMA_BIT_MASK(32); 138 139 static int spear_pcm_new(struct snd_soc_pcm_runtime *rtd) 140 { 141 struct snd_card *card = rtd->card->snd_card; 142 int ret; 143 144 if (!card->dev->dma_mask) 145 card->dev->dma_mask = &spear_pcm_dmamask; 146 if (!card->dev->coherent_dma_mask) 147 card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 148 149 if (rtd->cpu_dai->driver->playback.channels_min) { 150 ret = spear_pcm_preallocate_dma_buffer(rtd->pcm, 151 SNDRV_PCM_STREAM_PLAYBACK, 152 spear_pcm_hardware.buffer_bytes_max); 153 if (ret) 154 return ret; 155 } 156 157 if (rtd->cpu_dai->driver->capture.channels_min) { 158 ret = spear_pcm_preallocate_dma_buffer(rtd->pcm, 159 SNDRV_PCM_STREAM_CAPTURE, 160 spear_pcm_hardware.buffer_bytes_max); 161 if (ret) 162 return ret; 163 } 164 165 return 0; 166 } 167 168 static struct snd_soc_platform_driver spear_soc_platform = { 169 .ops = &spear_pcm_ops, 170 .pcm_new = spear_pcm_new, 171 .pcm_free = spear_pcm_free, 172 }; 173 174 static int spear_soc_platform_probe(struct platform_device *pdev) 175 { 176 return snd_soc_register_platform(&pdev->dev, &spear_soc_platform); 177 } 178 179 static int spear_soc_platform_remove(struct platform_device *pdev) 180 { 181 snd_soc_unregister_platform(&pdev->dev); 182 183 return 0; 184 } 185 186 static struct platform_driver spear_pcm_driver = { 187 .driver = { 188 .name = "spear-pcm-audio", 189 .owner = THIS_MODULE, 190 }, 191 192 .probe = spear_soc_platform_probe, 193 .remove = spear_soc_platform_remove, 194 }; 195 196 module_platform_driver(spear_pcm_driver); 197 198 MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); 199 MODULE_DESCRIPTION("SPEAr PCM DMA module"); 200 MODULE_LICENSE("GPL"); 201 MODULE_ALIAS("platform:spear-pcm-audio"); 202