1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/slab.h> 4 #include <linux/module.h> 5 #include <linux/dma-mapping.h> 6 #include <linux/dmaengine.h> 7 #include <linux/dma/pxa-dma.h> 8 9 #include <sound/core.h> 10 #include <sound/pcm.h> 11 #include <sound/pcm_params.h> 12 #include <sound/pxa2xx-lib.h> 13 #include <sound/dmaengine_pcm.h> 14 15 static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { 16 .info = SNDRV_PCM_INFO_MMAP | 17 SNDRV_PCM_INFO_MMAP_VALID | 18 SNDRV_PCM_INFO_INTERLEAVED | 19 SNDRV_PCM_INFO_PAUSE | 20 SNDRV_PCM_INFO_RESUME, 21 .formats = SNDRV_PCM_FMTBIT_S16_LE | 22 SNDRV_PCM_FMTBIT_S24_LE | 23 SNDRV_PCM_FMTBIT_S32_LE, 24 .period_bytes_min = 32, 25 .period_bytes_max = 8192 - 32, 26 .periods_min = 1, 27 .periods_max = 256, 28 .buffer_bytes_max = 128 * 1024, 29 .fifo_size = 32, 30 }; 31 32 int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, 33 struct snd_pcm_hw_params *params) 34 { 35 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 36 struct snd_soc_pcm_runtime *rtd = substream->private_data; 37 struct snd_dmaengine_dai_dma_data *dma_params; 38 struct dma_slave_config config; 39 int ret; 40 41 dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 42 if (!dma_params) 43 return 0; 44 45 ret = snd_hwparams_to_dma_slave_config(substream, params, &config); 46 if (ret) 47 return ret; 48 49 snd_dmaengine_pcm_set_config_from_dai_data(substream, 50 snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream), 51 &config); 52 53 ret = dmaengine_slave_config(chan, &config); 54 if (ret) 55 return ret; 56 57 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 58 59 return 0; 60 } 61 EXPORT_SYMBOL(pxa2xx_pcm_hw_params); 62 63 int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) 64 { 65 snd_pcm_set_runtime_buffer(substream, NULL); 66 return 0; 67 } 68 EXPORT_SYMBOL(pxa2xx_pcm_hw_free); 69 70 int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 71 { 72 return snd_dmaengine_pcm_trigger(substream, cmd); 73 } 74 EXPORT_SYMBOL(pxa2xx_pcm_trigger); 75 76 snd_pcm_uframes_t 77 pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 78 { 79 return snd_dmaengine_pcm_pointer(substream); 80 } 81 EXPORT_SYMBOL(pxa2xx_pcm_pointer); 82 83 int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 84 { 85 return 0; 86 } 87 EXPORT_SYMBOL(pxa2xx_pcm_prepare); 88 89 int pxa2xx_pcm_open(struct snd_pcm_substream *substream) 90 { 91 struct snd_soc_pcm_runtime *rtd = substream->private_data; 92 struct snd_pcm_runtime *runtime = substream->runtime; 93 struct snd_dmaengine_dai_dma_data *dma_params; 94 int ret; 95 96 runtime->hw = pxa2xx_pcm_hardware; 97 98 dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 99 if (!dma_params) 100 return 0; 101 102 /* 103 * For mysterious reasons (and despite what the manual says) 104 * playback samples are lost if the DMA count is not a multiple 105 * of the DMA burst size. Let's add a rule to enforce that. 106 */ 107 ret = snd_pcm_hw_constraint_step(runtime, 0, 108 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 109 if (ret) 110 return ret; 111 112 ret = snd_pcm_hw_constraint_step(runtime, 0, 113 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 114 if (ret) 115 return ret; 116 117 ret = snd_pcm_hw_constraint_integer(runtime, 118 SNDRV_PCM_HW_PARAM_PERIODS); 119 if (ret < 0) 120 return ret; 121 122 return snd_dmaengine_pcm_open( 123 substream, dma_request_slave_channel(asoc_rtd_to_cpu(rtd, 0)->dev, 124 dma_params->chan_name)); 125 } 126 EXPORT_SYMBOL(pxa2xx_pcm_open); 127 128 int pxa2xx_pcm_close(struct snd_pcm_substream *substream) 129 { 130 return snd_dmaengine_pcm_close_release_chan(substream); 131 } 132 EXPORT_SYMBOL(pxa2xx_pcm_close); 133 134 int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, 135 struct vm_area_struct *vma) 136 { 137 struct snd_pcm_runtime *runtime = substream->runtime; 138 return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, 139 runtime->dma_addr, runtime->dma_bytes); 140 } 141 EXPORT_SYMBOL(pxa2xx_pcm_mmap); 142 143 int pxa2xx_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 = pxa2xx_pcm_hardware.buffer_bytes_max; 148 buf->dev.type = SNDRV_DMA_TYPE_DEV; 149 buf->dev.dev = pcm->card->dev; 150 buf->private_data = NULL; 151 buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); 152 if (!buf->area) 153 return -ENOMEM; 154 buf->bytes = size; 155 return 0; 156 } 157 EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 158 159 void pxa2xx_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 buf = &substream->dma_buffer; 170 if (!buf->area) 171 continue; 172 dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); 173 buf->area = NULL; 174 } 175 } 176 EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); 177 178 void pxa2xx_soc_pcm_free(struct snd_soc_component *component, 179 struct snd_pcm *pcm) 180 { 181 pxa2xx_pcm_free_dma_buffers(pcm); 182 } 183 EXPORT_SYMBOL(pxa2xx_soc_pcm_free); 184 185 int pxa2xx_soc_pcm_new(struct snd_soc_component *component, 186 struct snd_soc_pcm_runtime *rtd) 187 { 188 struct snd_card *card = rtd->card->snd_card; 189 struct snd_pcm *pcm = rtd->pcm; 190 int ret; 191 192 ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 193 if (ret) 194 return ret; 195 196 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 197 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 198 SNDRV_PCM_STREAM_PLAYBACK); 199 if (ret) 200 goto out; 201 } 202 203 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 204 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 205 SNDRV_PCM_STREAM_CAPTURE); 206 if (ret) 207 goto out; 208 } 209 out: 210 return ret; 211 } 212 EXPORT_SYMBOL(pxa2xx_soc_pcm_new); 213 214 int pxa2xx_soc_pcm_open(struct snd_soc_component *component, 215 struct snd_pcm_substream *substream) 216 { 217 return pxa2xx_pcm_open(substream); 218 } 219 EXPORT_SYMBOL(pxa2xx_soc_pcm_open); 220 221 int pxa2xx_soc_pcm_close(struct snd_soc_component *component, 222 struct snd_pcm_substream *substream) 223 { 224 return pxa2xx_pcm_close(substream); 225 } 226 EXPORT_SYMBOL(pxa2xx_soc_pcm_close); 227 228 int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component, 229 struct snd_pcm_substream *substream, 230 struct snd_pcm_hw_params *params) 231 { 232 return pxa2xx_pcm_hw_params(substream, params); 233 } 234 EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params); 235 236 int pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component, 237 struct snd_pcm_substream *substream) 238 { 239 return pxa2xx_pcm_hw_free(substream); 240 } 241 EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_free); 242 243 int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component, 244 struct snd_pcm_substream *substream) 245 { 246 return pxa2xx_pcm_prepare(substream); 247 } 248 EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare); 249 250 int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component, 251 struct snd_pcm_substream *substream, int cmd) 252 { 253 return pxa2xx_pcm_trigger(substream, cmd); 254 } 255 EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger); 256 257 snd_pcm_uframes_t 258 pxa2xx_soc_pcm_pointer(struct snd_soc_component *component, 259 struct snd_pcm_substream *substream) 260 { 261 return pxa2xx_pcm_pointer(substream); 262 } 263 EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer); 264 265 int pxa2xx_soc_pcm_mmap(struct snd_soc_component *component, 266 struct snd_pcm_substream *substream, 267 struct vm_area_struct *vma) 268 { 269 return pxa2xx_pcm_mmap(substream, vma); 270 } 271 EXPORT_SYMBOL(pxa2xx_soc_pcm_mmap); 272 273 MODULE_AUTHOR("Nicolas Pitre"); 274 MODULE_DESCRIPTION("Intel PXA2xx sound library"); 275 MODULE_LICENSE("GPL"); 276