162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (C) 2019 Spreadtrum Communications Inc. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 562306a36Sopenharmony_ci#include <linux/dmaengine.h> 662306a36Sopenharmony_ci#include <linux/dma/sprd-dma.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <sound/pcm.h> 1262306a36Sopenharmony_ci#include <sound/pcm_params.h> 1362306a36Sopenharmony_ci#include <sound/soc.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "sprd-pcm-dma.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define SPRD_PCM_DMA_LINKLIST_SIZE 64 1862306a36Sopenharmony_ci#define SPRD_PCM_DMA_BRUST_LEN 640 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct sprd_pcm_dma_data { 2162306a36Sopenharmony_ci struct dma_chan *chan; 2262306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 2362306a36Sopenharmony_ci dma_cookie_t cookie; 2462306a36Sopenharmony_ci dma_addr_t phys; 2562306a36Sopenharmony_ci void *virt; 2662306a36Sopenharmony_ci int pre_pointer; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct sprd_pcm_dma_private { 3062306a36Sopenharmony_ci struct snd_pcm_substream *substream; 3162306a36Sopenharmony_ci struct sprd_pcm_dma_params *params; 3262306a36Sopenharmony_ci struct sprd_pcm_dma_data data[SPRD_PCM_CHANNEL_MAX]; 3362306a36Sopenharmony_ci int hw_chan; 3462306a36Sopenharmony_ci int dma_addr_offset; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct snd_pcm_hardware sprd_pcm_hardware = { 3862306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 3962306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | 4062306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 4162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, 4262306a36Sopenharmony_ci .period_bytes_min = 1, 4362306a36Sopenharmony_ci .period_bytes_max = 64 * 1024, 4462306a36Sopenharmony_ci .periods_min = 1, 4562306a36Sopenharmony_ci .periods_max = PAGE_SIZE / SPRD_PCM_DMA_LINKLIST_SIZE, 4662306a36Sopenharmony_ci .buffer_bytes_max = 64 * 1024, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int sprd_pcm_open(struct snd_soc_component *component, 5062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5362306a36Sopenharmony_ci struct device *dev = component->dev; 5462306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private; 5562306a36Sopenharmony_ci int hw_chan = SPRD_PCM_CHANNEL_MAX; 5662306a36Sopenharmony_ci int size, ret, i; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &sprd_pcm_hardware); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 6162306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 6262306a36Sopenharmony_ci SPRD_PCM_DMA_BRUST_LEN); 6362306a36Sopenharmony_ci if (ret < 0) 6462306a36Sopenharmony_ci return ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 6762306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 6862306a36Sopenharmony_ci SPRD_PCM_DMA_BRUST_LEN); 6962306a36Sopenharmony_ci if (ret < 0) 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, 7362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 7462306a36Sopenharmony_ci if (ret < 0) 7562306a36Sopenharmony_ci return ret; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci dma_private = devm_kzalloc(dev, sizeof(*dma_private), GFP_KERNEL); 7862306a36Sopenharmony_ci if (!dma_private) 7962306a36Sopenharmony_ci return -ENOMEM; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci size = runtime->hw.periods_max * SPRD_PCM_DMA_LINKLIST_SIZE; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci for (i = 0; i < hw_chan; i++) { 8462306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci data->virt = dmam_alloc_coherent(dev, size, &data->phys, 8762306a36Sopenharmony_ci GFP_KERNEL); 8862306a36Sopenharmony_ci if (!data->virt) { 8962306a36Sopenharmony_ci ret = -ENOMEM; 9062306a36Sopenharmony_ci goto error; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci dma_private->hw_chan = hw_chan; 9562306a36Sopenharmony_ci runtime->private_data = dma_private; 9662306a36Sopenharmony_ci dma_private->substream = substream; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cierror: 10162306a36Sopenharmony_ci for (i = 0; i < hw_chan; i++) { 10262306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (data->virt) 10562306a36Sopenharmony_ci dmam_free_coherent(dev, size, data->virt, data->phys); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci devm_kfree(dev, dma_private); 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int sprd_pcm_close(struct snd_soc_component *component, 11362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11662306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private = runtime->private_data; 11762306a36Sopenharmony_ci struct device *dev = component->dev; 11862306a36Sopenharmony_ci int size = runtime->hw.periods_max * SPRD_PCM_DMA_LINKLIST_SIZE; 11962306a36Sopenharmony_ci int i; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci for (i = 0; i < dma_private->hw_chan; i++) { 12262306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dmam_free_coherent(dev, size, data->virt, data->phys); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci devm_kfree(dev, dma_private); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void sprd_pcm_dma_complete(void *data) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private = data; 13562306a36Sopenharmony_ci struct snd_pcm_substream *substream = dma_private->substream; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void sprd_pcm_release_dma_channel(struct snd_pcm_substream *substream) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 14362306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private = runtime->private_data; 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci for (i = 0; i < SPRD_PCM_CHANNEL_MAX; i++) { 14762306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (data->chan) { 15062306a36Sopenharmony_ci dma_release_channel(data->chan); 15162306a36Sopenharmony_ci data->chan = NULL; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int sprd_pcm_request_dma_channel(struct snd_soc_component *component, 15762306a36Sopenharmony_ci struct snd_pcm_substream *substream, 15862306a36Sopenharmony_ci int channels) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 16162306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private = runtime->private_data; 16262306a36Sopenharmony_ci struct device *dev = component->dev; 16362306a36Sopenharmony_ci struct sprd_pcm_dma_params *dma_params = dma_private->params; 16462306a36Sopenharmony_ci int i; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (channels > SPRD_PCM_CHANNEL_MAX) { 16762306a36Sopenharmony_ci dev_err(dev, "invalid dma channel number:%d\n", channels); 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0; i < channels; i++) { 17262306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci data->chan = dma_request_slave_channel(dev, 17562306a36Sopenharmony_ci dma_params->chan_name[i]); 17662306a36Sopenharmony_ci if (!data->chan) { 17762306a36Sopenharmony_ci dev_err(dev, "failed to request dma channel:%s\n", 17862306a36Sopenharmony_ci dma_params->chan_name[i]); 17962306a36Sopenharmony_ci sprd_pcm_release_dma_channel(substream); 18062306a36Sopenharmony_ci return -ENODEV; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int sprd_pcm_hw_params(struct snd_soc_component *component, 18862306a36Sopenharmony_ci struct snd_pcm_substream *substream, 18962306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 19262306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private = runtime->private_data; 19362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 19462306a36Sopenharmony_ci struct sprd_pcm_dma_params *dma_params; 19562306a36Sopenharmony_ci size_t totsize = params_buffer_bytes(params); 19662306a36Sopenharmony_ci size_t period = params_period_bytes(params); 19762306a36Sopenharmony_ci int channels = params_channels(params); 19862306a36Sopenharmony_ci int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 19962306a36Sopenharmony_ci struct scatterlist *sg; 20062306a36Sopenharmony_ci unsigned long flags; 20162306a36Sopenharmony_ci int ret, i, j, sg_num; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 20462306a36Sopenharmony_ci if (!dma_params) { 20562306a36Sopenharmony_ci dev_warn(component->dev, "no dma parameters setting\n"); 20662306a36Sopenharmony_ci dma_private->params = NULL; 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!dma_private->params) { 21162306a36Sopenharmony_ci dma_private->params = dma_params; 21262306a36Sopenharmony_ci ret = sprd_pcm_request_dma_channel(component, 21362306a36Sopenharmony_ci substream, channels); 21462306a36Sopenharmony_ci if (ret) 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci sg_num = totsize / period; 21962306a36Sopenharmony_ci dma_private->dma_addr_offset = totsize / channels; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci sg = devm_kcalloc(component->dev, sg_num, sizeof(*sg), GFP_KERNEL); 22262306a36Sopenharmony_ci if (!sg) { 22362306a36Sopenharmony_ci ret = -ENOMEM; 22462306a36Sopenharmony_ci goto sg_err; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci for (i = 0; i < channels; i++) { 22862306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 22962306a36Sopenharmony_ci struct dma_chan *chan = data->chan; 23062306a36Sopenharmony_ci struct dma_slave_config config = { }; 23162306a36Sopenharmony_ci struct sprd_dma_linklist link = { }; 23262306a36Sopenharmony_ci enum dma_transfer_direction dir; 23362306a36Sopenharmony_ci struct scatterlist *sgt = sg; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci config.src_maxburst = dma_params->fragment_len[i]; 23662306a36Sopenharmony_ci config.src_addr_width = dma_params->datawidth[i]; 23762306a36Sopenharmony_ci config.dst_addr_width = dma_params->datawidth[i]; 23862306a36Sopenharmony_ci if (is_playback) { 23962306a36Sopenharmony_ci config.src_addr = runtime->dma_addr + 24062306a36Sopenharmony_ci i * dma_private->dma_addr_offset; 24162306a36Sopenharmony_ci config.dst_addr = dma_params->dev_phys[i]; 24262306a36Sopenharmony_ci dir = DMA_MEM_TO_DEV; 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci config.src_addr = dma_params->dev_phys[i]; 24562306a36Sopenharmony_ci config.dst_addr = runtime->dma_addr + 24662306a36Sopenharmony_ci i * dma_private->dma_addr_offset; 24762306a36Sopenharmony_ci dir = DMA_DEV_TO_MEM; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci sg_init_table(sgt, sg_num); 25162306a36Sopenharmony_ci for (j = 0; j < sg_num; j++, sgt++) { 25262306a36Sopenharmony_ci u32 sg_len = period / channels; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci sg_dma_len(sgt) = sg_len; 25562306a36Sopenharmony_ci sg_dma_address(sgt) = runtime->dma_addr + 25662306a36Sopenharmony_ci i * dma_private->dma_addr_offset + sg_len * j; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* 26062306a36Sopenharmony_ci * Configure the link-list address for the DMA engine link-list 26162306a36Sopenharmony_ci * mode. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci link.virt_addr = (unsigned long)data->virt; 26462306a36Sopenharmony_ci link.phy_addr = data->phys; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = dmaengine_slave_config(chan, &config); 26762306a36Sopenharmony_ci if (ret) { 26862306a36Sopenharmony_ci dev_err(component->dev, 26962306a36Sopenharmony_ci "failed to set slave configuration: %d\n", ret); 27062306a36Sopenharmony_ci goto config_err; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * We configure the DMA request mode, interrupt mode, channel 27562306a36Sopenharmony_ci * mode and channel trigger mode by the flags. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE, SPRD_DMA_NO_TRG, 27862306a36Sopenharmony_ci SPRD_DMA_FRAG_REQ, SPRD_DMA_TRANS_INT); 27962306a36Sopenharmony_ci data->desc = chan->device->device_prep_slave_sg(chan, sg, 28062306a36Sopenharmony_ci sg_num, dir, 28162306a36Sopenharmony_ci flags, &link); 28262306a36Sopenharmony_ci if (!data->desc) { 28362306a36Sopenharmony_ci dev_err(component->dev, "failed to prepare slave sg\n"); 28462306a36Sopenharmony_ci ret = -ENOMEM; 28562306a36Sopenharmony_ci goto config_err; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (!runtime->no_period_wakeup) { 28962306a36Sopenharmony_ci data->desc->callback = sprd_pcm_dma_complete; 29062306a36Sopenharmony_ci data->desc->callback_param = dma_private; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci devm_kfree(component->dev, sg); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciconfig_err: 29962306a36Sopenharmony_ci devm_kfree(component->dev, sg); 30062306a36Sopenharmony_cisg_err: 30162306a36Sopenharmony_ci sprd_pcm_release_dma_channel(substream); 30262306a36Sopenharmony_ci return ret; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int sprd_pcm_hw_free(struct snd_soc_component *component, 30662306a36Sopenharmony_ci struct snd_pcm_substream *substream) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci sprd_pcm_release_dma_channel(substream); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int sprd_pcm_trigger(struct snd_soc_component *component, 31462306a36Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private = 31762306a36Sopenharmony_ci substream->runtime->private_data; 31862306a36Sopenharmony_ci int ret = 0, i; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci switch (cmd) { 32162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 32262306a36Sopenharmony_ci for (i = 0; i < dma_private->hw_chan; i++) { 32362306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!data->desc) 32662306a36Sopenharmony_ci continue; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci data->cookie = dmaengine_submit(data->desc); 32962306a36Sopenharmony_ci ret = dma_submit_error(data->cookie); 33062306a36Sopenharmony_ci if (ret) { 33162306a36Sopenharmony_ci dev_err(component->dev, 33262306a36Sopenharmony_ci "failed to submit dma request: %d\n", 33362306a36Sopenharmony_ci ret); 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci dma_async_issue_pending(data->chan); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 34262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 34362306a36Sopenharmony_ci for (i = 0; i < dma_private->hw_chan; i++) { 34462306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (data->chan) 34762306a36Sopenharmony_ci dmaengine_resume(data->chan); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 35262306a36Sopenharmony_ci for (i = 0; i < dma_private->hw_chan; i++) { 35362306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (data->chan) 35662306a36Sopenharmony_ci dmaengine_terminate_async(data->chan); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 36162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 36262306a36Sopenharmony_ci for (i = 0; i < dma_private->hw_chan; i++) { 36362306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (data->chan) 36662306a36Sopenharmony_ci dmaengine_pause(data->chan); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci default: 37162306a36Sopenharmony_ci ret = -EINVAL; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic snd_pcm_uframes_t sprd_pcm_pointer(struct snd_soc_component *component, 37862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 38162306a36Sopenharmony_ci struct sprd_pcm_dma_private *dma_private = runtime->private_data; 38262306a36Sopenharmony_ci int pointer[SPRD_PCM_CHANNEL_MAX]; 38362306a36Sopenharmony_ci int bytes_of_pointer = 0, sel_max = 0, i; 38462306a36Sopenharmony_ci snd_pcm_uframes_t x; 38562306a36Sopenharmony_ci struct dma_tx_state state; 38662306a36Sopenharmony_ci enum dma_status status; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci for (i = 0; i < dma_private->hw_chan; i++) { 38962306a36Sopenharmony_ci struct sprd_pcm_dma_data *data = &dma_private->data[i]; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (!data->chan) 39262306a36Sopenharmony_ci continue; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci status = dmaengine_tx_status(data->chan, data->cookie, &state); 39562306a36Sopenharmony_ci if (status == DMA_ERROR) { 39662306a36Sopenharmony_ci dev_err(component->dev, 39762306a36Sopenharmony_ci "failed to get dma channel %d status\n", i); 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * We just get current transfer address from the DMA engine, so 40362306a36Sopenharmony_ci * we need convert to current pointer. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci pointer[i] = state.residue - runtime->dma_addr - 40662306a36Sopenharmony_ci i * dma_private->dma_addr_offset; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (i == 0) { 40962306a36Sopenharmony_ci bytes_of_pointer = pointer[i]; 41062306a36Sopenharmony_ci sel_max = pointer[i] < data->pre_pointer ? 1 : 0; 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci sel_max ^= pointer[i] < data->pre_pointer ? 1 : 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (sel_max) 41562306a36Sopenharmony_ci bytes_of_pointer = 41662306a36Sopenharmony_ci max(pointer[i], pointer[i - 1]) << 1; 41762306a36Sopenharmony_ci else 41862306a36Sopenharmony_ci bytes_of_pointer = 41962306a36Sopenharmony_ci min(pointer[i], pointer[i - 1]) << 1; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci data->pre_pointer = pointer[i]; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci x = bytes_to_frames(runtime, bytes_of_pointer); 42662306a36Sopenharmony_ci if (x == runtime->buffer_size) 42762306a36Sopenharmony_ci x = 0; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return x; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int sprd_pcm_new(struct snd_soc_component *component, 43362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct snd_card *card = rtd->card->snd_card; 43662306a36Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 43762306a36Sopenharmony_ci int ret; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 44062306a36Sopenharmony_ci if (ret) 44162306a36Sopenharmony_ci return ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 44462306a36Sopenharmony_ci card->dev, 44562306a36Sopenharmony_ci sprd_pcm_hardware.buffer_bytes_max); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic const struct snd_soc_component_driver sprd_soc_component = { 44962306a36Sopenharmony_ci .name = DRV_NAME, 45062306a36Sopenharmony_ci .open = sprd_pcm_open, 45162306a36Sopenharmony_ci .close = sprd_pcm_close, 45262306a36Sopenharmony_ci .hw_params = sprd_pcm_hw_params, 45362306a36Sopenharmony_ci .hw_free = sprd_pcm_hw_free, 45462306a36Sopenharmony_ci .trigger = sprd_pcm_trigger, 45562306a36Sopenharmony_ci .pointer = sprd_pcm_pointer, 45662306a36Sopenharmony_ci .pcm_construct = sprd_pcm_new, 45762306a36Sopenharmony_ci .compress_ops = &sprd_platform_compress_ops, 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int sprd_soc_platform_probe(struct platform_device *pdev) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 46362306a36Sopenharmony_ci int ret; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0); 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci dev_warn(&pdev->dev, 46862306a36Sopenharmony_ci "no reserved DMA memory for audio platform device\n"); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, 47162306a36Sopenharmony_ci NULL, 0); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register platform:%d\n", ret); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic const struct of_device_id sprd_pcm_of_match[] = { 47962306a36Sopenharmony_ci { .compatible = "sprd,pcm-platform", }, 48062306a36Sopenharmony_ci { }, 48162306a36Sopenharmony_ci}; 48262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sprd_pcm_of_match); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic struct platform_driver sprd_pcm_driver = { 48562306a36Sopenharmony_ci .driver = { 48662306a36Sopenharmony_ci .name = "sprd-pcm-audio", 48762306a36Sopenharmony_ci .of_match_table = sprd_pcm_of_match, 48862306a36Sopenharmony_ci }, 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci .probe = sprd_soc_platform_probe, 49162306a36Sopenharmony_ci}; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cimodule_platform_driver(sprd_pcm_driver); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum ASoC PCM DMA"); 49662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 49762306a36Sopenharmony_ciMODULE_ALIAS("platform:sprd-audio"); 498