Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * tegra_pcm.c - Tegra PCM driver
  4 *
  5 * Author: Stephen Warren <swarren@nvidia.com>
  6 * Copyright (C) 2010,2012 - NVIDIA, Inc.
  7 *
  8 * Based on code copyright/by:
  9 *
 10 * Copyright (c) 2009-2010, NVIDIA Corporation.
 11 * Scott Peterson <speterson@nvidia.com>
 12 * Vijay Mali <vmali@nvidia.com>
 13 *
 14 * Copyright (C) 2010 Google, Inc.
 15 * Iliyan Malchev <malchev@google.com>
 16 */
 17
 18#include <linux/module.h>
 19#include <linux/dma-mapping.h>
 20#include <sound/core.h>
 21#include <sound/pcm.h>
 22#include <sound/pcm_params.h>
 23#include <sound/soc.h>
 24#include <sound/dmaengine_pcm.h>
 25#include "tegra_pcm.h"
 26
 27static const struct snd_pcm_hardware tegra_pcm_hardware = {
 28	.info			= SNDRV_PCM_INFO_MMAP |
 29				  SNDRV_PCM_INFO_MMAP_VALID |
 30				  SNDRV_PCM_INFO_INTERLEAVED,
 31	.period_bytes_min	= 1024,
 32	.period_bytes_max	= PAGE_SIZE,
 33	.periods_min		= 2,
 34	.periods_max		= 8,
 35	.buffer_bytes_max	= PAGE_SIZE * 8,
 36	.fifo_size		= 4,
 37};
 38
 39static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = {
 40	.pcm_hardware = &tegra_pcm_hardware,
 41	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
 42	.prealloc_buffer_size = PAGE_SIZE * 8,
 43};
 44
 45int tegra_pcm_platform_register(struct device *dev)
 46{
 47	return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
 48}
 49EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
 50
 51int devm_tegra_pcm_platform_register(struct device *dev)
 52{
 53	return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
 54}
 55EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register);
 56
 57int tegra_pcm_platform_register_with_chan_names(struct device *dev,
 58				struct snd_dmaengine_pcm_config *config,
 59				char *txdmachan, char *rxdmachan)
 60{
 61	*config = tegra_dmaengine_pcm_config;
 62	config->dma_dev = dev->parent;
 63	config->chan_names[0] = txdmachan;
 64	config->chan_names[1] = rxdmachan;
 65
 66	return snd_dmaengine_pcm_register(dev, config, 0);
 67}
 68EXPORT_SYMBOL_GPL(tegra_pcm_platform_register_with_chan_names);
 69
 70void tegra_pcm_platform_unregister(struct device *dev)
 71{
 72	return snd_dmaengine_pcm_unregister(dev);
 73}
 74EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
 75
 76int tegra_pcm_open(struct snd_soc_component *component,
 77		   struct snd_pcm_substream *substream)
 78{
 79	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 80	struct snd_dmaengine_dai_dma_data *dmap;
 81	struct dma_chan *chan;
 82	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
 83	int ret;
 84
 85	if (rtd->dai_link->no_pcm)
 86		return 0;
 87
 88	dmap = snd_soc_dai_get_dma_data(cpu_dai, substream);
 89
 90	/* Set HW params now that initialization is complete */
 91	snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
 92
 93	/* Ensure period size is multiple of 8 */
 94	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
 95					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8);
 96	if (ret) {
 97		dev_err(rtd->dev, "failed to set constraint %d\n", ret);
 98		return ret;
 99	}
100
101	chan = dma_request_slave_channel(cpu_dai->dev, dmap->chan_name);
102	if (!chan) {
103		dev_err(cpu_dai->dev,
104			"dmaengine request slave channel failed! (%s)\n",
105			dmap->chan_name);
106		return -ENODEV;
107	}
108
109	ret = snd_dmaengine_pcm_open(substream, chan);
110	if (ret) {
111		dev_err(rtd->dev,
112			"dmaengine pcm open failed with err %d (%s)\n", ret,
113			dmap->chan_name);
114
115		dma_release_channel(chan);
116
117		return ret;
118	}
119
 
 
 
120	return 0;
121}
122EXPORT_SYMBOL_GPL(tegra_pcm_open);
123
124int tegra_pcm_close(struct snd_soc_component *component,
125		    struct snd_pcm_substream *substream)
126{
127	struct snd_soc_pcm_runtime *rtd = substream->private_data;
128
129	if (rtd->dai_link->no_pcm)
130		return 0;
131
132	snd_dmaengine_pcm_close_release_chan(substream);
133
134	return 0;
135}
136EXPORT_SYMBOL_GPL(tegra_pcm_close);
137
138int tegra_pcm_hw_params(struct snd_soc_component *component,
139			struct snd_pcm_substream *substream,
140			struct snd_pcm_hw_params *params)
141{
142	struct snd_soc_pcm_runtime *rtd = substream->private_data;
143	struct snd_dmaengine_dai_dma_data *dmap;
144	struct dma_slave_config slave_config;
145	struct dma_chan *chan;
146	int ret;
147
148	if (rtd->dai_link->no_pcm)
149		return 0;
150
151	dmap = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
152	if (!dmap)
153		return 0;
154
155	chan = snd_dmaengine_pcm_get_chan(substream);
156
157	ret = snd_hwparams_to_dma_slave_config(substream, params,
158					       &slave_config);
159	if (ret) {
160		dev_err(rtd->dev, "hw params config failed with err %d\n", ret);
161		return ret;
162	}
163
164	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
165		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
166		slave_config.dst_addr = dmap->addr;
167		slave_config.dst_maxburst = 8;
168	} else {
169		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
170		slave_config.src_addr = dmap->addr;
171		slave_config.src_maxburst = 8;
172	}
173
174	ret = dmaengine_slave_config(chan, &slave_config);
175	if (ret < 0) {
176		dev_err(rtd->dev, "dma slave config failed with err %d\n", ret);
177		return ret;
178	}
179
180	return 0;
181}
182EXPORT_SYMBOL_GPL(tegra_pcm_hw_params);
183
184snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component,
185				    struct snd_pcm_substream *substream)
186{
187	return snd_dmaengine_pcm_pointer(substream);
188}
189EXPORT_SYMBOL_GPL(tegra_pcm_pointer);
190
191static int tegra_pcm_dma_allocate(struct device *dev, struct snd_soc_pcm_runtime *rtd,
192				  size_t size)
193{
194	struct snd_pcm *pcm = rtd->pcm;
195	int ret;
196
197	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
198	if (ret < 0)
199		return ret;
200
201	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, dev, size);
202}
203
204int tegra_pcm_construct(struct snd_soc_component *component,
205			struct snd_soc_pcm_runtime *rtd)
206{
207	struct device *dev = component->dev;
208
209	/*
210	 * Fallback for backwards-compatibility with older device trees that
211	 * have the iommus property in the virtual, top-level "sound" node.
212	 */
213	if (!of_get_property(dev->of_node, "iommus", NULL))
214		dev = rtd->card->snd_card->dev;
215
216	return tegra_pcm_dma_allocate(dev, rtd, tegra_pcm_hardware.buffer_bytes_max);
217}
218EXPORT_SYMBOL_GPL(tegra_pcm_construct);
219
220MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
221MODULE_DESCRIPTION("Tegra PCM ASoC driver");
222MODULE_LICENSE("GPL");
v6.8
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * tegra_pcm.c - Tegra PCM driver
  4 *
  5 * Author: Stephen Warren <swarren@nvidia.com>
  6 * Copyright (C) 2010,2012 - NVIDIA, Inc.
  7 *
  8 * Based on code copyright/by:
  9 *
 10 * Copyright (c) 2009-2010, NVIDIA Corporation.
 11 * Scott Peterson <speterson@nvidia.com>
 12 * Vijay Mali <vmali@nvidia.com>
 13 *
 14 * Copyright (C) 2010 Google, Inc.
 15 * Iliyan Malchev <malchev@google.com>
 16 */
 17
 18#include <linux/module.h>
 19#include <linux/dma-mapping.h>
 20#include <sound/core.h>
 21#include <sound/pcm.h>
 22#include <sound/pcm_params.h>
 23#include <sound/soc.h>
 24#include <sound/dmaengine_pcm.h>
 25#include "tegra_pcm.h"
 26
 27static const struct snd_pcm_hardware tegra_pcm_hardware = {
 28	.info			= SNDRV_PCM_INFO_MMAP |
 29				  SNDRV_PCM_INFO_MMAP_VALID |
 30				  SNDRV_PCM_INFO_INTERLEAVED,
 31	.period_bytes_min	= 1024,
 32	.period_bytes_max	= PAGE_SIZE,
 33	.periods_min		= 2,
 34	.periods_max		= 8,
 35	.buffer_bytes_max	= PAGE_SIZE * 8,
 36	.fifo_size		= 4,
 37};
 38
 39static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = {
 40	.pcm_hardware = &tegra_pcm_hardware,
 41	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
 42	.prealloc_buffer_size = PAGE_SIZE * 8,
 43};
 44
 45int tegra_pcm_platform_register(struct device *dev)
 46{
 47	return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
 48}
 49EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
 50
 51int devm_tegra_pcm_platform_register(struct device *dev)
 52{
 53	return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0);
 54}
 55EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register);
 56
 57int tegra_pcm_platform_register_with_chan_names(struct device *dev,
 58				struct snd_dmaengine_pcm_config *config,
 59				char *txdmachan, char *rxdmachan)
 60{
 61	*config = tegra_dmaengine_pcm_config;
 62	config->dma_dev = dev->parent;
 63	config->chan_names[0] = txdmachan;
 64	config->chan_names[1] = rxdmachan;
 65
 66	return snd_dmaengine_pcm_register(dev, config, 0);
 67}
 68EXPORT_SYMBOL_GPL(tegra_pcm_platform_register_with_chan_names);
 69
 70void tegra_pcm_platform_unregister(struct device *dev)
 71{
 72	return snd_dmaengine_pcm_unregister(dev);
 73}
 74EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
 75
 76int tegra_pcm_open(struct snd_soc_component *component,
 77		   struct snd_pcm_substream *substream)
 78{
 79	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 80	struct snd_dmaengine_dai_dma_data *dmap;
 81	struct dma_chan *chan;
 82	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 83	int ret;
 84
 85	if (rtd->dai_link->no_pcm)
 86		return 0;
 87
 88	dmap = snd_soc_dai_get_dma_data(cpu_dai, substream);
 89
 90	/* Set HW params now that initialization is complete */
 91	snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
 92
 93	/* Ensure period size is multiple of 8 */
 94	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
 95					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8);
 96	if (ret) {
 97		dev_err(rtd->dev, "failed to set constraint %d\n", ret);
 98		return ret;
 99	}
100
101	chan = dma_request_chan(cpu_dai->dev, dmap->chan_name);
102	if (IS_ERR(chan)) {
103		dev_err(cpu_dai->dev,
104			"dmaengine request slave channel failed! (%s)\n",
105			dmap->chan_name);
106		return -ENODEV;
107	}
108
109	ret = snd_dmaengine_pcm_open(substream, chan);
110	if (ret) {
111		dev_err(rtd->dev,
112			"dmaengine pcm open failed with err %d (%s)\n", ret,
113			dmap->chan_name);
114
115		dma_release_channel(chan);
116
117		return ret;
118	}
119
120	/* Set wait time to 500ms by default */
121	substream->wait_time = 500;
122
123	return 0;
124}
125EXPORT_SYMBOL_GPL(tegra_pcm_open);
126
127int tegra_pcm_close(struct snd_soc_component *component,
128		    struct snd_pcm_substream *substream)
129{
130	struct snd_soc_pcm_runtime *rtd = substream->private_data;
131
132	if (rtd->dai_link->no_pcm)
133		return 0;
134
135	snd_dmaengine_pcm_close_release_chan(substream);
136
137	return 0;
138}
139EXPORT_SYMBOL_GPL(tegra_pcm_close);
140
141int tegra_pcm_hw_params(struct snd_soc_component *component,
142			struct snd_pcm_substream *substream,
143			struct snd_pcm_hw_params *params)
144{
145	struct snd_soc_pcm_runtime *rtd = substream->private_data;
146	struct snd_dmaengine_dai_dma_data *dmap;
147	struct dma_slave_config slave_config;
148	struct dma_chan *chan;
149	int ret;
150
151	if (rtd->dai_link->no_pcm)
152		return 0;
153
154	dmap = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
155	if (!dmap)
156		return 0;
157
158	chan = snd_dmaengine_pcm_get_chan(substream);
159
160	ret = snd_hwparams_to_dma_slave_config(substream, params,
161					       &slave_config);
162	if (ret) {
163		dev_err(rtd->dev, "hw params config failed with err %d\n", ret);
164		return ret;
165	}
166
167	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
168		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
169		slave_config.dst_addr = dmap->addr;
170		slave_config.dst_maxburst = 8;
171	} else {
172		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
173		slave_config.src_addr = dmap->addr;
174		slave_config.src_maxburst = 8;
175	}
176
177	ret = dmaengine_slave_config(chan, &slave_config);
178	if (ret < 0) {
179		dev_err(rtd->dev, "dma slave config failed with err %d\n", ret);
180		return ret;
181	}
182
183	return 0;
184}
185EXPORT_SYMBOL_GPL(tegra_pcm_hw_params);
186
187snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component,
188				    struct snd_pcm_substream *substream)
189{
190	return snd_dmaengine_pcm_pointer(substream);
191}
192EXPORT_SYMBOL_GPL(tegra_pcm_pointer);
193
194static int tegra_pcm_dma_allocate(struct device *dev, struct snd_soc_pcm_runtime *rtd,
195				  size_t size)
196{
197	struct snd_pcm *pcm = rtd->pcm;
198	int ret;
199
200	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
201	if (ret < 0)
202		return ret;
203
204	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, dev, size);
205}
206
207int tegra_pcm_construct(struct snd_soc_component *component,
208			struct snd_soc_pcm_runtime *rtd)
209{
210	struct device *dev = component->dev;
211
212	/*
213	 * Fallback for backwards-compatibility with older device trees that
214	 * have the iommus property in the virtual, top-level "sound" node.
215	 */
216	if (!of_get_property(dev->of_node, "iommus", NULL))
217		dev = rtd->card->snd_card->dev;
218
219	return tegra_pcm_dma_allocate(dev, rtd, tegra_pcm_hardware.buffer_bytes_max);
220}
221EXPORT_SYMBOL_GPL(tegra_pcm_construct);
222
223MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
224MODULE_DESCRIPTION("Tegra PCM ASoC driver");
225MODULE_LICENSE("GPL");