diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2015-01-08 02:52:36 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-01-12 13:59:32 +0100 |
commit | cfcefe01265cbaf5ca7209226d043b07bfa8b587 (patch) | |
tree | 6341e03635512f1842a7cd1a7dc62fca43c00aaf /sound/soc/sh/rcar | |
parent | ASoC: rcar: Remove unnecessary snd_pcm_lib_preallocate_free_for_all() (diff) | |
download | linux-cfcefe01265cbaf5ca7209226d043b07bfa8b587.tar.xz linux-cfcefe01265cbaf5ca7209226d043b07bfa8b587.zip |
ASoC: rsnd: add recovery support for under/over flow error on SRC
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>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/sh/rcar')
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 15 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 8 | ||||
-rw-r--r-- | sound/soc/sh/rcar/src.c | 179 |
3 files changed, 183 insertions, 19 deletions
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 87a6f2d62775..de0685f2abae 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 5826c8abf794..c45700380e59 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 eede3ac6eed2..648b35e7effc 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); } |