Blame view

sound/core/pcm_dmaengine.c 13.6 KB
a912e80bd   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
2
3
4
5
6
7
8
9
10
  /*
   *  Copyright (C) 2012, Analog Devices Inc.
   *	Author: Lars-Peter Clausen <lars@metafoo.de>
   *
   *  Based on:
   *	imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
   *	mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc.
   *	ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
   *		      Copyright (C) 2006 Applied Data Systems
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
11
12
13
14
15
16
17
18
19
20
21
22
23
   */
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/dmaengine.h>
  #include <linux/slab.h>
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/soc.h>
  
  #include <sound/dmaengine_pcm.h>
  
  struct dmaengine_pcm_runtime_data {
  	struct dma_chan *dma_chan;
3528f27a5   Lars-Peter Clausen   ASoC: dmaengine-p...
24
  	dma_cookie_t cookie;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
25
26
  
  	unsigned int pos;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
27
28
29
30
31
32
33
  };
  
  static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
  	const struct snd_pcm_substream *substream)
  {
  	return substream->runtime->private_data;
  }
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
  {
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
  
  	return prtd->dma_chan;
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
  
  /**
   * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config
   * @substream: PCM substream
   * @params: hw_params
   * @slave_config: DMA slave config
   *
   * This function can be used to initialize a dma_slave_config from a substream
   * and hw_params in a dmaengine based PCM driver implementation.
   */
  int 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)
  {
  	enum dma_slave_buswidth buswidth;
a655f75c7   Takashi Iwai   ALSA: pcm_dmaengi...
56
  	int bits;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
57

732814c8f   Peter Ujfalusi   ALSA: pcm_dmaengi...
58
  	bits = params_physical_width(params);
a655f75c7   Takashi Iwai   ALSA: pcm_dmaengi...
59
60
61
  	if (bits < 8 || bits > 64)
  		return -EINVAL;
  	else if (bits == 8)
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
62
  		buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
a655f75c7   Takashi Iwai   ALSA: pcm_dmaengi...
63
  	else if (bits == 16)
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
64
  		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
75f850fc2   Peter Ujfalusi   ALSA: pcm_dmaengi...
65
66
  	else if (bits == 24)
  		buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
a655f75c7   Takashi Iwai   ALSA: pcm_dmaengi...
67
  	else if (bits <= 32)
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
68
  		buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
a655f75c7   Takashi Iwai   ALSA: pcm_dmaengi...
69
70
  	else
  		buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
71
72
73
74
75
76
77
78
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		slave_config->direction = DMA_MEM_TO_DEV;
  		slave_config->dst_addr_width = buswidth;
  	} else {
  		slave_config->direction = DMA_DEV_TO_MEM;
  		slave_config->src_addr_width = buswidth;
  	}
5fa70f71d   Lars-Peter Clausen   ASoC: dmaengine_p...
79
  	slave_config->device_fc = false;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
80
81
82
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
85c9f9c5f   Lars-Peter Clausen   ASoC: dmaengine-p...
83
84
85
86
87
88
89
90
91
92
93
94
95
  /**
   * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config
   *  using DAI DMA data.
   * @substream: PCM substream
   * @dma_data: DAI DMA data
   * @slave_config: DMA slave configuration
   *
   * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and
   * slave_id fields of the DMA slave config from the same fields of the DAI DMA
   * data struct. The src and dst fields will be initialized depending on the
   * direction of the substream. If the substream is a playback stream the dst
   * fields will be initialized, if it is a capture stream the src fields will be
   * initialized. The {dst,src}_addr_width field will only be initialized if the
73fe01cfb   Matthias Reichl   ASoC: dmaengine_p...
96
97
98
   * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
   * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If
   * both conditions are met the latter takes priority.
85c9f9c5f   Lars-Peter Clausen   ASoC: dmaengine-p...
99
100
101
102
103
104
105
106
107
   */
  void 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)
  {
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		slave_config->dst_addr = dma_data->addr;
  		slave_config->dst_maxburst = dma_data->maxburst;
73fe01cfb   Matthias Reichl   ASoC: dmaengine_p...
108
109
110
  		if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
  			slave_config->dst_addr_width =
  				DMA_SLAVE_BUSWIDTH_UNDEFINED;
85c9f9c5f   Lars-Peter Clausen   ASoC: dmaengine-p...
111
112
113
114
115
  		if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
  			slave_config->dst_addr_width = dma_data->addr_width;
  	} else {
  		slave_config->src_addr = dma_data->addr;
  		slave_config->src_maxburst = dma_data->maxburst;
73fe01cfb   Matthias Reichl   ASoC: dmaengine_p...
116
117
118
  		if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
  			slave_config->src_addr_width =
  				DMA_SLAVE_BUSWIDTH_UNDEFINED;
85c9f9c5f   Lars-Peter Clausen   ASoC: dmaengine-p...
119
120
121
122
123
124
125
  		if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
  			slave_config->src_addr_width = dma_data->addr_width;
  	}
  
  	slave_config->slave_id = dma_data->slave_id;
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  static void dmaengine_pcm_dma_complete(void *arg)
  {
  	struct snd_pcm_substream *substream = arg;
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
  
  	prtd->pos += snd_pcm_lib_period_bytes(substream);
  	if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
  		prtd->pos = 0;
  
  	snd_pcm_period_elapsed(substream);
  }
  
  static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
  {
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
  	struct dma_chan *chan = prtd->dma_chan;
  	struct dma_async_tx_descriptor *desc;
  	enum dma_transfer_direction direction;
e7736cdea   Peter Ujfalusi   dmaengine: Add fl...
144
  	unsigned long flags = DMA_CTRL_ACK;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
145
146
  
  	direction = snd_pcm_substream_to_dma_direction(substream);
e7736cdea   Peter Ujfalusi   dmaengine: Add fl...
147
148
  	if (!substream->runtime->no_period_wakeup)
  		flags |= DMA_PREP_INTERRUPT;
7a08cf702   Mika Westerberg   ASoC: dmaengine_p...
149
  	prtd->pos = 0;
41ba6b711   Vinod Koul   ASoC: dmaengine_p...
150
  	desc = dmaengine_prep_dma_cyclic(chan,
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
151
152
  		substream->runtime->dma_addr,
  		snd_pcm_lib_buffer_bytes(substream),
e7736cdea   Peter Ujfalusi   dmaengine: Add fl...
153
  		snd_pcm_lib_period_bytes(substream), direction, flags);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
154
155
156
157
158
159
  
  	if (!desc)
  		return -ENOMEM;
  
  	desc->callback = dmaengine_pcm_dma_complete;
  	desc->callback_param = substream;
3528f27a5   Lars-Peter Clausen   ASoC: dmaengine-p...
160
  	prtd->cookie = dmaengine_submit(desc);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  
  	return 0;
  }
  
  /**
   * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation
   * @substream: PCM substream
   * @cmd: Trigger command
   *
   * Returns 0 on success, a negative error code otherwise.
   *
   * This function can be used as the PCM trigger callback for dmaengine based PCM
   * driver implementations.
   */
  int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  {
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
02fb05a59   Tushar Behera   ALSA: pcm_dmaengi...
178
  	struct snd_pcm_runtime *runtime = substream->runtime;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  	int ret;
  
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
  		ret = dmaengine_pcm_prepare_and_submit(substream);
  		if (ret)
  			return ret;
  		dma_async_issue_pending(prtd->dma_chan);
  		break;
  	case SNDRV_PCM_TRIGGER_RESUME:
  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  		dmaengine_resume(prtd->dma_chan);
  		break;
  	case SNDRV_PCM_TRIGGER_SUSPEND:
02fb05a59   Tushar Behera   ALSA: pcm_dmaengi...
193
194
195
  		if (runtime->info & SNDRV_PCM_INFO_PAUSE)
  			dmaengine_pause(prtd->dma_chan);
  		else
bc0e73451   Lars-Peter Clausen   ALSA: pcm_dmaengi...
196
  			dmaengine_terminate_async(prtd->dma_chan);
02fb05a59   Tushar Behera   ALSA: pcm_dmaengi...
197
  		break;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
198
199
200
201
  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  		dmaengine_pause(prtd->dma_chan);
  		break;
  	case SNDRV_PCM_TRIGGER_STOP:
bc0e73451   Lars-Peter Clausen   ALSA: pcm_dmaengi...
202
  		dmaengine_terminate_async(prtd->dma_chan);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
203
204
205
206
207
208
209
210
211
212
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
  
  /**
9883ab229   Lars-Peter Clausen   ASoC: dmaengine-p...
213
   * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
214
215
   * @substream: PCM substream
   *
9883ab229   Lars-Peter Clausen   ASoC: dmaengine-p...
216
217
   * This function is deprecated and should not be used by new drivers, as its
   * results may be unreliable.
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
218
   */
9883ab229   Lars-Peter Clausen   ASoC: dmaengine-p...
219
  snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
220
221
222
223
  {
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
  	return bytes_to_frames(substream->runtime, prtd->pos);
  }
9883ab229   Lars-Peter Clausen   ASoC: dmaengine-p...
224
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
225

3528f27a5   Lars-Peter Clausen   ASoC: dmaengine-p...
226
227
228
229
230
231
232
233
234
235
  /**
   * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
   * @substream: PCM substream
   *
   * This function can be used as the PCM pointer callback for dmaengine based PCM
   * driver implementations.
   */
  snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
  {
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
9d789dc04   Peter Ujfalusi   ALSA: dmaengine_p...
236
  	struct snd_pcm_runtime *runtime = substream->runtime;
3528f27a5   Lars-Peter Clausen   ASoC: dmaengine-p...
237
238
239
240
241
242
243
244
245
246
  	struct dma_tx_state state;
  	enum dma_status status;
  	unsigned int buf_size;
  	unsigned int pos = 0;
  
  	status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
  	if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
  		buf_size = snd_pcm_lib_buffer_bytes(substream);
  		if (state.residue > 0 && state.residue <= buf_size)
  			pos = buf_size - state.residue;
fa1f875c1   Peter Ujfalusi   ALSA: dmaengine_p...
247
248
249
  
  		runtime->delay = bytes_to_frames(runtime,
  						 state.in_flight_bytes);
3528f27a5   Lars-Peter Clausen   ASoC: dmaengine-p...
250
  	}
9d789dc04   Peter Ujfalusi   ALSA: dmaengine_p...
251
  	return bytes_to_frames(runtime, pos);
3528f27a5   Lars-Peter Clausen   ASoC: dmaengine-p...
252
253
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
c999836d3   Lars-Peter Clausen   ASoC: dmaengine_p...
254
255
256
257
258
259
260
261
262
263
  /**
   * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM
   * @filter_fn: Filter function used to request the DMA channel
   * @filter_data: Data passed to the DMA filter function
   *
   * Returns NULL or the requested DMA channel.
   *
   * This function request a DMA channel for usage with dmaengine PCM.
   */
  struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
264
  	void *filter_data)
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
265
266
267
268
269
270
  {
  	dma_cap_mask_t mask;
  
  	dma_cap_zero(mask);
  	dma_cap_set(DMA_SLAVE, mask);
  	dma_cap_set(DMA_CYCLIC, mask);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
271

7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
272
  	return dma_request_channel(mask, filter_fn, filter_data);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
273
  }
c999836d3   Lars-Peter Clausen   ASoC: dmaengine_p...
274
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
275
276
277
278
  
  /**
   * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream
   * @substream: PCM substream
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
279
   * @chan: DMA channel to use for data transfers
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
280
281
282
   *
   * Returns 0 on success, a negative error code otherwise.
   *
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
283
284
   * The function should usually be called from the pcm open callback. Note that
   * this function will use private_data field of the substream's runtime. So it
1a6ab46fa   Masanari Iida   ALSA: Fix spellin...
285
   * is not available to your pcm driver implementation.
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
286
287
   */
  int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
288
  	struct dma_chan *chan)
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
289
290
291
  {
  	struct dmaengine_pcm_runtime_data *prtd;
  	int ret;
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
292
293
  	if (!chan)
  		return -ENXIO;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
294
295
296
297
298
299
300
301
  	ret = snd_pcm_hw_constraint_integer(substream->runtime,
  					    SNDRV_PCM_HW_PARAM_PERIODS);
  	if (ret < 0)
  		return ret;
  
  	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
  	if (!prtd)
  		return -ENOMEM;
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
302
  	prtd->dma_chan = chan;
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
303
304
305
306
307
308
309
310
  
  	substream->runtime->private_data = prtd;
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
  
  /**
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
311
312
313
314
315
316
317
318
319
320
   * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel
   * @substream: PCM substream
   * @filter_fn: Filter function used to request the DMA channel
   * @filter_data: Data passed to the DMA filter function
   *
   * Returns 0 on success, a negative error code otherwise.
   *
   * This function will request a DMA channel using the passed filter function and
   * data. The function should usually be called from the pcm open callback. Note
   * that this function will use private_data field of the substream's runtime. So
1a6ab46fa   Masanari Iida   ALSA: Fix spellin...
321
   * it is not available to your pcm driver implementation.
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
322
323
324
325
326
   */
  int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
  	dma_filter_fn filter_fn, void *filter_data)
  {
  	return snd_dmaengine_pcm_open(substream,
c999836d3   Lars-Peter Clausen   ASoC: dmaengine_p...
327
  		    snd_dmaengine_pcm_request_channel(filter_fn, filter_data));
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
328
329
330
331
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
  
  /**
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
332
333
334
335
336
337
   * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
   * @substream: PCM substream
   */
  int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
  {
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
bc0e73451   Lars-Peter Clausen   ALSA: pcm_dmaengi...
338
  	dmaengine_synchronize(prtd->dma_chan);
e7f73a161   Lars-Peter Clausen   ASoC: Add dmaengi...
339
340
341
342
343
  	kfree(prtd);
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
cba69b4dc   Lothar Waßmann   ASoC: dmaengine_p...
344

7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
345
  /**
f7b6603c6   Mauro Carvalho Chehab   ALSA: fix kernel-...
346
347
   * snd_dmaengine_pcm_close_release_chan - Close a dmaengine based PCM
   *					  substream and release channel
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
348
349
350
351
352
353
354
   * @substream: PCM substream
   *
   * Releases the DMA channel associated with the PCM substream.
   */
  int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
  {
  	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
bc0e73451   Lars-Peter Clausen   ALSA: pcm_dmaengi...
355
  	dmaengine_synchronize(prtd->dma_chan);
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
356
  	dma_release_channel(prtd->dma_chan);
bc0e73451   Lars-Peter Clausen   ALSA: pcm_dmaengi...
357
  	kfree(prtd);
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
358

bc0e73451   Lars-Peter Clausen   ALSA: pcm_dmaengi...
359
  	return 0;
7c1c1d4a7   Lars-Peter Clausen   ASoC: dmaengine-p...
360
361
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
e957204e7   Shengjiu Wang   ASoC: pcm_dmaengi...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
  /**
   * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params
   * @substream: PCM substream
   * @dma_data: DAI DMA data
   * @hw: PCM hw params
   * @chan: DMA channel to use for data transfers
   *
   * Returns 0 on success, a negative error code otherwise.
   *
   * This function will query DMA capability, then refine the pcm hardware
   * parameters.
   */
  int 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)
  {
  	struct dma_slave_caps dma_caps;
  	u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
  			  BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
  			  BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
  	snd_pcm_format_t i;
  	int ret = 0;
  
  	if (!hw || !chan || !dma_data)
  		return -EINVAL;
  
  	ret = dma_get_slave_caps(chan, &dma_caps);
  	if (ret == 0) {
  		if (dma_caps.cmd_pause && dma_caps.cmd_resume)
  			hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
  		if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
  			hw->info |= SNDRV_PCM_INFO_BATCH;
  
  		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  			addr_widths = dma_caps.dst_addr_widths;
  		else
  			addr_widths = dma_caps.src_addr_widths;
  	}
  
  	/*
  	 * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
  	 * hw.formats set to 0, meaning no restrictions are in place.
  	 * In this case it's the responsibility of the DAI driver to
  	 * provide the supported format information.
  	 */
  	if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
  		/*
  		 * Prepare formats mask for valid/allowed sample types. If the
  		 * dma does not have support for the given physical word size,
  		 * it needs to be masked out so user space can not use the
  		 * format which produces corrupted audio.
  		 * In case the dma driver does not implement the slave_caps the
  		 * default assumption is that it supports 1, 2 and 4 bytes
  		 * widths.
  		 */
89e0b9a06   Takashi Iwai   ALSA: pcm_dmaengi...
419
  		pcm_for_each_format(i) {
e957204e7   Shengjiu Wang   ASoC: pcm_dmaengi...
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  			int bits = snd_pcm_format_physical_width(i);
  
  			/*
  			 * Enable only samples with DMA supported physical
  			 * widths
  			 */
  			switch (bits) {
  			case 8:
  			case 16:
  			case 24:
  			case 32:
  			case 64:
  				if (addr_widths & (1 << (bits / 8)))
  					hw->formats |= pcm_format_to_bits(i);
  				break;
  			default:
  				/* Unsupported types */
  				break;
  			}
  		}
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
cba69b4dc   Lothar Waßmann   ASoC: dmaengine_p...
444
  MODULE_LICENSE("GPL");