Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0a4d94c0 authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown
Browse files

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: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent 4b4dab82
Loading
Loading
Loading
Loading
+132 −0
Original line number Original line Diff line number Diff line
@@ -173,6 +173,138 @@ void rsnd_mod_init(struct rsnd_priv *priv,
	INIT_LIST_HEAD(&mod->list);
	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
 *	rsnd_dai functions
 */
 */
+32 −0
Original line number Original line Diff line number Diff line
@@ -13,9 +13,12 @@


#include <linux/clk.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/sh_dma.h>
#include <linux/workqueue.h>
#include <sound/rcar_snd.h>
#include <sound/rcar_snd.h>
#include <sound/soc.h>
#include <sound/soc.h>
#include <sound/pcm_params.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,
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
		    u32 mask, u32 data);
		    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
 *	R-Car sound mod
 */
 */
@@ -103,9 +132,12 @@ struct rsnd_mod {
	struct rsnd_priv *priv;
	struct rsnd_priv *priv;
	struct rsnd_mod_ops *ops;
	struct rsnd_mod_ops *ops;
	struct list_head list; /* connect to rsnd_dai playback/capture */
	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_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 rsnd_mod_id(mod) ((mod)->id)
#define for_each_rsnd_mod(pos, n, io)	\
#define for_each_rsnd_mod(pos, n, io)	\
	list_for_each_entry_safe(pos, n, &(io)->head, list)
	list_for_each_entry_safe(pos, n, &(io)->head, list)