xref: /linux/sound/core/pcm_dmaengine.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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