Commit 4e7d606c authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown

ASoC: rsnd: add salvage support for under/over flow error on SSI

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: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent f0ef0cb8
...@@ -202,14 +202,14 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, ...@@ -202,14 +202,14 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
} }
cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
DMEN : /* DMA : use DMA */ DMEN : /* DMA : enable DMA */
UIEN | OIEN | DIEN; /* PIO : enable interrupt */ DIEN; /* PIO : enable Data interrupt */
cr = ssi->cr_own | cr = ssi->cr_own |
ssi->cr_clk | ssi->cr_clk |
cr_mode | cr_mode |
EN; UIEN | OIEN | EN;
rsnd_mod_write(&ssi->mod, SSICR, cr); rsnd_mod_write(&ssi->mod, SSICR, cr);
...@@ -355,22 +355,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) ...@@ -355,22 +355,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
/* /*
* SSI PIO * 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) static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
{ {
struct rsnd_ssi *ssi = data; struct rsnd_ssi *ssi = data;
struct rsnd_dai *rdai = ssi->rdai;
struct rsnd_mod *mod = &ssi->mod; struct rsnd_mod *mod = &ssi->mod;
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
u32 status = rsnd_mod_read(mod, SSISR); u32 status = rsnd_mod_read(mod, SSISR);
irqreturn_t ret = IRQ_NONE;
if (io && (status & DIRQ)) { if (!io)
struct rsnd_dai *rdai = ssi->rdai; return IRQ_NONE;
/* PIO only */
if (status & DIRQ) {
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 *buf = (u32 *)(runtime->dma_area + u32 *buf = (u32 *)(runtime->dma_area +
rsnd_dai_pointer_offset(io, 0)); rsnd_dai_pointer_offset(io, 0));
rsnd_ssi_record_error(ssi, status);
/* /*
* 8/16/32 data can be assesse to TDR/RDR register * 8/16/32 data can be assesse to TDR/RDR register
* directly as 32bit data * directly as 32bit data
...@@ -382,11 +414,26 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) ...@@ -382,11 +414,26 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
*buf = rsnd_mod_read(mod, SSIRDR); *buf = rsnd_mod_read(mod, SSIRDR);
rsnd_dai_pointer_update(io, sizeof(*buf)); 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, static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
...@@ -412,37 +459,6 @@ 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; 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 = { static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.name = SSI_NAME, .name = SSI_NAME,
.probe = rsnd_ssi_pio_probe, .probe = rsnd_ssi_pio_probe,
...@@ -461,17 +477,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, ...@@ -461,17 +477,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
int dma_id = ssi->info->dma_id; int dma_id = ssi->info->dma_id;
int ret; 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( ret = rsnd_dma_init(
priv, rsnd_mod_to_dma(mod), priv, rsnd_mod_to_dma(mod),
rsnd_info_is_playback(priv, ssi), rsnd_info_is_playback(priv, ssi),
dma_id); dma_id);
if (ret)
goto rsnd_ssi_dma_probe_fail;
if (ret < 0) dev_dbg(dev, "%s[%d] (DMA) is probed\n",
dev_err(dev, "%s[%d] (DMA) is failed\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
rsnd_mod_name(mod), rsnd_mod_id(mod));
else return ret;
dev_dbg(dev, "%s[%d] (DMA) is probed\n",
rsnd_mod_name(mod), rsnd_mod_id(mod)); rsnd_ssi_dma_probe_fail:
dev_err(dev, "%s[%d] (DMA) is failed\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret; return ret;
} }
...@@ -479,8 +506,16 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, ...@@ -479,8 +506,16 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
struct rsnd_dai *rdai) 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)); 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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment