1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License version 2 as 4 * published by the Free Software Foundation. 5 */ 6 7 #include <linux/slab.h> 8 #include <linux/module.h> 9 #include <linux/dma-mapping.h> 10 #include <linux/dmaengine.h> 11 #include <linux/dma/pxa-dma.h> 12 13 #include <sound/core.h> 14 #include <sound/pcm.h> 15 #include <sound/pcm_params.h> 16 #include <sound/pxa2xx-lib.h> 17 #include <sound/dmaengine_pcm.h> 18 19 static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { 20 .info = SNDRV_PCM_INFO_MMAP | 21 SNDRV_PCM_INFO_MMAP_VALID | 22 SNDRV_PCM_INFO_INTERLEAVED | 23 SNDRV_PCM_INFO_PAUSE | 24 SNDRV_PCM_INFO_RESUME, 25 .formats = SNDRV_PCM_FMTBIT_S16_LE | 26 SNDRV_PCM_FMTBIT_S24_LE | 27 SNDRV_PCM_FMTBIT_S32_LE, 28 .period_bytes_min = 32, 29 .period_bytes_max = 8192 - 32, 30 .periods_min = 1, 31 .periods_max = 256, 32 .buffer_bytes_max = 128 * 1024, 33 .fifo_size = 32, 34 }; 35 36 int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, 37 struct snd_pcm_hw_params *params) 38 { 39 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 40 struct snd_soc_pcm_runtime *rtd = substream->private_data; 41 struct snd_dmaengine_dai_dma_data *dma_params; 42 struct dma_slave_config config; 43 int ret; 44 45 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 46 if (!dma_params) 47 return 0; 48 49 ret = snd_hwparams_to_dma_slave_config(substream, params, &config); 50 if (ret) 51 return ret; 52 53 snd_dmaengine_pcm_set_config_from_dai_data(substream, 54 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), 55 &config); 56 57 ret = dmaengine_slave_config(chan, &config); 58 if (ret) 59 return ret; 60 61 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 62 63 return 0; 64 } 65 EXPORT_SYMBOL(pxa2xx_pcm_hw_params); 66 67 int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) 68 { 69 snd_pcm_set_runtime_buffer(substream, NULL); 70 return 0; 71 } 72 EXPORT_SYMBOL(pxa2xx_pcm_hw_free); 73 74 int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 75 { 76 return snd_dmaengine_pcm_trigger(substream, cmd); 77 } 78 EXPORT_SYMBOL(pxa2xx_pcm_trigger); 79 80 snd_pcm_uframes_t 81 pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 82 { 83 return snd_dmaengine_pcm_pointer(substream); 84 } 85 EXPORT_SYMBOL(pxa2xx_pcm_pointer); 86 87 int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 88 { 89 return 0; 90 } 91 EXPORT_SYMBOL(pxa2xx_pcm_prepare); 92 93 int pxa2xx_pcm_open(struct snd_pcm_substream *substream) 94 { 95 struct snd_soc_pcm_runtime *rtd = substream->private_data; 96 struct snd_pcm_runtime *runtime = substream->runtime; 97 struct snd_dmaengine_dai_dma_data *dma_params; 98 int ret; 99 100 runtime->hw = pxa2xx_pcm_hardware; 101 102 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 103 if (!dma_params) 104 return 0; 105 106 /* 107 * For mysterious reasons (and despite what the manual says) 108 * playback samples are lost if the DMA count is not a multiple 109 * of the DMA burst size. Let's add a rule to enforce that. 110 */ 111 ret = snd_pcm_hw_constraint_step(runtime, 0, 112 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 113 if (ret) 114 return ret; 115 116 ret = snd_pcm_hw_constraint_step(runtime, 0, 117 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 118 if (ret) 119 return ret; 120 121 ret = snd_pcm_hw_constraint_integer(runtime, 122 SNDRV_PCM_HW_PARAM_PERIODS); 123 if (ret < 0) 124 return ret; 125 126 return snd_dmaengine_pcm_open( 127 substream, dma_request_slave_channel(rtd->cpu_dai->dev, 128 dma_params->chan_name)); 129 } 130 EXPORT_SYMBOL(pxa2xx_pcm_open); 131 132 int pxa2xx_pcm_close(struct snd_pcm_substream *substream) 133 { 134 return snd_dmaengine_pcm_close_release_chan(substream); 135 } 136 EXPORT_SYMBOL(pxa2xx_pcm_close); 137 138 int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, 139 struct vm_area_struct *vma) 140 { 141 struct snd_pcm_runtime *runtime = substream->runtime; 142 return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, 143 runtime->dma_addr, runtime->dma_bytes); 144 } 145 EXPORT_SYMBOL(pxa2xx_pcm_mmap); 146 147 int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 148 { 149 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 150 struct snd_dma_buffer *buf = &substream->dma_buffer; 151 size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; 152 buf->dev.type = SNDRV_DMA_TYPE_DEV; 153 buf->dev.dev = pcm->card->dev; 154 buf->private_data = NULL; 155 buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); 156 if (!buf->area) 157 return -ENOMEM; 158 buf->bytes = size; 159 return 0; 160 } 161 EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 162 163 void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 164 { 165 struct snd_pcm_substream *substream; 166 struct snd_dma_buffer *buf; 167 int stream; 168 169 for (stream = 0; stream < 2; stream++) { 170 substream = pcm->streams[stream].substream; 171 if (!substream) 172 continue; 173 buf = &substream->dma_buffer; 174 if (!buf->area) 175 continue; 176 dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); 177 buf->area = NULL; 178 } 179 } 180 EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); 181 182 int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) 183 { 184 struct snd_card *card = rtd->card->snd_card; 185 struct snd_pcm *pcm = rtd->pcm; 186 int ret; 187 188 ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 189 if (ret) 190 return ret; 191 192 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 193 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 194 SNDRV_PCM_STREAM_PLAYBACK); 195 if (ret) 196 goto out; 197 } 198 199 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 200 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 201 SNDRV_PCM_STREAM_CAPTURE); 202 if (ret) 203 goto out; 204 } 205 out: 206 return ret; 207 } 208 EXPORT_SYMBOL(pxa2xx_soc_pcm_new); 209 210 const struct snd_pcm_ops pxa2xx_pcm_ops = { 211 .open = pxa2xx_pcm_open, 212 .close = pxa2xx_pcm_close, 213 .ioctl = snd_pcm_lib_ioctl, 214 .hw_params = pxa2xx_pcm_hw_params, 215 .hw_free = pxa2xx_pcm_hw_free, 216 .prepare = pxa2xx_pcm_prepare, 217 .trigger = pxa2xx_pcm_trigger, 218 .pointer = pxa2xx_pcm_pointer, 219 .mmap = pxa2xx_pcm_mmap, 220 }; 221 EXPORT_SYMBOL(pxa2xx_pcm_ops); 222 223 MODULE_AUTHOR("Nicolas Pitre"); 224 MODULE_DESCRIPTION("Intel PXA2xx sound library"); 225 MODULE_LICENSE("GPL"); 226