1a912e80bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b7ae6f31SDaniel Mack /*
3b7ae6f31SDaniel Mack * Copyright (C) 2012, Analog Devices Inc.
4b7ae6f31SDaniel Mack * Author: Lars-Peter Clausen <lars@metafoo.de>
5b7ae6f31SDaniel Mack *
6b7ae6f31SDaniel Mack * Based on:
7b7ae6f31SDaniel Mack * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
8b7ae6f31SDaniel Mack * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc.
9b7ae6f31SDaniel Mack * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
10b7ae6f31SDaniel Mack * Copyright (C) 2006 Applied Data Systems
11b7ae6f31SDaniel Mack */
12b7ae6f31SDaniel Mack #include <linux/module.h>
13b7ae6f31SDaniel Mack #include <linux/init.h>
14b7ae6f31SDaniel Mack #include <linux/dmaengine.h>
15b7ae6f31SDaniel Mack #include <linux/slab.h>
16b7ae6f31SDaniel Mack #include <sound/pcm.h>
17b7ae6f31SDaniel Mack #include <sound/pcm_params.h>
18b7ae6f31SDaniel Mack #include <sound/soc.h>
19b7ae6f31SDaniel Mack
20b7ae6f31SDaniel Mack #include <sound/dmaengine_pcm.h>
21b7ae6f31SDaniel Mack
22b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data {
23b7ae6f31SDaniel Mack struct dma_chan *dma_chan;
24b7ae6f31SDaniel Mack dma_cookie_t cookie;
25b7ae6f31SDaniel Mack
26b7ae6f31SDaniel Mack unsigned int pos;
27b7ae6f31SDaniel Mack };
28b7ae6f31SDaniel Mack
substream_to_prtd(const struct snd_pcm_substream * substream)29b7ae6f31SDaniel Mack static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
30b7ae6f31SDaniel Mack const struct snd_pcm_substream *substream)
31b7ae6f31SDaniel Mack {
32b7ae6f31SDaniel Mack return substream->runtime->private_data;
33b7ae6f31SDaniel Mack }
34b7ae6f31SDaniel Mack
snd_dmaengine_pcm_get_chan(struct snd_pcm_substream * substream)35b7ae6f31SDaniel Mack struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
36b7ae6f31SDaniel Mack {
37b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
38b7ae6f31SDaniel Mack
39b7ae6f31SDaniel Mack return prtd->dma_chan;
40b7ae6f31SDaniel Mack }
41b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
42b7ae6f31SDaniel Mack
43b7ae6f31SDaniel Mack /**
44b7ae6f31SDaniel Mack * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config
45b7ae6f31SDaniel Mack * @substream: PCM substream
46b7ae6f31SDaniel Mack * @params: hw_params
47b7ae6f31SDaniel Mack * @slave_config: DMA slave config
48b7ae6f31SDaniel Mack *
49b7ae6f31SDaniel Mack * This function can be used to initialize a dma_slave_config from a substream
50b7ae6f31SDaniel Mack * and hw_params in a dmaengine based PCM driver implementation.
515c121d63STakashi Iwai *
525c121d63STakashi Iwai * Return: zero if successful, or a negative error code
53b7ae6f31SDaniel Mack */
snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream * substream,const struct snd_pcm_hw_params * params,struct dma_slave_config * slave_config)54b7ae6f31SDaniel Mack int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
55b7ae6f31SDaniel Mack const struct snd_pcm_hw_params *params,
56b7ae6f31SDaniel Mack struct dma_slave_config *slave_config)
57b7ae6f31SDaniel Mack {
58b7ae6f31SDaniel Mack enum dma_slave_buswidth buswidth;
59a655f75cSTakashi Iwai int bits;
60b7ae6f31SDaniel Mack
61732814c8SPeter Ujfalusi bits = params_physical_width(params);
62a655f75cSTakashi Iwai if (bits < 8 || bits > 64)
63b7ae6f31SDaniel Mack return -EINVAL;
64a655f75cSTakashi Iwai else if (bits == 8)
65a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
66a655f75cSTakashi Iwai else if (bits == 16)
67a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
6875f850fcSPeter Ujfalusi else if (bits == 24)
6975f850fcSPeter Ujfalusi buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
70a655f75cSTakashi Iwai else if (bits <= 32)
71a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
72a655f75cSTakashi Iwai else
73a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
74b7ae6f31SDaniel Mack
75b7ae6f31SDaniel Mack if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
76b7ae6f31SDaniel Mack slave_config->direction = DMA_MEM_TO_DEV;
77b7ae6f31SDaniel Mack slave_config->dst_addr_width = buswidth;
78b7ae6f31SDaniel Mack } else {
79b7ae6f31SDaniel Mack slave_config->direction = DMA_DEV_TO_MEM;
80b7ae6f31SDaniel Mack slave_config->src_addr_width = buswidth;
81b7ae6f31SDaniel Mack }
82b7ae6f31SDaniel Mack
83b7ae6f31SDaniel Mack slave_config->device_fc = false;
84b7ae6f31SDaniel Mack
85b7ae6f31SDaniel Mack return 0;
86b7ae6f31SDaniel Mack }
87b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
88b7ae6f31SDaniel Mack
89b7ae6f31SDaniel Mack /**
90b7ae6f31SDaniel Mack * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config
91b7ae6f31SDaniel Mack * using DAI DMA data.
92b7ae6f31SDaniel Mack * @substream: PCM substream
93b7ae6f31SDaniel Mack * @dma_data: DAI DMA data
94b7ae6f31SDaniel Mack * @slave_config: DMA slave configuration
95b7ae6f31SDaniel Mack *
96bdecfcefSArnd Bergmann * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width
97bdecfcefSArnd Bergmann * fields of the DMA slave config from the same fields of the DAI DMA
98b7ae6f31SDaniel Mack * data struct. The src and dst fields will be initialized depending on the
99b7ae6f31SDaniel Mack * direction of the substream. If the substream is a playback stream the dst
100b7ae6f31SDaniel Mack * fields will be initialized, if it is a capture stream the src fields will be
101b7ae6f31SDaniel Mack * initialized. The {dst,src}_addr_width field will only be initialized if the
10273fe01cfSMatthias Reichl * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
10373fe01cfSMatthias Reichl * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If
10473fe01cfSMatthias Reichl * both conditions are met the latter takes priority.
105b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_set_config_from_dai_data(const struct snd_pcm_substream * substream,const struct snd_dmaengine_dai_dma_data * dma_data,struct dma_slave_config * slave_config)106b7ae6f31SDaniel Mack void snd_dmaengine_pcm_set_config_from_dai_data(
107b7ae6f31SDaniel Mack const struct snd_pcm_substream *substream,
108b7ae6f31SDaniel Mack const struct snd_dmaengine_dai_dma_data *dma_data,
109b7ae6f31SDaniel Mack struct dma_slave_config *slave_config)
110b7ae6f31SDaniel Mack {
111b7ae6f31SDaniel Mack if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
112b7ae6f31SDaniel Mack slave_config->dst_addr = dma_data->addr;
113b7ae6f31SDaniel Mack slave_config->dst_maxburst = dma_data->maxburst;
11473fe01cfSMatthias Reichl if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
11573fe01cfSMatthias Reichl slave_config->dst_addr_width =
11673fe01cfSMatthias Reichl DMA_SLAVE_BUSWIDTH_UNDEFINED;
117b7ae6f31SDaniel Mack if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
118b7ae6f31SDaniel Mack slave_config->dst_addr_width = dma_data->addr_width;
119b7ae6f31SDaniel Mack } else {
120b7ae6f31SDaniel Mack slave_config->src_addr = dma_data->addr;
121b7ae6f31SDaniel Mack slave_config->src_maxburst = dma_data->maxburst;
12273fe01cfSMatthias Reichl if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
12373fe01cfSMatthias Reichl slave_config->src_addr_width =
12473fe01cfSMatthias Reichl DMA_SLAVE_BUSWIDTH_UNDEFINED;
125b7ae6f31SDaniel Mack if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
126b7ae6f31SDaniel Mack slave_config->src_addr_width = dma_data->addr_width;
127b7ae6f31SDaniel Mack }
128b7ae6f31SDaniel Mack
129500c9f8cSShengjiu Wang slave_config->peripheral_config = dma_data->peripheral_config;
130500c9f8cSShengjiu Wang slave_config->peripheral_size = dma_data->peripheral_size;
131b7ae6f31SDaniel Mack }
132b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data);
133b7ae6f31SDaniel Mack
dmaengine_pcm_dma_complete(void * arg)134b7ae6f31SDaniel Mack static void dmaengine_pcm_dma_complete(void *arg)
135b7ae6f31SDaniel Mack {
136d1c44201SAndreas Pape unsigned int new_pos;
137b7ae6f31SDaniel Mack struct snd_pcm_substream *substream = arg;
138b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
139b7ae6f31SDaniel Mack
140d1c44201SAndreas Pape new_pos = prtd->pos + snd_pcm_lib_period_bytes(substream);
141d1c44201SAndreas Pape if (new_pos >= snd_pcm_lib_buffer_bytes(substream))
142d1c44201SAndreas Pape new_pos = 0;
143d1c44201SAndreas Pape prtd->pos = new_pos;
144b7ae6f31SDaniel Mack
145b7ae6f31SDaniel Mack snd_pcm_period_elapsed(substream);
146b7ae6f31SDaniel Mack }
147b7ae6f31SDaniel Mack
dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream * substream)148b7ae6f31SDaniel Mack static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
149b7ae6f31SDaniel Mack {
150b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
151b7ae6f31SDaniel Mack struct dma_chan *chan = prtd->dma_chan;
152b7ae6f31SDaniel Mack struct dma_async_tx_descriptor *desc;
153b7ae6f31SDaniel Mack enum dma_transfer_direction direction;
154b7ae6f31SDaniel Mack unsigned long flags = DMA_CTRL_ACK;
155b7ae6f31SDaniel Mack
156b7ae6f31SDaniel Mack direction = snd_pcm_substream_to_dma_direction(substream);
157b7ae6f31SDaniel Mack
158b7ae6f31SDaniel Mack if (!substream->runtime->no_period_wakeup)
159b7ae6f31SDaniel Mack flags |= DMA_PREP_INTERRUPT;
160b7ae6f31SDaniel Mack
161b7ae6f31SDaniel Mack prtd->pos = 0;
162b7ae6f31SDaniel Mack desc = dmaengine_prep_dma_cyclic(chan,
163b7ae6f31SDaniel Mack substream->runtime->dma_addr,
164b7ae6f31SDaniel Mack snd_pcm_lib_buffer_bytes(substream),
165b7ae6f31SDaniel Mack snd_pcm_lib_period_bytes(substream), direction, flags);
166b7ae6f31SDaniel Mack
167b7ae6f31SDaniel Mack if (!desc)
168b7ae6f31SDaniel Mack return -ENOMEM;
169b7ae6f31SDaniel Mack
170b7ae6f31SDaniel Mack desc->callback = dmaengine_pcm_dma_complete;
171b7ae6f31SDaniel Mack desc->callback_param = substream;
172b7ae6f31SDaniel Mack prtd->cookie = dmaengine_submit(desc);
173b7ae6f31SDaniel Mack
174b7ae6f31SDaniel Mack return 0;
175b7ae6f31SDaniel Mack }
176b7ae6f31SDaniel Mack
177b7ae6f31SDaniel Mack /**
178b7ae6f31SDaniel Mack * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation
179b7ae6f31SDaniel Mack * @substream: PCM substream
180b7ae6f31SDaniel Mack * @cmd: Trigger command
181b7ae6f31SDaniel Mack *
182b7ae6f31SDaniel Mack * This function can be used as the PCM trigger callback for dmaengine based PCM
183b7ae6f31SDaniel Mack * driver implementations.
1845c121d63STakashi Iwai *
1855c121d63STakashi Iwai * Return: 0 on success, a negative error code otherwise
186b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_trigger(struct snd_pcm_substream * substream,int cmd)187b7ae6f31SDaniel Mack int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
188b7ae6f31SDaniel Mack {
189b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
19002fb05a5STushar Behera struct snd_pcm_runtime *runtime = substream->runtime;
191b7ae6f31SDaniel Mack int ret;
192b7ae6f31SDaniel Mack
193b7ae6f31SDaniel Mack switch (cmd) {
194b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_START:
195b7ae6f31SDaniel Mack ret = dmaengine_pcm_prepare_and_submit(substream);
196b7ae6f31SDaniel Mack if (ret)
197b7ae6f31SDaniel Mack return ret;
198b7ae6f31SDaniel Mack dma_async_issue_pending(prtd->dma_chan);
199b7ae6f31SDaniel Mack break;
200b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_RESUME:
201b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
202b7ae6f31SDaniel Mack dmaengine_resume(prtd->dma_chan);
203b7ae6f31SDaniel Mack break;
204b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_SUSPEND:
20502fb05a5STushar Behera if (runtime->info & SNDRV_PCM_INFO_PAUSE)
20602fb05a5STushar Behera dmaengine_pause(prtd->dma_chan);
20702fb05a5STushar Behera else
208bc0e7345SLars-Peter Clausen dmaengine_terminate_async(prtd->dma_chan);
20902fb05a5STushar Behera break;
210b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
211b7ae6f31SDaniel Mack dmaengine_pause(prtd->dma_chan);
212b7ae6f31SDaniel Mack break;
213b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_STOP:
214bc0e7345SLars-Peter Clausen dmaengine_terminate_async(prtd->dma_chan);
215b7ae6f31SDaniel Mack break;
216b7ae6f31SDaniel Mack default:
217b7ae6f31SDaniel Mack return -EINVAL;
218b7ae6f31SDaniel Mack }
219b7ae6f31SDaniel Mack
220b7ae6f31SDaniel Mack return 0;
221b7ae6f31SDaniel Mack }
222b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
223b7ae6f31SDaniel Mack
224b7ae6f31SDaniel Mack /**
225b7ae6f31SDaniel Mack * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation
226b7ae6f31SDaniel Mack * @substream: PCM substream
227b7ae6f31SDaniel Mack *
228b7ae6f31SDaniel Mack * This function is deprecated and should not be used by new drivers, as its
229b7ae6f31SDaniel Mack * results may be unreliable.
2305c121d63STakashi Iwai *
2315c121d63STakashi Iwai * Return: PCM position in frames
232b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream * substream)233b7ae6f31SDaniel Mack snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
234b7ae6f31SDaniel Mack {
235b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
236b7ae6f31SDaniel Mack return bytes_to_frames(substream->runtime, prtd->pos);
237b7ae6f31SDaniel Mack }
238b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
239b7ae6f31SDaniel Mack
240b7ae6f31SDaniel Mack /**
241b7ae6f31SDaniel Mack * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
242b7ae6f31SDaniel Mack * @substream: PCM substream
243b7ae6f31SDaniel Mack *
244b7ae6f31SDaniel Mack * This function can be used as the PCM pointer callback for dmaengine based PCM
245b7ae6f31SDaniel Mack * driver implementations.
2465c121d63STakashi Iwai *
2475c121d63STakashi Iwai * Return: PCM position in frames
248b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_pointer(struct snd_pcm_substream * substream)249b7ae6f31SDaniel Mack snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
250b7ae6f31SDaniel Mack {
251b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
2529d789dc0SPeter Ujfalusi struct snd_pcm_runtime *runtime = substream->runtime;
253b7ae6f31SDaniel Mack struct dma_tx_state state;
254b7ae6f31SDaniel Mack enum dma_status status;
255b7ae6f31SDaniel Mack unsigned int buf_size;
256b7ae6f31SDaniel Mack unsigned int pos = 0;
257b7ae6f31SDaniel Mack
258b7ae6f31SDaniel Mack status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
259b7ae6f31SDaniel Mack if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
260b7ae6f31SDaniel Mack buf_size = snd_pcm_lib_buffer_bytes(substream);
261b7ae6f31SDaniel Mack if (state.residue > 0 && state.residue <= buf_size)
262b7ae6f31SDaniel Mack pos = buf_size - state.residue;
263fa1f875cSPeter Ujfalusi
264fa1f875cSPeter Ujfalusi runtime->delay = bytes_to_frames(runtime,
265fa1f875cSPeter Ujfalusi state.in_flight_bytes);
266b7ae6f31SDaniel Mack }
267b7ae6f31SDaniel Mack
2689d789dc0SPeter Ujfalusi return bytes_to_frames(runtime, pos);
269b7ae6f31SDaniel Mack }
270b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
271b7ae6f31SDaniel Mack
272b7ae6f31SDaniel Mack /**
273b7ae6f31SDaniel Mack * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM
274b7ae6f31SDaniel Mack * @filter_fn: Filter function used to request the DMA channel
275b7ae6f31SDaniel Mack * @filter_data: Data passed to the DMA filter function
276b7ae6f31SDaniel Mack *
277b7ae6f31SDaniel Mack * This function request a DMA channel for usage with dmaengine PCM.
2785c121d63STakashi Iwai *
2795c121d63STakashi Iwai * Return: NULL or the requested DMA channel
280b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,void * filter_data)281b7ae6f31SDaniel Mack struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
282b7ae6f31SDaniel Mack void *filter_data)
283b7ae6f31SDaniel Mack {
284b7ae6f31SDaniel Mack dma_cap_mask_t mask;
285b7ae6f31SDaniel Mack
286b7ae6f31SDaniel Mack dma_cap_zero(mask);
287b7ae6f31SDaniel Mack dma_cap_set(DMA_SLAVE, mask);
288b7ae6f31SDaniel Mack dma_cap_set(DMA_CYCLIC, mask);
289b7ae6f31SDaniel Mack
290b7ae6f31SDaniel Mack return dma_request_channel(mask, filter_fn, filter_data);
291b7ae6f31SDaniel Mack }
292b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
293b7ae6f31SDaniel Mack
294b7ae6f31SDaniel Mack /**
295b7ae6f31SDaniel Mack * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream
296b7ae6f31SDaniel Mack * @substream: PCM substream
297b7ae6f31SDaniel Mack * @chan: DMA channel to use for data transfers
298b7ae6f31SDaniel Mack *
299b7ae6f31SDaniel Mack * The function should usually be called from the pcm open callback. Note that
300b7ae6f31SDaniel Mack * this function will use private_data field of the substream's runtime. So it
3011a6ab46fSMasanari Iida * is not available to your pcm driver implementation.
3025c121d63STakashi Iwai *
3035c121d63STakashi Iwai * Return: 0 on success, a negative error code otherwise
304b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_open(struct snd_pcm_substream * substream,struct dma_chan * chan)305b7ae6f31SDaniel Mack int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
306b7ae6f31SDaniel Mack struct dma_chan *chan)
307b7ae6f31SDaniel Mack {
308b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd;
309b7ae6f31SDaniel Mack int ret;
310b7ae6f31SDaniel Mack
311b7ae6f31SDaniel Mack if (!chan)
312b7ae6f31SDaniel Mack return -ENXIO;
313b7ae6f31SDaniel Mack
314b7ae6f31SDaniel Mack ret = snd_pcm_hw_constraint_integer(substream->runtime,
315b7ae6f31SDaniel Mack SNDRV_PCM_HW_PARAM_PERIODS);
316b7ae6f31SDaniel Mack if (ret < 0)
317b7ae6f31SDaniel Mack return ret;
318b7ae6f31SDaniel Mack
319b7ae6f31SDaniel Mack prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
320b7ae6f31SDaniel Mack if (!prtd)
321b7ae6f31SDaniel Mack return -ENOMEM;
322b7ae6f31SDaniel Mack
323b7ae6f31SDaniel Mack prtd->dma_chan = chan;
324b7ae6f31SDaniel Mack
325b7ae6f31SDaniel Mack substream->runtime->private_data = prtd;
326b7ae6f31SDaniel Mack
327b7ae6f31SDaniel Mack return 0;
328b7ae6f31SDaniel Mack }
329b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
330b7ae6f31SDaniel Mack
331b7ae6f31SDaniel Mack /**
332b7ae6f31SDaniel Mack * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel
333b7ae6f31SDaniel Mack * @substream: PCM substream
334b7ae6f31SDaniel Mack * @filter_fn: Filter function used to request the DMA channel
335b7ae6f31SDaniel Mack * @filter_data: Data passed to the DMA filter function
336b7ae6f31SDaniel Mack *
337b7ae6f31SDaniel Mack * This function will request a DMA channel using the passed filter function and
338b7ae6f31SDaniel Mack * data. The function should usually be called from the pcm open callback. Note
339b7ae6f31SDaniel Mack * that this function will use private_data field of the substream's runtime. So
3401a6ab46fSMasanari Iida * it is not available to your pcm driver implementation.
3415c121d63STakashi Iwai *
3425c121d63STakashi Iwai * Return: 0 on success, a negative error code otherwise
343b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream * substream,dma_filter_fn filter_fn,void * filter_data)344b7ae6f31SDaniel Mack int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
345b7ae6f31SDaniel Mack dma_filter_fn filter_fn, void *filter_data)
346b7ae6f31SDaniel Mack {
347b7ae6f31SDaniel Mack return snd_dmaengine_pcm_open(substream,
348b7ae6f31SDaniel Mack snd_dmaengine_pcm_request_channel(filter_fn, filter_data));
349b7ae6f31SDaniel Mack }
350b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
351b7ae6f31SDaniel Mack
snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream * substream)352e8343410SJai Luthra int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream)
353e8343410SJai Luthra {
354e8343410SJai Luthra struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
355*88e98af9SShengjiu Wang struct dma_tx_state state;
356*88e98af9SShengjiu Wang enum dma_status status;
357e8343410SJai Luthra
358*88e98af9SShengjiu Wang status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
359*88e98af9SShengjiu Wang if (status != DMA_PAUSED)
360e8343410SJai Luthra dmaengine_synchronize(prtd->dma_chan);
361e8343410SJai Luthra
362e8343410SJai Luthra return 0;
363e8343410SJai Luthra }
364e8343410SJai Luthra EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop);
365e8343410SJai Luthra
__snd_dmaengine_pcm_close(struct snd_pcm_substream * substream,bool release_channel)366337b7b0bSTakashi Iwai static void __snd_dmaengine_pcm_close(struct snd_pcm_substream *substream,
367337b7b0bSTakashi Iwai bool release_channel)
368b7ae6f31SDaniel Mack {
369b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
3706a7db25aSShengjiu Wang struct dma_tx_state state;
3716a7db25aSShengjiu Wang enum dma_status status;
3726a7db25aSShengjiu Wang
3736a7db25aSShengjiu Wang status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
3746a7db25aSShengjiu Wang if (status == DMA_PAUSED)
3756a7db25aSShengjiu Wang dmaengine_terminate_async(prtd->dma_chan);
376b7ae6f31SDaniel Mack
377bc0e7345SLars-Peter Clausen dmaengine_synchronize(prtd->dma_chan);
378337b7b0bSTakashi Iwai if (release_channel)
379337b7b0bSTakashi Iwai dma_release_channel(prtd->dma_chan);
380b7ae6f31SDaniel Mack kfree(prtd);
381337b7b0bSTakashi Iwai }
382b7ae6f31SDaniel Mack
383337b7b0bSTakashi Iwai /**
384337b7b0bSTakashi Iwai * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
385337b7b0bSTakashi Iwai * @substream: PCM substream
386337b7b0bSTakashi Iwai *
387337b7b0bSTakashi Iwai * Return: 0 on success, a negative error code otherwise
388337b7b0bSTakashi Iwai */
snd_dmaengine_pcm_close(struct snd_pcm_substream * substream)389337b7b0bSTakashi Iwai int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
390337b7b0bSTakashi Iwai {
391337b7b0bSTakashi Iwai __snd_dmaengine_pcm_close(substream, false);
392b7ae6f31SDaniel Mack return 0;
393b7ae6f31SDaniel Mack }
394b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
395b7ae6f31SDaniel Mack
396b7ae6f31SDaniel Mack /**
397f7b6603cSMauro Carvalho Chehab * snd_dmaengine_pcm_close_release_chan - Close a dmaengine based PCM
398f7b6603cSMauro Carvalho Chehab * substream and release channel
399b7ae6f31SDaniel Mack * @substream: PCM substream
400b7ae6f31SDaniel Mack *
401b7ae6f31SDaniel Mack * Releases the DMA channel associated with the PCM substream.
4025c121d63STakashi Iwai *
4035c121d63STakashi Iwai * Return: zero if successful, or a negative error code
404b7ae6f31SDaniel Mack */
snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream * substream)405b7ae6f31SDaniel Mack int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
406b7ae6f31SDaniel Mack {
407337b7b0bSTakashi Iwai __snd_dmaengine_pcm_close(substream, true);
408bc0e7345SLars-Peter Clausen return 0;
409b7ae6f31SDaniel Mack }
410b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
411b7ae6f31SDaniel Mack
412e957204eSShengjiu Wang /**
413e957204eSShengjiu Wang * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params
414e957204eSShengjiu Wang * @substream: PCM substream
415e957204eSShengjiu Wang * @dma_data: DAI DMA data
416e957204eSShengjiu Wang * @hw: PCM hw params
417e957204eSShengjiu Wang * @chan: DMA channel to use for data transfers
418e957204eSShengjiu Wang *
419e957204eSShengjiu Wang * This function will query DMA capability, then refine the pcm hardware
420e957204eSShengjiu Wang * parameters.
4215c121d63STakashi Iwai *
4225c121d63STakashi Iwai * Return: 0 on success, a negative error code otherwise
423e957204eSShengjiu Wang */
snd_dmaengine_pcm_refine_runtime_hwparams(struct snd_pcm_substream * substream,struct snd_dmaengine_dai_dma_data * dma_data,struct snd_pcm_hardware * hw,struct dma_chan * chan)424e957204eSShengjiu Wang int snd_dmaengine_pcm_refine_runtime_hwparams(
425e957204eSShengjiu Wang struct snd_pcm_substream *substream,
426e957204eSShengjiu Wang struct snd_dmaengine_dai_dma_data *dma_data,
427e957204eSShengjiu Wang struct snd_pcm_hardware *hw,
428e957204eSShengjiu Wang struct dma_chan *chan)
429e957204eSShengjiu Wang {
430e957204eSShengjiu Wang struct dma_slave_caps dma_caps;
431e957204eSShengjiu Wang u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
432e957204eSShengjiu Wang BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
433e957204eSShengjiu Wang BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
434e957204eSShengjiu Wang snd_pcm_format_t i;
435e957204eSShengjiu Wang int ret = 0;
436e957204eSShengjiu Wang
437e957204eSShengjiu Wang if (!hw || !chan || !dma_data)
438e957204eSShengjiu Wang return -EINVAL;
439e957204eSShengjiu Wang
440e957204eSShengjiu Wang ret = dma_get_slave_caps(chan, &dma_caps);
441e957204eSShengjiu Wang if (ret == 0) {
442e957204eSShengjiu Wang if (dma_caps.cmd_pause && dma_caps.cmd_resume)
443e957204eSShengjiu Wang hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
444e957204eSShengjiu Wang if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
445e957204eSShengjiu Wang hw->info |= SNDRV_PCM_INFO_BATCH;
446e957204eSShengjiu Wang
447e957204eSShengjiu Wang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
448e957204eSShengjiu Wang addr_widths = dma_caps.dst_addr_widths;
449e957204eSShengjiu Wang else
450e957204eSShengjiu Wang addr_widths = dma_caps.src_addr_widths;
451e957204eSShengjiu Wang }
452e957204eSShengjiu Wang
453e957204eSShengjiu Wang /*
454e957204eSShengjiu Wang * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
455e957204eSShengjiu Wang * hw.formats set to 0, meaning no restrictions are in place.
456e957204eSShengjiu Wang * In this case it's the responsibility of the DAI driver to
457e957204eSShengjiu Wang * provide the supported format information.
458e957204eSShengjiu Wang */
459e957204eSShengjiu Wang if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
460e957204eSShengjiu Wang /*
461e957204eSShengjiu Wang * Prepare formats mask for valid/allowed sample types. If the
462e957204eSShengjiu Wang * dma does not have support for the given physical word size,
463e957204eSShengjiu Wang * it needs to be masked out so user space can not use the
464e957204eSShengjiu Wang * format which produces corrupted audio.
465e957204eSShengjiu Wang * In case the dma driver does not implement the slave_caps the
466e957204eSShengjiu Wang * default assumption is that it supports 1, 2 and 4 bytes
467e957204eSShengjiu Wang * widths.
468e957204eSShengjiu Wang */
46989e0b9a0STakashi Iwai pcm_for_each_format(i) {
470e957204eSShengjiu Wang int bits = snd_pcm_format_physical_width(i);
471e957204eSShengjiu Wang
472e957204eSShengjiu Wang /*
473e957204eSShengjiu Wang * Enable only samples with DMA supported physical
474e957204eSShengjiu Wang * widths
475e957204eSShengjiu Wang */
476e957204eSShengjiu Wang switch (bits) {
477e957204eSShengjiu Wang case 8:
478e957204eSShengjiu Wang case 16:
479e957204eSShengjiu Wang case 24:
480e957204eSShengjiu Wang case 32:
481e957204eSShengjiu Wang case 64:
482e957204eSShengjiu Wang if (addr_widths & (1 << (bits / 8)))
483e957204eSShengjiu Wang hw->formats |= pcm_format_to_bits(i);
484e957204eSShengjiu Wang break;
485e957204eSShengjiu Wang default:
486e957204eSShengjiu Wang /* Unsupported types */
487e957204eSShengjiu Wang break;
488e957204eSShengjiu Wang }
489e957204eSShengjiu Wang }
490e957204eSShengjiu Wang
491e957204eSShengjiu Wang return ret;
492e957204eSShengjiu Wang }
493e957204eSShengjiu Wang EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
494e957204eSShengjiu Wang
495b58a6b1eSTakashi Iwai MODULE_DESCRIPTION("PCM dmaengine helper APIs");
496b7ae6f31SDaniel Mack MODULE_LICENSE("GPL");
497