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 51150f4d57SDmitry Osipenko int devm_tegra_pcm_platform_register(struct device *dev) 52150f4d57SDmitry Osipenko { 53150f4d57SDmitry Osipenko return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0); 54150f4d57SDmitry Osipenko } 55150f4d57SDmitry Osipenko EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register); 56150f4d57SDmitry Osipenko 575608bd3eSStephen Warren int tegra_pcm_platform_register_with_chan_names(struct device *dev, 585608bd3eSStephen Warren struct snd_dmaengine_pcm_config *config, 595608bd3eSStephen Warren char *txdmachan, char *rxdmachan) 605608bd3eSStephen Warren { 615608bd3eSStephen Warren *config = tegra_dmaengine_pcm_config; 625608bd3eSStephen Warren config->dma_dev = dev->parent; 635608bd3eSStephen Warren config->chan_names[0] = txdmachan; 645608bd3eSStephen Warren config->chan_names[1] = rxdmachan; 655608bd3eSStephen Warren 665608bd3eSStephen Warren return snd_dmaengine_pcm_register(dev, config, 0); 675608bd3eSStephen Warren } 685608bd3eSStephen Warren EXPORT_SYMBOL_GPL(tegra_pcm_platform_register_with_chan_names); 695608bd3eSStephen Warren 704652a0d0SBill Pemberton void tegra_pcm_platform_unregister(struct device *dev) 717605eb5bSStephen Warren { 7211a8576aSLars-Peter Clausen return snd_dmaengine_pcm_unregister(dev); 737605eb5bSStephen Warren } 74518de86bSStephen Warren EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister); 757605eb5bSStephen Warren 76f74028e1SSameer Pujar int tegra_pcm_open(struct snd_soc_component *component, 77f74028e1SSameer Pujar struct snd_pcm_substream *substream) 78f74028e1SSameer Pujar { 79*3beb985aSKrzysztof Kozlowski struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 80f74028e1SSameer Pujar struct snd_dmaengine_dai_dma_data *dmap; 81f74028e1SSameer Pujar struct dma_chan *chan; 82436f4c70SKuninori Morimoto struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 83f74028e1SSameer Pujar int ret; 84f74028e1SSameer Pujar 85f74028e1SSameer Pujar if (rtd->dai_link->no_pcm) 86f74028e1SSameer Pujar return 0; 87f74028e1SSameer Pujar 88f74028e1SSameer Pujar dmap = snd_soc_dai_get_dma_data(cpu_dai, substream); 89f74028e1SSameer Pujar 90f74028e1SSameer Pujar /* Set HW params now that initialization is complete */ 91f74028e1SSameer Pujar snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); 92f74028e1SSameer Pujar 93f74028e1SSameer Pujar /* Ensure period size is multiple of 8 */ 94f74028e1SSameer Pujar ret = snd_pcm_hw_constraint_step(substream->runtime, 0, 95f74028e1SSameer Pujar SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8); 96f74028e1SSameer Pujar if (ret) { 97f74028e1SSameer Pujar dev_err(rtd->dev, "failed to set constraint %d\n", ret); 98f74028e1SSameer Pujar return ret; 99f74028e1SSameer Pujar } 100f74028e1SSameer Pujar 1018df73570SChristophe JAILLET chan = dma_request_chan(cpu_dai->dev, dmap->chan_name); 1028df73570SChristophe JAILLET if (IS_ERR(chan)) { 103f74028e1SSameer Pujar dev_err(cpu_dai->dev, 104f74028e1SSameer Pujar "dmaengine request slave channel failed! (%s)\n", 105f74028e1SSameer Pujar dmap->chan_name); 106f74028e1SSameer Pujar return -ENODEV; 107f74028e1SSameer Pujar } 108f74028e1SSameer Pujar 109f74028e1SSameer Pujar ret = snd_dmaengine_pcm_open(substream, chan); 110f74028e1SSameer Pujar if (ret) { 111f74028e1SSameer Pujar dev_err(rtd->dev, 112f74028e1SSameer Pujar "dmaengine pcm open failed with err %d (%s)\n", ret, 113f74028e1SSameer Pujar dmap->chan_name); 114f74028e1SSameer Pujar 115f74028e1SSameer Pujar dma_release_channel(chan); 116f74028e1SSameer Pujar 117f74028e1SSameer Pujar return ret; 118f74028e1SSameer Pujar } 119f74028e1SSameer Pujar 120f9fd804aSJon Hunter /* Set wait time to 500ms by default */ 121f9fd804aSJon Hunter substream->wait_time = 500; 122f9fd804aSJon Hunter 123f74028e1SSameer Pujar return 0; 124f74028e1SSameer Pujar } 125f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_open); 126f74028e1SSameer Pujar 127f74028e1SSameer Pujar int tegra_pcm_close(struct snd_soc_component *component, 128f74028e1SSameer Pujar struct snd_pcm_substream *substream) 129f74028e1SSameer Pujar { 130*3beb985aSKrzysztof Kozlowski struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 131f74028e1SSameer Pujar 132f74028e1SSameer Pujar if (rtd->dai_link->no_pcm) 133f74028e1SSameer Pujar return 0; 134f74028e1SSameer Pujar 135f74028e1SSameer Pujar snd_dmaengine_pcm_close_release_chan(substream); 136f74028e1SSameer Pujar 137f74028e1SSameer Pujar return 0; 138f74028e1SSameer Pujar } 139f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_close); 140f74028e1SSameer Pujar 141f74028e1SSameer Pujar int tegra_pcm_hw_params(struct snd_soc_component *component, 142f74028e1SSameer Pujar struct snd_pcm_substream *substream, 143f74028e1SSameer Pujar struct snd_pcm_hw_params *params) 144f74028e1SSameer Pujar { 145*3beb985aSKrzysztof Kozlowski struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 146f74028e1SSameer Pujar struct snd_dmaengine_dai_dma_data *dmap; 147f74028e1SSameer Pujar struct dma_slave_config slave_config; 148f74028e1SSameer Pujar struct dma_chan *chan; 149f74028e1SSameer Pujar int ret; 150f74028e1SSameer Pujar 151f74028e1SSameer Pujar if (rtd->dai_link->no_pcm) 152f74028e1SSameer Pujar return 0; 153f74028e1SSameer Pujar 154436f4c70SKuninori Morimoto dmap = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream); 155f74028e1SSameer Pujar if (!dmap) 156f74028e1SSameer Pujar return 0; 157f74028e1SSameer Pujar 158f74028e1SSameer Pujar chan = snd_dmaengine_pcm_get_chan(substream); 159f74028e1SSameer Pujar 160f74028e1SSameer Pujar ret = snd_hwparams_to_dma_slave_config(substream, params, 161f74028e1SSameer Pujar &slave_config); 162f74028e1SSameer Pujar if (ret) { 163f74028e1SSameer Pujar dev_err(rtd->dev, "hw params config failed with err %d\n", ret); 164f74028e1SSameer Pujar return ret; 165f74028e1SSameer Pujar } 166f74028e1SSameer Pujar 167f74028e1SSameer Pujar if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 168f74028e1SSameer Pujar slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 169f74028e1SSameer Pujar slave_config.dst_addr = dmap->addr; 170f74028e1SSameer Pujar slave_config.dst_maxburst = 8; 171f74028e1SSameer Pujar } else { 172f74028e1SSameer Pujar slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 173f74028e1SSameer Pujar slave_config.src_addr = dmap->addr; 174f74028e1SSameer Pujar slave_config.src_maxburst = 8; 175f74028e1SSameer Pujar } 176f74028e1SSameer Pujar 177f74028e1SSameer Pujar ret = dmaengine_slave_config(chan, &slave_config); 178f74028e1SSameer Pujar if (ret < 0) { 179f74028e1SSameer Pujar dev_err(rtd->dev, "dma slave config failed with err %d\n", ret); 180f74028e1SSameer Pujar return ret; 181f74028e1SSameer Pujar } 182f74028e1SSameer Pujar 183f74028e1SSameer Pujar return 0; 184f74028e1SSameer Pujar } 185f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_hw_params); 186f74028e1SSameer Pujar 187f74028e1SSameer Pujar snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component, 188f74028e1SSameer Pujar struct snd_pcm_substream *substream) 189f74028e1SSameer Pujar { 190f74028e1SSameer Pujar return snd_dmaengine_pcm_pointer(substream); 191f74028e1SSameer Pujar } 192f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_pointer); 193f74028e1SSameer Pujar 1940dfc21c1SThierry Reding static int tegra_pcm_dma_allocate(struct device *dev, struct snd_soc_pcm_runtime *rtd, 195f74028e1SSameer Pujar size_t size) 196f74028e1SSameer Pujar { 197f74028e1SSameer Pujar struct snd_pcm *pcm = rtd->pcm; 198f74028e1SSameer Pujar int ret; 199f74028e1SSameer Pujar 2000dfc21c1SThierry Reding ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 201f74028e1SSameer Pujar if (ret < 0) 202f74028e1SSameer Pujar return ret; 203f74028e1SSameer Pujar 20418936487STakashi Iwai return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, dev, size); 205f74028e1SSameer Pujar } 206f74028e1SSameer Pujar 207f74028e1SSameer Pujar int tegra_pcm_construct(struct snd_soc_component *component, 208f74028e1SSameer Pujar struct snd_soc_pcm_runtime *rtd) 209f74028e1SSameer Pujar { 2100dfc21c1SThierry Reding struct device *dev = component->dev; 2110dfc21c1SThierry Reding 2120dfc21c1SThierry Reding /* 2130dfc21c1SThierry Reding * Fallback for backwards-compatibility with older device trees that 2140dfc21c1SThierry Reding * have the iommus property in the virtual, top-level "sound" node. 2150dfc21c1SThierry Reding */ 2160dfc21c1SThierry Reding if (!of_get_property(dev->of_node, "iommus", NULL)) 2170dfc21c1SThierry Reding dev = rtd->card->snd_card->dev; 2180dfc21c1SThierry Reding 2190dfc21c1SThierry Reding return tegra_pcm_dma_allocate(dev, rtd, tegra_pcm_hardware.buffer_bytes_max); 220f74028e1SSameer Pujar } 221f74028e1SSameer Pujar EXPORT_SYMBOL_GPL(tegra_pcm_construct); 222f74028e1SSameer Pujar 2237605eb5bSStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 2247605eb5bSStephen Warren MODULE_DESCRIPTION("Tegra PCM ASoC driver"); 2257605eb5bSStephen Warren MODULE_LICENSE("GPL"); 226