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 21static 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 38int __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} 67EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); 68 69int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) 70{ 71 snd_pcm_set_runtime_buffer(substream, NULL); 72 return 0; 73} 74EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); 75 76int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 77{ 78 return snd_dmaengine_pcm_trigger(substream, cmd); 79} 80EXPORT_SYMBOL(pxa2xx_pcm_trigger); 81 82snd_pcm_uframes_t 83pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 84{ 85 return snd_dmaengine_pcm_pointer(substream); 86} 87EXPORT_SYMBOL(pxa2xx_pcm_pointer); 88 89int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 90{ 91 return 0; 92} 93EXPORT_SYMBOL(__pxa2xx_pcm_prepare); 94 95int __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} 132EXPORT_SYMBOL(__pxa2xx_pcm_open); 133 134int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) 135{ 136 return snd_dmaengine_pcm_close_release_chan(substream); 137} 138EXPORT_SYMBOL(__pxa2xx_pcm_close); 139 140int 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_writecombine(substream->pcm->card->dev, vma, 145 runtime->dma_area, 146 runtime->dma_addr, 147 runtime->dma_bytes); 148} 149EXPORT_SYMBOL(pxa2xx_pcm_mmap); 150 151int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 152{ 153 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 154 struct snd_dma_buffer *buf = &substream->dma_buffer; 155 size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; 156 buf->dev.type = SNDRV_DMA_TYPE_DEV; 157 buf->dev.dev = pcm->card->dev; 158 buf->private_data = NULL; 159 buf->area = dma_alloc_writecombine(pcm->card->dev, size, 160 &buf->addr, GFP_KERNEL); 161 if (!buf->area) 162 return -ENOMEM; 163 buf->bytes = size; 164 return 0; 165} 166EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 167 168void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 169{ 170 struct snd_pcm_substream *substream; 171 struct snd_dma_buffer *buf; 172 int stream; 173 174 for (stream = 0; stream < 2; stream++) { 175 substream = pcm->streams[stream].substream; 176 if (!substream) 177 continue; 178 buf = &substream->dma_buffer; 179 if (!buf->area) 180 continue; 181 dma_free_writecombine(pcm->card->dev, buf->bytes, 182 buf->area, buf->addr); 183 buf->area = NULL; 184 } 185} 186EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); 187 188MODULE_AUTHOR("Nicolas Pitre"); 189MODULE_DESCRIPTION("Intel PXA2xx sound library"); 190MODULE_LICENSE("GPL"); 191