[alsa-devel] [PATCH 0/20] ASoC: rsnd: add Synchronous SRC and U/O error salvage support
Hi Mark.
These patches adds [1] Synchronous SRC, [2] under/over run error salvage support to Renesas sound driver.
1) - 16) : prepare cleanup/tidyup for 17) - 19) 17) : Synchronous SRC 18)- 19) : salvage support for under/over run error 20) : cleanup for 18) - 19)
Current Renesas sound can use Asynchronous/Synchronous sampling rate convert. Asynchronous sampling rate convert is already supported on upstream. ("feature" is supported, "DT" bindings is not yet supported) 17) adds Synchronous sampling rate convert.
Renesas sound has HW bug (?) which switches L/R channel if under/over run error had occurred. 18) - 19) adds salvage supported for it.
20) is cleanup patch which is based on 18)-19) patch
Kuninori Morimoto (20): 1. ASoC: rsnd: add .fallback callback 2. ASoC: rsnd: add callback status check method 3. ASoC: rsnd: rsnd_src_ssiu_stop() stops SSIU compulsorily 4. ASoC: rsnd: tidyup PIO/DMA mode settings method 5. ASoC: rsnd: tidyup SSI interrupt enable/disable method 6. ASoC: rsnd: care SSIWSR register in rsnd_ssi_hw_start() 7. ASoC: rsnd: clear status register when HW start 8. ASoC: rsnd: synchronize SSI start/stop sequence between PIO/DMA mode 9. ASoC: rsnd: show master clock rate when ADG probe 10. ASoC: rsnd: move snd_kcontrol_new fucntions to core.c 11. ASoC: rsnd: tidyup rsnd_io_to_runtime() macro 12. ASoC: rsnd: use rsnd_src_convert_rate() once on rsnd_src_set_convert_rate_gen2() 13. ASoC: rsnd: initialize SRC on rsnd_src_init() 14. ASoC: rsnd: set SRC_ROUTE_MODE0 on each rsnd_src_set_convert_rate() 15. ASoC: rsnd: share SSI starting method between PIO/DMA mode 16. ASoC: rsnd: remove un-necessary parameter from rsnd_src_start/stop() 17. ASoC: rsnd: add Synchronous SRC mode 18. ASoC: rsnd: add salvage support for under/over flow error on SSI 19. ASoC: rsnd: add salvage support for under/over flow error on SRC 20. ASoC: rsnd: rename SSI function name of PIO
include/sound/rcar_snd.h | 9 +- sound/soc/sh/rcar/adg.c | 2 +- sound/soc/sh/rcar/core.c | 166 +++++++++++++++++++- sound/soc/sh/rcar/dvc.c | 177 ++-------------------- sound/soc/sh/rcar/gen.c | 15 ++ sound/soc/sh/rcar/rsnd.h | 94 +++++++++++- sound/soc/sh/rcar/src.c | 378 +++++++++++++++++++++++++++++++++++++++------- sound/soc/sh/rcar/ssi.c | 205 +++++++++++++++---------- 8 files changed, 736 insertions(+), 310 deletions(-)
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current R-Car sound has PIO fallback support if it couldn't use DMA. This fallback is done in .remove callback, but, it should have .fallback callback. Otherwise, normal .remove callback will have strange behavior. This patch adds .fallback callback.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 9 +++++++-- sound/soc/sh/rcar/rsnd.h | 2 ++ sound/soc/sh/rcar/ssi.c | 11 +++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 110b99d..0785f84 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -368,7 +368,7 @@ rsnd_dma_channel_err: /* * DMA failed. try to PIO mode * see - * rsnd_ssi_dma_remove() + * rsnd_ssi_fallback() * rsnd_rdai_continuance_probe() */ return -EAGAIN; @@ -1023,7 +1023,7 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, * SSI will be switch to PIO mode if it was DMA mode * see * rsnd_dma_init() - * rsnd_ssi_dma_remove() + * rsnd_ssi_fallback() */ rsnd_dai_call(remove, io, rdai);
@@ -1034,6 +1034,11 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, rsnd_path_break(priv, io, dvc);
/* + * fallback + */ + rsnd_dai_call(fallback, io, rdai); + + /* * retry to "probe". * DAI has SSI which is PIO mode only now. */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index ed44ca8..83e1066 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -206,6 +206,8 @@ struct rsnd_mod_ops { int (*pcm_new)(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd); + int (*fallback)(struct rsnd_mod *mod, + struct rsnd_dai *rdai); };
struct rsnd_dai_stream; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 346d3dc..e03e70b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -465,11 +465,17 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); + + return 0; +} + +static int rsnd_ssi_fallback(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv);
- rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); - /* * fallback to PIO * @@ -541,6 +547,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_dma_start, .stop = rsnd_ssi_dma_stop, + .fallback = rsnd_ssi_fallback, };
/*
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
R-Car sound can use SSI/SRC/DVC modules, and these are controlled as rsnd_mod in rsnd driver. These rsnd_mod has each own function as callback. Basically these callback function has pair like probe/remove, start/stop, etc. And, these functions are called by order to each stage like below. 1. src->probe 2. ssi->probe 3. dvc->probe 4. src->start 5. ssi->start 6. dvc->start 7. src->stop 8. ssi->stop 9. dvc->stop 10. src->remove 11. ssi->remove 12. dvc->remove
But, current rsnd driver doesn't care about its status which indicates which function is called. For example, if 5) returns error, 6) is not called. In such case, 9) should not be called. This patch care about each modules status.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 13 ++++++++++--- sound/soc/sh/rcar/rsnd.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 0785f84..fce61a0 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -416,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - dev_dbg(dev, "%s[%d] %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ - (mod)->ops->func(mod, rdai); \ + u32 mask = 1 << __rsnd_mod_shift_##func; \ + u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ + int ret = 0; \ + if ((mod->status & mask) == call) { \ + dev_dbg(dev, "%s[%d] %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ + ret = (mod)->ops->func(mod, rdai); \ + mod->status = (mod->status & ~mask) | (~call & mask); \ + } \ + ret; \ })
#define rsnd_mod_call(mod, func, rdai...) \ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 83e1066..c74c239 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -218,7 +218,35 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_dma dma; struct rsnd_dai_stream *io; + u32 status; }; +/* + * status + * + * bit + * 0 0: probe 1: remove + * 1 0: init 1: quit + * 2 0: start 1: stop + * 3 0: pcm_new + * 4 0: fallback + */ +#define __rsnd_mod_shift_probe 0 +#define __rsnd_mod_shift_remove 0 +#define __rsnd_mod_shift_init 1 +#define __rsnd_mod_shift_quit 1 +#define __rsnd_mod_shift_start 2 +#define __rsnd_mod_shift_stop 2 +#define __rsnd_mod_shift_pcm_new 3 +#define __rsnd_mod_shift_fallback 4 + +#define __rsnd_mod_call_probe 0 +#define __rsnd_mod_call_remove 1 +#define __rsnd_mod_call_init 0 +#define __rsnd_mod_call_quit 1 +#define __rsnd_mod_call_start 0 +#define __rsnd_mod_call_stop 1 +#define __rsnd_mod_call_pcm_new 0 +#define __rsnd_mod_call_fallback 0
#define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma)
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
rsnd_src_ssiu_stop() is used to stop SSIU, but it shouldn't depend on whether it is using SSIU. This patch stops SSIU compulsorily.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/rsnd.h | 3 +-- sound/soc/sh/rcar/src.c | 6 ++---- sound/soc/sh/rcar/ssi.c | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index c74c239..1344c3a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -431,8 +431,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai, int use_busif); int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai, - int use_busif); + struct rsnd_dai *rdai); int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai);
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 4679501..384af90 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -175,14 +175,12 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, }
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai, - int use_busif) + struct rsnd_dai *rdai) { /* * DMA settings for SSIU */ - if (use_busif) - rsnd_mod_write(ssi_mod, SSI_CTRL, 0); + rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
return 0; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index e03e70b..a200452 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -424,7 +424,7 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
rsnd_ssi_hw_stop(ssi, rdai);
- rsnd_src_ssiu_stop(mod, rdai, 0); + rsnd_src_ssiu_stop(mod, rdai);
return 0; } @@ -528,7 +528,7 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
rsnd_dma_stop(dma);
- rsnd_src_ssiu_stop(mod, rdai, 1); + rsnd_src_ssiu_stop(mod, rdai);
return 0; }
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current ssi.c has .cr_etc which is used for SSICR's etc settings. but, it is used as PIO/DMA switching purpose now. This patch tidyup this method. This is prepare for under/over run issue handling
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/rsnd.h | 1 + sound/soc/sh/rcar/ssi.c | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 1344c3a..12e8945 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -445,6 +445,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
/* * R-Car DVC diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index a200452..8928913 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -68,7 +68,6 @@ struct rsnd_ssi { struct rsnd_dai *rdai; u32 cr_own; u32 cr_clk; - u32 cr_etc; int err; unsigned int usrcnt; unsigned int rate; @@ -185,6 +184,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, { struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); struct device *dev = rsnd_priv_to_dev(priv); + u32 cr_mode; u32 cr;
if (0 == ssi->usrcnt) { @@ -198,9 +198,14 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, } }
+ cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? + DMEN : /* DMA : use DMA */ + UIEN | OIEN | DIEN; /* PIO : enable interrupt */ + + cr = ssi->cr_own | ssi->cr_clk | - ssi->cr_etc | + cr_mode | EN;
rsnd_mod_write(&ssi->mod, SSICR, cr); @@ -403,9 +408,6 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
- /* enable PIO IRQ */ - ssi->cr_etc = UIEN | OIEN | DIEN; - rsnd_src_ssiu_start(mod, rdai, 0);
rsnd_src_enable_ssi_irq(mod, rdai); @@ -420,8 +422,6 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- ssi->cr_etc = 0; - rsnd_ssi_hw_stop(ssi, rdai);
rsnd_src_ssiu_stop(mod, rdai); @@ -498,9 +498,6 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
- /* enable DMA transfer */ - ssi->cr_etc = DMEN; - rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
rsnd_dma_start(dma); @@ -520,8 +517,6 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, 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_record_error(ssi, rsnd_mod_read(mod, SSISR));
rsnd_ssi_hw_stop(ssi, rdai); @@ -550,6 +545,12 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .fallback = rsnd_ssi_fallback, };
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) +{ + return mod->ops == &rsnd_ssi_dma_ops; +} + + /* * Non SSI */
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current SSI doesn't care interrupt "disable" method. And, it is used when PIO mode only at this point. SSI interrupt will be used for sound R/L issue workaround when DMA mode too. This patch tidyup SSI interrupt enable/disable method.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/rsnd.h | 4 +++- sound/soc/sh/rcar/src.c | 25 ++++++++++++++++++++++--- sound/soc/sh/rcar/ssi.c | 6 ++++-- 3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 12e8945..48999b1 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -432,8 +432,10 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, int use_busif); int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai); -int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, +int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai); +int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai);
#define rsnd_src_nr(priv) ((priv)->src_nr)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 384af90..c301195 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -185,18 +185,37 @@ int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, return 0; }
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, +int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
- /* enable PIO interrupt if Gen2 */ - if (rsnd_is_gen2(priv)) + if (rsnd_is_gen1(priv)) + return 0; + + /* enable SSI interrupt if Gen2 */ + if (rsnd_ssi_is_dma_mode(ssi_mod)) + rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000); + else rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
return 0; }
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + + if (rsnd_is_gen1(priv)) + return 0; + + /* disable SSI interrupt if Gen2 */ + rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000); + + return 0; +} + unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 8928913..42d7c7e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -410,10 +410,10 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
rsnd_src_ssiu_start(mod, rdai, 0);
- rsnd_src_enable_ssi_irq(mod, rdai); - rsnd_ssi_hw_start(ssi, rdai, io);
+ rsnd_src_ssi_irq_enable(mod, rdai); + return 0; }
@@ -422,6 +422,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ rsnd_src_ssi_irq_disable(mod, rdai); + rsnd_ssi_hw_stop(ssi, rdai);
rsnd_src_ssiu_stop(mod, rdai);
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current SSI is careing SSIWSR which controls WS continue mode when DMA mode, but, it should be cared when PIO mode too. This patch fixup it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/ssi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 42d7c7e..d67bca9 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -210,6 +210,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
rsnd_mod_write(&ssi->mod, SSICR, cr);
+ /* enable WS continue */ + if (rsnd_dai_is_clk_master(rdai)) + rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + ssi->usrcnt++;
dev_dbg(dev, "%s[%d] hw started\n", @@ -506,10 +510,6 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
rsnd_ssi_hw_start(ssi, ssi->rdai, io);
- /* enable WS continue */ - if (rsnd_dai_is_clk_master(rdai)) - rsnd_mod_write(&ssi->mod, SSIWSR, CONT); - return 0; }
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Let's clear SSI status when HW start
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/ssi.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d67bca9..3c0d31d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -214,6 +214,9 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, if (rsnd_dai_is_clk_master(rdai)) rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+ /* clear error status */ + rsnd_mod_write(&ssi->mod, SSISR, 0); + ssi->usrcnt++;
dev_dbg(dev, "%s[%d] hw started\n",
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current SSI start/stop sequence is different between PIO/DMA mode, but, almost all are same. this patch synchronize it. It will be shared in the future.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/ssi.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 3c0d31d..292e98b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -95,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod) struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int use_busif = 0;
+ if (!rsnd_ssi_is_dma_mode(mod)) + return 0; + if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF)) use_busif = 1; if (rsnd_io_to_mod_src(io)) @@ -415,7 +418,7 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
- rsnd_src_ssiu_start(mod, rdai, 0); + rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
rsnd_ssi_hw_start(ssi, rdai, io);
@@ -431,6 +434,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
rsnd_src_ssi_irq_disable(mod, rdai);
+ rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); + rsnd_ssi_hw_stop(ssi, rdai);
rsnd_src_ssiu_stop(mod, rdai); @@ -509,10 +514,10 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
- rsnd_dma_start(dma); - rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+ rsnd_dma_start(dma); + return 0; }
@@ -522,12 +527,12 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
+ rsnd_dma_stop(dma); + rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
rsnd_ssi_hw_stop(ssi, rdai);
- rsnd_dma_stop(dma); - rsnd_src_ssiu_stop(mod, rdai);
return 0;
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
master clock rate is useful information for debug.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/adg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index fc41a0e..14d1a71 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev, adg->clk[CLKI] = devm_clk_get(dev, "clk_i");
for_each_rsnd_clk(clk, adg, i) - dev_dbg(dev, "clk %d : %p\n", i, clk); + dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
rsnd_adg_ssi_clk_init(priv, adg);
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current DVC is using snd_kcontrol_new functions, but, SRC will need same method. Move common functions to core.c
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 144 +++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/dvc.c | 177 ++++------------------------------------------ sound/soc/sh/rcar/rsnd.h | 45 ++++++++++++ 3 files changed, 204 insertions(+), 162 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index fce61a0..77af008 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -970,6 +970,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = { };
/* + * snd_kcontrol + */ +#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value) +static int rsnd_kctrl_info(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_info *uinfo) +{ + struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); + + if (cfg->texts) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = cfg->size; + uinfo->value.enumerated.items = cfg->max; + if (uinfo->value.enumerated.item >= cfg->max) + uinfo->value.enumerated.item = cfg->max - 1; + strlcpy(uinfo->value.enumerated.name, + cfg->texts[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + } else { + uinfo->count = cfg->size; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = cfg->max; + uinfo->type = (cfg->max == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : + SNDRV_CTL_ELEM_TYPE_INTEGER; + } + + return 0; +} + +static int rsnd_kctrl_get(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uc) +{ + struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); + int i; + + for (i = 0; i < cfg->size; i++) + if (cfg->texts) + uc->value.enumerated.item[i] = cfg->val[i]; + else + uc->value.integer.value[i] = cfg->val[i]; + + return 0; +} + +static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uc) +{ + struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); + struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); + int i, change = 0; + + for (i = 0; i < cfg->size; i++) { + if (cfg->texts) { + change |= (uc->value.enumerated.item[i] != cfg->val[i]); + cfg->val[i] = uc->value.enumerated.item[i]; + } else { + change |= (uc->value.integer.value[i] != cfg->val[i]); + cfg->val[i] = uc->value.integer.value[i]; + } + } + + if (change) + cfg->update(mod); + + return change; +} + +static int __rsnd_kctrl_new(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_kctrl_cfg *cfg, + void (*update)(struct rsnd_mod *mod)) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_kcontrol *kctrl; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = name, + .info = rsnd_kctrl_info, + .get = rsnd_kctrl_get, + .put = rsnd_kctrl_put, + .private_value = (unsigned long)cfg, + }; + int ret; + + kctrl = snd_ctl_new1(&knew, mod); + if (!kctrl) + return -ENOMEM; + + ret = snd_ctl_add(card, kctrl); + if (ret < 0) + return ret; + + cfg->update = update; + + return 0; +} + +int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_m *_cfg, + u32 max) +{ + _cfg->cfg.max = max; + _cfg->cfg.size = RSND_DVC_CHANNELS; + _cfg->cfg.val = _cfg->val; + return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update); +} + +int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_s *_cfg, + u32 max) +{ + _cfg->cfg.max = max; + _cfg->cfg.size = 1; + _cfg->cfg.val = &_cfg->val; + return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update); +} + +int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_kctrl_cfg_s *_cfg, + void (*update)(struct rsnd_mod *mod), + const char * const *texts, + u32 max) +{ + _cfg->cfg.max = max; + _cfg->cfg.size = 1; + _cfg->cfg.val = &_cfg->val; + _cfg->cfg.texts = texts; + return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update); +} + +/* * snd_soc_platform */
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index e2c8473..5380a48 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -11,36 +11,18 @@ #include "rsnd.h"
#define RSND_DVC_NAME_SIZE 16 -#define RSND_DVC_CHANNELS 2
#define DVC_NAME "dvc"
-struct rsnd_dvc_cfg { - unsigned int max; - unsigned int size; - u32 *val; - const char * const *texts; -}; - -struct rsnd_dvc_cfg_m { - struct rsnd_dvc_cfg cfg; - u32 val[RSND_DVC_CHANNELS]; -}; - -struct rsnd_dvc_cfg_s { - struct rsnd_dvc_cfg cfg; - u32 val; -}; - struct rsnd_dvc { struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; - struct rsnd_dvc_cfg_m volume; - struct rsnd_dvc_cfg_m mute; - struct rsnd_dvc_cfg_s ren; /* Ramp Enable */ - struct rsnd_dvc_cfg_s rup; /* Ramp Rate Up */ - struct rsnd_dvc_cfg_s rdown; /* Ramp Rate Down */ + struct rsnd_kctrl_cfg_m volume; + struct rsnd_kctrl_cfg_m mute; + struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ + struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ + struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ };
#define rsnd_mod_to_dvc(_mod) \ @@ -222,140 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod, return 0; }
-static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_info *uinfo) -{ - struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - - if (cfg->texts) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = cfg->size; - uinfo->value.enumerated.items = cfg->max; - if (uinfo->value.enumerated.item >= cfg->max) - uinfo->value.enumerated.item = cfg->max - 1; - strlcpy(uinfo->value.enumerated.name, - cfg->texts[uinfo->value.enumerated.item], - sizeof(uinfo->value.enumerated.name)); - } else { - uinfo->count = cfg->size; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = cfg->max; - uinfo->type = (cfg->max == 1) ? - SNDRV_CTL_ELEM_TYPE_BOOLEAN : - SNDRV_CTL_ELEM_TYPE_INTEGER; - } - - return 0; -} - -static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *ucontrol) -{ - struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - int i; - - for (i = 0; i < cfg->size; i++) - if (cfg->texts) - ucontrol->value.enumerated.item[i] = cfg->val[i]; - else - ucontrol->value.integer.value[i] = cfg->val[i]; - - return 0; -} - -static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *ucontrol) -{ - struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); - struct rsnd_dvc_cfg *cfg = (struct rsnd_dvc_cfg *)kctrl->private_value; - int i, change = 0; - - for (i = 0; i < cfg->size; i++) { - if (cfg->texts) { - change |= (ucontrol->value.enumerated.item[i] != cfg->val[i]); - cfg->val[i] = ucontrol->value.enumerated.item[i]; - } else { - change |= (ucontrol->value.integer.value[i] != cfg->val[i]); - cfg->val[i] = ucontrol->value.integer.value[i]; - } - } - - if (change) - rsnd_dvc_volume_update(mod); - - return change; -} - -static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg *private) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_kcontrol *kctrl; - struct snd_kcontrol_new knew = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = name, - .info = rsnd_dvc_volume_info, - .get = rsnd_dvc_volume_get, - .put = rsnd_dvc_volume_put, - .private_value = (unsigned long)private, - }; - int ret; - - kctrl = snd_ctl_new1(&knew, mod); - if (!kctrl) - return -ENOMEM; - - ret = snd_ctl_add(card, kctrl); - if (ret < 0) - return ret; - - return 0; -} - -static int _rsnd_dvc_pcm_new_m(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg_m *private, - u32 max) -{ - private->cfg.max = max; - private->cfg.size = RSND_DVC_CHANNELS; - private->cfg.val = private->val; - return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); -} - -static int _rsnd_dvc_pcm_new_s(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg_s *private, - u32 max) -{ - private->cfg.max = max; - private->cfg.size = 1; - private->cfg.val = &private->val; - return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); -} - -static int _rsnd_dvc_pcm_new_e(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - struct rsnd_dvc_cfg_s *private, - const char * const *texts, - u32 max) -{ - private->cfg.max = max; - private->cfg.size = 1; - private->cfg.val = &private->val; - private->cfg.texts = texts; - return __rsnd_dvc_pcm_new(mod, rdai, rtd, name, &private->cfg); -} - static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct snd_soc_pcm_runtime *rtd) @@ -365,41 +213,46 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, int ret;
/* Volume */ - ret = _rsnd_dvc_pcm_new_m(mod, rdai, rtd, + ret = rsnd_kctrl_new_m(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Playback Volume" : "DVC In Capture Volume", + rsnd_dvc_volume_update, &dvc->volume, 0x00800000 - 1); if (ret < 0) return ret;
/* Mute */ - ret = _rsnd_dvc_pcm_new_m(mod, rdai, rtd, + ret = rsnd_kctrl_new_m(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Mute Switch" : "DVC In Mute Switch", + rsnd_dvc_volume_update, &dvc->mute, 1); if (ret < 0) return ret;
/* Ramp */ - ret = _rsnd_dvc_pcm_new_s(mod, rdai, rtd, + ret = rsnd_kctrl_new_s(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Ramp Switch" : "DVC In Ramp Switch", + rsnd_dvc_volume_update, &dvc->ren, 1); if (ret < 0) return ret;
- ret = _rsnd_dvc_pcm_new_e(mod, rdai, rtd, + ret = rsnd_kctrl_new_e(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", &dvc->rup, + rsnd_dvc_volume_update, dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate)); if (ret < 0) return ret;
- ret = _rsnd_dvc_pcm_new_e(mod, rdai, rtd, + ret = rsnd_kctrl_new_e(mod, rdai, rtd, rsnd_dai_is_play(rdai, io) ? "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", &dvc->rdown, + rsnd_dvc_volume_update, dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
if (ret < 0) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 48999b1..133ba1f 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -418,6 +418,51 @@ struct rsnd_priv { })
/* + * rsnd_kctrl + */ +struct rsnd_kctrl_cfg { + unsigned int max; + unsigned int size; + u32 *val; + const char * const *texts; + void (*update)(struct rsnd_mod *mod); +}; + +#define RSND_DVC_CHANNELS 2 +struct rsnd_kctrl_cfg_m { + struct rsnd_kctrl_cfg cfg; + u32 val[RSND_DVC_CHANNELS]; +}; + +struct rsnd_kctrl_cfg_s { + struct rsnd_kctrl_cfg cfg; + u32 val; +}; + +int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_m *_cfg, + u32 max); +int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + void (*update)(struct rsnd_mod *mod), + struct rsnd_kctrl_cfg_s *_cfg, + u32 max); +int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + struct rsnd_kctrl_cfg_s *_cfg, + void (*update)(struct rsnd_mod *mod), + const char * const *texts, + u32 max); + +/* * R-Car SRC */ int rsnd_src_probe(struct platform_device *pdev,
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Avoid NULL pointer access
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/rsnd.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 133ba1f..5826c8a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -303,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); 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) +#define rsnd_io_to_runtime(io) ((io)->substream ? \ + (io)->substream->runtime : NULL)
void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
using many rsnd_src_convert_rate() is not readable.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/src.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index c301195..0a56ccd 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -525,16 +525,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); uint ratio; int ret;
/* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (!rsnd_src_convert_rate(src)) + if (!convert_rate) ratio = 0; - else if (rsnd_src_convert_rate(src) > runtime->rate) - ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate; + else if (convert_rate > runtime->rate) + ratio = 100 * convert_rate / runtime->rate; else - ratio = 100 * runtime->rate / rsnd_src_convert_rate(src); + ratio = 100 * runtime->rate / convert_rate;
if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n");
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current src initialize SRC on rsnd_src_set_convert_rate() but, it should be done on rsnd_src_init().
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/src.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 0a56ccd..6a63f8f 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -256,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 1);
- /* - * Initialize the operation of the SRC internal circuits - * see rsnd_src_start() - */ - rsnd_mod_write(mod, SRC_SRCIR, 1); - /* Set channel number and output bit length */ rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
@@ -286,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod,
clk_prepare_enable(src->clk);
+ /* + * Initialize the operation of the SRC internal circuits + * see rsnd_src_start() + */ + rsnd_mod_write(mod, SRC_SRCIR, 1); + return 0; }
@@ -306,7 +306,7 @@ static int rsnd_src_start(struct rsnd_mod *mod,
/* * Cancel the initialization and operate the SRC function - * see rsnd_src_set_convert_rate() + * see rsnd_src_init() */ rsnd_mod_write(mod, SRC_SRCIR, 0);
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current src.c sets SRC_ROUTE_MODE0 on rsnd_src_start(), but, set it in rsnd_src_set_convert_rate() is natural.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/src.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 6a63f8f..0b6b230 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -302,17 +302,12 @@ static int rsnd_src_quit(struct rsnd_mod *mod, static int rsnd_src_start(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_src *src = rsnd_mod_to_src(mod); - /* * Cancel the initialization and operate the SRC function * see rsnd_src_init() */ rsnd_mod_write(mod, SRC_SRCIR, 0);
- if (rsnd_src_convert_rate(src)) - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); - return 0; }
@@ -320,11 +315,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_src *src = rsnd_mod_to_src(mod); - - if (rsnd_src_convert_rate(src)) - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); - + /* nothing to do */ return 0; }
@@ -431,6 +422,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod, static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + struct rsnd_src *src = rsnd_mod_to_src(mod); int ret;
ret = rsnd_src_set_convert_rate(mod, rdai); @@ -444,6 +436,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_MNFSR, rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
+ /* Gen1/Gen2 are not compatible */ + if (rsnd_src_convert_rate(src)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
return 0; @@ -548,6 +544,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
+ if (convert_rate) { + /* Gen1/Gen2 are not compatible */ + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + } + switch (rsnd_mod_id(mod)) { case 5: case 6:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Basically, SSI starting method is same between PIO/DMA mode. Let's share it
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/ssi.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-)
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 292e98b..5af016e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -412,8 +412,8 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, return ret; }
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); @@ -427,8 +427,8 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, return 0; }
-static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@@ -448,8 +448,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .probe = rsnd_ssi_pio_probe, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, - .start = rsnd_ssi_pio_start, - .stop = rsnd_ssi_pio_stop, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, };
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, @@ -508,13 +508,9 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, static int rsnd_ssi_dma_start(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - - rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
- rsnd_ssi_hw_start(ssi, ssi->rdai, io); + rsnd_ssi_start(mod, rdai);
rsnd_dma_start(dma);
@@ -524,16 +520,11 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
rsnd_dma_stop(dma);
- rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - - rsnd_ssi_hw_stop(ssi, rdai); - - rsnd_src_ssiu_stop(mod, rdai); + rsnd_ssi_stop(mod, rdai);
return 0; }
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
rsnd_src_start/stop() requests struct rsnd_dai as parameter. but, it is not used, and become more complex in L/R error handling. Let's remove it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/src.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 0b6b230..eede3ac 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -299,8 +299,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, return 0; }
-static int rsnd_src_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_src_start(struct rsnd_mod *mod) { /* * Cancel the initialization and operate the SRC function @@ -311,9 +310,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, return 0; }
- -static int rsnd_src_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai) +static int rsnd_src_stop(struct rsnd_mod *mod) { /* nothing to do */ return 0; @@ -488,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
- return rsnd_src_start(mod, rdai); + return rsnd_src_start(mod); }
static int rsnd_src_stop_gen1(struct rsnd_mod *mod, @@ -498,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
- return rsnd_src_stop(mod, rdai); + return rsnd_src_stop(mod); }
static struct rsnd_mod_ops rsnd_src_gen1_ops = { @@ -646,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SRC_CTRL, val);
- return rsnd_src_start(mod, rdai); + return rsnd_src_start(mod); }
static int rsnd_src_stop_gen2(struct rsnd_mod *mod, @@ -658,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
- return rsnd_src_stop(mod, rdai); + return rsnd_src_stop(mod); }
static struct rsnd_mod_ops rsnd_src_gen2_ops = {
On Thu, Nov 27, 2014 at 08:07:28AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
rsnd_src_start/stop() requests struct rsnd_dai as parameter. but, it is not used, and become more complex in L/R error handling. Let's remove it.
Applied everything up to here, thanks. I've not yet looked at the rest of the patches.
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Renesas R-Car sound SRC (= Sampling Rate Converter) has Asynchronous/Synchronous SRC mode. Asynchronous mode is already supported. This patch adds Synchronous mode.
The condition of enabling Synchronous mode are - SoC is clock master - sound uses SRC - sound is playback or sound is capture without DVC
amixer set "SRC Playback Sync Convert Rate" on aplay xxx.wav & amixer set "SRC Playback Sync Convert Rate" 44100
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/src.c | 126 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 5 deletions(-)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index eede3ac..1482772 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -16,11 +16,13 @@ struct rsnd_src { struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; + struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ + struct rsnd_kctrl_cfg_s sync; /* sync convert */ };
#define RSND_SRC_NAME_SIZE 16
-#define rsnd_src_convert_rate(p) ((p)->info->convert_rate) +#define rsnd_enable_sync_convert(src) ((src)->sen.val) #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) #define rsnd_src_dma_available(src) \ @@ -216,6 +218,30 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod, return 0; }
+u32 rsnd_src_convert_rate(struct rsnd_src *src) +{ + struct rsnd_mod *mod = &src->mod; + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 convert_rate; + + if (!runtime) + return 0; + + if (!rsnd_enable_sync_convert(src)) + return src->info->convert_rate; + + convert_rate = src->sync.val; + + if (!convert_rate) + convert_rate = src->info->convert_rate; + + if (!convert_rate) + convert_rate = runtime->rate; + + return convert_rate; +} + unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime) @@ -280,6 +306,9 @@ static int rsnd_src_init(struct rsnd_mod *mod,
clk_prepare_enable(src->clk);
+ /* reset sync convert_rate */ + src->sync.val = 0; + /* * Initialize the operation of the SRC internal circuits * see rsnd_src_start() @@ -296,6 +325,9 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
clk_disable_unprepare(src->clk);
+ /* reset sync convert_rate */ + src->sync.val = 0; + return 0; }
@@ -519,6 +551,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(src); + u32 cr, route; uint ratio; int ret;
@@ -539,13 +572,21 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, if (ret < 0) return ret;
- rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); - + cr = 0x00011110; + route = 0x0; if (convert_rate) { - /* Gen1/Gen2 are not compatible */ - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + route = 0x1; + + if (rsnd_enable_sync_convert(src)) { + cr |= 0x1; + route |= rsnd_dai_is_play(rdai, io) ? + (0x1 << 24) : (0x1 << 25); + } }
+ rsnd_mod_write(mod, SRC_SRCCR, cr); + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + switch (rsnd_mod_id(mod)) { case 5: case 6: @@ -658,6 +699,80 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod, return rsnd_src_stop(mod); }
+static void rsnd_src_reconvert_update(struct rsnd_mod *mod) +{ + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); + u32 fsrate = 0; + + if (!rsnd_enable_sync_convert(src)) + return; + + if (!convert_rate) + return; + + fsrate = 0x0400000 / convert_rate * runtime->rate; + + /* update IFS */ + rsnd_mod_write(mod, SRC_IFSVR, fsrate); +} + +static int rsnd_src_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_src *src = rsnd_mod_to_src(mod); + int ret; + + /* + * enable SRC sync convert if possible + */ + + /* + * Gen1 is not supported + */ + if (rsnd_is_gen1(priv)) + return 0; + + /* + * SRC sync convert needs clock master + */ + if (!rsnd_dai_is_clk_master(rdai)) + return 0; + + /* + * We can't use SRC sync convert + * if it has DVC + */ + if (rsnd_io_to_mod_dvc(io)) + return 0; + + /* + * enable sync convert + */ + ret = rsnd_kctrl_new_s(mod, rdai, rtd, + rsnd_dai_is_play(rdai, io) ? + "SRC Playback Sync Convert Rate Switch" : + "SRC Capture Sync Convert Rate Switch", + rsnd_src_reconvert_update, + &src->sen, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_s(mod, rdai, rtd, + rsnd_dai_is_play(rdai, io) ? + "SRC Playback Sync Convert Rate" : + "SRC Capture Sync Convert Rate", + rsnd_src_reconvert_update, + &src->sync, 192000); + + return ret; +} + static struct rsnd_mod_ops rsnd_src_gen2_ops = { .name = SRC_NAME, .probe = rsnd_src_probe_gen2, @@ -666,6 +781,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = { .quit = rsnd_src_quit, .start = rsnd_src_start_gen2, .stop = rsnd_src_stop_gen2, + .pcm_new = rsnd_src_pcm_new, };
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SSI.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/ssi.c | 129 ++++++++++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 47 deletions(-)
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5af016e..6f7080b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -202,14 +202,14 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, }
cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? - DMEN : /* DMA : use DMA */ - UIEN | OIEN | DIEN; /* PIO : enable interrupt */ + DMEN : /* DMA : enable DMA */ + DIEN; /* PIO : enable Data interrupt */
cr = ssi->cr_own | ssi->cr_clk | cr_mode | - EN; + UIEN | OIEN | EN;
rsnd_mod_write(&ssi->mod, SSICR, cr);
@@ -355,22 +355,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) /* * SSI PIO */ +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + + rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); + + rsnd_ssi_hw_start(ssi, rdai, io); + + rsnd_src_ssi_irq_enable(mod, rdai); + + return 0; +} + +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + rsnd_src_ssi_irq_disable(mod, rdai); + + rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); + + rsnd_ssi_hw_stop(ssi, rdai); + + rsnd_src_ssiu_stop(mod, rdai); + + return 0; +} + static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) { struct rsnd_ssi *ssi = data; + struct rsnd_dai *rdai = ssi->rdai; struct rsnd_mod *mod = &ssi->mod; struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); u32 status = rsnd_mod_read(mod, SSISR); - irqreturn_t ret = IRQ_NONE;
- if (io && (status & DIRQ)) { - struct rsnd_dai *rdai = ssi->rdai; + if (!io) + return IRQ_NONE; + + /* PIO only */ + if (status & DIRQ) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 *buf = (u32 *)(runtime->dma_area + rsnd_dai_pointer_offset(io, 0));
- rsnd_ssi_record_error(ssi, status); - /* * 8/16/32 data can be assesse to TDR/RDR register * directly as 32bit data @@ -382,11 +414,26 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) *buf = rsnd_mod_read(mod, SSIRDR);
rsnd_dai_pointer_update(io, sizeof(*buf)); + }
- ret = IRQ_HANDLED; + /* PIO / DMA */ + if (status & (UIRQ | OIRQ)) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + /* + * restart SSI + */ + rsnd_ssi_stop(mod, rdai); + rsnd_ssi_start(mod, rdai); + + dev_dbg(dev, "%s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); }
- return ret; + rsnd_ssi_record_error(ssi, status); + + return IRQ_HANDLED; }
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, @@ -412,37 +459,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, return ret; }
-static int rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - - rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); - - rsnd_ssi_hw_start(ssi, rdai, io); - - rsnd_src_ssi_irq_enable(mod, rdai); - - return 0; -} - -static int rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - rsnd_src_ssi_irq_disable(mod, rdai); - - rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - - rsnd_ssi_hw_stop(ssi, rdai); - - rsnd_src_ssiu_stop(mod, rdai); - - return 0; -} - static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, .probe = rsnd_ssi_pio_probe, @@ -461,17 +477,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, int dma_id = ssi->info->dma_id; int ret;
+ ret = devm_request_irq(dev, ssi->info->pio_irq, + rsnd_ssi_pio_interrupt, + IRQF_SHARED, + dev_name(dev), ssi); + if (ret) + goto rsnd_ssi_dma_probe_fail; + ret = rsnd_dma_init( priv, rsnd_mod_to_dma(mod), rsnd_info_is_playback(priv, ssi), dma_id); + if (ret) + goto rsnd_ssi_dma_probe_fail;
- if (ret < 0) - dev_err(dev, "%s[%d] (DMA) is failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - else - dev_dbg(dev, "%s[%d] (DMA) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_dbg(dev, "%s[%d] (DMA) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return ret; + +rsnd_ssi_dma_probe_fail: + dev_err(dev, "%s[%d] (DMA) is failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret; } @@ -479,8 +506,16 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct device *dev = rsnd_priv_to_dev(priv); + int irq = ssi->info->pio_irq; + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
+ /* PIO will request IRQ again */ + devm_free_irq(dev, irq, ssi); + return 0; }
On Thu, Nov 27, 2014 at 08:07:47AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SSI.
Applied, thanks. Normally in English this'd be called "recovery" rather than "salvage".
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SRC.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/rcar_snd.h | 1 + sound/soc/sh/rcar/gen.c | 15 ++++ sound/soc/sh/rcar/rsnd.h | 8 +++ sound/soc/sh/rcar/src.c | 178 +++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 183 insertions(+), 19 deletions(-)
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index d76412b..080e9e4 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -55,6 +55,7 @@ struct rsnd_ssi_platform_info { struct rsnd_src_platform_info { u32 convert_rate; /* sampling rate convert */ int dma_id; /* for Gen2 SCU */ + int irq; };
/* diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 87a6f2d..de0685f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -309,8 +309,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), + RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), + RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), + RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), + RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4), RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), @@ -403,6 +408,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40), + /* + * ADD US + * + * SRC_STATUS + * SRC_INT_EN + * SCU_SYS_STATUS0 + * SCU_SYS_STATUS1 + * SCU_SYS_INT_EN0 + * SCU_SYS_INT_EN1 + */ }; struct rsnd_regmap_field_conf conf_adg[] = { RSND_GEN_S_REG(BRRA, 0x00), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 5826c8a..c457003 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -44,6 +44,8 @@ enum rsnd_reg { RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSVR, RSND_REG_SRC_SRCCR, + RSND_REG_SCU_SYS_STATUS0, + RSND_REG_SCU_SYS_INT_EN0, RSND_REG_CMD_ROUTE_SLCT, RSND_REG_DVC_SWRSR, RSND_REG_DVC_DVUIR, @@ -94,6 +96,9 @@ enum rsnd_reg { RSND_REG_SHARE23, RSND_REG_SHARE24, RSND_REG_SHARE25, + RSND_REG_SHARE26, + RSND_REG_SHARE27, + RSND_REG_SHARE28,
RSND_REG_MAX, }; @@ -135,6 +140,9 @@ enum rsnd_reg { #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 +#define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 +#define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 +#define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28
struct rsnd_of_data; struct rsnd_priv; diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 1482772..aba35e0 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -12,12 +12,20 @@
#define SRC_NAME "src"
+/* SRCx_STATUS */ +#define OUF_SRCO ((1 << 12) | (1 << 13)) +#define OUF_SRCI ((1 << 9) | (1 << 8)) + +/* SCU_SYSTEM_STATUS0/1 */ +#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) + struct rsnd_src { struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ + int err; };
#define RSND_SRC_NAME_SIZE 16 @@ -306,6 +314,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
clk_prepare_enable(src->clk);
+ src->err = 0; /* reset sync convert_rate */ src->sync.val = 0;
@@ -322,12 +331,17 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_src *src = rsnd_mod_to_src(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv);
clk_disable_unprepare(src->clk);
/* reset sync convert_rate */ src->sync.val = 0;
+ if (src->err) + dev_warn(dev, "src under/over flow err = %d\n", src->err); + return 0; }
@@ -542,6 +556,110 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = { /* * Gen2 functions */ +#define rsnd_src_irq_enable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 1) +#define rsnd_src_irq_disable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 0) +static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 sys_int_val, int_val, sys_int_mask; + int irq = src->info->irq; + int id = rsnd_mod_id(mod); + + sys_int_val = + sys_int_mask = OUF_SRC(id); + int_val = 0x3300; + + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_probe_gen2() + */ + if ((irq <= 0) || !enable) { + sys_int_val = 0; + int_val = 0; + } + + rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); +} + +static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) +{ + u32 val = OUF_SRC(rsnd_mod_id(mod)); + + rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val); + rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); +} + +static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) +{ + u32 val = OUF_SRC(rsnd_mod_id(mod)); + bool ret = false; + + if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) || + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) { + struct rsnd_src *src = rsnd_mod_to_src(mod); + + src->err++; + ret = true; + } + + /* clear error static */ + rsnd_src_error_clear_gen2(mod); + + return ret; +} + +static int _rsnd_src_start_gen2(struct rsnd_mod *mod) +{ + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; + + rsnd_mod_write(mod, SRC_CTRL, val); + + rsnd_src_error_clear_gen2(mod); + + rsnd_src_start(mod); + + rsnd_src_irq_enable_gen2(mod); + + return 0; +} + +static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) +{ + rsnd_src_irq_disable_gen2(mod); + + rsnd_mod_write(mod, SRC_CTRL, 0); + + rsnd_src_error_record_gen2(mod); + + return rsnd_src_stop(mod); +} + +static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +{ + struct rsnd_mod *mod = data; + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + + if (!io) + return IRQ_NONE; + + if (rsnd_src_error_record_gen2(mod)) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + _rsnd_src_stop_gen2(mod); + _rsnd_src_start_gen2(mod); + + dev_dbg(dev, "%s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + return IRQ_HANDLED; +} + static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { @@ -629,18 +747,38 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); + int irq = src->info->irq; int ret;
+ if (irq > 0) { + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_irq_enable_gen2() + */ + ret = devm_request_irq(dev, irq, + rsnd_src_interrupt_gen2, + IRQF_SHARED, + dev_name(dev), mod); + if (ret) + goto rsnd_src_probe_gen2_fail; + } + ret = rsnd_dma_init(priv, rsnd_mod_to_dma(mod), rsnd_info_is_playback(priv, src), src->info->dma_id); - if (ret < 0) - dev_err(dev, "%s[%d] (Gen2) failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - else - dev_dbg(dev, "%s[%d] (Gen2) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + if (ret) + goto rsnd_src_probe_gen2_fail; + + dev_dbg(dev, "%s[%d] (Gen2) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return ret; + +rsnd_src_probe_gen2_fail: + dev_err(dev, "%s[%d] (Gen2) failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret; } @@ -676,27 +814,21 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, static int rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; - - rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); + rsnd_dma_start(rsnd_mod_to_dma(mod));
- rsnd_mod_write(mod, SRC_CTRL, val); - - return rsnd_src_start(mod); + return _rsnd_src_start_gen2(mod); }
static int rsnd_src_stop_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_src *src = rsnd_mod_to_src(mod); + int ret;
- rsnd_mod_write(mod, SRC_CTRL, 0); + ret = _rsnd_src_stop_gen2(mod);
- rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); + rsnd_dma_stop(rsnd_mod_to_dma(mod));
- return rsnd_src_stop(mod); + return ret; }
static void rsnd_src_reconvert_update(struct rsnd_mod *mod) @@ -797,10 +929,11 @@ static void rsnd_of_parse_src(struct platform_device *pdev, struct rsnd_priv *priv) { struct device_node *src_node; + struct device_node *np; struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_src_platform_info *src_info; struct device *dev = &pdev->dev; - int nr; + int nr, i;
if (!of_data) return; @@ -824,6 +957,13 @@ static void rsnd_of_parse_src(struct platform_device *pdev, info->src_info = src_info; info->src_info_nr = nr;
+ i = 0; + for_each_child_of_node(src_node, np) { + src_info[i].irq = irq_of_parse_and_map(np, 0); + + i++; + } + rsnd_of_parse_src_end: of_node_put(src_node); }
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current R-Car sound SSI PIO/DMA mode are using interrupt. it is no longer "xxx_pio_xxx", rename it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/rcar_snd.h | 8 ++++---- sound/soc/sh/rcar/ssi.c | 23 +++++++++++------------ 2 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 080e9e4..4cecd0c 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,14 +36,14 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
-#define RSND_SSI(_dma_id, _pio_irq, _flags) \ -{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI(_dma_id, _irq, _flags) \ +{ .dma_id = _dma_id, .irq = _irq, .flags = _flags } #define RSND_SSI_UNUSED \ -{ .dma_id = -1, .pio_irq = -1, .flags = 0 } +{ .dma_id = -1, .irq = -1, .flags = 0 }
struct rsnd_ssi_platform_info { int dma_id; - int pio_irq; + int irq; u32 flags; };
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 6f7080b..3844fbe 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -82,7 +82,7 @@ struct rsnd_ssi { #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #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_pio_available(ssi) ((ssi)->info->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) @@ -352,9 +352,6 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) } }
-/* - * SSI PIO - */ static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai *rdai) { @@ -386,7 +383,7 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, return 0; }
-static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) +static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) { struct rsnd_ssi *ssi = data; struct rsnd_dai *rdai = ssi->rdai; @@ -436,17 +433,19 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) return IRQ_HANDLED; }
+/* + * SSI PIO + */ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int irq = ssi->info->pio_irq; int ret;
- ret = devm_request_irq(dev, irq, - rsnd_ssi_pio_interrupt, + ret = devm_request_irq(dev, ssi->info->irq, + rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); if (ret) @@ -477,8 +476,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, int dma_id = ssi->info->dma_id; int ret;
- ret = devm_request_irq(dev, ssi->info->pio_irq, - rsnd_ssi_pio_interrupt, + ret = devm_request_irq(dev, ssi->info->irq, + rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); if (ret) @@ -509,7 +508,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); - int irq = ssi->info->pio_irq; + int irq = ssi->info->irq;
rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
@@ -680,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, /* * irq */ - ssi_info->pio_irq = irq_of_parse_and_map(np, 0); + ssi_info->irq = irq_of_parse_and_map(np, 0);
/* * DMA
On Thu, Nov 27, 2014 at 08:08:10AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current R-Car sound SSI PIO/DMA mode are using interrupt. it is no longer "xxx_pio_xxx", rename it.
Applied, thanks.
Hi Mark.
Kuninori Morimoto (20): 1. ASoC: rsnd: add .fallback callback 2. ASoC: rsnd: add callback status check method 3. ASoC: rsnd: rsnd_src_ssiu_stop() stops SSIU compulsorily 4. ASoC: rsnd: tidyup PIO/DMA mode settings method 5. ASoC: rsnd: tidyup SSI interrupt enable/disable method 6. ASoC: rsnd: care SSIWSR register in rsnd_ssi_hw_start() 7. ASoC: rsnd: clear status register when HW start 8. ASoC: rsnd: synchronize SSI start/stop sequence between PIO/DMA mode 9. ASoC: rsnd: show master clock rate when ADG probe 10. ASoC: rsnd: move snd_kcontrol_new fucntions to core.c 11. ASoC: rsnd: tidyup rsnd_io_to_runtime() macro 12. ASoC: rsnd: use rsnd_src_convert_rate() once on rsnd_src_set_convert_rate_gen2() 13. ASoC: rsnd: initialize SRC on rsnd_src_init() 14. ASoC: rsnd: set SRC_ROUTE_MODE0 on each rsnd_src_set_convert_rate() 15. ASoC: rsnd: share SSI starting method between PIO/DMA mode 16. ASoC: rsnd: remove un-necessary parameter from rsnd_src_start/stop() 17. ASoC: rsnd: add Synchronous SRC mode 18. ASoC: rsnd: add salvage support for under/over flow error on SSI 19. ASoC: rsnd: add salvage support for under/over flow error on SRC 20. ASoC: rsnd: rename SSI function name of PIO
I think I have not got comment/response about these patches.
17. ASoC: rsnd: add Synchronous SRC mode 19. ASoC: rsnd: add salvage support for under/over flow error on SRC
Can you give me feedback ?
Best regards --- Kuninori Morimoto
On Wed, Dec 24, 2014 at 02:01:34AM +0000, Kuninori Morimoto wrote:
I think I have not got comment/response about these patches.
17. ASoC: rsnd: add Synchronous SRC mode 19. ASoC: rsnd: add salvage support for under/over flow error on SRC
Can you give me feedback ?
The problem with those is that they add the first patch adds explicit controls for the rates which look awfully like the sort of thing people are supposed to be doing using DPCM (I've said before these drivers should be using that...). I need to figure out if we have existing controls which do this sort of thing in the driver at which point this isn't so bad but still not good or if this is a new thing for the driver.
Hi Mark
I think I have not got comment/response about these patches.
17. ASoC: rsnd: add Synchronous SRC mode 19. ASoC: rsnd: add salvage support for under/over flow error on SRC
Can you give me feedback ?
The problem with those is that they add the first patch adds explicit controls for the rates which look awfully like the sort of thing people are supposed to be doing using DPCM (I've said before these drivers should be using that...). I need to figure out if we have existing controls which do this sort of thing in the driver at which point this isn't so bad but still not good or if this is a new thing for the driver.
Thank you for your feedback. I need to explain more about this. Yes, I know you are requesting to use DPCM for rate convert. Our sound device has 2 type of rate convert. 1st is "static" rate convert, and, 2nd is "dynamic" rate convert.
1st "static" rate convert is used to convert rate to fixed sampling rate which is based on codec device. And I will implement it as DPCM. This is no problem.
2nd "dynamic" rate convert which is this patch is used as revision. I don't know detail of use case, but, according to HW team, sound clock has small accidental error. Because of this, if we used 48kHz sound in long term, but, the real sound can be 48001 Hz or 47999 Hz or something like that. So, if we use it without revision in long term, the sound will has small noise. This will be problem for TV or radio (?). Thus, sound player wants to revise it by "dynamic" rate convert. I guess DPCM is good match for "static" rate convert, but not good for "dynamic".
Best regards --- Kuninori Morimoto
On Mon, Jan 05, 2015 at 12:42:27AM +0000, Kuninori Morimoto wrote:
2nd "dynamic" rate convert which is this patch is used as revision. I don't know detail of use case, but, according to HW team, sound clock has small accidental error. Because of this, if we used 48kHz sound in long term, but, the real sound can be 48001 Hz or 47999 Hz or something like that. So, if we use it without revision in long term, the sound will has small noise. This will be problem for TV or radio (?). Thus, sound player wants to revise it by "dynamic" rate convert. I guess DPCM is good match for "static" rate convert, but not good for "dynamic".
In general it should be fine for anything - the driver for the SoC shouldn't need to understand why the rates are being converted, it should let the system integration worry about picking the rates. That way we can more easily make the frameworks smarter and get benefits over all machines.
Though having said that I'm a little confused as to what exactly this control is intended for - am I right in thinking that what's actually going on here is that something in userspace is measuring the actual measured rate and feeding that information back to the DSP for correction? If that's the case that does seem reasonable but also like we ought to have a general control interface for it.
Adding Tim and Pierre since they might have views if my guess is correct, I could be wrong though.
Hi Mark
Thank you for your feedback
2nd "dynamic" rate convert which is this patch is used as revision. I don't know detail of use case, but, according to HW team, sound clock has small accidental error. Because of this, if we used 48kHz sound in long term, but, the real sound can be 48001 Hz or 47999 Hz or something like that. So, if we use it without revision in long term, the sound will has small noise. This will be problem for TV or radio (?). Thus, sound player wants to revise it by "dynamic" rate convert. I guess DPCM is good match for "static" rate convert, but not good for "dynamic".
In general it should be fine for anything - the driver for the SoC shouldn't need to understand why the rates are being converted, it should let the system integration worry about picking the rates. That way we can more easily make the frameworks smarter and get benefits over all machines.
Though having said that I'm a little confused as to what exactly this control is intended for - am I right in thinking that what's actually going on here is that something in userspace is measuring the actual measured rate and feeding that information back to the DSP for correction? If that's the case that does seem reasonable but also like we ought to have a general control interface for it.
Yes, you are correct. Actually, I don't know detail of this userspace. (How does it measure actual rate) But, message actual rate -> feedback to SoC is the purpose. (if actual rate was slow -> clock up, actual rate was fast -> clock down)
Is this "general control interface" means "implemented under framework, not drvier" ?
Adding Tim and Pierre since they might have views if my guess is correct, I could be wrong though.
Best regards --- Kuninori Morimoto
On 1/6/15 10:12 PM, Kuninori Morimoto wrote:
Hi Mark
Thank you for your feedback
2nd "dynamic" rate convert which is this patch is used as revision. I don't know detail of use case, but, according to HW team, sound clock has small accidental error. Because of this, if we used 48kHz sound in long term, but, the real sound can be 48001 Hz or 47999 Hz or something like that. So, if we use it without revision in long term, the sound will has small noise. This will be problem for TV or radio (?). Thus, sound player wants to revise it by "dynamic" rate convert. I guess DPCM is good match for "static" rate convert, but not good for "dynamic".
In general it should be fine for anything - the driver for the SoC shouldn't need to understand why the rates are being converted, it should let the system integration worry about picking the rates. That way we can more easily make the frameworks smarter and get benefits over all machines.
Though having said that I'm a little confused as to what exactly this control is intended for - am I right in thinking that what's actually going on here is that something in userspace is measuring the actual measured rate and feeding that information back to the DSP for correction? If that's the case that does seem reasonable but also like we ought to have a general control interface for it.
Yes, you are correct. Actually, I don't know detail of this userspace. (How does it measure actual rate) But, message actual rate -> feedback to SoC is the purpose. (if actual rate was slow -> clock up, actual rate was fast -> clock down)
This matches my definition of an asynchronous SRC. I really don't get what 'synchronous' means here, and it'd seem odd anyway to have 1Hz granularity for the updates?
Is this "general control interface" means "implemented under framework, not drvier" ?
Adding Tim and Pierre since they might have views if my guess is correct, I could be wrong though.
I'd be good to have a snd_pcm_set_rate() generic framework, but I wonder if it's practical. Hardware with ASRC or tunable PLLs isn't very common except in pro audio, stb, networking and automotive, and usually has specific M/N dividers or rates, I am not sure it can be done generically. Also most servo loops are implemented in firmware or in environments that don't use Linux, not sure how many people would benefit from such additions. At the same time people usually don't use ALSA in such environments because it doesn't do what they want, so it might be worth relooking at adjustable clocks, presentation timestamps, PCR, etc.
On Thu, Jan 08, 2015 at 11:44:44AM -0600, Pierre-Louis Bossart wrote:
This matches my definition of an asynchronous SRC. I really don't get what 'synchronous' means here, and it'd seem odd anyway to have 1Hz granularity for the updates?
AIUI it's referring to the intended usage of synchronizing the rates in two domains (which isn't the normal audio terminology).
Adding Tim and Pierre since they might have views if my guess is correct, I could be wrong though.
I'd be good to have a snd_pcm_set_rate() generic framework, but I wonder if it's practical. Hardware with ASRC or tunable PLLs isn't very common except in pro audio, stb, networking and automotive, and usually has specific M/N dividers or rates, I am not sure it can be done generically. Also most servo
ASRC at least is very common in mobile, and in that market people have been driving requirements for on the fly reprogrammability for some use cases (though they've not been delivered universally and this sort of small adjustment in rate wasn't one of the requirements).
I'm wondering if a way of specifying the target rate and a way of reading back the constraints might solve the problem, it seems like a lot of work though.
loops are implemented in firmware or in environments that don't use Linux, not sure how many people would benefit from such additions. At the same time people usually don't use ALSA in such environments because it doesn't do what they want, so it might be worth relooking at adjustable clocks, presentation timestamps, PCR, etc.
Right, that's kind of my thinking here. I'm aware of people doing this stuff in Linux environments outside of ALSA and of people talking about similar hardware capabilities but I don't know what those look like. I don't know if for the time being just defining a control name for the target rate might be enough - doing this fully is probably a big task and I'm not sure anyone is interested right now.
Hi Mark
I'm wondering if a way of specifying the target rate and a way of reading back the constraints might solve the problem, it seems like a lot of work though.
loops are implemented in firmware or in environments that don't use Linux, not sure how many people would benefit from such additions. At the same time people usually don't use ALSA in such environments because it doesn't do what they want, so it might be worth relooking at adjustable clocks, presentation timestamps, PCR, etc.
Right, that's kind of my thinking here. I'm aware of people doing this stuff in Linux environments outside of ALSA and of people talking about similar hardware capabilities but I don't know what those look like. I don't know if for the time being just defining a control name for the target rate might be enough - doing this fully is probably a big task and I'm not sure anyone is interested right now.
I asked our use case to customer Team. According to them, they are using this rate converter in Digital TV. Digital TV is including its sampling rate, and exchanges it sometimes. Our SoC can exchange sampling rate in SRC, but other SoC needs external circuit (?)
This means my 1st explain (= 48kHz can be 48001 or 47999) seems wrong. They doesn't care about real audio clock.
And, this system (= Digital TV software) is implemented as middleware. So, this is Renesas specific feature, can't be big task (?)
Best regards --- Kuninori Morimoto
On Wed, Jan 14, 2015 at 01:11:11AM +0000, Kuninori Morimoto wrote:
I asked our use case to customer Team. According to them, they are using this rate converter in Digital TV. Digital TV is including its sampling rate, and exchanges it sometimes. Our SoC can exchange sampling rate in SRC, but other SoC needs external circuit (?)
This means my 1st explain (= 48kHz can be 48001 or 47999) seems wrong. They doesn't care about real audio clock.
And, this system (= Digital TV software) is implemented as middleware. So, this is Renesas specific feature, can't be big task (?)
OK, that's more what I thought this was originally. It sounds like a fairly standard ASRC deployment where the goal is to match the rate the system wants to play audio at to the rate that the target hardware needs. That's not too different to the telephony cases where the system is in one clock domain (possibly at a very different sample rate) and the baseband another and definitely the sort of thing that DPCM is intended to support.
This really would be better done with DPCM with the userspace control being provided by the driver for the tuner or the machine driver and passed through to set the rate of the back end. Do you think that's achieveable (even if it's just a mostly dummy DAI here)? That way if this is used in a system with a CODEC with a real driver on the end of the link things should just work.
Hi Mark
Thank you for your feedback
OK, that's more what I thought this was originally. It sounds like a fairly standard ASRC deployment where the goal is to match the rate the system wants to play audio at to the rate that the target hardware needs. That's not too different to the telephony cases where the system is in one clock domain (possibly at a very different sample rate) and the baseband another and definitely the sort of thing that DPCM is intended to support.
This really would be better done with DPCM with the userspace control being provided by the driver for the tuner or the machine driver and passed through to set the rate of the back end. Do you think that's achieveable (even if it's just a mostly dummy DAI here)? That way if this is used in a system with a CODEC with a real driver on the end of the link things should just work.
Can I confirm about this ? This means, we can..
- add "sampling rate convert" support on DPCM (= our "static" sampling rate convert can use it) - add "sampling rate convert interface" support for userland on DPCM (= our "dynamic" sampling rate convert can use it)
I think this is achieveable.
My concern about this is card driver with DT. I and Mark talked about it in LinuxCon before, and adding DPCM support with DT on simple-card is very difficult (= simple-card is not "simple" today...) So, I will create new sound card driver which includes DPCM DT support for this purpose. Then, should I create "renesas-sound-card" under sound/soc/sh ? or can I create "multi-card" (I don't know this is good naming...) under sound/soc/generic ? If we can select later choice, it will have very limited feature, but simple DPCM support
Best regards --- Kuninori Morimoto
On Tue, Jan 20, 2015 at 02:10:11AM +0000, Kuninori Morimoto wrote:
This really would be better done with DPCM with the userspace control being provided by the driver for the tuner or the machine driver and passed through to set the rate of the back end. Do you think that's achieveable (even if it's just a mostly dummy DAI here)? That way if this is used in a system with a CODEC with a real driver on the end of the link things should just work.
Can I confirm about this ? This means, we can..
- add "sampling rate convert" support on DPCM (= our "static" sampling rate convert can use it)
- add "sampling rate convert interface" support for userland on DPCM (= our "dynamic" sampling rate convert can use it)
I think this is achieveable.
Yes.
My concern about this is card driver with DT. I and Mark talked about it in LinuxCon before, and adding DPCM support with DT on simple-card is very difficult (= simple-card is not "simple" today...) So, I will create new sound card driver which includes DPCM DT support for this purpose. Then, should I create "renesas-sound-card" under sound/soc/sh ? or can I create "multi-card" (I don't know this is good naming...) under sound/soc/generic ? If we can select later choice, it will have very limited feature, but simple DPCM support
A Renesas card (well, a SoC specific name is probably better) seems OK, though as I said elsewhere collaborating on the generic graph work *might* be a good way forwards too. That definitely shouldn't be a requirement for getting things done though.
Hi Mark.
Kuninori Morimoto (20): 1. ASoC: rsnd: add .fallback callback 2. ASoC: rsnd: add callback status check method 3. ASoC: rsnd: rsnd_src_ssiu_stop() stops SSIU compulsorily 4. ASoC: rsnd: tidyup PIO/DMA mode settings method 5. ASoC: rsnd: tidyup SSI interrupt enable/disable method 6. ASoC: rsnd: care SSIWSR register in rsnd_ssi_hw_start() 7. ASoC: rsnd: clear status register when HW start 8. ASoC: rsnd: synchronize SSI start/stop sequence between PIO/DMA mode 9. ASoC: rsnd: show master clock rate when ADG probe 10. ASoC: rsnd: move snd_kcontrol_new fucntions to core.c 11. ASoC: rsnd: tidyup rsnd_io_to_runtime() macro 12. ASoC: rsnd: use rsnd_src_convert_rate() once on rsnd_src_set_convert_rate_gen2() 13. ASoC: rsnd: initialize SRC on rsnd_src_init() 14. ASoC: rsnd: set SRC_ROUTE_MODE0 on each rsnd_src_set_convert_rate() 15. ASoC: rsnd: share SSI starting method between PIO/DMA mode 16. ASoC: rsnd: remove un-necessary parameter from rsnd_src_start/stop() 17. ASoC: rsnd: add Synchronous SRC mode 18. ASoC: rsnd: add salvage support for under/over flow error on SSI 19. ASoC: rsnd: add salvage support for under/over flow error on SRC 20. ASoC: rsnd: rename SSI function name of PIO
Now, 17) and 19) are not accepted, but the reason of 19) was bacause it is based on 17) I will resend 19) patch which is in-depend from 17)
Best regards --- Kuninori Morimoto
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SRC.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v1 -> v2
- "salvage" -> "recovery" in Subject - in-depend from Synchronous SRC mode patch
include/sound/rcar_snd.h | 1 + sound/soc/sh/rcar/gen.c | 15 ++++ sound/soc/sh/rcar/rsnd.h | 8 +++ sound/soc/sh/rcar/src.c | 179 +++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 184 insertions(+), 19 deletions(-)
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 83284ca..4cecd0c 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -55,6 +55,7 @@ struct rsnd_ssi_platform_info { struct rsnd_src_platform_info { u32 convert_rate; /* sampling rate convert */ int dma_id; /* for Gen2 SCU */ + int irq; };
/* diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 87a6f2d..de0685f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -309,8 +309,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), + RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), + RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), + RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), + RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4), RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), @@ -403,6 +408,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40), + /* + * ADD US + * + * SRC_STATUS + * SRC_INT_EN + * SCU_SYS_STATUS0 + * SCU_SYS_STATUS1 + * SCU_SYS_INT_EN0 + * SCU_SYS_INT_EN1 + */ }; struct rsnd_regmap_field_conf conf_adg[] = { RSND_GEN_S_REG(BRRA, 0x00), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 5826c8a..c457003 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -44,6 +44,8 @@ enum rsnd_reg { RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSVR, RSND_REG_SRC_SRCCR, + RSND_REG_SCU_SYS_STATUS0, + RSND_REG_SCU_SYS_INT_EN0, RSND_REG_CMD_ROUTE_SLCT, RSND_REG_DVC_SWRSR, RSND_REG_DVC_DVUIR, @@ -94,6 +96,9 @@ enum rsnd_reg { RSND_REG_SHARE23, RSND_REG_SHARE24, RSND_REG_SHARE25, + RSND_REG_SHARE26, + RSND_REG_SHARE27, + RSND_REG_SHARE28,
RSND_REG_MAX, }; @@ -135,6 +140,9 @@ enum rsnd_reg { #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 +#define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 +#define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 +#define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28
struct rsnd_of_data; struct rsnd_priv; diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index eede3ac..648b35e 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -12,10 +12,18 @@
#define SRC_NAME "src"
+/* SRCx_STATUS */ +#define OUF_SRCO ((1 << 12) | (1 << 13)) +#define OUF_SRCI ((1 << 9) | (1 << 8)) + +/* SCU_SYSTEM_STATUS0/1 */ +#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) + struct rsnd_src { struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct clk *clk; + int err; };
#define RSND_SRC_NAME_SIZE 16 @@ -280,6 +288,8 @@ static int rsnd_src_init(struct rsnd_mod *mod,
clk_prepare_enable(src->clk);
+ src->err = 0; + /* * Initialize the operation of the SRC internal circuits * see rsnd_src_start() @@ -293,9 +303,14 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_src *src = rsnd_mod_to_src(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv);
clk_disable_unprepare(src->clk);
+ if (src->err) + dev_warn(dev, "src under/over flow err = %d\n", src->err); + return 0; }
@@ -510,6 +525,110 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = { /* * Gen2 functions */ +#define rsnd_src_irq_enable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 1) +#define rsnd_src_irq_disable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 0) +static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 sys_int_val, int_val, sys_int_mask; + int irq = src->info->irq; + int id = rsnd_mod_id(mod); + + sys_int_val = + sys_int_mask = OUF_SRC(id); + int_val = 0x3300; + + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_probe_gen2() + */ + if ((irq <= 0) || !enable) { + sys_int_val = 0; + int_val = 0; + } + + rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); +} + +static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) +{ + u32 val = OUF_SRC(rsnd_mod_id(mod)); + + rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val); + rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); +} + +static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) +{ + u32 val = OUF_SRC(rsnd_mod_id(mod)); + bool ret = false; + + if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) || + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) { + struct rsnd_src *src = rsnd_mod_to_src(mod); + + src->err++; + ret = true; + } + + /* clear error static */ + rsnd_src_error_clear_gen2(mod); + + return ret; +} + +static int _rsnd_src_start_gen2(struct rsnd_mod *mod) +{ + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; + + rsnd_mod_write(mod, SRC_CTRL, val); + + rsnd_src_error_clear_gen2(mod); + + rsnd_src_start(mod); + + rsnd_src_irq_enable_gen2(mod); + + return 0; +} + +static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) +{ + rsnd_src_irq_disable_gen2(mod); + + rsnd_mod_write(mod, SRC_CTRL, 0); + + rsnd_src_error_record_gen2(mod); + + return rsnd_src_stop(mod); +} + +static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +{ + struct rsnd_mod *mod = data; + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + + if (!io) + return IRQ_NONE; + + if (rsnd_src_error_record_gen2(mod)) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + _rsnd_src_stop_gen2(mod); + _rsnd_src_start_gen2(mod); + + dev_dbg(dev, "%s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + return IRQ_HANDLED; +} + static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { @@ -588,18 +707,38 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); + int irq = src->info->irq; int ret;
+ if (irq > 0) { + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_irq_enable_gen2() + */ + ret = devm_request_irq(dev, irq, + rsnd_src_interrupt_gen2, + IRQF_SHARED, + dev_name(dev), mod); + if (ret) + goto rsnd_src_probe_gen2_fail; + } + ret = rsnd_dma_init(priv, rsnd_mod_to_dma(mod), rsnd_info_is_playback(priv, src), src->info->dma_id); - if (ret < 0) - dev_err(dev, "%s[%d] (Gen2) failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - else - dev_dbg(dev, "%s[%d] (Gen2) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + if (ret) + goto rsnd_src_probe_gen2_fail; + + dev_dbg(dev, "%s[%d] (Gen2) is probed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return ret; + +rsnd_src_probe_gen2_fail: + dev_err(dev, "%s[%d] (Gen2) failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret; } @@ -635,27 +774,21 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, static int rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; - - rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); + rsnd_dma_start(rsnd_mod_to_dma(mod));
- rsnd_mod_write(mod, SRC_CTRL, val); - - return rsnd_src_start(mod); + return _rsnd_src_start_gen2(mod); }
static int rsnd_src_stop_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { - struct rsnd_src *src = rsnd_mod_to_src(mod); + int ret;
- rsnd_mod_write(mod, SRC_CTRL, 0); + ret = _rsnd_src_stop_gen2(mod);
- rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); + rsnd_dma_stop(rsnd_mod_to_dma(mod));
- return rsnd_src_stop(mod); + return ret; }
static struct rsnd_mod_ops rsnd_src_gen2_ops = { @@ -681,10 +814,11 @@ static void rsnd_of_parse_src(struct platform_device *pdev, struct rsnd_priv *priv) { struct device_node *src_node; + struct device_node *np; struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_src_platform_info *src_info; struct device *dev = &pdev->dev; - int nr; + int nr, i;
if (!of_data) return; @@ -708,6 +842,13 @@ static void rsnd_of_parse_src(struct platform_device *pdev, info->src_info = src_info; info->src_info_nr = nr;
+ i = 0; + for_each_child_of_node(src_node, np) { + src_info[i].irq = irq_of_parse_and_map(np, 0); + + i++; + } + rsnd_of_parse_src_end: of_node_put(src_node); }
On Thu, Jan 08, 2015 at 01:52:36AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SRC.
Applied, but please don't send new patches in reply to old serieses - it makes it harder to follow what's going on and results in things being less noticable.
Hi Mark
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SRC.
Applied, but please don't send new patches in reply to old serieses - it makes it harder to follow what's going on and results in things being less noticable.
I'm sorry about it.
BTW, I couln't find this patch on mark/topic/rcar. Which branch ?
Best regards --- Kuninori Morimoto
On Tue, Jan 13, 2015 at 07:20:12AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
L/R channel will be switched if under/over flow error happen on Renesas R-Car sound device by the HW bugs. Then, HW restart is required for salvage. This patch add salvage support for SRC.
Applied, but please don't send new patches in reply to old serieses - it makes it harder to follow what's going on and results in things being less noticable.
I'm sorry about it.
BTW, I couln't find this patch on mark/topic/rcar. Which branch ?
It should be on there, I might not've pushed when you looked though.
Hi Mark
BTW, I couln't find this patch on mark/topic/rcar. Which branch ?
It should be on there, I might not've pushed when you looked though.
Thank you for your help I could find it
Best regards --- Kuninori Morimoto
participants (3)
-
Kuninori Morimoto
-
Mark Brown
-
Pierre-Louis Bossart