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>
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_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 @@
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 @@
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 @@
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 @@
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 @@
#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 @@
clk_prepare_enable(src->clk);
+ src->err = 0;
+
/*
* Initialize the operation of the SRC internal circuits
* see rsnd_src_start()
@@ -293,9 +303,14 @@
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 @@
/*
* 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 @@
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_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(mod));
- rsnd_dma_start(rsnd_mod_to_dma(&src->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 @@
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 @@
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);
}