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