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