Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
soc-dmaengine-pcm.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012, Analog Devices Inc.
3  * Author: Lars-Peter Clausen <[email protected]>
4  *
5  * Based on:
6  * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <[email protected]>
7  * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc.
8  * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <[email protected]>
9  * Copyright (C) 2006 Applied Data Systems
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21 #include <linux/module.h>
22 #include <linux/init.h>
23 #include <linux/dmaengine.h>
24 #include <linux/slab.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
28 
29 #include <sound/dmaengine_pcm.h>
30 
32  struct dma_chan *dma_chan;
34 
35  unsigned int pos;
36 
37  void *data;
38 };
39 
40 static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
41  const struct snd_pcm_substream *substream)
42 {
43  return substream->runtime->private_data;
44 }
45 
51 void snd_dmaengine_pcm_set_data(struct snd_pcm_substream *substream, void *data)
52 {
53  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
54 
55  prtd->data = data;
56 }
58 
66 {
67  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
68 
69  return prtd->data;
70 }
72 
74 {
75  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
76 
77  return prtd->dma_chan;
78 }
80 
91  const struct snd_pcm_hw_params *params,
93 {
95 
96  switch (params_format(params)) {
98  buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
99  break;
101  buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
102  break;
107  buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
108  break;
109  default:
110  return -EINVAL;
111  }
112 
113  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
114  slave_config->direction = DMA_MEM_TO_DEV;
115  slave_config->dst_addr_width = buswidth;
116  } else {
117  slave_config->direction = DMA_DEV_TO_MEM;
118  slave_config->src_addr_width = buswidth;
119  }
120 
121  return 0;
122 }
124 
125 static void dmaengine_pcm_dma_complete(void *arg)
126 {
127  struct snd_pcm_substream *substream = arg;
128  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
129 
130  prtd->pos += snd_pcm_lib_period_bytes(substream);
131  if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
132  prtd->pos = 0;
133 
134  snd_pcm_period_elapsed(substream);
135 }
136 
137 static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
138 {
139  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
140  struct dma_chan *chan = prtd->dma_chan;
143  unsigned long flags = DMA_CTRL_ACK;
144 
145  direction = snd_pcm_substream_to_dma_direction(substream);
146 
147  if (!substream->runtime->no_period_wakeup)
148  flags |= DMA_PREP_INTERRUPT;
149 
150  prtd->pos = 0;
151  desc = dmaengine_prep_dma_cyclic(chan,
152  substream->runtime->dma_addr,
153  snd_pcm_lib_buffer_bytes(substream),
154  snd_pcm_lib_period_bytes(substream), direction, flags);
155 
156  if (!desc)
157  return -ENOMEM;
158 
159  desc->callback = dmaengine_pcm_dma_complete;
160  desc->callback_param = substream;
161  prtd->cookie = dmaengine_submit(desc);
162 
163  return 0;
164 }
165 
177 {
178  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
179  int ret;
180 
181  switch (cmd) {
183  ret = dmaengine_pcm_prepare_and_submit(substream);
184  if (ret)
185  return ret;
186  dma_async_issue_pending(prtd->dma_chan);
187  break;
190  dmaengine_resume(prtd->dma_chan);
191  break;
194  dmaengine_pause(prtd->dma_chan);
195  break;
197  dmaengine_terminate_all(prtd->dma_chan);
198  break;
199  default:
200  return -EINVAL;
201  }
202 
203  return 0;
204 }
206 
215 {
216  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
217  return bytes_to_frames(substream->runtime, prtd->pos);
218 }
220 
229 {
230  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
231  struct dma_tx_state state;
232  enum dma_status status;
233  unsigned int buf_size;
234  unsigned int pos = 0;
235 
236  status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
237  if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
238  buf_size = snd_pcm_lib_buffer_bytes(substream);
239  if (state.residue > 0 && state.residue <= buf_size)
240  pos = buf_size - state.residue;
241  }
242 
243  return bytes_to_frames(substream->runtime, pos);
244 }
246 
247 static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd,
248  dma_filter_fn filter_fn, void *filter_data)
249 {
251 
252  dma_cap_zero(mask);
253  dma_cap_set(DMA_SLAVE, mask);
254  dma_cap_set(DMA_CYCLIC, mask);
255  prtd->dma_chan = dma_request_channel(mask, filter_fn, filter_data);
256 
257  if (!prtd->dma_chan)
258  return -ENXIO;
259 
260  return 0;
261 }
262 
280  dma_filter_fn filter_fn, void *filter_data)
281 {
282  struct dmaengine_pcm_runtime_data *prtd;
283  int ret;
284 
285  ret = snd_pcm_hw_constraint_integer(substream->runtime,
287  if (ret < 0)
288  return ret;
289 
290  prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
291  if (!prtd)
292  return -ENOMEM;
293 
294  ret = dmaengine_pcm_request_channel(prtd, filter_fn, filter_data);
295  if (ret < 0) {
296  kfree(prtd);
297  return ret;
298  }
299 
300  substream->runtime->private_data = prtd;
301 
302  return 0;
303 }
305 
311 {
312  struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
313 
315  kfree(prtd);
316 
317  return 0;
318 }