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 #include "pxa2xx-pcm.h" 20 21 static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { 22 .info = SNDRV_PCM_INFO_MMAP | 23 SNDRV_PCM_INFO_MMAP_VALID | 24 SNDRV_PCM_INFO_INTERLEAVED | 25 SNDRV_PCM_INFO_PAUSE | 26 SNDRV_PCM_INFO_RESUME, 27 .formats = SNDRV_PCM_FMTBIT_S16_LE | 28 SNDRV_PCM_FMTBIT_S24_LE | 29 SNDRV_PCM_FMTBIT_S32_LE, 30 .period_bytes_min = 32, 31 .period_bytes_max = 8192 - 32, 32 .periods_min = 1, 33 .periods_max = 256, 34 .buffer_bytes_max = 128 * 1024, 35 .fifo_size = 32, 36 }; 37 38 int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, 39 struct snd_pcm_hw_params *params) 40 { 41 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 42 struct snd_soc_pcm_runtime *rtd = substream->private_data; 43 struct snd_dmaengine_dai_dma_data *dma_params; 44 struct dma_slave_config config; 45 int ret; 46 47 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 48 if (!dma_params) 49 return 0; 50 51 ret = snd_hwparams_to_dma_slave_config(substream, params, &config); 52 if (ret) 53 return ret; 54 55 snd_dmaengine_pcm_set_config_from_dai_data(substream, 56 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), 57 &config); 58 59 ret = dmaengine_slave_config(chan, &config); 60 if (ret) 61 return ret; 62 63 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 64 65 return 0; 66 } 67 EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); 68 69 int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) 70 { 71 snd_pcm_set_runtime_buffer(substream, NULL); 72 return 0; 73 } 74 EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); 75 76 int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 77 { 78 return snd_dmaengine_pcm_trigger(substream, cmd); 79 } 80 EXPORT_SYMBOL(pxa2xx_pcm_trigger); 81 82 snd_pcm_uframes_t 83 pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 84 { 85 return snd_dmaengine_pcm_pointer(substream); 86 } 87 EXPORT_SYMBOL(pxa2xx_pcm_pointer); 88 89 int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 90 { 91 return 0; 92 } 93 EXPORT_SYMBOL(__pxa2xx_pcm_prepare); 94 95 int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) 96 { 97 struct snd_soc_pcm_runtime *rtd = substream->private_data; 98 struct snd_pcm_runtime *runtime = substream->runtime; 99 struct snd_dmaengine_dai_dma_data *dma_params; 100 int ret; 101 102 runtime->hw = pxa2xx_pcm_hardware; 103 104 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 105 if (!dma_params) 106 return 0; 107 108 /* 109 * For mysterious reasons (and despite what the manual says) 110 * playback samples are lost if the DMA count is not a multiple 111 * of the DMA burst size. Let's add a rule to enforce that. 112 */ 113 ret = snd_pcm_hw_constraint_step(runtime, 0, 114 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 115 if (ret) 116 return ret; 117 118 ret = snd_pcm_hw_constraint_step(runtime, 0, 119 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 120 if (ret) 121 return ret; 122 123 ret = snd_pcm_hw_constraint_integer(runtime, 124 SNDRV_PCM_HW_PARAM_PERIODS); 125 if (ret < 0) 126 return ret; 127 128 return snd_dmaengine_pcm_open_request_chan(substream, 129 pxad_filter_fn, 130 dma_params->filter_data); 131 } 132 EXPORT_SYMBOL(__pxa2xx_pcm_open); 133 134 int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) 135 { 136 return snd_dmaengine_pcm_close_release_chan(substream); 137 } 138 EXPORT_SYMBOL(__pxa2xx_pcm_close); 139 140 int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, 141 struct vm_area_struct *vma) 142 { 143 struct snd_pcm_runtime *runtime = substream->runtime; 144 return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, 145 runtime->dma_addr, runtime->dma_bytes); 146 } 147 EXPORT_SYMBOL(pxa2xx_pcm_mmap); 148 149 int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 150 { 151 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 152 struct snd_dma_buffer *buf = &substream->dma_buffer; 153 size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; 154 buf->dev.type = SNDRV_DMA_TYPE_DEV; 155 buf->dev.dev = pcm->card->dev; 156 buf->private_data = NULL; 157 buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); 158 if (!buf->area) 159 return -ENOMEM; 160 buf->bytes = size; 161 return 0; 162 } 163 EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 164 165 void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 166 { 167 struct snd_pcm_substream *substream; 168 struct snd_dma_buffer *buf; 169 int stream; 170 171 for (stream = 0; stream < 2; stream++) { 172 substream = pcm->streams[stream].substream; 173 if (!substream) 174 continue; 175 buf = &substream->dma_buffer; 176 if (!buf->area) 177 continue; 178 dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); 179 buf->area = NULL; 180 } 181 } 182 EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); 183 184 MODULE_AUTHOR("Nicolas Pitre"); 185 MODULE_DESCRIPTION("Intel PXA2xx sound library"); 186 MODULE_LICENSE("GPL"); 187