[alsa-devel] [PATCH 18/20] ASoC: rsnd: add salvage support for under/over flow error on SSI

Kuninori Morimoto kuninori.morimoto.gx at renesas.com
Thu Nov 27 09:07:47 CET 2014


From: Kuninori Morimoto <kuninori.morimoto.gx at 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 at 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;
 }
 
-- 
1.7.9.5



More information about the Alsa-devel mailing list