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(rtd->cpu_dai, 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(rtd->cpu_dai, 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(rtd->cpu_dai, 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(rtd->cpu_dai->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 int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) 179 { 180 struct snd_card *card = rtd->card->snd_card; 181 struct snd_pcm *pcm = rtd->pcm; 182 int ret; 183 184 ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 185 if (ret) 186 return ret; 187 188 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 189 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 190 SNDRV_PCM_STREAM_PLAYBACK); 191 if (ret) 192 goto out; 193 } 194 195 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 196 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 197 SNDRV_PCM_STREAM_CAPTURE); 198 if (ret) 199 goto out; 200 } 201 out: 202 return ret; 203 } 204 EXPORT_SYMBOL(pxa2xx_soc_pcm_new); 205 206 const struct snd_pcm_ops pxa2xx_pcm_ops = { 207 .open = pxa2xx_pcm_open, 208 .close = pxa2xx_pcm_close, 209 .ioctl = snd_pcm_lib_ioctl, 210 .hw_params = pxa2xx_pcm_hw_params, 211 .hw_free = pxa2xx_pcm_hw_free, 212 .prepare = pxa2xx_pcm_prepare, 213 .trigger = pxa2xx_pcm_trigger, 214 .pointer = pxa2xx_pcm_pointer, 215 .mmap = pxa2xx_pcm_mmap, 216 }; 217 EXPORT_SYMBOL(pxa2xx_pcm_ops); 218 219 MODULE_AUTHOR("Nicolas Pitre"); 220 MODULE_DESCRIPTION("Intel PXA2xx sound library"); 221 MODULE_LICENSE("GPL"); 222