ASoC: rsnd: add salvage support for under/over flow error on SSI
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Thu, 27 Nov 2014 08:07:47 +0000 (08:07 +0000)
committerMark Brown <broonie@kernel.org>
Sun, 7 Dec 2014 13:45:27 +0000 (13:45 +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 SSI.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sh/rcar/ssi.c

index 5af016e730b17638fa00058dd767cd07597aa356..6f7080b017fa534bd2c3b2e1cf1108770aaa2fb8 100644 (file)
@@ -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;
 }