| From 8580bc44656280e0b71c0afb33279c977880d9bc Mon Sep 17 00:00:00 2001 |
| From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Date: Sun, 28 Jul 2013 18:58:50 -0700 |
| Subject: 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> |
| (cherry picked from commit 0a4d94c07ce782e645a8c0484d52221758b4c398) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| sound/soc/sh/rcar/core.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++ |
| sound/soc/sh/rcar/rsnd.h | 32 ++++++++++++ |
| 2 files changed, 164 insertions(+) |
| |
| 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, |
| } |
| |
| /* |
| + * rsnd_dma functions |
| + */ |
| +static void rsnd_dma_continue(struct rsnd_dma *dma) |
| +{ |
| + /* push next A or B plane */ |
| + dma->submit_loop = 1; |
| + schedule_work(&dma->work); |
| +} |
| + |
| +void rsnd_dma_start(struct rsnd_dma *dma) |
| +{ |
| + /* push both A and B plane*/ |
| + dma->submit_loop = 2; |
| + schedule_work(&dma->work); |
| +} |
| + |
| +void rsnd_dma_stop(struct rsnd_dma *dma) |
| +{ |
| + dma->submit_loop = 0; |
| + cancel_work_sync(&dma->work); |
| + dmaengine_terminate_all(dma->chan); |
| +} |
| + |
| +static void rsnd_dma_complete(void *data) |
| +{ |
| + struct rsnd_dma *dma = (struct rsnd_dma *)data; |
| + struct rsnd_priv *priv = dma->priv; |
| + unsigned long flags; |
| + |
| + rsnd_lock(priv, flags); |
| + |
| + dma->complete(dma); |
| + |
| + if (dma->submit_loop) |
| + rsnd_dma_continue(dma); |
| + |
| + rsnd_unlock(priv, flags); |
| +} |
| + |
| +static void rsnd_dma_do_work(struct work_struct *work) |
| +{ |
| + struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); |
| + struct rsnd_priv *priv = dma->priv; |
| + struct device *dev = rsnd_priv_to_dev(priv); |
| + struct dma_async_tx_descriptor *desc; |
| + dma_addr_t buf; |
| + size_t len; |
| + int i; |
| + |
| + for (i = 0; i < dma->submit_loop; i++) { |
| + |
| + if (dma->inquiry(dma, &buf, &len) < 0) |
| + return; |
| + |
| + desc = dmaengine_prep_slave_single( |
| + dma->chan, buf, len, dma->dir, |
| + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
| + if (!desc) { |
| + dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); |
| + return; |
| + } |
| + |
| + desc->callback = rsnd_dma_complete; |
| + desc->callback_param = dma; |
| + |
| + if (dmaengine_submit(desc) < 0) { |
| + dev_err(dev, "dmaengine_submit() fail\n"); |
| + return; |
| + } |
| + |
| + } |
| + |
| + dma_async_issue_pending(dma->chan); |
| +} |
| + |
| +int rsnd_dma_available(struct rsnd_dma *dma) |
| +{ |
| + return !!dma->chan; |
| +} |
| + |
| +static bool rsnd_dma_filter(struct dma_chan *chan, void *param) |
| +{ |
| + chan->private = param; |
| + |
| + return true; |
| +} |
| + |
| +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, |
| + int is_play, int id, |
| + int (*inquiry)(struct rsnd_dma *dma, |
| + dma_addr_t *buf, int *len), |
| + int (*complete)(struct rsnd_dma *dma)) |
| +{ |
| + struct device *dev = rsnd_priv_to_dev(priv); |
| + dma_cap_mask_t mask; |
| + |
| + if (dma->chan) { |
| + dev_err(dev, "it already has dma channel\n"); |
| + return -EIO; |
| + } |
| + |
| + dma_cap_zero(mask); |
| + dma_cap_set(DMA_SLAVE, mask); |
| + |
| + dma->slave.shdma_slave.slave_id = id; |
| + |
| + dma->chan = dma_request_channel(mask, rsnd_dma_filter, |
| + &dma->slave.shdma_slave); |
| + if (!dma->chan) { |
| + dev_err(dev, "can't get dma channel\n"); |
| + return -EIO; |
| + } |
| + |
| + dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; |
| + dma->priv = priv; |
| + dma->inquiry = inquiry; |
| + dma->complete = complete; |
| + INIT_WORK(&dma->work, rsnd_dma_do_work); |
| + |
| + return 0; |
| +} |
| + |
| +void rsnd_dma_quit(struct rsnd_priv *priv, |
| + struct rsnd_dma *dma) |
| +{ |
| + if (dma->chan) |
| + dma_release_channel(dma->chan); |
| + |
| + dma->chan = NULL; |
| +} |
| + |
| +/* |
| * rsnd_dai functions |
| */ |
| #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 @@ |
| |
| #include <linux/clk.h> |
| #include <linux/device.h> |
| +#include <linux/dma-mapping.h> |
| #include <linux/io.h> |
| #include <linux/list.h> |
| #include <linux/module.h> |
| +#include <linux/sh_dma.h> |
| +#include <linux/workqueue.h> |
| #include <sound/rcar_snd.h> |
| #include <sound/soc.h> |
| #include <sound/pcm_params.h> |
| @@ -79,6 +82,32 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, |
| u32 mask, u32 data); |
| |
| /* |
| + * R-Car DMA |
| + */ |
| +struct rsnd_dma { |
| + struct rsnd_priv *priv; |
| + struct sh_dmae_slave slave; |
| + struct work_struct work; |
| + struct dma_chan *chan; |
| + enum dma_data_direction dir; |
| + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); |
| + int (*complete)(struct rsnd_dma *dma); |
| + |
| + int submit_loop; |
| +}; |
| + |
| +void rsnd_dma_start(struct rsnd_dma *dma); |
| +void rsnd_dma_stop(struct rsnd_dma *dma); |
| +int rsnd_dma_available(struct rsnd_dma *dma); |
| +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, |
| + int is_play, int id, |
| + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), |
| + int (*complete)(struct rsnd_dma *dma)); |
| +void rsnd_dma_quit(struct rsnd_priv *priv, |
| + struct rsnd_dma *dma); |
| + |
| + |
| +/* |
| * R-Car sound mod |
| */ |
| |
| @@ -103,9 +132,12 @@ struct rsnd_mod { |
| struct rsnd_priv *priv; |
| struct rsnd_mod_ops *ops; |
| struct list_head list; /* connect to rsnd_dai playback/capture */ |
| + struct rsnd_dma dma; |
| }; |
| |
| #define rsnd_mod_to_priv(mod) ((mod)->priv) |
| +#define rsnd_mod_to_dma(mod) (&(mod)->dma) |
| +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) |
| #define rsnd_mod_id(mod) ((mod)->id) |
| #define for_each_rsnd_mod(pos, n, io) \ |
| list_for_each_entry_safe(pos, n, &(io)->head, list) |
| -- |
| 1.8.5.rc3 |
| |