diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2013-07-28 21:58:50 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-08-06 12:56:13 -0400 |
commit | 0a4d94c07ce782e645a8c0484d52221758b4c398 (patch) | |
tree | 06b9b94e4da866f625d5754a94dcd199687798f4 /sound/soc/sh | |
parent | 4b4dab82340d969521f4f86108441cb597c8595d (diff) |
ASoC: rsnd: add common DMAEngine method
R-Car Sound driver will support DMA transfer in the future,
then, SSI/SRU/SRC will use it.
Current R-Car can't use soc-dmaengine-pcm.c since its DMAEngine
doesn't support dmaengine_prep_dma_cyclic(),
and SSI needs double plane transfer (which needs special submit) on DMAC.
This patch adds common DMAEngine method for it
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'sound/soc/sh')
-rw-r--r-- | sound/soc/sh/rcar/core.c | 132 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 32 |
2 files changed, 164 insertions, 0 deletions
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 420d6df9c3d0..a35706028514 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c | |||
@@ -174,6 +174,138 @@ void rsnd_mod_init(struct rsnd_priv *priv, | |||
174 | } | 174 | } |
175 | 175 | ||
176 | /* | 176 | /* |
177 | * rsnd_dma functions | ||
178 | */ | ||
179 | static void rsnd_dma_continue(struct rsnd_dma *dma) | ||
180 | { | ||
181 | /* push next A or B plane */ | ||
182 | dma->submit_loop = 1; | ||
183 | schedule_work(&dma->work); | ||
184 | } | ||
185 | |||
186 | void rsnd_dma_start(struct rsnd_dma *dma) | ||
187 | { | ||
188 | /* push both A and B plane*/ | ||
189 | dma->submit_loop = 2; | ||
190 | schedule_work(&dma->work); | ||
191 | } | ||
192 | |||
193 | void rsnd_dma_stop(struct rsnd_dma *dma) | ||
194 | { | ||
195 | dma->submit_loop = 0; | ||
196 | cancel_work_sync(&dma->work); | ||
197 | dmaengine_terminate_all(dma->chan); | ||
198 | } | ||
199 | |||
200 | static void rsnd_dma_complete(void *data) | ||
201 | { | ||
202 | struct rsnd_dma *dma = (struct rsnd_dma *)data; | ||
203 | struct rsnd_priv *priv = dma->priv; | ||
204 | unsigned long flags; | ||
205 | |||
206 | rsnd_lock(priv, flags); | ||
207 | |||
208 | dma->complete(dma); | ||
209 | |||
210 | if (dma->submit_loop) | ||
211 | rsnd_dma_continue(dma); | ||
212 | |||
213 | rsnd_unlock(priv, flags); | ||
214 | } | ||
215 | |||
216 | static void rsnd_dma_do_work(struct work_struct *work) | ||
217 | { | ||
218 | struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); | ||
219 | struct rsnd_priv *priv = dma->priv; | ||
220 | struct device *dev = rsnd_priv_to_dev(priv); | ||
221 | struct dma_async_tx_descriptor *desc; | ||
222 | dma_addr_t buf; | ||
223 | size_t len; | ||
224 | int i; | ||
225 | |||
226 | for (i = 0; i < dma->submit_loop; i++) { | ||
227 | |||
228 | if (dma->inquiry(dma, &buf, &len) < 0) | ||
229 | return; | ||
230 | |||
231 | desc = dmaengine_prep_slave_single( | ||
232 | dma->chan, buf, len, dma->dir, | ||
233 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
234 | if (!desc) { | ||
235 | dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); | ||
236 | return; | ||
237 | } | ||
238 | |||
239 | desc->callback = rsnd_dma_complete; | ||
240 | desc->callback_param = dma; | ||
241 | |||
242 | if (dmaengine_submit(desc) < 0) { | ||
243 | dev_err(dev, "dmaengine_submit() fail\n"); | ||
244 | return; | ||
245 | } | ||
246 | |||
247 | } | ||
248 | |||
249 | dma_async_issue_pending(dma->chan); | ||
250 | } | ||
251 | |||
252 | int rsnd_dma_available(struct rsnd_dma *dma) | ||
253 | { | ||
254 | return !!dma->chan; | ||
255 | } | ||
256 | |||
257 | static bool rsnd_dma_filter(struct dma_chan *chan, void *param) | ||
258 | { | ||
259 | chan->private = param; | ||
260 | |||
261 | return true; | ||
262 | } | ||
263 | |||
264 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | ||
265 | int is_play, int id, | ||
266 | int (*inquiry)(struct rsnd_dma *dma, | ||
267 | dma_addr_t *buf, int *len), | ||
268 | int (*complete)(struct rsnd_dma *dma)) | ||
269 | { | ||
270 | struct device *dev = rsnd_priv_to_dev(priv); | ||
271 | dma_cap_mask_t mask; | ||
272 | |||
273 | if (dma->chan) { | ||
274 | dev_err(dev, "it already has dma channel\n"); | ||
275 | return -EIO; | ||
276 | } | ||
277 | |||
278 | dma_cap_zero(mask); | ||
279 | dma_cap_set(DMA_SLAVE, mask); | ||
280 | |||
281 | dma->slave.shdma_slave.slave_id = id; | ||
282 | |||
283 | dma->chan = dma_request_channel(mask, rsnd_dma_filter, | ||
284 | &dma->slave.shdma_slave); | ||
285 | if (!dma->chan) { | ||
286 | dev_err(dev, "can't get dma channel\n"); | ||
287 | return -EIO; | ||
288 | } | ||
289 | |||
290 | dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; | ||
291 | dma->priv = priv; | ||
292 | dma->inquiry = inquiry; | ||
293 | dma->complete = complete; | ||
294 | INIT_WORK(&dma->work, rsnd_dma_do_work); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | void rsnd_dma_quit(struct rsnd_priv *priv, | ||
300 | struct rsnd_dma *dma) | ||
301 | { | ||
302 | if (dma->chan) | ||
303 | dma_release_channel(dma->chan); | ||
304 | |||
305 | dma->chan = NULL; | ||
306 | } | ||
307 | |||
308 | /* | ||
177 | * rsnd_dai functions | 309 | * rsnd_dai functions |
178 | */ | 310 | */ |
179 | #define rsnd_dai_call(rdai, io, fn) \ | 311 | #define rsnd_dai_call(rdai, io, fn) \ |
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9243e387104c..15dccd598960 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h | |||
@@ -13,9 +13,12 @@ | |||
13 | 13 | ||
14 | #include <linux/clk.h> | 14 | #include <linux/clk.h> |
15 | #include <linux/device.h> | 15 | #include <linux/device.h> |
16 | #include <linux/dma-mapping.h> | ||
16 | #include <linux/io.h> | 17 | #include <linux/io.h> |
17 | #include <linux/list.h> | 18 | #include <linux/list.h> |
18 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/sh_dma.h> | ||
21 | #include <linux/workqueue.h> | ||
19 | #include <sound/rcar_snd.h> | 22 | #include <sound/rcar_snd.h> |
20 | #include <sound/soc.h> | 23 | #include <sound/soc.h> |
21 | #include <sound/pcm_params.h> | 24 | #include <sound/pcm_params.h> |
@@ -79,6 +82,32 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, | |||
79 | u32 mask, u32 data); | 82 | u32 mask, u32 data); |
80 | 83 | ||
81 | /* | 84 | /* |
85 | * R-Car DMA | ||
86 | */ | ||
87 | struct rsnd_dma { | ||
88 | struct rsnd_priv *priv; | ||
89 | struct sh_dmae_slave slave; | ||
90 | struct work_struct work; | ||
91 | struct dma_chan *chan; | ||
92 | enum dma_data_direction dir; | ||
93 | int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); | ||
94 | int (*complete)(struct rsnd_dma *dma); | ||
95 | |||
96 | int submit_loop; | ||
97 | }; | ||
98 | |||
99 | void rsnd_dma_start(struct rsnd_dma *dma); | ||
100 | void rsnd_dma_stop(struct rsnd_dma *dma); | ||
101 | int rsnd_dma_available(struct rsnd_dma *dma); | ||
102 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | ||
103 | int is_play, int id, | ||
104 | int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), | ||
105 | int (*complete)(struct rsnd_dma *dma)); | ||
106 | void rsnd_dma_quit(struct rsnd_priv *priv, | ||
107 | struct rsnd_dma *dma); | ||
108 | |||
109 | |||
110 | /* | ||
82 | * R-Car sound mod | 111 | * R-Car sound mod |
83 | */ | 112 | */ |
84 | 113 | ||
@@ -103,9 +132,12 @@ struct rsnd_mod { | |||
103 | struct rsnd_priv *priv; | 132 | struct rsnd_priv *priv; |
104 | struct rsnd_mod_ops *ops; | 133 | struct rsnd_mod_ops *ops; |
105 | struct list_head list; /* connect to rsnd_dai playback/capture */ | 134 | struct list_head list; /* connect to rsnd_dai playback/capture */ |
135 | struct rsnd_dma dma; | ||
106 | }; | 136 | }; |
107 | 137 | ||
108 | #define rsnd_mod_to_priv(mod) ((mod)->priv) | 138 | #define rsnd_mod_to_priv(mod) ((mod)->priv) |
139 | #define rsnd_mod_to_dma(mod) (&(mod)->dma) | ||
140 | #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) | ||
109 | #define rsnd_mod_id(mod) ((mod)->id) | 141 | #define rsnd_mod_id(mod) ((mod)->id) |
110 | #define for_each_rsnd_mod(pos, n, io) \ | 142 | #define for_each_rsnd_mod(pos, n, io) \ |
111 | list_for_each_entry_safe(pos, n, &(io)->head, list) | 143 | list_for_each_entry_safe(pos, n, &(io)->head, list) |