ASoC: rsnd: add recovery support for under/over flow error on SRC
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Thu, 8 Jan 2015 01:52:36 +0000 (01:52 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 12 Jan 2015 12:59:32 +0000 (12:59 +0000)
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>
include/sound/rcar_snd.h
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c

index 83284cae464c492bf77cf127958f46469ee4b9c0..4cecd0c175f607c7f3cae2a2fb03660f81131799 100644 (file)
@@ -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;
 };
 
 /*
index 87a6f2d627753c0f69ba0b51fb7e2d0bc1438f45..de0685f2abaee64f4d729745497b7a6a4ae51e0e 100644 (file)
@@ -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),
index 5826c8abf7949d3ed1807dfe1c61a7fa8a219f84..c45700380e5985244b834db1c6e501332efdff14 100644 (file)
@@ -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;
index eede3ac6eed232b12307881574a8d041f1d88352..648b35e7effce6fe84c8b0897fabc07982c840c0 100644 (file)
 
 #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);
 }