18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Freescale ASRC ALSA SoC Platform (DMA) driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2014 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Author: Nicolin Chen <nicoleotsuka@gmail.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_data/dma-imx.h> 128c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 138c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "fsl_asrc_common.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define FSL_ASRC_DMABUF_SIZE (256 * 1024) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic struct snd_pcm_hardware snd_imx_hardware = { 208c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 218c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 228c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 238c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID, 248c2ecf20Sopenharmony_ci .buffer_bytes_max = FSL_ASRC_DMABUF_SIZE, 258c2ecf20Sopenharmony_ci .period_bytes_min = 128, 268c2ecf20Sopenharmony_ci .period_bytes_max = 65535, /* Limited by SDMA engine */ 278c2ecf20Sopenharmony_ci .periods_min = 2, 288c2ecf20Sopenharmony_ci .periods_max = 255, 298c2ecf20Sopenharmony_ci .fifo_size = 0, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic bool filter(struct dma_chan *chan, void *param) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci if (!imx_dma_is_general_purpose(chan)) 358c2ecf20Sopenharmony_ci return false; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci chan->private = param; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return true; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void fsl_asrc_dma_complete(void *arg) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = arg; 458c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 468c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair = runtime->private_data; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci pair->pos += snd_pcm_lib_period_bytes(substream); 498c2ecf20Sopenharmony_ci if (pair->pos >= snd_pcm_lib_buffer_bytes(substream)) 508c2ecf20Sopenharmony_ci pair->pos = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream, 568c2ecf20Sopenharmony_ci struct snd_soc_component *component) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN; 598c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 608c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair = runtime->private_data; 618c2ecf20Sopenharmony_ci struct device *dev = component->dev; 628c2ecf20Sopenharmony_ci unsigned long flags = DMA_CTRL_ACK; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* Prepare and submit Front-End DMA channel */ 658c2ecf20Sopenharmony_ci if (!substream->runtime->no_period_wakeup) 668c2ecf20Sopenharmony_ci flags |= DMA_PREP_INTERRUPT; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci pair->pos = 0; 698c2ecf20Sopenharmony_ci pair->desc[!dir] = dmaengine_prep_dma_cyclic( 708c2ecf20Sopenharmony_ci pair->dma_chan[!dir], runtime->dma_addr, 718c2ecf20Sopenharmony_ci snd_pcm_lib_buffer_bytes(substream), 728c2ecf20Sopenharmony_ci snd_pcm_lib_period_bytes(substream), 738c2ecf20Sopenharmony_ci dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, flags); 748c2ecf20Sopenharmony_ci if (!pair->desc[!dir]) { 758c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare slave DMA for Front-End\n"); 768c2ecf20Sopenharmony_ci return -ENOMEM; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci pair->desc[!dir]->callback = fsl_asrc_dma_complete; 808c2ecf20Sopenharmony_ci pair->desc[!dir]->callback_param = substream; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci dmaengine_submit(pair->desc[!dir]); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Prepare and submit Back-End DMA channel */ 858c2ecf20Sopenharmony_ci pair->desc[dir] = dmaengine_prep_dma_cyclic( 868c2ecf20Sopenharmony_ci pair->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0); 878c2ecf20Sopenharmony_ci if (!pair->desc[dir]) { 888c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare slave DMA for Back-End\n"); 898c2ecf20Sopenharmony_ci return -ENOMEM; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci dmaengine_submit(pair->desc[dir]); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int fsl_asrc_dma_trigger(struct snd_soc_component *component, 988c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1018c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair = runtime->private_data; 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci switch (cmd) { 1058c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1068c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 1078c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1088c2ecf20Sopenharmony_ci ret = fsl_asrc_dma_prepare_and_submit(substream, component); 1098c2ecf20Sopenharmony_ci if (ret) 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci dma_async_issue_pending(pair->dma_chan[IN]); 1128c2ecf20Sopenharmony_ci dma_async_issue_pending(pair->dma_chan[OUT]); 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 1158c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 1168c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1178c2ecf20Sopenharmony_ci dmaengine_terminate_all(pair->dma_chan[OUT]); 1188c2ecf20Sopenharmony_ci dmaengine_terminate_all(pair->dma_chan[IN]); 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci default: 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int fsl_asrc_dma_hw_params(struct snd_soc_component *component, 1288c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 1298c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; 1328c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1338c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 1348c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL; 1358c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; 1368c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1378c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair = runtime->private_data; 1388c2ecf20Sopenharmony_ci struct dma_chan *tmp_chan = NULL, *be_chan = NULL; 1398c2ecf20Sopenharmony_ci struct snd_soc_component *component_be = NULL; 1408c2ecf20Sopenharmony_ci struct fsl_asrc *asrc = pair->asrc; 1418c2ecf20Sopenharmony_ci struct dma_slave_config config_fe, config_be; 1428c2ecf20Sopenharmony_ci enum asrc_pair_index index = pair->index; 1438c2ecf20Sopenharmony_ci struct device *dev = component->dev; 1448c2ecf20Sopenharmony_ci int stream = substream->stream; 1458c2ecf20Sopenharmony_ci struct imx_dma_data *tmp_data; 1468c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 1478c2ecf20Sopenharmony_ci struct device *dev_be; 1488c2ecf20Sopenharmony_ci u8 dir = tx ? OUT : IN; 1498c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 1508c2ecf20Sopenharmony_ci int ret, width; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Fetch the Back-End dma_data from DPCM */ 1538c2ecf20Sopenharmony_ci for_each_dpcm_be(rtd, stream, dpcm) { 1548c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 1558c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream_be; 1568c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = asoc_rtd_to_cpu(be, 0); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (dpcm->fe != rtd) 1598c2ecf20Sopenharmony_ci continue; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci substream_be = snd_soc_dpcm_get_substream(be, stream); 1628c2ecf20Sopenharmony_ci dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be); 1638c2ecf20Sopenharmony_ci dev_be = dai->dev; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!dma_params_be) { 1688c2ecf20Sopenharmony_ci dev_err(dev, "failed to get the substream of Back-End\n"); 1698c2ecf20Sopenharmony_ci return -EINVAL; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Override dma_data of the Front-End and config its dmaengine */ 1738c2ecf20Sopenharmony_ci dma_params_fe = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 1748c2ecf20Sopenharmony_ci dma_params_fe->addr = asrc->paddr + asrc->get_fifo_addr(!dir, index); 1758c2ecf20Sopenharmony_ci dma_params_fe->maxburst = dma_params_be->maxburst; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci pair->dma_chan[!dir] = asrc->get_dma_channel(pair, !dir); 1788c2ecf20Sopenharmony_ci if (!pair->dma_chan[!dir]) { 1798c2ecf20Sopenharmony_ci dev_err(dev, "failed to request DMA channel\n"); 1808c2ecf20Sopenharmony_ci return -EINVAL; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci memset(&config_fe, 0, sizeof(config_fe)); 1848c2ecf20Sopenharmony_ci ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe); 1858c2ecf20Sopenharmony_ci if (ret) { 1868c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare DMA config for Front-End\n"); 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(pair->dma_chan[!dir], &config_fe); 1918c2ecf20Sopenharmony_ci if (ret) { 1928c2ecf20Sopenharmony_ci dev_err(dev, "failed to config DMA channel for Front-End\n"); 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Request and config DMA channel for Back-End */ 1978c2ecf20Sopenharmony_ci dma_cap_zero(mask); 1988c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 1998c2ecf20Sopenharmony_ci dma_cap_set(DMA_CYCLIC, mask); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * The Back-End device might have already requested a DMA channel, 2038c2ecf20Sopenharmony_ci * so try to reuse it first, and then request a new one upon NULL. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME); 2068c2ecf20Sopenharmony_ci if (component_be) { 2078c2ecf20Sopenharmony_ci be_chan = soc_component_to_pcm(component_be)->chan[substream->stream]; 2088c2ecf20Sopenharmony_ci tmp_chan = be_chan; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci if (!tmp_chan) { 2118c2ecf20Sopenharmony_ci tmp_chan = dma_request_chan(dev_be, tx ? "tx" : "rx"); 2128c2ecf20Sopenharmony_ci if (IS_ERR(tmp_chan)) { 2138c2ecf20Sopenharmony_ci dev_err(dev, "failed to request DMA channel for Back-End\n"); 2148c2ecf20Sopenharmony_ci return -EINVAL; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * An EDMA DEV_TO_DEV channel is fixed and bound with DMA event of each 2208c2ecf20Sopenharmony_ci * peripheral, unlike SDMA channel that is allocated dynamically. So no 2218c2ecf20Sopenharmony_ci * need to configure dma_request and dma_request2, but get dma_chan of 2228c2ecf20Sopenharmony_ci * Back-End device directly via dma_request_chan. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci if (!asrc->use_edma) { 2258c2ecf20Sopenharmony_ci /* Get DMA request of Back-End */ 2268c2ecf20Sopenharmony_ci tmp_data = tmp_chan->private; 2278c2ecf20Sopenharmony_ci pair->dma_data.dma_request = tmp_data->dma_request; 2288c2ecf20Sopenharmony_ci if (!be_chan) 2298c2ecf20Sopenharmony_ci dma_release_channel(tmp_chan); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Get DMA request of Front-End */ 2328c2ecf20Sopenharmony_ci tmp_chan = asrc->get_dma_channel(pair, dir); 2338c2ecf20Sopenharmony_ci tmp_data = tmp_chan->private; 2348c2ecf20Sopenharmony_ci pair->dma_data.dma_request2 = tmp_data->dma_request; 2358c2ecf20Sopenharmony_ci pair->dma_data.peripheral_type = tmp_data->peripheral_type; 2368c2ecf20Sopenharmony_ci pair->dma_data.priority = tmp_data->priority; 2378c2ecf20Sopenharmony_ci dma_release_channel(tmp_chan); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci pair->dma_chan[dir] = 2408c2ecf20Sopenharmony_ci dma_request_channel(mask, filter, &pair->dma_data); 2418c2ecf20Sopenharmony_ci pair->req_dma_chan = true; 2428c2ecf20Sopenharmony_ci } else { 2438c2ecf20Sopenharmony_ci pair->dma_chan[dir] = tmp_chan; 2448c2ecf20Sopenharmony_ci /* Do not flag to release if we are reusing the Back-End one */ 2458c2ecf20Sopenharmony_ci pair->req_dma_chan = !be_chan; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!pair->dma_chan[dir]) { 2498c2ecf20Sopenharmony_ci dev_err(dev, "failed to request DMA channel for Back-End\n"); 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci width = snd_pcm_format_physical_width(asrc->asrc_format); 2548c2ecf20Sopenharmony_ci if (width < 8 || width > 64) 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci else if (width == 8) 2578c2ecf20Sopenharmony_ci buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; 2588c2ecf20Sopenharmony_ci else if (width == 16) 2598c2ecf20Sopenharmony_ci buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; 2608c2ecf20Sopenharmony_ci else if (width == 24) 2618c2ecf20Sopenharmony_ci buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES; 2628c2ecf20Sopenharmony_ci else if (width <= 32) 2638c2ecf20Sopenharmony_ci buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; 2648c2ecf20Sopenharmony_ci else 2658c2ecf20Sopenharmony_ci buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci config_be.direction = DMA_DEV_TO_DEV; 2688c2ecf20Sopenharmony_ci config_be.src_addr_width = buswidth; 2698c2ecf20Sopenharmony_ci config_be.src_maxburst = dma_params_be->maxburst; 2708c2ecf20Sopenharmony_ci config_be.dst_addr_width = buswidth; 2718c2ecf20Sopenharmony_ci config_be.dst_maxburst = dma_params_be->maxburst; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (tx) { 2748c2ecf20Sopenharmony_ci config_be.src_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index); 2758c2ecf20Sopenharmony_ci config_be.dst_addr = dma_params_be->addr; 2768c2ecf20Sopenharmony_ci } else { 2778c2ecf20Sopenharmony_ci config_be.dst_addr = asrc->paddr + asrc->get_fifo_addr(IN, index); 2788c2ecf20Sopenharmony_ci config_be.src_addr = dma_params_be->addr; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be); 2828c2ecf20Sopenharmony_ci if (ret) { 2838c2ecf20Sopenharmony_ci dev_err(dev, "failed to config DMA channel for Back-End\n"); 2848c2ecf20Sopenharmony_ci if (pair->req_dma_chan) 2858c2ecf20Sopenharmony_ci dma_release_channel(pair->dma_chan[dir]); 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int fsl_asrc_dma_hw_free(struct snd_soc_component *component, 2958c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 2988c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2998c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair = runtime->private_data; 3008c2ecf20Sopenharmony_ci u8 dir = tx ? OUT : IN; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, NULL); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (pair->dma_chan[!dir]) 3058c2ecf20Sopenharmony_ci dma_release_channel(pair->dma_chan[!dir]); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* release dev_to_dev chan if we aren't reusing the Back-End one */ 3088c2ecf20Sopenharmony_ci if (pair->dma_chan[dir] && pair->req_dma_chan) 3098c2ecf20Sopenharmony_ci dma_release_channel(pair->dma_chan[dir]); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci pair->dma_chan[!dir] = NULL; 3128c2ecf20Sopenharmony_ci pair->dma_chan[dir] = NULL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int fsl_asrc_dma_startup(struct snd_soc_component *component, 3188c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 3218c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 3228c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3238c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 3248c2ecf20Sopenharmony_ci struct device *dev = component->dev; 3258c2ecf20Sopenharmony_ci struct fsl_asrc *asrc = dev_get_drvdata(dev); 3268c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair; 3278c2ecf20Sopenharmony_ci struct dma_chan *tmp_chan = NULL; 3288c2ecf20Sopenharmony_ci u8 dir = tx ? OUT : IN; 3298c2ecf20Sopenharmony_ci bool release_pair = true; 3308c2ecf20Sopenharmony_ci int ret = 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(substream->runtime, 3338c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 3348c2ecf20Sopenharmony_ci if (ret < 0) { 3358c2ecf20Sopenharmony_ci dev_err(dev, "failed to set pcm hw params periods\n"); 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL); 3408c2ecf20Sopenharmony_ci if (!pair) 3418c2ecf20Sopenharmony_ci return -ENOMEM; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci pair->asrc = asrc; 3448c2ecf20Sopenharmony_ci pair->private = (void *)pair + sizeof(struct fsl_asrc_pair); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci runtime->private_data = pair; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Request a dummy pair, which will be released later. 3498c2ecf20Sopenharmony_ci * Request pair function needs channel num as input, for this 3508c2ecf20Sopenharmony_ci * dummy pair, we just request "1" channel temporarily. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci ret = asrc->request_pair(1, pair); 3538c2ecf20Sopenharmony_ci if (ret < 0) { 3548c2ecf20Sopenharmony_ci dev_err(dev, "failed to request asrc pair\n"); 3558c2ecf20Sopenharmony_ci goto req_pair_err; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Request a dummy dma channel, which will be released later. */ 3598c2ecf20Sopenharmony_ci tmp_chan = asrc->get_dma_channel(pair, dir); 3608c2ecf20Sopenharmony_ci if (!tmp_chan) { 3618c2ecf20Sopenharmony_ci dev_err(dev, "failed to get dma channel\n"); 3628c2ecf20Sopenharmony_ci ret = -EINVAL; 3638c2ecf20Sopenharmony_ci goto dma_chan_err; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Refine the snd_imx_hardware according to caps of DMA. */ 3698c2ecf20Sopenharmony_ci ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, 3708c2ecf20Sopenharmony_ci dma_data, 3718c2ecf20Sopenharmony_ci &snd_imx_hardware, 3728c2ecf20Sopenharmony_ci tmp_chan); 3738c2ecf20Sopenharmony_ci if (ret < 0) { 3748c2ecf20Sopenharmony_ci dev_err(dev, "failed to refine runtime hwparams\n"); 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci release_pair = false; 3798c2ecf20Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciout: 3828c2ecf20Sopenharmony_ci dma_release_channel(tmp_chan); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cidma_chan_err: 3858c2ecf20Sopenharmony_ci asrc->release_pair(pair); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cireq_pair_err: 3888c2ecf20Sopenharmony_ci if (release_pair) 3898c2ecf20Sopenharmony_ci kfree(pair); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int fsl_asrc_dma_shutdown(struct snd_soc_component *component, 3958c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3988c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair = runtime->private_data; 3998c2ecf20Sopenharmony_ci struct fsl_asrc *asrc; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!pair) 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci asrc = pair->asrc; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (asrc->pair[pair->index] == pair) 4078c2ecf20Sopenharmony_ci asrc->pair[pair->index] = NULL; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci kfree(pair); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 4158c2ecf20Sopenharmony_cifsl_asrc_dma_pcm_pointer(struct snd_soc_component *component, 4168c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4198c2ecf20Sopenharmony_ci struct fsl_asrc_pair *pair = runtime->private_data; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, pair->pos); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int fsl_asrc_dma_pcm_new(struct snd_soc_component *component, 4258c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct snd_card *card = rtd->card->snd_card; 4288c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 4298c2ecf20Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 4308c2ecf20Sopenharmony_ci int ret, i; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 4338c2ecf20Sopenharmony_ci if (ret) { 4348c2ecf20Sopenharmony_ci dev_err(card->dev, "failed to set DMA mask\n"); 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci for_each_pcm_streams(i) { 4398c2ecf20Sopenharmony_ci substream = pcm->streams[i].substream; 4408c2ecf20Sopenharmony_ci if (!substream) 4418c2ecf20Sopenharmony_ci continue; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 4448c2ecf20Sopenharmony_ci FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer); 4458c2ecf20Sopenharmony_ci if (ret) { 4468c2ecf20Sopenharmony_ci dev_err(card->dev, "failed to allocate DMA buffer\n"); 4478c2ecf20Sopenharmony_ci goto err; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cierr: 4548c2ecf20Sopenharmony_ci if (--i == 0 && pcm->streams[i].substream) 4558c2ecf20Sopenharmony_ci snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void fsl_asrc_dma_pcm_free(struct snd_soc_component *component, 4618c2ecf20Sopenharmony_ci struct snd_pcm *pcm) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 4648c2ecf20Sopenharmony_ci int i; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci for_each_pcm_streams(i) { 4678c2ecf20Sopenharmony_ci substream = pcm->streams[i].substream; 4688c2ecf20Sopenharmony_ci if (!substream) 4698c2ecf20Sopenharmony_ci continue; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci snd_dma_free_pages(&substream->dma_buffer); 4728c2ecf20Sopenharmony_ci substream->dma_buffer.area = NULL; 4738c2ecf20Sopenharmony_ci substream->dma_buffer.addr = 0; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistruct snd_soc_component_driver fsl_asrc_component = { 4788c2ecf20Sopenharmony_ci .name = DRV_NAME, 4798c2ecf20Sopenharmony_ci .hw_params = fsl_asrc_dma_hw_params, 4808c2ecf20Sopenharmony_ci .hw_free = fsl_asrc_dma_hw_free, 4818c2ecf20Sopenharmony_ci .trigger = fsl_asrc_dma_trigger, 4828c2ecf20Sopenharmony_ci .open = fsl_asrc_dma_startup, 4838c2ecf20Sopenharmony_ci .close = fsl_asrc_dma_shutdown, 4848c2ecf20Sopenharmony_ci .pointer = fsl_asrc_dma_pcm_pointer, 4858c2ecf20Sopenharmony_ci .pcm_construct = fsl_asrc_dma_pcm_new, 4868c2ecf20Sopenharmony_ci .pcm_destruct = fsl_asrc_dma_pcm_free, 4878c2ecf20Sopenharmony_ci}; 4888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_asrc_component); 489