18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include <linux/slab.h> 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 68c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 78c2ecf20Sopenharmony_ci#include <linux/dma/pxa-dma.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <sound/core.h> 108c2ecf20Sopenharmony_ci#include <sound/pcm.h> 118c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 128c2ecf20Sopenharmony_ci#include <sound/pxa2xx-lib.h> 138c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware pxa2xx_pcm_hardware = { 168c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 178c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 188c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 198c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 208c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 218c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 228c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 238c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 248c2ecf20Sopenharmony_ci .period_bytes_min = 32, 258c2ecf20Sopenharmony_ci .period_bytes_max = 8192 - 32, 268c2ecf20Sopenharmony_ci .periods_min = 1, 278c2ecf20Sopenharmony_ci .periods_max = 256, 288c2ecf20Sopenharmony_ci .buffer_bytes_max = 128 * 1024, 298c2ecf20Sopenharmony_ci .fifo_size = 32, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciint pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, 338c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 368c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = substream->private_data; 378c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_params; 388c2ecf20Sopenharmony_ci struct dma_slave_config config; 398c2ecf20Sopenharmony_ci int ret; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 428c2ecf20Sopenharmony_ci if (!dma_params) 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = snd_hwparams_to_dma_slave_config(substream, params, &config); 468c2ecf20Sopenharmony_ci if (ret) 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci snd_dmaengine_pcm_set_config_from_dai_data(substream, 508c2ecf20Sopenharmony_ci snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream), 518c2ecf20Sopenharmony_ci &config); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(chan, &config); 548c2ecf20Sopenharmony_ci if (ret) 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_hw_params); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, NULL); 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_hw_free); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciint pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_trigger(substream, cmd); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_trigger); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cisnd_pcm_uframes_t 778c2ecf20Sopenharmony_cipxa2xx_pcm_pointer(struct snd_pcm_substream *substream) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_pointer(substream); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_pointer); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciint pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_prepare); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciint pxa2xx_pcm_open(struct snd_pcm_substream *substream) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = substream->private_data; 928c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 938c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_params; 948c2ecf20Sopenharmony_ci int ret; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci runtime->hw = pxa2xx_pcm_hardware; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 998c2ecf20Sopenharmony_ci if (!dma_params) 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * For mysterious reasons (and despite what the manual says) 1048c2ecf20Sopenharmony_ci * playback samples are lost if the DMA count is not a multiple 1058c2ecf20Sopenharmony_ci * of the DMA burst size. Let's add a rule to enforce that. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 1088c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 1098c2ecf20Sopenharmony_ci if (ret) 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 1138c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 1148c2ecf20Sopenharmony_ci if (ret) 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, 1188c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 1198c2ecf20Sopenharmony_ci if (ret < 0) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_open( 1238c2ecf20Sopenharmony_ci substream, dma_request_slave_channel(asoc_rtd_to_cpu(rtd, 0)->dev, 1248c2ecf20Sopenharmony_ci dma_params->chan_name)); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_open); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciint pxa2xx_pcm_close(struct snd_pcm_substream *substream) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_close_release_chan(substream); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_close); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ciint pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, 1358c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1388c2ecf20Sopenharmony_ci return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, 1398c2ecf20Sopenharmony_ci runtime->dma_addr, runtime->dma_bytes); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_mmap); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciint pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = pcm->streams[stream].substream; 1468c2ecf20Sopenharmony_ci struct snd_dma_buffer *buf = &substream->dma_buffer; 1478c2ecf20Sopenharmony_ci size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; 1488c2ecf20Sopenharmony_ci buf->dev.type = SNDRV_DMA_TYPE_DEV; 1498c2ecf20Sopenharmony_ci buf->dev.dev = pcm->card->dev; 1508c2ecf20Sopenharmony_ci buf->private_data = NULL; 1518c2ecf20Sopenharmony_ci buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); 1528c2ecf20Sopenharmony_ci if (!buf->area) 1538c2ecf20Sopenharmony_ci return -ENOMEM; 1548c2ecf20Sopenharmony_ci buf->bytes = size; 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_civoid pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1628c2ecf20Sopenharmony_ci struct snd_dma_buffer *buf; 1638c2ecf20Sopenharmony_ci int stream; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci for (stream = 0; stream < 2; stream++) { 1668c2ecf20Sopenharmony_ci substream = pcm->streams[stream].substream; 1678c2ecf20Sopenharmony_ci if (!substream) 1688c2ecf20Sopenharmony_ci continue; 1698c2ecf20Sopenharmony_ci buf = &substream->dma_buffer; 1708c2ecf20Sopenharmony_ci if (!buf->area) 1718c2ecf20Sopenharmony_ci continue; 1728c2ecf20Sopenharmony_ci dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); 1738c2ecf20Sopenharmony_ci buf->area = NULL; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_civoid pxa2xx_soc_pcm_free(struct snd_soc_component *component, 1798c2ecf20Sopenharmony_ci struct snd_pcm *pcm) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci pxa2xx_pcm_free_dma_buffers(pcm); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_free); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_new(struct snd_soc_component *component, 1868c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct snd_card *card = rtd->card->snd_card; 1898c2ecf20Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 1938c2ecf20Sopenharmony_ci if (ret) 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 1978c2ecf20Sopenharmony_ci ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 1988c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK); 1998c2ecf20Sopenharmony_ci if (ret) 2008c2ecf20Sopenharmony_ci goto out; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 2048c2ecf20Sopenharmony_ci ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 2058c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_CAPTURE); 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci goto out; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci out: 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_new); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_open(struct snd_soc_component *component, 2158c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci return pxa2xx_pcm_open(substream); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_open); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_close(struct snd_soc_component *component, 2228c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci return pxa2xx_pcm_close(substream); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_close); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component, 2298c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 2308c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci return pxa2xx_pcm_hw_params(substream, params); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component, 2378c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return pxa2xx_pcm_hw_free(substream); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_hw_free); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_prepare(struct snd_soc_component *component, 2448c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci return pxa2xx_pcm_prepare(substream); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_prepare); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_trigger(struct snd_soc_component *component, 2518c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci return pxa2xx_pcm_trigger(substream, cmd); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_trigger); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cisnd_pcm_uframes_t 2588c2ecf20Sopenharmony_cipxa2xx_soc_pcm_pointer(struct snd_soc_component *component, 2598c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci return pxa2xx_pcm_pointer(substream); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_pointer); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciint pxa2xx_soc_pcm_mmap(struct snd_soc_component *component, 2668c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 2678c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return pxa2xx_pcm_mmap(substream, vma); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxa2xx_soc_pcm_mmap); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nicolas Pitre"); 2748c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel PXA2xx sound library"); 2758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 276