summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0001-dmaengine-rcar-dmac-ensure-CHCR-DE-bit-is-actually-0.patch83
-rw-r--r--meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0002-dmaengine-rcar-dmac-use-TCRB-instead-of-TCR-for-resi.patch104
-rw-r--r--meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0003-ASoC-rcar-revert-IOMMU-support-so-far.patch191
3 files changed, 378 insertions, 0 deletions
diff --git a/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0001-dmaengine-rcar-dmac-ensure-CHCR-DE-bit-is-actually-0.patch b/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0001-dmaengine-rcar-dmac-ensure-CHCR-DE-bit-is-actually-0.patch
new file mode 100644
index 0000000..64782f7
--- /dev/null
+++ b/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0001-dmaengine-rcar-dmac-ensure-CHCR-DE-bit-is-actually-0.patch
@@ -0,0 +1,83 @@
+From 1066e8ae45193732f235df740ac31f3e60fc1fe6 Mon Sep 17 00:00:00 2001
+From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+Date: Fri, 10 Nov 2017 13:07:55 +0900
+Subject: [PATCH 1/3] dmaengine: rcar-dmac: ensure CHCR DE bit is actually 0
+ after clear
+
+DMAC reads data from source device, and buffered it until transferable
+size for shink deivce. Because of this behavoir, DMAC is including
+buffered data .
+
+Now, CHCR DE bit is controlling DMA transfer enable/disable.
+
+If DE bit was cleared during data transfering, or during buffering,
+it will flush buffered data if source device was peripheral device
+(The buffered data will be removed if source device was memory).
+Because of this behavior, driver should ensure that DE bit is actually
+0 after cleared.
+
+This patch adds new rcar_dmac_chcr_de_barrier() and call it after CHCR
+register access.
+
+Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+---
+ drivers/dma/sh/rcar-dmac.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
+index 2b2c7db..16ebd5d 100644
+--- a/drivers/dma/sh/rcar-dmac.c
++++ b/drivers/dma/sh/rcar-dmac.c
+@@ -10,6 +10,7 @@
+ * published by the Free Software Foundation.
+ */
+
++#include <linux/delay.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/dmaengine.h>
+ #include <linux/interrupt.h>
+@@ -741,6 +742,24 @@ static int rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan,
+ /* -----------------------------------------------------------------------------
+ * Stop and reset
+ */
++static void rcar_dmac_chcr_de_barrier(struct rcar_dmac_chan *chan)
++{
++ u32 chcr;
++ int i;
++
++ /*
++ * Ensure that the setting of the DE bit is actually 0 after
++ * clearing it.
++ */
++ for (i = 0; i < 1024; i++) {
++ chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
++ if (!(chcr & RCAR_DMACHCR_DE))
++ return;
++ udelay(1);
++ }
++
++ dev_err(chan->chan.device->dev, "CHCR DE check error\n");
++}
+
+ static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan)
+ {
+@@ -749,6 +768,7 @@ static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan)
+ chcr &= ~(RCAR_DMACHCR_DSE | RCAR_DMACHCR_DSIE | RCAR_DMACHCR_IE |
+ RCAR_DMACHCR_TE | RCAR_DMACHCR_DE);
+ rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr);
++ rcar_dmac_chcr_de_barrier(chan);
+ }
+
+ static void rcar_dmac_chan_reinit(struct rcar_dmac_chan *chan)
+@@ -1481,6 +1501,8 @@ static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev)
+ if (chcr & RCAR_DMACHCR_TE)
+ mask |= RCAR_DMACHCR_DE;
+ rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr & ~mask);
++ if (mask & RCAR_DMACHCR_DE)
++ rcar_dmac_chcr_de_barrier(chan);
+
+ if (chcr & RCAR_DMACHCR_DSE)
+ ret |= rcar_dmac_isr_desc_stage_end(chan);
+--
+1.9.1
+
diff --git a/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0002-dmaengine-rcar-dmac-use-TCRB-instead-of-TCR-for-resi.patch b/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0002-dmaengine-rcar-dmac-use-TCRB-instead-of-TCR-for-resi.patch
new file mode 100644
index 0000000..3454dff
--- /dev/null
+++ b/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0002-dmaengine-rcar-dmac-use-TCRB-instead-of-TCR-for-resi.patch
@@ -0,0 +1,104 @@
+From f5c857fb5913dcb4eea2e213ee69790b6dc127dd Mon Sep 17 00:00:00 2001
+From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+Date: Mon, 13 Nov 2017 13:34:04 +0900
+Subject: [PATCH 2/3] dmaengine: rcar-dmac: use TCRB instead of TCR for residue
+
+SYS/RT/Audio DMAC includes independent data buffers for reading
+and writing. Therefore, the read transfer counter and write transfer
+counter have different values.
+TCR indicates read counter, and TCRB indicates write counter.
+The relationship is like below.
+
+ TCR TCRB
+ [SOURCE] -> [DMAC] -> [SINK]
+
+In the MEM_TO_DEV direction, what really matters is how much data has
+been written to the device. If the DMA is interrupted between read and
+write, then, the data doesn't end up in the destination, so shouldn't
+be counted. TCRB is thus the register we should use in this cases.
+
+In the DEV_TO_MEM direction, the situation is more complex. Both the
+read and write side are important. What matters from a data consumer
+point of view is how much data has been written to memory.
+On the other hand, if the transfer is interrupted between read and
+write, we'll end up losing data. It can also be important to report.
+
+In the MEM_TO_MEM direction, what matters is of course how much data
+has been written to memory from data consumer point of view.
+Here, because read and write have independent data buffers, it will
+take a while for TCR and TCRB to become equal. Thus we should check
+TCRB in this case, too.
+
+Thus, all cases we should check TCRB instead of TCR.
+
+Without this patch, Sound Capture has noise after PluseAudio support
+(= 07b7acb51d2 ("ASoC: rsnd: update pointer more accurate")), because
+the recorder will use wrong residue counter which indicates transferred
+from sound device, but in reality the data was not yet put to memory
+and recorder will record it.
+
+However, because DMAC is buffering data until it can be transferable
+size, TCRB might not be updated.
+For example, if consumer doesn't know how much data can be receaved,
+it requests enough size to DMAC. But in reality, it might receave very
+few data. In such case, DMAC just buffered it untile transferable size,
+and no TCRB updated.
+
+In such case, this buffered data will be transfered if CHCR::DE bit was
+cleared, and this is happen if rcar_dmac_chan_halt(). In other word, it
+happen when consumer called dmaengine_terminate_all().
+
+Because of this behavior, it need to flush buffered data when it returns
+"residue" (= dmaengine_tx_status()).
+Otherwise, consumer might calculate wrong things if it called
+dmaengine_tx_status() and dmaengine_terminate_all() consecutively.
+
+Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+---
+ drivers/dma/sh/rcar-dmac.c | 22 +++++++++++++++++++++-
+ 1 file changed, 21 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
+index 16ebd5d..9cb85b2 100644
+--- a/drivers/dma/sh/rcar-dmac.c
++++ b/drivers/dma/sh/rcar-dmac.c
+@@ -761,6 +761,23 @@ static void rcar_dmac_chcr_de_barrier(struct rcar_dmac_chan *chan)
+ dev_err(chan->chan.device->dev, "CHCR DE check error\n");
+ }
+
++static void rcar_dmac_sync_tcr(struct rcar_dmac_chan *chan)
++{
++ u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
++
++ if (!(chcr & RCAR_DMACHCR_DE))
++ return;
++
++ /* set DE=0 and flush remaining data */
++ rcar_dmac_chan_write(chan, RCAR_DMACHCR, (chcr & ~RCAR_DMACHCR_DE));
++
++ /* make sure all remaining data was fulshed */
++ rcar_dmac_chcr_de_barrier(chan);
++
++ /* back DE */
++ rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr);
++}
++
+ static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan)
+ {
+ u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
+@@ -1329,8 +1346,11 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
+ residue += chunk->size;
+ }
+
++ if (desc->direction == DMA_DEV_TO_MEM)
++ rcar_dmac_sync_tcr(chan);
++
+ /* Add the residue for the current chunk. */
+- residue += rcar_dmac_chan_read(chan, RCAR_DMATCR) << desc->xfer_shift;
++ residue += rcar_dmac_chan_read(chan, RCAR_DMATCRB) << desc->xfer_shift;
+
+ return residue;
+ }
+--
+1.9.1
+
diff --git a/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0003-ASoC-rcar-revert-IOMMU-support-so-far.patch b/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0003-ASoC-rcar-revert-IOMMU-support-so-far.patch
new file mode 100644
index 0000000..f5456e7
--- /dev/null
+++ b/meta-rcar-gen3/recipes-kernel/linux/linux-renesas/0003-ASoC-rcar-revert-IOMMU-support-so-far.patch
@@ -0,0 +1,191 @@
+From 1ad1207acf10070c94e1e3be598d9f5a2e9dd43e Mon Sep 17 00:00:00 2001
+From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+Date: Mon, 13 Nov 2017 13:57:56 +0900
+Subject: [PATCH 3/3] ASoC: rcar: revert IOMMU support so far
+
+commit 4821d914fe7 ("ASoC: rsnd: use dma_sync_single_for_xxx() for
+IOMMU") had supported IOMMU, but it breaks normal sound "recorde"
+and both PulseAudio's "playback/recorde". The sound will be noisy.
+
+This commit is using dma_sync_single_for_xxx(), and driver should
+make sure memory is protected during CPU or Device are using it.
+But if driver returns current "residue" data size correctly on pointer
+function, player/recorder will access to protected memory.
+
+This feature should be supported, but I don't know how to handle it
+without problem at this point. Thus, this patch simply revert it to
+avoid noisy sound.
+
+Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+---
+ sound/soc/sh/rcar/core.c | 4 +--
+ sound/soc/sh/rcar/dma.c | 86 ++++--------------------------------------------
+ 2 files changed, 8 insertions(+), 82 deletions(-)
+
+diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
+index 21335d7..d22fd6d 100644
+--- a/sound/soc/sh/rcar/core.c
++++ b/sound/soc/sh/rcar/core.c
+@@ -1394,8 +1394,8 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
+
+ return snd_pcm_lib_preallocate_pages_for_all(
+ rtd->pcm,
+- SNDRV_DMA_TYPE_CONTINUOUS,
+- snd_dma_continuous_data(GFP_KERNEL),
++ SNDRV_DMA_TYPE_DEV,
++ rtd->card->snd_card->dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+ }
+
+diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
+index fd557ab..4d750bdf 100644
+--- a/sound/soc/sh/rcar/dma.c
++++ b/sound/soc/sh/rcar/dma.c
+@@ -26,10 +26,7 @@
+ struct rsnd_dmaen {
+ struct dma_chan *chan;
+ dma_cookie_t cookie;
+- dma_addr_t dma_buf;
+ unsigned int dma_len;
+- unsigned int dma_period;
+- unsigned int dma_cnt;
+ };
+
+ struct rsnd_dmapp {
+@@ -71,38 +68,10 @@ struct rsnd_dma_ctrl {
+ /*
+ * Audio DMAC
+ */
+-#define rsnd_dmaen_sync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 1)
+-#define rsnd_dmaen_unsync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 0)
+-static void __rsnd_dmaen_sync(struct rsnd_dmaen *dmaen, struct rsnd_dai_stream *io,
+- int i, int sync)
+-{
+- struct device *dev = dmaen->chan->device->dev;
+- enum dma_data_direction dir;
+- int is_play = rsnd_io_is_play(io);
+- dma_addr_t buf;
+- int len, max;
+- size_t period;
+-
+- len = dmaen->dma_len;
+- period = dmaen->dma_period;
+- max = len / period;
+- i = i % max;
+- buf = dmaen->dma_buf + (period * i);
+-
+- dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+-
+- if (sync)
+- dma_sync_single_for_device(dev, buf, period, dir);
+- else
+- dma_sync_single_for_cpu(dev, buf, period, dir);
+-}
+-
+ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+ {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+- struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+- struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+ bool elapsed = false;
+ unsigned long flags;
+
+@@ -115,22 +84,9 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
+ */
+ spin_lock_irqsave(&priv->lock, flags);
+
+- if (rsnd_io_is_working(io)) {
+- rsnd_dmaen_unsync(dmaen, io, dmaen->dma_cnt);
+-
+- /*
+- * Next period is already started.
+- * Let's sync Next Next period
+- * see
+- * rsnd_dmaen_start()
+- */
+- rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2);
+-
++ if (rsnd_io_is_working(io))
+ elapsed = true;
+
+- dmaen->dma_cnt++;
+- }
+-
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (elapsed)
+@@ -165,14 +121,8 @@ static int rsnd_dmaen_stop(struct rsnd_mod *mod,
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+- if (dmaen->chan) {
+- int is_play = rsnd_io_is_play(io);
+-
++ if (dmaen->chan)
+ dmaengine_terminate_all(dmaen->chan);
+- dma_unmap_single(dmaen->chan->device->dev,
+- dmaen->dma_buf, dmaen->dma_len,
+- is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+- }
+
+ return 0;
+ }
+@@ -237,11 +187,7 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct dma_async_tx_descriptor *desc;
+ struct dma_slave_config cfg = {};
+- dma_addr_t buf;
+- size_t len;
+- size_t period;
+ int is_play = rsnd_io_is_play(io);
+- int i;
+ int ret;
+
+ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+@@ -258,19 +204,10 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
+ if (ret < 0)
+ return ret;
+
+- len = snd_pcm_lib_buffer_bytes(substream);
+- period = snd_pcm_lib_period_bytes(substream);
+- buf = dma_map_single(dmaen->chan->device->dev,
+- substream->runtime->dma_area,
+- len,
+- is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+- if (dma_mapping_error(dmaen->chan->device->dev, buf)) {
+- dev_err(dev, "dma map failed\n");
+- return -EIO;
+- }
+-
+ desc = dmaengine_prep_dma_cyclic(dmaen->chan,
+- buf, len, period,
++ substream->runtime->dma_addr,
++ snd_pcm_lib_buffer_bytes(substream),
++ snd_pcm_lib_period_bytes(substream),
+ is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+@@ -282,18 +219,7 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
+ desc->callback = rsnd_dmaen_complete;
+ desc->callback_param = rsnd_mod_get(dma);
+
+- dmaen->dma_buf = buf;
+- dmaen->dma_len = len;
+- dmaen->dma_period = period;
+- dmaen->dma_cnt = 0;
+-
+- /*
+- * synchronize this and next period
+- * see
+- * __rsnd_dmaen_complete()
+- */
+- for (i = 0; i < 2; i++)
+- rsnd_dmaen_sync(dmaen, io, i);
++ dmaen->dma_len = snd_pcm_lib_buffer_bytes(substream);
+
+ dmaen->cookie = dmaengine_submit(desc);
+ if (dmaen->cookie < 0) {
+--
+1.9.1
+