162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Renesas R-Car Audio DMAC support
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2015 Renesas Electronics Corp.
662306a36Sopenharmony_ci// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/of_dma.h>
1062306a36Sopenharmony_ci#include "rsnd.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * Audio DMAC peri peri register
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci#define PDMASAR		0x00
1662306a36Sopenharmony_ci#define PDMADAR		0x04
1762306a36Sopenharmony_ci#define PDMACHCR	0x0c
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* PDMACHCR */
2062306a36Sopenharmony_ci#define PDMACHCR_DE		(1 << 0)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct rsnd_dmaen {
2462306a36Sopenharmony_ci	struct dma_chan		*chan;
2562306a36Sopenharmony_ci	dma_cookie_t		cookie;
2662306a36Sopenharmony_ci	unsigned int		dma_len;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct rsnd_dmapp {
3062306a36Sopenharmony_ci	int			dmapp_id;
3162306a36Sopenharmony_ci	u32			chcr;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct rsnd_dma {
3562306a36Sopenharmony_ci	struct rsnd_mod		mod;
3662306a36Sopenharmony_ci	struct rsnd_mod		*mod_from;
3762306a36Sopenharmony_ci	struct rsnd_mod		*mod_to;
3862306a36Sopenharmony_ci	dma_addr_t		src_addr;
3962306a36Sopenharmony_ci	dma_addr_t		dst_addr;
4062306a36Sopenharmony_ci	union {
4162306a36Sopenharmony_ci		struct rsnd_dmaen en;
4262306a36Sopenharmony_ci		struct rsnd_dmapp pp;
4362306a36Sopenharmony_ci	} dma;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct rsnd_dma_ctrl {
4762306a36Sopenharmony_ci	void __iomem *ppbase;
4862306a36Sopenharmony_ci	phys_addr_t ppres;
4962306a36Sopenharmony_ci	int dmaen_num;
5062306a36Sopenharmony_ci	int dmapp_num;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define rsnd_priv_to_dmac(p)	((struct rsnd_dma_ctrl *)(p)->dma)
5462306a36Sopenharmony_ci#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod)
5562306a36Sopenharmony_ci#define rsnd_dma_to_dmaen(dma)	(&(dma)->dma.en)
5662306a36Sopenharmony_ci#define rsnd_dma_to_dmapp(dma)	(&(dma)->dma.pp)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* for DEBUG */
5962306a36Sopenharmony_cistatic struct rsnd_mod_ops mem_ops = {
6062306a36Sopenharmony_ci	.name = "mem",
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic struct rsnd_mod mem = {
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci *		Audio DMAC
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_cistatic void __rsnd_dmaen_complete(struct rsnd_mod *mod,
7062306a36Sopenharmony_ci				  struct rsnd_dai_stream *io)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	if (rsnd_io_is_working(io))
7362306a36Sopenharmony_ci		rsnd_dai_period_elapsed(io);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void rsnd_dmaen_complete(void *data)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct rsnd_mod *mod = data;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
8462306a36Sopenharmony_ci						   struct rsnd_mod *mod_from,
8562306a36Sopenharmony_ci						   struct rsnd_mod *mod_to)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if ((!mod_from && !mod_to) ||
8862306a36Sopenharmony_ci	    (mod_from && mod_to))
8962306a36Sopenharmony_ci		return NULL;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (mod_from)
9262306a36Sopenharmony_ci		return rsnd_mod_dma_req(io, mod_from);
9362306a36Sopenharmony_ci	else
9462306a36Sopenharmony_ci		return rsnd_mod_dma_req(io, mod_to);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int rsnd_dmaen_stop(struct rsnd_mod *mod,
9862306a36Sopenharmony_ci			   struct rsnd_dai_stream *io,
9962306a36Sopenharmony_ci			   struct rsnd_priv *priv)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
10262306a36Sopenharmony_ci	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (dmaen->chan)
10562306a36Sopenharmony_ci		dmaengine_terminate_async(dmaen->chan);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int rsnd_dmaen_cleanup(struct rsnd_mod *mod,
11162306a36Sopenharmony_ci			      struct rsnd_dai_stream *io,
11262306a36Sopenharmony_ci			      struct rsnd_priv *priv)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
11562306a36Sopenharmony_ci	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * DMAEngine release uses mutex lock.
11962306a36Sopenharmony_ci	 * Thus, it shouldn't be called under spinlock.
12062306a36Sopenharmony_ci	 * Let's call it under prepare
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	if (dmaen->chan)
12362306a36Sopenharmony_ci		dma_release_channel(dmaen->chan);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	dmaen->chan = NULL;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int rsnd_dmaen_prepare(struct rsnd_mod *mod,
13162306a36Sopenharmony_ci			      struct rsnd_dai_stream *io,
13262306a36Sopenharmony_ci			      struct rsnd_priv *priv)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
13562306a36Sopenharmony_ci	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
13662306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* maybe suspended */
13962306a36Sopenharmony_ci	if (dmaen->chan)
14062306a36Sopenharmony_ci		return 0;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * DMAEngine request uses mutex lock.
14462306a36Sopenharmony_ci	 * Thus, it shouldn't be called under spinlock.
14562306a36Sopenharmony_ci	 * Let's call it under prepare
14662306a36Sopenharmony_ci	 */
14762306a36Sopenharmony_ci	dmaen->chan = rsnd_dmaen_request_channel(io,
14862306a36Sopenharmony_ci						 dma->mod_from,
14962306a36Sopenharmony_ci						 dma->mod_to);
15062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(dmaen->chan)) {
15162306a36Sopenharmony_ci		dmaen->chan = NULL;
15262306a36Sopenharmony_ci		dev_err(dev, "can't get dma channel\n");
15362306a36Sopenharmony_ci		return -EIO;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int rsnd_dmaen_start(struct rsnd_mod *mod,
16062306a36Sopenharmony_ci			    struct rsnd_dai_stream *io,
16162306a36Sopenharmony_ci			    struct rsnd_priv *priv)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
16462306a36Sopenharmony_ci	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
16562306a36Sopenharmony_ci	struct snd_pcm_substream *substream = io->substream;
16662306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
16762306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
16862306a36Sopenharmony_ci	struct dma_slave_config cfg = {};
16962306a36Sopenharmony_ci	enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
17062306a36Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/*
17462306a36Sopenharmony_ci	 * in case of monaural data writing or reading through Audio-DMAC
17562306a36Sopenharmony_ci	 * data is always in Left Justified format, so both src and dst
17662306a36Sopenharmony_ci	 * DMA Bus width need to be set equal to physical data width.
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	if (rsnd_runtime_channel_original(io) == 1) {
17962306a36Sopenharmony_ci		struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
18062306a36Sopenharmony_ci		int bits = snd_pcm_format_physical_width(runtime->format);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		switch (bits) {
18362306a36Sopenharmony_ci		case 8:
18462306a36Sopenharmony_ci			buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
18562306a36Sopenharmony_ci			break;
18662306a36Sopenharmony_ci		case 16:
18762306a36Sopenharmony_ci			buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
18862306a36Sopenharmony_ci			break;
18962306a36Sopenharmony_ci		case 32:
19062306a36Sopenharmony_ci			buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
19162306a36Sopenharmony_ci			break;
19262306a36Sopenharmony_ci		default:
19362306a36Sopenharmony_ci			dev_err(dev, "invalid format width %d\n", bits);
19462306a36Sopenharmony_ci			return -EINVAL;
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	cfg.direction	= is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
19962306a36Sopenharmony_ci	cfg.src_addr	= dma->src_addr;
20062306a36Sopenharmony_ci	cfg.dst_addr	= dma->dst_addr;
20162306a36Sopenharmony_ci	cfg.src_addr_width = buswidth;
20262306a36Sopenharmony_ci	cfg.dst_addr_width = buswidth;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	dev_dbg(dev, "%s %pad -> %pad\n",
20562306a36Sopenharmony_ci		rsnd_mod_name(mod),
20662306a36Sopenharmony_ci		&cfg.src_addr, &cfg.dst_addr);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ret = dmaengine_slave_config(dmaen->chan, &cfg);
20962306a36Sopenharmony_ci	if (ret < 0)
21062306a36Sopenharmony_ci		return ret;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	desc = dmaengine_prep_dma_cyclic(dmaen->chan,
21362306a36Sopenharmony_ci					 substream->runtime->dma_addr,
21462306a36Sopenharmony_ci					 snd_pcm_lib_buffer_bytes(substream),
21562306a36Sopenharmony_ci					 snd_pcm_lib_period_bytes(substream),
21662306a36Sopenharmony_ci					 is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
21762306a36Sopenharmony_ci					 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (!desc) {
22062306a36Sopenharmony_ci		dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
22162306a36Sopenharmony_ci		return -EIO;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	desc->callback		= rsnd_dmaen_complete;
22562306a36Sopenharmony_ci	desc->callback_param	= rsnd_mod_get(dma);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	dmaen->dma_len		= snd_pcm_lib_buffer_bytes(substream);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	dmaen->cookie = dmaengine_submit(desc);
23062306a36Sopenharmony_ci	if (dmaen->cookie < 0) {
23162306a36Sopenharmony_ci		dev_err(dev, "dmaengine_submit() fail\n");
23262306a36Sopenharmony_ci		return -EIO;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	dma_async_issue_pending(dmaen->chan);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return 0;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistruct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name,
24162306a36Sopenharmony_ci					  struct rsnd_mod *mod, char *x)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
24462306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
24562306a36Sopenharmony_ci	struct dma_chan *chan = NULL;
24662306a36Sopenharmony_ci	struct device_node *np;
24762306a36Sopenharmony_ci	int i = 0;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	for_each_child_of_node(of_node, np) {
25062306a36Sopenharmony_ci		i = rsnd_node_fixed_index(dev, np, name, i);
25162306a36Sopenharmony_ci		if (i < 0) {
25262306a36Sopenharmony_ci			chan = NULL;
25362306a36Sopenharmony_ci			of_node_put(np);
25462306a36Sopenharmony_ci			break;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		if (i == rsnd_mod_id_raw(mod) && (!chan))
25862306a36Sopenharmony_ci			chan = of_dma_request_slave_channel(np, x);
25962306a36Sopenharmony_ci		i++;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* It should call of_node_put(), since, it is rsnd_xxx_of_node() */
26362306a36Sopenharmony_ci	of_node_put(of_node);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return chan;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
26962306a36Sopenharmony_ci			   struct rsnd_dma *dma,
27062306a36Sopenharmony_ci			   struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_io_to_priv(io);
27362306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
27462306a36Sopenharmony_ci	struct dma_chan *chan;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* try to get DMAEngine channel */
27762306a36Sopenharmony_ci	chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
27862306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(chan)) {
27962306a36Sopenharmony_ci		/* Let's follow when -EPROBE_DEFER case */
28062306a36Sopenharmony_ci		if (PTR_ERR(chan) == -EPROBE_DEFER)
28162306a36Sopenharmony_ci			return PTR_ERR(chan);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/*
28462306a36Sopenharmony_ci		 * DMA failed. try to PIO mode
28562306a36Sopenharmony_ci		 * see
28662306a36Sopenharmony_ci		 *	rsnd_ssi_fallback()
28762306a36Sopenharmony_ci		 *	rsnd_rdai_continuance_probe()
28862306a36Sopenharmony_ci		 */
28962306a36Sopenharmony_ci		return -EAGAIN;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/*
29362306a36Sopenharmony_ci	 * use it for IPMMU if needed
29462306a36Sopenharmony_ci	 * see
29562306a36Sopenharmony_ci	 *	rsnd_preallocate_pages()
29662306a36Sopenharmony_ci	 */
29762306a36Sopenharmony_ci	io->dmac_dev = chan->device->dev;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	dma_release_channel(chan);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	dmac->dmaen_num++;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int rsnd_dmaen_pointer(struct rsnd_mod *mod,
30762306a36Sopenharmony_ci			      struct rsnd_dai_stream *io,
30862306a36Sopenharmony_ci			      snd_pcm_uframes_t *pointer)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
31162306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
31262306a36Sopenharmony_ci	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
31362306a36Sopenharmony_ci	struct dma_tx_state state;
31462306a36Sopenharmony_ci	enum dma_status status;
31562306a36Sopenharmony_ci	unsigned int pos = 0;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state);
31862306a36Sopenharmony_ci	if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
31962306a36Sopenharmony_ci		if (state.residue > 0 && state.residue <= dmaen->dma_len)
32062306a36Sopenharmony_ci			pos = dmaen->dma_len - state.residue;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	*pointer = bytes_to_frames(runtime, pos);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic struct rsnd_mod_ops rsnd_dmaen_ops = {
32862306a36Sopenharmony_ci	.name		= "audmac",
32962306a36Sopenharmony_ci	.prepare	= rsnd_dmaen_prepare,
33062306a36Sopenharmony_ci	.cleanup	= rsnd_dmaen_cleanup,
33162306a36Sopenharmony_ci	.start		= rsnd_dmaen_start,
33262306a36Sopenharmony_ci	.stop		= rsnd_dmaen_stop,
33362306a36Sopenharmony_ci	.pointer	= rsnd_dmaen_pointer,
33462306a36Sopenharmony_ci	.get_status	= rsnd_mod_get_status,
33562306a36Sopenharmony_ci};
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/*
33862306a36Sopenharmony_ci *		Audio DMAC peri peri
33962306a36Sopenharmony_ci */
34062306a36Sopenharmony_cistatic const u8 gen2_id_table_ssiu[] = {
34162306a36Sopenharmony_ci	/* SSI00 ~ SSI07 */
34262306a36Sopenharmony_ci	0x00, 0x01, 0x02, 0x03, 0x39, 0x3a, 0x3b, 0x3c,
34362306a36Sopenharmony_ci	/* SSI10 ~ SSI17 */
34462306a36Sopenharmony_ci	0x04, 0x05, 0x06, 0x07, 0x3d, 0x3e, 0x3f, 0x40,
34562306a36Sopenharmony_ci	/* SSI20 ~ SSI27 */
34662306a36Sopenharmony_ci	0x08, 0x09, 0x0a, 0x0b, 0x41, 0x42, 0x43, 0x44,
34762306a36Sopenharmony_ci	/* SSI30 ~ SSI37 */
34862306a36Sopenharmony_ci	0x0c, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
34962306a36Sopenharmony_ci	/* SSI40 ~ SSI47 */
35062306a36Sopenharmony_ci	0x0d, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
35162306a36Sopenharmony_ci	/* SSI5 */
35262306a36Sopenharmony_ci	0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35362306a36Sopenharmony_ci	/* SSI6 */
35462306a36Sopenharmony_ci	0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35562306a36Sopenharmony_ci	/* SSI7 */
35662306a36Sopenharmony_ci	0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35762306a36Sopenharmony_ci	/* SSI8 */
35862306a36Sopenharmony_ci	0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35962306a36Sopenharmony_ci	/* SSI90 ~ SSI97 */
36062306a36Sopenharmony_ci	0x12, 0x13, 0x14, 0x15, 0x53, 0x54, 0x55, 0x56,
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_cistatic const u8 gen2_id_table_scu[] = {
36362306a36Sopenharmony_ci	0x2d, /* SCU_SRCI0 */
36462306a36Sopenharmony_ci	0x2e, /* SCU_SRCI1 */
36562306a36Sopenharmony_ci	0x2f, /* SCU_SRCI2 */
36662306a36Sopenharmony_ci	0x30, /* SCU_SRCI3 */
36762306a36Sopenharmony_ci	0x31, /* SCU_SRCI4 */
36862306a36Sopenharmony_ci	0x32, /* SCU_SRCI5 */
36962306a36Sopenharmony_ci	0x33, /* SCU_SRCI6 */
37062306a36Sopenharmony_ci	0x34, /* SCU_SRCI7 */
37162306a36Sopenharmony_ci	0x35, /* SCU_SRCI8 */
37262306a36Sopenharmony_ci	0x36, /* SCU_SRCI9 */
37362306a36Sopenharmony_ci};
37462306a36Sopenharmony_cistatic const u8 gen2_id_table_cmd[] = {
37562306a36Sopenharmony_ci	0x37, /* SCU_CMD0 */
37662306a36Sopenharmony_ci	0x38, /* SCU_CMD1 */
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
38062306a36Sopenharmony_ci			     struct rsnd_mod *mod)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
38362306a36Sopenharmony_ci	struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
38462306a36Sopenharmony_ci	struct rsnd_mod *src = rsnd_io_to_mod_src(io);
38562306a36Sopenharmony_ci	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
38662306a36Sopenharmony_ci	const u8 *entry = NULL;
38762306a36Sopenharmony_ci	int id = 255;
38862306a36Sopenharmony_ci	int size = 0;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if ((mod == ssi) ||
39162306a36Sopenharmony_ci	    (mod == ssiu)) {
39262306a36Sopenharmony_ci		int busif = rsnd_mod_id_sub(ssiu);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		entry = gen2_id_table_ssiu;
39562306a36Sopenharmony_ci		size = ARRAY_SIZE(gen2_id_table_ssiu);
39662306a36Sopenharmony_ci		id = (rsnd_mod_id(mod) * 8) + busif;
39762306a36Sopenharmony_ci	} else if (mod == src) {
39862306a36Sopenharmony_ci		entry = gen2_id_table_scu;
39962306a36Sopenharmony_ci		size = ARRAY_SIZE(gen2_id_table_scu);
40062306a36Sopenharmony_ci		id = rsnd_mod_id(mod);
40162306a36Sopenharmony_ci	} else if (mod == dvc) {
40262306a36Sopenharmony_ci		entry = gen2_id_table_cmd;
40362306a36Sopenharmony_ci		size = ARRAY_SIZE(gen2_id_table_cmd);
40462306a36Sopenharmony_ci		id = rsnd_mod_id(mod);
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if ((!entry) || (size <= id)) {
40862306a36Sopenharmony_ci		struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		dev_err(dev, "unknown connection (%s)\n", rsnd_mod_name(mod));
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/* use non-prohibited SRS number as error */
41362306a36Sopenharmony_ci		return 0x00; /* SSI00 */
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return entry[id];
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
42062306a36Sopenharmony_ci			       struct rsnd_mod *mod_from,
42162306a36Sopenharmony_ci			       struct rsnd_mod *mod_to)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	return	(rsnd_dmapp_get_id(io, mod_from) << 24) +
42462306a36Sopenharmony_ci		(rsnd_dmapp_get_id(io, mod_to) << 16);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci#define rsnd_dmapp_addr(dmac, dma, reg) \
42862306a36Sopenharmony_ci	(dmac->ppbase + 0x20 + reg + \
42962306a36Sopenharmony_ci	 (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
43062306a36Sopenharmony_cistatic void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct rsnd_mod *mod = rsnd_mod_get(dma);
43362306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
43462306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
43562306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	dev_dbg(dev, "w 0x%px : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg));
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct rsnd_mod *mod = rsnd_mod_get(dma);
44562306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
44662306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic void rsnd_dmapp_bset(struct rsnd_dma *dma, u32 data, u32 mask, u32 reg)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct rsnd_mod *mod = rsnd_mod_get(dma);
45462306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
45562306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
45662306a36Sopenharmony_ci	void __iomem *addr = rsnd_dmapp_addr(dmac, dma, reg);
45762306a36Sopenharmony_ci	u32 val = ioread32(addr);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	val &= ~mask;
46062306a36Sopenharmony_ci	val |= (data & mask);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	iowrite32(val, addr);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int rsnd_dmapp_stop(struct rsnd_mod *mod,
46662306a36Sopenharmony_ci			   struct rsnd_dai_stream *io,
46762306a36Sopenharmony_ci			   struct rsnd_priv *priv)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
47062306a36Sopenharmony_ci	int i;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	rsnd_dmapp_bset(dma, 0,  PDMACHCR_DE, PDMACHCR);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	for (i = 0; i < 1024; i++) {
47562306a36Sopenharmony_ci		if (0 == (rsnd_dmapp_read(dma, PDMACHCR) & PDMACHCR_DE))
47662306a36Sopenharmony_ci			return 0;
47762306a36Sopenharmony_ci		udelay(1);
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return -EIO;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic int rsnd_dmapp_start(struct rsnd_mod *mod,
48462306a36Sopenharmony_ci			    struct rsnd_dai_stream *io,
48562306a36Sopenharmony_ci			    struct rsnd_priv *priv)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
48862306a36Sopenharmony_ci	struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	rsnd_dmapp_write(dma, dma->src_addr,	PDMASAR);
49162306a36Sopenharmony_ci	rsnd_dmapp_write(dma, dma->dst_addr,	PDMADAR);
49262306a36Sopenharmony_ci	rsnd_dmapp_write(dma, dmapp->chcr,	PDMACHCR);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return 0;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
49862306a36Sopenharmony_ci			     struct rsnd_dma *dma,
49962306a36Sopenharmony_ci			     struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
50262306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_io_to_priv(io);
50362306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
50462306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	dmapp->dmapp_id = dmac->dmapp_num;
50762306a36Sopenharmony_ci	dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	dmac->dmapp_num++;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
51262306a36Sopenharmony_ci		dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return 0;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
51862306a36Sopenharmony_cistatic void rsnd_dmapp_debug_info(struct seq_file *m,
51962306a36Sopenharmony_ci				  struct rsnd_dai_stream *io,
52062306a36Sopenharmony_ci				  struct rsnd_mod *mod)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
52362306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
52462306a36Sopenharmony_ci	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
52562306a36Sopenharmony_ci	struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	rsnd_debugfs_reg_show(m, dmac->ppres, dmac->ppbase,
52862306a36Sopenharmony_ci			      0x20 + 0x10 * dmapp->dmapp_id, 0x10);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci#define DEBUG_INFO .debug_info = rsnd_dmapp_debug_info
53162306a36Sopenharmony_ci#else
53262306a36Sopenharmony_ci#define DEBUG_INFO
53362306a36Sopenharmony_ci#endif
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic struct rsnd_mod_ops rsnd_dmapp_ops = {
53662306a36Sopenharmony_ci	.name		= "audmac-pp",
53762306a36Sopenharmony_ci	.start		= rsnd_dmapp_start,
53862306a36Sopenharmony_ci	.stop		= rsnd_dmapp_stop,
53962306a36Sopenharmony_ci	.quit		= rsnd_dmapp_stop,
54062306a36Sopenharmony_ci	.get_status	= rsnd_mod_get_status,
54162306a36Sopenharmony_ci	DEBUG_INFO
54262306a36Sopenharmony_ci};
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/*
54562306a36Sopenharmony_ci *		Common DMAC Interface
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci/*
54962306a36Sopenharmony_ci *	DMA read/write register offset
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci *	RSND_xxx_I_N	for Audio DMAC input
55262306a36Sopenharmony_ci *	RSND_xxx_O_N	for Audio DMAC output
55362306a36Sopenharmony_ci *	RSND_xxx_I_P	for Audio DMAC peri peri input
55462306a36Sopenharmony_ci *	RSND_xxx_O_P	for Audio DMAC peri peri output
55562306a36Sopenharmony_ci *
55662306a36Sopenharmony_ci *	ex) R-Car H2 case
55762306a36Sopenharmony_ci *	      mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out
55862306a36Sopenharmony_ci *	SSI : 0xec541000 / 0xec241008 / 0xec24100c
55962306a36Sopenharmony_ci *	SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000
56062306a36Sopenharmony_ci *	SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
56162306a36Sopenharmony_ci *	CMD : 0xec500000 /            / 0xec008000                0xec308000
56262306a36Sopenharmony_ci */
56362306a36Sopenharmony_ci#define RDMA_SSI_I_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
56462306a36Sopenharmony_ci#define RDMA_SSI_O_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci#define RDMA_SSIU_I_N(addr, i, j) (addr ##_reg - 0x00441000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4)))
56762306a36Sopenharmony_ci#define RDMA_SSIU_O_N(addr, i, j) RDMA_SSIU_I_N(addr, i, j)
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci#define RDMA_SSIU_I_P(addr, i, j) (addr ##_reg - 0x00141000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4)))
57062306a36Sopenharmony_ci#define RDMA_SSIU_O_P(addr, i, j) RDMA_SSIU_I_P(addr, i, j)
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci#define RDMA_SRC_I_N(addr, i)	(addr ##_reg - 0x00500000 + (0x400 * i))
57362306a36Sopenharmony_ci#define RDMA_SRC_O_N(addr, i)	(addr ##_reg - 0x004fc000 + (0x400 * i))
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci#define RDMA_SRC_I_P(addr, i)	(addr ##_reg - 0x00200000 + (0x400 * i))
57662306a36Sopenharmony_ci#define RDMA_SRC_O_P(addr, i)	(addr ##_reg - 0x001fc000 + (0x400 * i))
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci#define RDMA_CMD_O_N(addr, i)	(addr ##_reg - 0x004f8000 + (0x400 * i))
57962306a36Sopenharmony_ci#define RDMA_CMD_O_P(addr, i)	(addr ##_reg - 0x001f8000 + (0x400 * i))
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic dma_addr_t
58262306a36Sopenharmony_cirsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
58362306a36Sopenharmony_ci		   struct rsnd_mod *mod,
58462306a36Sopenharmony_ci		   int is_play, int is_from)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_io_to_priv(io);
58762306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
58862306a36Sopenharmony_ci	phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
58962306a36Sopenharmony_ci	phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
59062306a36Sopenharmony_ci	int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) ||
59162306a36Sopenharmony_ci		     !!(rsnd_io_to_mod_ssiu(io) == mod);
59262306a36Sopenharmony_ci	int use_src = !!rsnd_io_to_mod_src(io);
59362306a36Sopenharmony_ci	int use_cmd = !!rsnd_io_to_mod_dvc(io) ||
59462306a36Sopenharmony_ci		      !!rsnd_io_to_mod_mix(io) ||
59562306a36Sopenharmony_ci		      !!rsnd_io_to_mod_ctu(io);
59662306a36Sopenharmony_ci	int id = rsnd_mod_id(mod);
59762306a36Sopenharmony_ci	int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io));
59862306a36Sopenharmony_ci	struct dma_addr {
59962306a36Sopenharmony_ci		dma_addr_t out_addr;
60062306a36Sopenharmony_ci		dma_addr_t in_addr;
60162306a36Sopenharmony_ci	} dma_addrs[3][2][3] = {
60262306a36Sopenharmony_ci		/* SRC */
60362306a36Sopenharmony_ci		/* Capture */
60462306a36Sopenharmony_ci		{{{ 0,				0 },
60562306a36Sopenharmony_ci		  { RDMA_SRC_O_N(src, id),	RDMA_SRC_I_P(src, id) },
60662306a36Sopenharmony_ci		  { RDMA_CMD_O_N(src, id),	RDMA_SRC_I_P(src, id) } },
60762306a36Sopenharmony_ci		 /* Playback */
60862306a36Sopenharmony_ci		 {{ 0,				0, },
60962306a36Sopenharmony_ci		  { RDMA_SRC_O_P(src, id),	RDMA_SRC_I_N(src, id) },
61062306a36Sopenharmony_ci		  { RDMA_CMD_O_P(src, id),	RDMA_SRC_I_N(src, id) } }
61162306a36Sopenharmony_ci		},
61262306a36Sopenharmony_ci		/* SSI */
61362306a36Sopenharmony_ci		/* Capture */
61462306a36Sopenharmony_ci		{{{ RDMA_SSI_O_N(ssi, id),		0 },
61562306a36Sopenharmony_ci		  { RDMA_SSIU_O_P(ssi, id, busif),	0 },
61662306a36Sopenharmony_ci		  { RDMA_SSIU_O_P(ssi, id, busif),	0 } },
61762306a36Sopenharmony_ci		 /* Playback */
61862306a36Sopenharmony_ci		 {{ 0,			RDMA_SSI_I_N(ssi, id) },
61962306a36Sopenharmony_ci		  { 0,			RDMA_SSIU_I_P(ssi, id, busif) },
62062306a36Sopenharmony_ci		  { 0,			RDMA_SSIU_I_P(ssi, id, busif) } }
62162306a36Sopenharmony_ci		},
62262306a36Sopenharmony_ci		/* SSIU */
62362306a36Sopenharmony_ci		/* Capture */
62462306a36Sopenharmony_ci		{{{ RDMA_SSIU_O_N(ssi, id, busif),	0 },
62562306a36Sopenharmony_ci		  { RDMA_SSIU_O_P(ssi, id, busif),	0 },
62662306a36Sopenharmony_ci		  { RDMA_SSIU_O_P(ssi, id, busif),	0 } },
62762306a36Sopenharmony_ci		 /* Playback */
62862306a36Sopenharmony_ci		 {{ 0,			RDMA_SSIU_I_N(ssi, id, busif) },
62962306a36Sopenharmony_ci		  { 0,			RDMA_SSIU_I_P(ssi, id, busif) },
63062306a36Sopenharmony_ci		  { 0,			RDMA_SSIU_I_P(ssi, id, busif) } } },
63162306a36Sopenharmony_ci	};
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/*
63462306a36Sopenharmony_ci	 * FIXME
63562306a36Sopenharmony_ci	 *
63662306a36Sopenharmony_ci	 * We can't support SSI9-4/5/6/7, because its address is
63762306a36Sopenharmony_ci	 * out of calculation rule
63862306a36Sopenharmony_ci	 */
63962306a36Sopenharmony_ci	if ((id == 9) && (busif >= 4))
64062306a36Sopenharmony_ci		dev_err(dev, "This driver doesn't support SSI%d-%d, so far",
64162306a36Sopenharmony_ci			id, busif);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* it shouldn't happen */
64462306a36Sopenharmony_ci	if (use_cmd && !use_src)
64562306a36Sopenharmony_ci		dev_err(dev, "DVC is selected without SRC\n");
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/* use SSIU or SSI ? */
64862306a36Sopenharmony_ci	if (is_ssi && rsnd_ssi_use_busif(io))
64962306a36Sopenharmony_ci		is_ssi++;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return (is_from) ?
65262306a36Sopenharmony_ci		dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr :
65362306a36Sopenharmony_ci		dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci/*
65762306a36Sopenharmony_ci *	Gen4 DMA read/write register offset
65862306a36Sopenharmony_ci *
65962306a36Sopenharmony_ci *	ex) R-Car V4H case
66062306a36Sopenharmony_ci *		  mod		/ SYS-DMAC in	/ SYS-DMAC out
66162306a36Sopenharmony_ci *	SSI_SDMC: 0xec400000	/ 0xec400000	/ 0xec400000
66262306a36Sopenharmony_ci */
66362306a36Sopenharmony_ci#define RDMA_SSI_SDMC(addr, i)	(addr + (0x8000 * i))
66462306a36Sopenharmony_cistatic dma_addr_t
66562306a36Sopenharmony_cirsnd_gen4_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
66662306a36Sopenharmony_ci		   int is_play, int is_from)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_io_to_priv(io);
66962306a36Sopenharmony_ci	phys_addr_t addr = rsnd_gen_get_phy_addr(priv, RSND_GEN4_SDMC);
67062306a36Sopenharmony_ci	int id = rsnd_mod_id(mod);
67162306a36Sopenharmony_ci	int busif = rsnd_mod_id_sub(mod);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	/*
67462306a36Sopenharmony_ci	 * SSI0 only is supported
67562306a36Sopenharmony_ci	 */
67662306a36Sopenharmony_ci	if (id != 0) {
67762306a36Sopenharmony_ci		struct device *dev = rsnd_priv_to_dev(priv);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		dev_err(dev, "This driver doesn't support non SSI0");
68062306a36Sopenharmony_ci		return -EINVAL;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	return RDMA_SSI_SDMC(addr, busif);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
68762306a36Sopenharmony_ci				struct rsnd_mod *mod,
68862306a36Sopenharmony_ci				int is_play, int is_from)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_io_to_priv(io);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (!mod)
69362306a36Sopenharmony_ci		return 0;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	/*
69662306a36Sopenharmony_ci	 * gen1 uses default DMA addr
69762306a36Sopenharmony_ci	 */
69862306a36Sopenharmony_ci	if (rsnd_is_gen1(priv))
69962306a36Sopenharmony_ci		return 0;
70062306a36Sopenharmony_ci	else if (rsnd_is_gen4(priv))
70162306a36Sopenharmony_ci		return rsnd_gen4_dma_addr(io, mod, is_play, is_from);
70262306a36Sopenharmony_ci	else
70362306a36Sopenharmony_ci		return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
70762306a36Sopenharmony_cistatic void rsnd_dma_of_path(struct rsnd_mod *this,
70862306a36Sopenharmony_ci			     struct rsnd_dai_stream *io,
70962306a36Sopenharmony_ci			     int is_play,
71062306a36Sopenharmony_ci			     struct rsnd_mod **mod_from,
71162306a36Sopenharmony_ci			     struct rsnd_mod **mod_to)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct rsnd_mod *ssi;
71462306a36Sopenharmony_ci	struct rsnd_mod *src = rsnd_io_to_mod_src(io);
71562306a36Sopenharmony_ci	struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
71662306a36Sopenharmony_ci	struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
71762306a36Sopenharmony_ci	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
71862306a36Sopenharmony_ci	struct rsnd_mod *mod[MOD_MAX];
71962306a36Sopenharmony_ci	struct rsnd_mod *mod_start, *mod_end;
72062306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(this);
72162306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
72262306a36Sopenharmony_ci	int nr, i, idx;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/*
72562306a36Sopenharmony_ci	 * It should use "rcar_sound,ssiu" on DT.
72662306a36Sopenharmony_ci	 * But, we need to keep compatibility for old version.
72762306a36Sopenharmony_ci	 *
72862306a36Sopenharmony_ci	 * If it has "rcar_sound.ssiu", it will be used.
72962306a36Sopenharmony_ci	 * If not, "rcar_sound.ssi" will be used.
73062306a36Sopenharmony_ci	 * see
73162306a36Sopenharmony_ci	 *	rsnd_ssiu_dma_req()
73262306a36Sopenharmony_ci	 *	rsnd_ssi_dma_req()
73362306a36Sopenharmony_ci	 */
73462306a36Sopenharmony_ci	if (rsnd_ssiu_of_node(priv)) {
73562306a36Sopenharmony_ci		struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		/* use SSIU */
73862306a36Sopenharmony_ci		ssi = ssiu;
73962306a36Sopenharmony_ci		if (this == rsnd_io_to_mod_ssi(io))
74062306a36Sopenharmony_ci			this = ssiu;
74162306a36Sopenharmony_ci	} else {
74262306a36Sopenharmony_ci		/* keep compatible, use SSI */
74362306a36Sopenharmony_ci		ssi = rsnd_io_to_mod_ssi(io);
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (!ssi)
74762306a36Sopenharmony_ci		return;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	nr = 0;
75062306a36Sopenharmony_ci	for (i = 0; i < MOD_MAX; i++) {
75162306a36Sopenharmony_ci		mod[i] = NULL;
75262306a36Sopenharmony_ci		nr += !!rsnd_io_to_mod(io, i);
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/*
75662306a36Sopenharmony_ci	 * [S] -*-> [E]
75762306a36Sopenharmony_ci	 * [S] -*-> SRC -o-> [E]
75862306a36Sopenharmony_ci	 * [S] -*-> SRC -> DVC -o-> [E]
75962306a36Sopenharmony_ci	 * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E]
76062306a36Sopenharmony_ci	 *
76162306a36Sopenharmony_ci	 * playback	[S] = mem
76262306a36Sopenharmony_ci	 *		[E] = SSI
76362306a36Sopenharmony_ci	 *
76462306a36Sopenharmony_ci	 * capture	[S] = SSI
76562306a36Sopenharmony_ci	 *		[E] = mem
76662306a36Sopenharmony_ci	 *
76762306a36Sopenharmony_ci	 * -*->		Audio DMAC
76862306a36Sopenharmony_ci	 * -o->		Audio DMAC peri peri
76962306a36Sopenharmony_ci	 */
77062306a36Sopenharmony_ci	mod_start	= (is_play) ? NULL : ssi;
77162306a36Sopenharmony_ci	mod_end		= (is_play) ? ssi  : NULL;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	idx = 0;
77462306a36Sopenharmony_ci	mod[idx++] = mod_start;
77562306a36Sopenharmony_ci	for (i = 1; i < nr; i++) {
77662306a36Sopenharmony_ci		if (src) {
77762306a36Sopenharmony_ci			mod[idx++] = src;
77862306a36Sopenharmony_ci			src = NULL;
77962306a36Sopenharmony_ci		} else if (ctu) {
78062306a36Sopenharmony_ci			mod[idx++] = ctu;
78162306a36Sopenharmony_ci			ctu = NULL;
78262306a36Sopenharmony_ci		} else if (mix) {
78362306a36Sopenharmony_ci			mod[idx++] = mix;
78462306a36Sopenharmony_ci			mix = NULL;
78562306a36Sopenharmony_ci		} else if (dvc) {
78662306a36Sopenharmony_ci			mod[idx++] = dvc;
78762306a36Sopenharmony_ci			dvc = NULL;
78862306a36Sopenharmony_ci		}
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci	mod[idx] = mod_end;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/*
79362306a36Sopenharmony_ci	 *		| SSI | SRC |
79462306a36Sopenharmony_ci	 * -------------+-----+-----+
79562306a36Sopenharmony_ci	 *  is_play	|  o  |  *  |
79662306a36Sopenharmony_ci	 * !is_play	|  *  |  o  |
79762306a36Sopenharmony_ci	 */
79862306a36Sopenharmony_ci	if ((this == ssi) == (is_play)) {
79962306a36Sopenharmony_ci		*mod_from	= mod[idx - 1];
80062306a36Sopenharmony_ci		*mod_to		= mod[idx];
80162306a36Sopenharmony_ci	} else {
80262306a36Sopenharmony_ci		*mod_from	= mod[0];
80362306a36Sopenharmony_ci		*mod_to		= mod[1];
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	dev_dbg(dev, "module connection (this is %s)\n", rsnd_mod_name(this));
80762306a36Sopenharmony_ci	for (i = 0; i <= idx; i++) {
80862306a36Sopenharmony_ci		dev_dbg(dev, "  %s%s\n",
80962306a36Sopenharmony_ci			rsnd_mod_name(mod[i] ? mod[i] : &mem),
81062306a36Sopenharmony_ci			(mod[i] == *mod_from) ? " from" :
81162306a36Sopenharmony_ci			(mod[i] == *mod_to)   ? " to" : "");
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
81662306a36Sopenharmony_ci			  struct rsnd_mod **dma_mod)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct rsnd_mod *mod_from = NULL;
81962306a36Sopenharmony_ci	struct rsnd_mod *mod_to = NULL;
82062306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_io_to_priv(io);
82162306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
82262306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
82362306a36Sopenharmony_ci	struct rsnd_dma *dma;
82462306a36Sopenharmony_ci	struct rsnd_mod_ops *ops;
82562306a36Sopenharmony_ci	enum rsnd_mod_type type;
82662306a36Sopenharmony_ci	int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma,
82762306a36Sopenharmony_ci		      struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
82862306a36Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
82962306a36Sopenharmony_ci	int ret, dma_id;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/*
83262306a36Sopenharmony_ci	 * DMA failed. try to PIO mode
83362306a36Sopenharmony_ci	 * see
83462306a36Sopenharmony_ci	 *	rsnd_ssi_fallback()
83562306a36Sopenharmony_ci	 *	rsnd_rdai_continuance_probe()
83662306a36Sopenharmony_ci	 */
83762306a36Sopenharmony_ci	if (!dmac)
83862306a36Sopenharmony_ci		return -EAGAIN;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/* for Gen2 or later */
84362306a36Sopenharmony_ci	if (mod_from && mod_to) {
84462306a36Sopenharmony_ci		ops	= &rsnd_dmapp_ops;
84562306a36Sopenharmony_ci		attach	= rsnd_dmapp_attach;
84662306a36Sopenharmony_ci		dma_id	= dmac->dmapp_num;
84762306a36Sopenharmony_ci		type	= RSND_MOD_AUDMAPP;
84862306a36Sopenharmony_ci	} else {
84962306a36Sopenharmony_ci		ops	= &rsnd_dmaen_ops;
85062306a36Sopenharmony_ci		attach	= rsnd_dmaen_attach;
85162306a36Sopenharmony_ci		dma_id	= dmac->dmaen_num;
85262306a36Sopenharmony_ci		type	= RSND_MOD_AUDMA;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	/* for Gen1, overwrite */
85662306a36Sopenharmony_ci	if (rsnd_is_gen1(priv)) {
85762306a36Sopenharmony_ci		ops	= &rsnd_dmaen_ops;
85862306a36Sopenharmony_ci		attach	= rsnd_dmaen_attach;
85962306a36Sopenharmony_ci		dma_id	= dmac->dmaen_num;
86062306a36Sopenharmony_ci		type	= RSND_MOD_AUDMA;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
86462306a36Sopenharmony_ci	if (!dma)
86562306a36Sopenharmony_ci		return -ENOMEM;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	*dma_mod = rsnd_mod_get(dma);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
87062306a36Sopenharmony_ci			    type, dma_id);
87162306a36Sopenharmony_ci	if (ret < 0)
87262306a36Sopenharmony_ci		return ret;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	dev_dbg(dev, "%s %s -> %s\n",
87562306a36Sopenharmony_ci		rsnd_mod_name(*dma_mod),
87662306a36Sopenharmony_ci		rsnd_mod_name(mod_from ? mod_from : &mem),
87762306a36Sopenharmony_ci		rsnd_mod_name(mod_to   ? mod_to   : &mem));
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	ret = attach(io, dma, mod_from, mod_to);
88062306a36Sopenharmony_ci	if (ret < 0)
88162306a36Sopenharmony_ci		return ret;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
88462306a36Sopenharmony_ci	dma->dst_addr = rsnd_dma_addr(io, mod_to,   is_play, 0);
88562306a36Sopenharmony_ci	dma->mod_from = mod_from;
88662306a36Sopenharmony_ci	dma->mod_to   = mod_to;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ciint rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
89262306a36Sopenharmony_ci		    struct rsnd_mod **dma_mod)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	if (!(*dma_mod)) {
89562306a36Sopenharmony_ci		int ret = rsnd_dma_alloc(io, mod, dma_mod);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		if (ret < 0)
89862306a36Sopenharmony_ci			return ret;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type);
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ciint rsnd_dma_probe(struct rsnd_priv *priv)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct platform_device *pdev = rsnd_priv_to_pdev(priv);
90762306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
90862306a36Sopenharmony_ci	struct rsnd_dma_ctrl *dmac;
90962306a36Sopenharmony_ci	struct resource *res;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/*
91262306a36Sopenharmony_ci	 * for Gen1
91362306a36Sopenharmony_ci	 */
91462306a36Sopenharmony_ci	if (rsnd_is_gen1(priv))
91562306a36Sopenharmony_ci		return 0;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	/*
91862306a36Sopenharmony_ci	 * for Gen2 or later
91962306a36Sopenharmony_ci	 */
92062306a36Sopenharmony_ci	dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL);
92162306a36Sopenharmony_ci	if (!dmac) {
92262306a36Sopenharmony_ci		dev_err(dev, "dma allocate failed\n");
92362306a36Sopenharmony_ci		return 0; /* it will be PIO mode */
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	/* for Gen4 doesn't have DMA-pp */
92762306a36Sopenharmony_ci	if (rsnd_is_gen4(priv))
92862306a36Sopenharmony_ci		goto audmapp_end;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
93162306a36Sopenharmony_ci	if (!res) {
93262306a36Sopenharmony_ci		dev_err(dev, "lack of audmapp in DT\n");
93362306a36Sopenharmony_ci		return 0; /* it will be PIO mode */
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	dmac->dmapp_num = 0;
93762306a36Sopenharmony_ci	dmac->ppres  = res->start;
93862306a36Sopenharmony_ci	dmac->ppbase = devm_ioremap_resource(dev, res);
93962306a36Sopenharmony_ci	if (IS_ERR(dmac->ppbase))
94062306a36Sopenharmony_ci		return PTR_ERR(dmac->ppbase);
94162306a36Sopenharmony_ciaudmapp_end:
94262306a36Sopenharmony_ci	priv->dma = dmac;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	/* dummy mem mod for debug */
94562306a36Sopenharmony_ci	return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, 0, 0);
94662306a36Sopenharmony_ci}
947