diff options
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 + |