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 = snd_soc_substream_to_rtd(substream); 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(snd_soc_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(snd_soc_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 } 59 EXPORT_SYMBOL(pxa2xx_pcm_hw_params); 60 61 int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 62 { 63 return snd_dmaengine_pcm_trigger(substream, cmd); 64 } 65 EXPORT_SYMBOL(pxa2xx_pcm_trigger); 66 67 snd_pcm_uframes_t 68 pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 69 { 70 return snd_dmaengine_pcm_pointer(substream); 71 } 72 EXPORT_SYMBOL(pxa2xx_pcm_pointer); 73 74 int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 75 { 76 return 0; 77 } 78 EXPORT_SYMBOL(pxa2xx_pcm_prepare); 79 80 int pxa2xx_pcm_open(struct snd_pcm_substream *substream) 81 { 82 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 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(snd_soc_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(snd_soc_rtd_to_cpu(rtd, 0)->dev, 115 dma_params->chan_name)); 116 } 117 EXPORT_SYMBOL(pxa2xx_pcm_open); 118 119 int pxa2xx_pcm_close(struct snd_pcm_substream *substream) 120 { 121 return snd_dmaengine_pcm_close_release_chan(substream); 122 } 123 EXPORT_SYMBOL(pxa2xx_pcm_close); 124 125 int 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 } 132 EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 133 134 int 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 } 147 EXPORT_SYMBOL(pxa2xx_soc_pcm_new); 148 149 int pxa2xx_soc_pcm_open(struct snd_soc_component *component, 150 struct snd_pcm_substream *substream) 151 { 152 return pxa2xx_pcm_open(substream); 153 } 154 EXPORT_SYMBOL(pxa2xx_soc_pcm_open); 155 156 int pxa2xx_soc_pcm_close(struct snd_soc_component *component, 157 struct snd_pcm_substream *substream) 158 { 159 return pxa2xx_pcm_close(substream); 160 } 161 EXPORT_SYMBOL(pxa2xx_soc_pcm_close); 162 163 int 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 } 169 EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params); 170 171 int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component, 172 struct snd_pcm_substream *substream) 173 { 174 return pxa2xx_pcm_prepare(substream); 175 } 176 EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare); 177 178 int 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 } 183 EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger); 184 185 snd_pcm_uframes_t 186 pxa2xx_soc_pcm_pointer(struct snd_soc_component *component, 187 struct snd_pcm_substream *substream) 188 { 189 return pxa2xx_pcm_pointer(substream); 190 } 191 EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer); 192 193 MODULE_AUTHOR("Nicolas Pitre"); 194 MODULE_DESCRIPTION("Intel PXA2xx sound library"); 195 MODULE_LICENSE("GPL"); 196
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.