pxa2xx-pcm-lib.c (5194B)
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 15static 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 32int 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 return 0; 58} 59EXPORT_SYMBOL(pxa2xx_pcm_hw_params); 60 61int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 62{ 63 return snd_dmaengine_pcm_trigger(substream, cmd); 64} 65EXPORT_SYMBOL(pxa2xx_pcm_trigger); 66 67snd_pcm_uframes_t 68pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 69{ 70 return snd_dmaengine_pcm_pointer(substream); 71} 72EXPORT_SYMBOL(pxa2xx_pcm_pointer); 73 74int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 75{ 76 return 0; 77} 78EXPORT_SYMBOL(pxa2xx_pcm_prepare); 79 80int pxa2xx_pcm_open(struct snd_pcm_substream *substream) 81{ 82 struct snd_soc_pcm_runtime *rtd = substream->private_data; 83 struct snd_pcm_runtime *runtime = substream->runtime; 84 struct snd_dmaengine_dai_dma_data *dma_params; 85 int ret; 86 87 runtime->hw = pxa2xx_pcm_hardware; 88 89 dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 90 if (!dma_params) 91 return 0; 92 93 /* 94 * For mysterious reasons (and despite what the manual says) 95 * playback samples are lost if the DMA count is not a multiple 96 * of the DMA burst size. Let's add a rule to enforce that. 97 */ 98 ret = snd_pcm_hw_constraint_step(runtime, 0, 99 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 100 if (ret) 101 return ret; 102 103 ret = snd_pcm_hw_constraint_step(runtime, 0, 104 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 105 if (ret) 106 return ret; 107 108 ret = snd_pcm_hw_constraint_integer(runtime, 109 SNDRV_PCM_HW_PARAM_PERIODS); 110 if (ret < 0) 111 return ret; 112 113 return snd_dmaengine_pcm_open( 114 substream, dma_request_slave_channel(asoc_rtd_to_cpu(rtd, 0)->dev, 115 dma_params->chan_name)); 116} 117EXPORT_SYMBOL(pxa2xx_pcm_open); 118 119int pxa2xx_pcm_close(struct snd_pcm_substream *substream) 120{ 121 return snd_dmaengine_pcm_close_release_chan(substream); 122} 123EXPORT_SYMBOL(pxa2xx_pcm_close); 124 125int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm) 126{ 127 size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; 128 129 return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, 130 pcm->card->dev, size); 131} 132EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 133 134int pxa2xx_soc_pcm_new(struct snd_soc_component *component, 135 struct snd_soc_pcm_runtime *rtd) 136{ 137 struct snd_card *card = rtd->card->snd_card; 138 struct snd_pcm *pcm = rtd->pcm; 139 int ret; 140 141 ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 142 if (ret) 143 return ret; 144 145 return pxa2xx_pcm_preallocate_dma_buffer(pcm); 146} 147EXPORT_SYMBOL(pxa2xx_soc_pcm_new); 148 149int pxa2xx_soc_pcm_open(struct snd_soc_component *component, 150 struct snd_pcm_substream *substream) 151{ 152 return pxa2xx_pcm_open(substream); 153} 154EXPORT_SYMBOL(pxa2xx_soc_pcm_open); 155 156int pxa2xx_soc_pcm_close(struct snd_soc_component *component, 157 struct snd_pcm_substream *substream) 158{ 159 return pxa2xx_pcm_close(substream); 160} 161EXPORT_SYMBOL(pxa2xx_soc_pcm_close); 162 163int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component, 164 struct snd_pcm_substream *substream, 165 struct snd_pcm_hw_params *params) 166{ 167 return pxa2xx_pcm_hw_params(substream, params); 168} 169EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params); 170 171int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component, 172 struct snd_pcm_substream *substream) 173{ 174 return pxa2xx_pcm_prepare(substream); 175} 176EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare); 177 178int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component, 179 struct snd_pcm_substream *substream, int cmd) 180{ 181 return pxa2xx_pcm_trigger(substream, cmd); 182} 183EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger); 184 185snd_pcm_uframes_t 186pxa2xx_soc_pcm_pointer(struct snd_soc_component *component, 187 struct snd_pcm_substream *substream) 188{ 189 return pxa2xx_pcm_pointer(substream); 190} 191EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer); 192 193MODULE_AUTHOR("Nicolas Pitre"); 194MODULE_DESCRIPTION("Intel PXA2xx sound library"); 195MODULE_LICENSE("GPL");