18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/sound/soc/pxa/mmp-pcm.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Marvell International Ltd. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_data/dma-mmp_tdma.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_data/mmp_audio.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <sound/pxa2xx-lib.h> 178c2ecf20Sopenharmony_ci#include <sound/core.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 208c2ecf20Sopenharmony_ci#include <sound/soc.h> 218c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DRV_NAME "mmp-pcm" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct mmp_dma_data { 268c2ecf20Sopenharmony_ci int ssp_id; 278c2ecf20Sopenharmony_ci struct resource *dma_res; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ 318c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | \ 328c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | \ 338c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | \ 348c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | \ 358c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct snd_pcm_hardware mmp_pcm_hardware[] = { 388c2ecf20Sopenharmony_ci { 398c2ecf20Sopenharmony_ci .info = MMP_PCM_INFO, 408c2ecf20Sopenharmony_ci .period_bytes_min = 1024, 418c2ecf20Sopenharmony_ci .period_bytes_max = 2048, 428c2ecf20Sopenharmony_ci .periods_min = 2, 438c2ecf20Sopenharmony_ci .periods_max = 32, 448c2ecf20Sopenharmony_ci .buffer_bytes_max = 4096, 458c2ecf20Sopenharmony_ci .fifo_size = 32, 468c2ecf20Sopenharmony_ci }, 478c2ecf20Sopenharmony_ci { 488c2ecf20Sopenharmony_ci .info = MMP_PCM_INFO, 498c2ecf20Sopenharmony_ci .period_bytes_min = 1024, 508c2ecf20Sopenharmony_ci .period_bytes_max = 2048, 518c2ecf20Sopenharmony_ci .periods_min = 2, 528c2ecf20Sopenharmony_ci .periods_max = 32, 538c2ecf20Sopenharmony_ci .buffer_bytes_max = 4096, 548c2ecf20Sopenharmony_ci .fifo_size = 32, 558c2ecf20Sopenharmony_ci }, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int mmp_pcm_hw_params(struct snd_soc_component *component, 598c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 608c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 638c2ecf20Sopenharmony_ci struct dma_slave_config slave_config; 648c2ecf20Sopenharmony_ci int ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci ret = 678c2ecf20Sopenharmony_ci snd_dmaengine_pcm_prepare_slave_config(substream, params, 688c2ecf20Sopenharmony_ci &slave_config); 698c2ecf20Sopenharmony_ci if (ret) 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(chan, &slave_config); 738c2ecf20Sopenharmony_ci if (ret) 748c2ecf20Sopenharmony_ci return ret; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int mmp_pcm_trigger(struct snd_soc_component *component, 828c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_trigger(substream, cmd); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t mmp_pcm_pointer(struct snd_soc_component *component, 888c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_pointer(substream); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic bool filter(struct dma_chan *chan, void *param) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct mmp_dma_data *dma_data = param; 968c2ecf20Sopenharmony_ci bool found = false; 978c2ecf20Sopenharmony_ci char *devname; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name, 1008c2ecf20Sopenharmony_ci dma_data->ssp_id); 1018c2ecf20Sopenharmony_ci if (devname && (strcmp(dev_name(chan->device->dev), devname) == 0) && 1028c2ecf20Sopenharmony_ci (chan->chan_id == dma_data->dma_res->start)) { 1038c2ecf20Sopenharmony_ci found = true; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci kfree(devname); 1078c2ecf20Sopenharmony_ci return found; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int mmp_pcm_open(struct snd_soc_component *component, 1118c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1148c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(component->dev); 1158c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 1168c2ecf20Sopenharmony_ci struct mmp_dma_data dma_data; 1178c2ecf20Sopenharmony_ci struct resource *r; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream); 1208c2ecf20Sopenharmony_ci if (!r) 1218c2ecf20Sopenharmony_ci return -EBUSY; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, 1248c2ecf20Sopenharmony_ci &mmp_pcm_hardware[substream->stream]); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci dma_data.dma_res = r; 1278c2ecf20Sopenharmony_ci dma_data.ssp_id = cpu_dai->id; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_open_request_chan(substream, filter, 1308c2ecf20Sopenharmony_ci &dma_data); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int mmp_pcm_close(struct snd_soc_component *component, 1348c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return snd_dmaengine_pcm_close_release_chan(substream); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int mmp_pcm_mmap(struct snd_soc_component *component, 1408c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 1418c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1448c2ecf20Sopenharmony_ci unsigned long off = vma->vm_pgoff; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 1478c2ecf20Sopenharmony_ci return remap_pfn_range(vma, vma->vm_start, 1488c2ecf20Sopenharmony_ci __phys_to_pfn(runtime->dma_addr) + off, 1498c2ecf20Sopenharmony_ci vma->vm_end - vma->vm_start, vma->vm_page_prot); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void mmp_pcm_free_dma_buffers(struct snd_soc_component *component, 1538c2ecf20Sopenharmony_ci struct snd_pcm *pcm) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1568c2ecf20Sopenharmony_ci struct snd_dma_buffer *buf; 1578c2ecf20Sopenharmony_ci int stream; 1588c2ecf20Sopenharmony_ci struct gen_pool *gpool; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci gpool = sram_get_gpool("asram"); 1618c2ecf20Sopenharmony_ci if (!gpool) 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci for (stream = 0; stream < 2; stream++) { 1658c2ecf20Sopenharmony_ci size_t size = mmp_pcm_hardware[stream].buffer_bytes_max; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci substream = pcm->streams[stream].substream; 1688c2ecf20Sopenharmony_ci if (!substream) 1698c2ecf20Sopenharmony_ci continue; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci buf = &substream->dma_buffer; 1728c2ecf20Sopenharmony_ci if (!buf->area) 1738c2ecf20Sopenharmony_ci continue; 1748c2ecf20Sopenharmony_ci gen_pool_free(gpool, (unsigned long)buf->area, size); 1758c2ecf20Sopenharmony_ci buf->area = NULL; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream, 1818c2ecf20Sopenharmony_ci int stream) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct snd_dma_buffer *buf = &substream->dma_buffer; 1848c2ecf20Sopenharmony_ci size_t size = mmp_pcm_hardware[stream].buffer_bytes_max; 1858c2ecf20Sopenharmony_ci struct gen_pool *gpool; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci buf->dev.type = SNDRV_DMA_TYPE_DEV; 1888c2ecf20Sopenharmony_ci buf->dev.dev = substream->pcm->card->dev; 1898c2ecf20Sopenharmony_ci buf->private_data = NULL; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci gpool = sram_get_gpool("asram"); 1928c2ecf20Sopenharmony_ci if (!gpool) 1938c2ecf20Sopenharmony_ci return -ENOMEM; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci buf->area = gen_pool_dma_alloc(gpool, size, &buf->addr); 1968c2ecf20Sopenharmony_ci if (!buf->area) 1978c2ecf20Sopenharmony_ci return -ENOMEM; 1988c2ecf20Sopenharmony_ci buf->bytes = size; 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int mmp_pcm_new(struct snd_soc_component *component, 2038c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 2068c2ecf20Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 2078c2ecf20Sopenharmony_ci int ret = 0, stream; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci for (stream = 0; stream < 2; stream++) { 2108c2ecf20Sopenharmony_ci substream = pcm->streams[stream].substream; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = mmp_pcm_preallocate_dma_buffer(substream, stream); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci goto err; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cierr: 2208c2ecf20Sopenharmony_ci mmp_pcm_free_dma_buffers(component, pcm); 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver mmp_soc_component = { 2258c2ecf20Sopenharmony_ci .name = DRV_NAME, 2268c2ecf20Sopenharmony_ci .open = mmp_pcm_open, 2278c2ecf20Sopenharmony_ci .close = mmp_pcm_close, 2288c2ecf20Sopenharmony_ci .hw_params = mmp_pcm_hw_params, 2298c2ecf20Sopenharmony_ci .trigger = mmp_pcm_trigger, 2308c2ecf20Sopenharmony_ci .pointer = mmp_pcm_pointer, 2318c2ecf20Sopenharmony_ci .mmap = mmp_pcm_mmap, 2328c2ecf20Sopenharmony_ci .pcm_construct = mmp_pcm_new, 2338c2ecf20Sopenharmony_ci .pcm_destruct = mmp_pcm_free_dma_buffers, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int mmp_pcm_probe(struct platform_device *pdev) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct mmp_audio_platdata *pdata = pdev->dev.platform_data; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (pdata) { 2418c2ecf20Sopenharmony_ci mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max = 2428c2ecf20Sopenharmony_ci pdata->buffer_max_playback; 2438c2ecf20Sopenharmony_ci mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max = 2448c2ecf20Sopenharmony_ci pdata->period_max_playback; 2458c2ecf20Sopenharmony_ci mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max = 2468c2ecf20Sopenharmony_ci pdata->buffer_max_capture; 2478c2ecf20Sopenharmony_ci mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max = 2488c2ecf20Sopenharmony_ci pdata->period_max_capture; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(&pdev->dev, &mmp_soc_component, 2518c2ecf20Sopenharmony_ci NULL, 0); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic struct platform_driver mmp_pcm_driver = { 2558c2ecf20Sopenharmony_ci .driver = { 2568c2ecf20Sopenharmony_ci .name = "mmp-pcm-audio", 2578c2ecf20Sopenharmony_ci }, 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci .probe = mmp_pcm_probe, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cimodule_platform_driver(mmp_pcm_driver); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); 2658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MMP Soc Audio DMA module"); 2668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2678c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mmp-pcm-audio"); 268