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>
This commit is contained in:
Kuninori Morimoto 2013-07-28 18:58:50 -07:00 committed by Mark Brown
parent 4b4dab8234
commit 0a4d94c07c
2 changed files with 164 additions and 0 deletions

View File

@ -173,6 +173,138 @@ void rsnd_mod_init(struct rsnd_priv *priv,
INIT_LIST_HEAD(&mod->list);
}
/*
* 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
*/

View File

@ -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>
@ -78,6 +81,32 @@ void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
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)