[alsa-devel] [PATCH 0/5] ASoC: R-Car sound DMA transfer support
Hi Mark
These are DMAEngine support patches for R-Car sound
Last patch cleanups empty SCU functions. I will work for regmap support soon, but I need more time for it.
This patch doesn't use soc-dmaengine-pcm.c method, becouse current R-Car chip doesn't suuport dmaengine_prep_dma_cyclic(). Of course I can re-work to use it if chip/rcar-dmaengine supports it.
these are based on mark/topic/rcar branch
Kuninori Morimoto (5): ASoC: rsnd: remove platform dai and add dai_id on platform setting ASoC: rsnd: add common DMAEngine method ASoC: rsnd: SSI supports DMA transfer ASoC: rsnd: SSI supports DMA transfer via BUSIF ASoC: rsnd: scu: cleanup empty functions
include/sound/rcar_snd.h | 25 ++++-- sound/soc/sh/rcar/core.c | 192 +++++++++++++++++++++++++++++++++++++++++----- sound/soc/sh/rcar/gen.c | 20 +++-- sound/soc/sh/rcar/rsnd.h | 44 +++++++++++ sound/soc/sh/rcar/scu.c | 163 ++++++++++++++++++++++++++++++++------- sound/soc/sh/rcar/ssi.c | 150 ++++++++++++++++++++++++++++++++++-- 6 files changed, 529 insertions(+), 65 deletions(-)
Best regards --- Kuninori Morimoto
Current rsnd driver is using struct rsnd_dai_platform_info so that indicate sound DAI information (playback/capture SSI ID). But, SSI settings were also required separately. Thus, platform settings was very un-understandable. This patch adds dai_id to SSI settings, and removed rsnd_dai_platform_info.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/rcar_snd.h | 18 +++++++------- sound/soc/sh/rcar/core.c | 60 ++++++++++++++++++++++++++++++++-------------- sound/soc/sh/rcar/gen.c | 10 ++++---- sound/soc/sh/rcar/rsnd.h | 3 +++ sound/soc/sh/rcar/ssi.c | 22 +++++++++++++++++ 5 files changed, 82 insertions(+), 31 deletions(-)
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 99d8dd0..33233ed 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -28,15 +28,24 @@ /* * flags * - * 0xA0000000 + * 0xAB000000 * * A : clock sharing settings + * B : SSI direction */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */
+#define RSND_SSI_PLAY (1 << 24) + +#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dai_id = -1, .pio_irq = -1, .flags = 0 } + struct rsnd_ssi_platform_info { + int dai_id; int pio_irq; u32 flags; }; @@ -45,11 +54,6 @@ struct rsnd_scu_platform_info { u32 flags; };
-struct rsnd_dai_platform_info { - int ssi_id_playback; - int ssi_id_capture; -}; - /* * flags * @@ -66,8 +70,6 @@ struct rcar_snd_info { int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; - struct rsnd_dai_platform_info *dai_info; - int dai_info_nr; int (*start)(int id); int (*stop)(int id); }; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 9a5469d..420d6df 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -219,6 +219,16 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod) return 0; }
+int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) +{ + int id = rdai - priv->rdai; + + if ((id < 0) || (id >= rsnd_dai_nr(priv))) + return -EINVAL; + + return id; +} + struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) { return priv->rdai + id; @@ -315,9 +325,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); - int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : - info->ssi_id_capture; + struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + int ssi_id = rsnd_mod_id(mod); int ret; unsigned long flags;
@@ -439,10 +450,24 @@ static int rsnd_dai_probe(struct platform_device *pdev, { struct snd_soc_dai_driver *drv; struct rsnd_dai *rdai; + struct rsnd_mod *pmod, *cmod; struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_platform_info *dai_info; - int dai_nr = info->dai_info_nr; - int i, pid, cid; + int dai_nr; + int i; + + /* get max dai nr */ + for (dai_nr = 0; dai_nr < 32; dai_nr++) { + pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); + + if (!pmod && !cmod) + break; + } + + if (!dai_nr) { + dev_err(dev, "no dai\n"); + return -EIO; + }
drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); @@ -452,10 +477,9 @@ static int rsnd_dai_probe(struct platform_device *pdev, }
for (i = 0; i < dai_nr; i++) { - dai_info = &info->dai_info[i];
- pid = dai_info->ssi_id_playback; - cid = dai_info->ssi_id_capture; + pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0);
/* * init rsnd_dai @@ -463,8 +487,6 @@ static int rsnd_dai_probe(struct platform_device *pdev, INIT_LIST_HEAD(&rdai[i].playback.head); INIT_LIST_HEAD(&rdai[i].capture.head);
- rdai[i].info = dai_info; - snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);
/* @@ -472,20 +494,22 @@ static int rsnd_dai_probe(struct platform_device *pdev, */ drv[i].name = rdai[i].name; drv[i].ops = &rsnd_soc_dai_ops; - if (pid >= 0) { + if (pmod) { drv[i].playback.rates = RSND_RATES; drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; } - if (cid >= 0) { + if (cmod) { drv[i].capture.rates = RSND_RATES; drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; }
- dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); + dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, + pmod ? "play" : " -- ", + cmod ? "capture" : " -- "); }
priv->dai_nr = dai_nr; @@ -627,10 +651,6 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret;
- ret = rsnd_dai_probe(pdev, info, priv); - if (ret < 0) - return ret; - ret = rsnd_scu_probe(pdev, info, priv); if (ret < 0) return ret; @@ -643,6 +663,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret;
+ ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 5e4ae0d..a49db66 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -49,7 +49,6 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); struct rsnd_mod *mod; int ret; int id; @@ -67,10 +66,11 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, * Then, SSI id = SCU id here */
- if (rsnd_dai_is_play(rdai, io)) - id = info->ssi_id_playback; - else - id = info->ssi_id_capture; + /* get SSI's ID */ + mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + id = rsnd_mod_id(mod);
/* SSI */ mod = rsnd_ssi_mod_get(priv, id); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 0e7727c..9243e38 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -157,6 +157,7 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod); int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) #define rsnd_io_to_runtime(io) ((io)->substream->runtime)
@@ -254,5 +255,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play);
#endif diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 061ac7e..c48a6c7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -87,6 +87,7 @@ struct rsnd_ssiu { #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) #define rsnd_ssi_to_ssiu(ssi)\ (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1)
@@ -502,6 +503,27 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { /* * ssi mod function */ +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play) +{ + struct rsnd_ssi *ssi; + int i, has_play; + + is_play = !!is_play; + + for_each_rsnd_ssi(ssi, priv, i) { + if (rsnd_ssi_dai_id(ssi) != dai_id) + continue; + + has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); + + if (is_play == has_play) + return &ssi->mod; + } + + return NULL; +} + struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv));
R-Car Sound driver will support DMA transfer in the future, then, SSI/SRU/SRC will use it. Current R-Car can't use soc-dmaengine-pcm.c since its DMAEngine doesn't support dmaengine_prep_dma_cyclic(), and SSI needs double plane transfer (which needs special submit) on DMAC. This patch adds common DMAEngine method for it
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 32 +++++++++++ 2 files changed, 164 insertions(+)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 420d6df..a357060 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -174,6 +174,138 @@ void rsnd_mod_init(struct rsnd_priv *priv, }
/* + * rsnd_dma functions + */ +static void rsnd_dma_continue(struct rsnd_dma *dma) +{ + /* push next A or B plane */ + dma->submit_loop = 1; + schedule_work(&dma->work); +} + +void rsnd_dma_start(struct rsnd_dma *dma) +{ + /* push both A and B plane*/ + dma->submit_loop = 2; + schedule_work(&dma->work); +} + +void rsnd_dma_stop(struct rsnd_dma *dma) +{ + dma->submit_loop = 0; + cancel_work_sync(&dma->work); + dmaengine_terminate_all(dma->chan); +} + +static void rsnd_dma_complete(void *data) +{ + struct rsnd_dma *dma = (struct rsnd_dma *)data; + struct rsnd_priv *priv = dma->priv; + unsigned long flags; + + rsnd_lock(priv, flags); + + dma->complete(dma); + + if (dma->submit_loop) + rsnd_dma_continue(dma); + + rsnd_unlock(priv, flags); +} + +static void rsnd_dma_do_work(struct work_struct *work) +{ + struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); + struct rsnd_priv *priv = dma->priv; + struct device *dev = rsnd_priv_to_dev(priv); + struct dma_async_tx_descriptor *desc; + dma_addr_t buf; + size_t len; + int i; + + for (i = 0; i < dma->submit_loop; i++) { + + if (dma->inquiry(dma, &buf, &len) < 0) + return; + + desc = dmaengine_prep_slave_single( + dma->chan, buf, len, dma->dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); + return; + } + + desc->callback = rsnd_dma_complete; + desc->callback_param = dma; + + if (dmaengine_submit(desc) < 0) { + dev_err(dev, "dmaengine_submit() fail\n"); + return; + } + + } + + dma_async_issue_pending(dma->chan); +} + +int rsnd_dma_available(struct rsnd_dma *dma) +{ + return !!dma->chan; +} + +static bool rsnd_dma_filter(struct dma_chan *chan, void *param) +{ + chan->private = param; + + return true; +} + +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, + int is_play, int id, + int (*inquiry)(struct rsnd_dma *dma, + dma_addr_t *buf, int *len), + int (*complete)(struct rsnd_dma *dma)) +{ + struct device *dev = rsnd_priv_to_dev(priv); + dma_cap_mask_t mask; + + if (dma->chan) { + dev_err(dev, "it already has dma channel\n"); + return -EIO; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dma->slave.shdma_slave.slave_id = id; + + dma->chan = dma_request_channel(mask, rsnd_dma_filter, + &dma->slave.shdma_slave); + if (!dma->chan) { + dev_err(dev, "can't get dma channel\n"); + return -EIO; + } + + dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + dma->priv = priv; + dma->inquiry = inquiry; + dma->complete = complete; + INIT_WORK(&dma->work, rsnd_dma_do_work); + + return 0; +} + +void rsnd_dma_quit(struct rsnd_priv *priv, + struct rsnd_dma *dma) +{ + if (dma->chan) + dma_release_channel(dma->chan); + + dma->chan = NULL; +} + +/* * rsnd_dai functions */ #define rsnd_dai_call(rdai, io, fn) \ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9243e38..15dccd5 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -13,9 +13,12 @@
#include <linux/clk.h> #include <linux/device.h> +#include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/sh_dma.h> +#include <linux/workqueue.h> #include <sound/rcar_snd.h> #include <sound/soc.h> #include <sound/pcm_params.h> @@ -79,6 +82,32 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data);
/* + * R-Car DMA + */ +struct rsnd_dma { + struct rsnd_priv *priv; + struct sh_dmae_slave slave; + struct work_struct work; + struct dma_chan *chan; + enum dma_data_direction dir; + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); + int (*complete)(struct rsnd_dma *dma); + + int submit_loop; +}; + +void rsnd_dma_start(struct rsnd_dma *dma); +void rsnd_dma_stop(struct rsnd_dma *dma); +int rsnd_dma_available(struct rsnd_dma *dma); +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, + int is_play, int id, + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), + int (*complete)(struct rsnd_dma *dma)); +void rsnd_dma_quit(struct rsnd_priv *priv, + struct rsnd_dma *dma); + + +/* * R-Car sound mod */
@@ -103,9 +132,12 @@ struct rsnd_mod { struct rsnd_priv *priv; struct rsnd_mod_ops *ops; struct list_head list; /* connect to rsnd_dai playback/capture */ + struct rsnd_dma dma; };
#define rsnd_mod_to_priv(mod) ((mod)->priv) +#define rsnd_mod_to_dma(mod) (&(mod)->dma) +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) #define rsnd_mod_id(mod) ((mod)->id) #define for_each_rsnd_mod(pos, n, io) \ list_for_each_entry_safe(pos, n, &(io)->head, list)
This patch adds DMAEngine transfer on SSI. But, it transfers sound data from memory to SSI directly without using HPBIF at this time. It will be updated soon
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/rcar_snd.h | 7 +-- sound/soc/sh/rcar/ssi.c | 110 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 6 deletions(-)
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 33233ed..a72687d 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -39,13 +39,14 @@
#define RSND_SSI_PLAY (1 << 24)
-#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ -{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } #define RSND_SSI_UNUSED \ -{ .dai_id = -1, .pio_irq = -1, .flags = 0 } +{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 }
struct rsnd_ssi_platform_info { int dai_id; + int dma_id; int pio_irq; u32 flags; }; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index c48a6c7..2079ccf 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -19,6 +19,7 @@ * SSICR */ #define FORCE (1 << 31) /* Fixed */ +#define DMEN (1 << 28) /* DMA Enable */ #define UIEN (1 << 27) /* Underflow Interrupt Enable */ #define OIEN (1 << 26) /* Overflow Interrupt Enable */ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ @@ -51,6 +52,11 @@ #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ #define DIRQ (1 << 24) /* Data Interrupt Status Flag */
+/* + * SSIWSR + */ +#define CONT (1 << 8) /* WS Continue Function */ + struct rsnd_ssi { struct clk *clk; struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ @@ -63,6 +69,7 @@ struct rsnd_ssi { u32 cr_clk; u32 cr_etc; int err; + int dma_offset; unsigned int usrcnt; unsigned int rate; }; @@ -83,7 +90,10 @@ struct rsnd_ssiu {
#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) +#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_dma_available(ssi) \ + rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) @@ -477,6 +487,79 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .stop = rsnd_ssi_pio_stop, };
+static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *len = io->byte_per_period; + *buf = runtime->dma_addr + + rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); + ssi->dma_offset = *len; /* it cares A/B plane */ + + return 0; +} + +static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + + rsnd_ssi_record_error(ssi, status); + + rsnd_dai_pointer_update(ssi->io, io->byte_per_period); + + return 0; +} + +static int rsnd_ssi_dma_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + /* enable DMA transfer */ + ssi->cr_etc = DMEN; + ssi->dma_offset = 0; + + rsnd_dma_start(dma); + + rsnd_ssi_hw_start(ssi, ssi->rdai, io); + + /* enable WS continue */ + if (rsnd_rdai_is_clk_master(rdai)) + rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + + return 0; +} + +static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + rsnd_dma_stop(dma); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_dma_ops = { + .name = "ssi (dma)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_dma_start, + .stop = rsnd_ssi_dma_stop, +}; + /* * Non SSI */ @@ -574,9 +657,26 @@ int rsnd_ssi_probe(struct platform_device *pdev, ops = &rsnd_ssi_non_ops;
/* + * SSI DMA case + */ + if (pinfo->dma_id > 0) { + ret = rsnd_dma_init( + priv, rsnd_mod_to_dma(&ssi->mod), + (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), + pinfo->dma_id, + rsnd_ssi_dma_inquiry, + rsnd_ssi_dma_complete); + if (ret < 0) + dev_info(dev, "SSI DMA failed. try PIO transter\n"); + else + ops = &rsnd_ssi_dma_ops; + } + + /* * SSI PIO case */ - if (rsnd_ssi_is_pio(ssi)) { + if (!rsnd_ssi_dma_available(ssi) && + rsnd_ssi_pio_available(ssi)) { ret = devm_request_irq(dev, pinfo->pio_irq, &rsnd_ssi_pio_interrupt, IRQF_SHARED, @@ -605,6 +705,10 @@ void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_ssi *ssi; int i;
- for_each_rsnd_ssi(ssi, priv, i) + for_each_rsnd_ssi(ssi, priv, i) { clk_put(ssi->clk); + if (rsnd_ssi_dma_available(ssi)) + rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); + } + }
This patch adds BUSIF support for R-Car sound DMAEngine transfer. The sound data will be transferred via FIFO which can cover blank time which will happen when DMA channel is switching.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/rcar_snd.h | 6 ++ sound/soc/sh/rcar/gen.c | 10 ++- sound/soc/sh/rcar/rsnd.h | 9 +++ sound/soc/sh/rcar/scu.c | 154 +++++++++++++++++++++++++++++++++++++++++++++- sound/soc/sh/rcar/ssi.c | 18 +++++- 5 files changed, 190 insertions(+), 7 deletions(-)
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index a72687d..d35412a 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,6 +36,7 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */
#define RSND_SSI_PLAY (1 << 24)
@@ -51,6 +52,11 @@ struct rsnd_ssi_platform_info { u32 flags; };
+/* + * flags + */ +#define RSND_SCU_USB_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ + struct rsnd_scu_platform_info { u32 flags; }; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index a49db66..80ef4c8 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -34,9 +34,6 @@ struct rsnd_gen {
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
-#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) -#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) - /* * Gen2 * will be filled in the future @@ -115,8 +112,15 @@ static struct rsnd_gen_ops rsnd_gen1_ops = {
static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) { + RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214);
RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 15dccd5..9cc6986 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -32,8 +32,15 @@ */ enum rsnd_reg { /* SRU/SCU */ + RSND_REG_SRC_ROUTE_SEL, + RSND_REG_SRC_TMG_SEL0, + RSND_REG_SRC_TMG_SEL1, + RSND_REG_SRC_TMG_SEL2, + RSND_REG_SRC_CTRL, RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, + RSND_REG_BUSIF_MODE, + RSND_REG_BUSIF_ADINR,
/* ADG */ RSND_REG_BRRA, @@ -213,6 +220,8 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv, void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2)
/* * R-Car ADG diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index c12e65f..29837e3 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -15,6 +15,18 @@ struct rsnd_scu { struct rsnd_mod mod; };
+#define rsnd_scu_mode_flags(p) ((p)->info->flags) + +/* + * ADINR + */ +#define OTBL_24 (0 << 16) +#define OTBL_22 (2 << 16) +#define OTBL_20 (4 << 16) +#define OTBL_18 (6 << 16) +#define OTBL_16 (8 << 16) + + #define rsnd_mod_to_scu(_mod) \ container_of((_mod), struct rsnd_scu, mod)
@@ -24,6 +36,116 @@ struct rsnd_scu { ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ i++)
+static int rsnd_scu_set_route(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct scu_route_config { + u32 mask; + int shift; + } routes[] = { + { 0xF, 0, }, /* 0 */ + { 0xF, 4, }, /* 1 */ + { 0xF, 8, }, /* 2 */ + { 0x7, 12, }, /* 3 */ + { 0x7, 16, }, /* 4 */ + { 0x7, 20, }, /* 5 */ + { 0x7, 24, }, /* 6 */ + { 0x3, 28, }, /* 7 */ + { 0x3, 30, }, /* 8 */ + }; + + u32 mask; + u32 val; + int shift; + int id; + + /* + * Gen1 only + */ + if (!rsnd_is_gen1(priv)) + return 0; + + id = rsnd_mod_id(mod); + if (id < 0 || id > ARRAY_SIZE(routes)) + return -EIO; + + /* + * SRC_ROUTE_SELECT + */ + val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; + val = val << routes[id].shift; + mask = routes[id].mask << routes[id].shift; + + rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + + /* + * SRC_TIMING_SELECT + */ + shift = (id % 4) * 8; + mask = 0x1F << shift; + if (8 == id) /* SRU8 is very special */ + val = id << shift; + else + val = (id + 1) << shift; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + break; + } + + return 0; +} + +static int rsnd_scu_set_mode(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + u32 val; + + if (rsnd_is_gen1(priv)) { + val = (1 << id); + rsnd_mod_bset(mod, SRC_CTRL, val, val); + } + + return 0; +} + +static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 adinr = runtime->channels; + + switch (runtime->sample_bits) { + case 16: + adinr |= OTBL_16; + break; + case 32: + adinr |= OTBL_24; + break; + default: + return -EIO; + } + + rsnd_mod_write(mod, BUSIF_MODE, 1); + rsnd_mod_write(mod, BUSIF_ADINR, adinr); + + return 0; +} + static int rsnd_scu_init(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -53,9 +175,36 @@ static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); struct device *dev = rsnd_priv_to_dev(priv); + u32 flags = rsnd_scu_mode_flags(scu); + int ret; + + /* + * SCU will be used if it has RSND_SCU_USB_HPBIF flags + */ + if (!(flags & RSND_SCU_USB_HPBIF)) { + /* it use PIO transter */ + dev_dbg(dev, "%s%d is not used\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; + } + + /* it use DMA transter */ + ret = rsnd_scu_set_route(priv, mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_mode(priv, mod, rdai, io); + if (ret < 0) + return ret;
- dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
return 0; } @@ -112,8 +261,9 @@ int rsnd_scu_probe(struct platform_device *pdev, rsnd_mod_init(priv, &scu->mod, &rsnd_scu_ops, i); scu->info = &info->scu_info[i]; - }
+ dev_dbg(dev, "SCU%d probed\n", i); + } dev_dbg(dev, "scu probed\n");
return 0; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 2079ccf..fae26d3 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -104,6 +104,7 @@ struct rsnd_ssiu { static void rsnd_ssi_mode_init(struct rsnd_priv *priv, struct rsnd_ssiu *ssiu) { + struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi; u32 flags; u32 val; @@ -113,8 +114,17 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, * SSI_MODE0 */ ssiu->ssi_mode0 = 0; - for_each_rsnd_ssi(ssi, priv, i) - ssiu->ssi_mode0 |= (1 << i); + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + /* see also BUSIF_MODE */ + if (!(flags & RSND_SSI_DEPENDENT)) { + ssiu->ssi_mode0 |= (1 << i); + dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); + } else { + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); + } + }
/* * SSI_MODE1 @@ -670,6 +680,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, dev_info(dev, "SSI DMA failed. try PIO transter\n"); else ops = &rsnd_ssi_dma_ops; + + dev_dbg(dev, "SSI%d use DMA transfer\n", i); }
/* @@ -687,6 +699,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, }
ops = &rsnd_ssi_pio_ops; + + dev_dbg(dev, "SSI%d use PIO transfer\n", i); }
rsnd_mod_init(priv, &ssi->mod, ops, i);
This patch cleanups empty functions on scu
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/scu.c | 39 --------------------------------------- 1 file changed, 39 deletions(-)
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index 29837e3..184d900 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -146,30 +146,6 @@ static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, return 0; }
-static int rsnd_scu_init(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - -static int rsnd_scu_quit(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -209,24 +185,9 @@ static int rsnd_scu_start(struct rsnd_mod *mod, return 0; }
-static int rsnd_scu_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static struct rsnd_mod_ops rsnd_scu_ops = { .name = "scu", - .init = rsnd_scu_init, - .quit = rsnd_scu_quit, .start = rsnd_scu_start, - .stop = rsnd_scu_stop, };
struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id)
participants (2)
-
Kuninori Morimoto
-
Mark Brown