12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27605eb5bSStephen Warren /* 37605eb5bSStephen Warren * tegra_pcm.c - Tegra PCM driver 47605eb5bSStephen Warren * 57605eb5bSStephen Warren * Author: Stephen Warren <swarren@nvidia.com> 6518de86bSStephen Warren * Copyright (C) 2010,2012 - NVIDIA, Inc. 77605eb5bSStephen Warren * 87605eb5bSStephen Warren * Based on code copyright/by: 97605eb5bSStephen Warren * 107605eb5bSStephen Warren * Copyright (c) 2009-2010, NVIDIA Corporation. 117605eb5bSStephen Warren * Scott Peterson <speterson@nvidia.com> 127605eb5bSStephen Warren * Vijay Mali <vmali@nvidia.com> 137605eb5bSStephen Warren * 147605eb5bSStephen Warren * Copyright (C) 2010 Google, Inc. 157605eb5bSStephen Warren * Iliyan Malchev <malchev@google.com> 167605eb5bSStephen Warren */ 177605eb5bSStephen Warren 187613c508SStephen Warren #include <linux/module.h> 19f74028e1SSameer Pujar #include <linux/dma-mapping.h> 207605eb5bSStephen Warren #include <sound/core.h> 217605eb5bSStephen Warren #include <sound/pcm.h> 227605eb5bSStephen Warren #include <sound/pcm_params.h> 237605eb5bSStephen Warren #include <sound/soc.h> 24df79f55dSLaxman Dewangan #include <sound/dmaengine_pcm.h> 257605eb5bSStephen Warren #include "tegra_pcm.h" 267605eb5bSStephen Warren 277605eb5bSStephen Warren static const struct snd_pcm_hardware tegra_pcm_hardware = { 287605eb5bSStephen Warren .info = SNDRV_PCM_INFO_MMAP | 297605eb5bSStephen Warren SNDRV_PCM_INFO_MMAP_VALID | 307605eb5bSStephen Warren SNDRV_PCM_INFO_INTERLEAVED, 317605eb5bSStephen Warren .period_bytes_min = 1024, 327605eb5bSStephen Warren .period_bytes_max = PAGE_SIZE, 337605eb5bSStephen Warren .periods_min = 2, 347605eb5bSStephen Warren .periods_max = 8, 357605eb5bSStephen Warren .buffer_bytes_max = PAGE_SIZE * 8, 367605eb5bSStephen Warren .fifo_size = 4, 377605eb5bSStephen Warren }; 387605eb5bSStephen Warren 3911a8576aSLars-Peter Clausen static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = { 4011a8576aSLars-Peter Clausen .pcm_hardware = &tegra_pcm_hardware, 4111a8576aSLars-Peter Clausen .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 4211a8576aSLars-Peter Clausen .prealloc_buffer_size = PAGE_SIZE * 8, 437605eb5bSStephen Warren }; 447605eb5bSStephen Warren 454652a0d0SBill Pemberton int tegra_pcm_platform_register(struct device *dev) 467605eb5bSStephen Warren { 475608bd3eSStephen Warren return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0); 487605eb5bSStephen Warren } 49518de86bSStephen Warren EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); 507605eb5bSStephen Warren 515608bd3eSStephen Warren int tegra_pcm_platform_register_with_chan_names(struct device *dev, 525608bd3eSStephen Warren struct snd_dmaengine_pcm_config *config, 535608bd3eSStephen Warren char *txdmachan, char *rxdmachan) 545608bd3eSStephen Warren { 555608bd3eSStephen Warren *config = tegra_dmaengine_pcm_config; 565608bd3eSStephen Warren config->dma_dev = dev->parent; 575608bd3eSStephen Warren config->chan_names[0] = txdmachan; 585608bd3eSStephen Warren config->chan_names[1] = rxdmachan; 595608bd3eSStephen Warren 605608bd3eSStephen Warren return snd_dmaengine_pcm_register(dev, config, 0); 615608bd3eSStephen Warren } 625608bd3eSStephen Warren EXPORT_SYMBOL_GPL(tegra_pcm_platform_register_with_chan_names); 635608bd3eSStephen Warren 644652a0d0SBill Pemberton void tegra_pcm_platform_unregister(struct device *dev) 657605eb5bSStephen Warren { 6611a8576aSLars-Peter Clausen return snd_dmaengine_pcm_unregister(dev); 677605eb5bSStephen Warren } 68518de86bSStephen Warren EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister); 697605eb5bSStephen Warren 70f74028e1SSameer Pujar int tegra_pcm_open(struct snd_soc_component *component, 71f74028e1SSameer Pujar struct snd_pcm_substream *substream) 72f74028e1SSameer Pujar { 73f74028e1SSameer Pujar struct snd_soc_pcm_runtime *rtd = substream->private_data; 74f74028e1SSameer Pujar struct snd_dmaengine_dai_dma_data *dmap; 75f74028e1SSameer Pujar struct dma_chan *chan; 76f74028e1SSameer Pujar struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 77f74028e1SSameer Pujar int ret; 78f74028e1SSameer Pujar 79f74028e1SSameer Pujar if (rtd->dai_link->no_pcm) 80f74028e1SSameer Pujar return 0; 81f74028e1SSameer Pujar 82f74028e1SSameer Pujar dmap = snd_soc_dai_get_dma_data(cpu_dai, substream); 83f74028e1SSameer Pujar 84f74028e1SSameer Pujar /* Set HW params now that initialization is complete */ 85f74028e1SSameer Pujar snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); 86f74028e1SSameer Pujar 87f74028e1SSameer Pujar /* Ensure period size is multiple of 8 */ 88f74028e1SSameer Pujar ret = snd_pcm_hw_constraint_step(substream->runtime, 0, 89f74028e1SSameer Pujar SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8); 90f74028e1SSameer Pujar if (ret) { 91f74028e1SSameer Pujar dev_err(rtd->dev, "failed to set constraint %d\n", ret); 92f74028e1SSameer Pujar return ret; 93f74028e1SSameer Pujar } 94f74028e1SSameer Pujar 95f74028e1SSameer Pujar chan = dma_request_slave_channel(cpu_dai->dev, dmap->chan_name); 96f74028e1SSameer Pujar if (!chan) { 97f74028e1SSameer Pujar dev_err(cpu_dai->dev, 98f74028e1SSameer Pujar "dmaengine request slave channel failed! (%s)\n", 99f74028e1SSameer Pujar dmap->chan_name); 100f74028e1SSameer Pujar return -ENODEV; 101f74028e1SSameer Pujar } 102f74028e1SSameer Pujar 103f74028e1SSameer Pujar ret = snd_dmaengine_pcm_open(substream, chan); 104f74028e1SSameer Pujar if (ret) { 105f74028e1SSameer Pujar dev_err(rtd->dev, 106f74028e1SSameer Pujar "dmaengine pcm open failed with err %d (%s)\n", ret, 107f74028e1SSameer Pujar dmap->chan_name); 108f74028e1SSameer Pujar 109f74028e1SSameer Pujar dma_release_channel(chan); 110f74028e1SSameer Pujar 111f74028e1SSameer Pujar return ret; 112f74028e1SSameer Pujar } 113f74028e1SSameer Pujar 114f74028e1SSameer Pujar return 0; 115f74028e1SSameer Pujar } 116f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_open); 117f74028e1SSameer Pujar 118f74028e1SSameer Pujar int tegra_pcm_close(struct snd_soc_component *component, 119f74028e1SSameer Pujar struct snd_pcm_substream *substream) 120f74028e1SSameer Pujar { 121f74028e1SSameer Pujar struct snd_soc_pcm_runtime *rtd = substream->private_data; 122f74028e1SSameer Pujar 123f74028e1SSameer Pujar if (rtd->dai_link->no_pcm) 124f74028e1SSameer Pujar return 0; 125f74028e1SSameer Pujar 126f74028e1SSameer Pujar snd_dmaengine_pcm_close_release_chan(substream); 127f74028e1SSameer Pujar 128f74028e1SSameer Pujar return 0; 129f74028e1SSameer Pujar } 130f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_close); 131f74028e1SSameer Pujar 132f74028e1SSameer Pujar int tegra_pcm_hw_params(struct snd_soc_component *component, 133f74028e1SSameer Pujar struct snd_pcm_substream *substream, 134f74028e1SSameer Pujar struct snd_pcm_hw_params *params) 135f74028e1SSameer Pujar { 136f74028e1SSameer Pujar struct snd_soc_pcm_runtime *rtd = substream->private_data; 137f74028e1SSameer Pujar struct snd_dmaengine_dai_dma_data *dmap; 138f74028e1SSameer Pujar struct dma_slave_config slave_config; 139f74028e1SSameer Pujar struct dma_chan *chan; 140f74028e1SSameer Pujar int ret; 141f74028e1SSameer Pujar 142f74028e1SSameer Pujar if (rtd->dai_link->no_pcm) 143f74028e1SSameer Pujar return 0; 144f74028e1SSameer Pujar 145f74028e1SSameer Pujar dmap = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); 146f74028e1SSameer Pujar if (!dmap) 147f74028e1SSameer Pujar return 0; 148f74028e1SSameer Pujar 149f74028e1SSameer Pujar chan = snd_dmaengine_pcm_get_chan(substream); 150f74028e1SSameer Pujar 151f74028e1SSameer Pujar ret = snd_hwparams_to_dma_slave_config(substream, params, 152f74028e1SSameer Pujar &slave_config); 153f74028e1SSameer Pujar if (ret) { 154f74028e1SSameer Pujar dev_err(rtd->dev, "hw params config failed with err %d\n", ret); 155f74028e1SSameer Pujar return ret; 156f74028e1SSameer Pujar } 157f74028e1SSameer Pujar 158f74028e1SSameer Pujar if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 159f74028e1SSameer Pujar slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 160f74028e1SSameer Pujar slave_config.dst_addr = dmap->addr; 161f74028e1SSameer Pujar slave_config.dst_maxburst = 8; 162f74028e1SSameer Pujar } else { 163f74028e1SSameer Pujar slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 164f74028e1SSameer Pujar slave_config.src_addr = dmap->addr; 165f74028e1SSameer Pujar slave_config.src_maxburst = 8; 166f74028e1SSameer Pujar } 167f74028e1SSameer Pujar 168f74028e1SSameer Pujar ret = dmaengine_slave_config(chan, &slave_config); 169f74028e1SSameer Pujar if (ret < 0) { 170f74028e1SSameer Pujar dev_err(rtd->dev, "dma slave config failed with err %d\n", ret); 171f74028e1SSameer Pujar return ret; 172f74028e1SSameer Pujar } 173f74028e1SSameer Pujar 174f74028e1SSameer Pujar return 0; 175f74028e1SSameer Pujar } 176f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_hw_params); 177f74028e1SSameer Pujar 178f74028e1SSameer Pujar snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component, 179f74028e1SSameer Pujar struct snd_pcm_substream *substream) 180f74028e1SSameer Pujar { 181f74028e1SSameer Pujar return snd_dmaengine_pcm_pointer(substream); 182f74028e1SSameer Pujar } 183f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_pointer); 184f74028e1SSameer Pujar 1850dfc21c1SThierry Reding static int tegra_pcm_dma_allocate(struct device *dev, struct snd_soc_pcm_runtime *rtd, 186f74028e1SSameer Pujar size_t size) 187f74028e1SSameer Pujar { 188f74028e1SSameer Pujar struct snd_pcm *pcm = rtd->pcm; 189f74028e1SSameer Pujar int ret; 190f74028e1SSameer Pujar 1910dfc21c1SThierry Reding ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 192f74028e1SSameer Pujar if (ret < 0) 193f74028e1SSameer Pujar return ret; 194f74028e1SSameer Pujar 195*18936487STakashi Iwai return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, dev, size); 196f74028e1SSameer Pujar } 197f74028e1SSameer Pujar 198f74028e1SSameer Pujar int tegra_pcm_construct(struct snd_soc_component *component, 199f74028e1SSameer Pujar struct snd_soc_pcm_runtime *rtd) 200f74028e1SSameer Pujar { 2010dfc21c1SThierry Reding struct device *dev = component->dev; 2020dfc21c1SThierry Reding 2030dfc21c1SThierry Reding /* 2040dfc21c1SThierry Reding * Fallback for backwards-compatibility with older device trees that 2050dfc21c1SThierry Reding * have the iommus property in the virtual, top-level "sound" node. 2060dfc21c1SThierry Reding */ 2070dfc21c1SThierry Reding if (!of_get_property(dev->of_node, "iommus", NULL)) 2080dfc21c1SThierry Reding dev = rtd->card->snd_card->dev; 2090dfc21c1SThierry Reding 2100dfc21c1SThierry Reding return tegra_pcm_dma_allocate(dev, rtd, tegra_pcm_hardware.buffer_bytes_max); 211f74028e1SSameer Pujar } 212f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_construct); 213f74028e1SSameer Pujar 2147605eb5bSStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 2157605eb5bSStephen Warren MODULE_DESCRIPTION("Tegra PCM ASoC driver"); 2167605eb5bSStephen Warren MODULE_LICENSE("GPL"); 217