Commit cfcefe01 authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown

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: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent ddf3335b
...@@ -55,6 +55,7 @@ struct rsnd_ssi_platform_info { ...@@ -55,6 +55,7 @@ struct rsnd_ssi_platform_info {
struct rsnd_src_platform_info { struct rsnd_src_platform_info {
u32 convert_rate; /* sampling rate convert */ u32 convert_rate; /* sampling rate convert */
int dma_id; /* for Gen2 SCU */ int dma_id; /* for Gen2 SCU */
int irq;
}; };
/* /*
......
...@@ -309,8 +309,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, ...@@ -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_BUSIF_MODE, 0x0, 0x20),
RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
RSND_GEN_M_REG(SRC_CTRL, 0x10, 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_ROUTE_SLCT, 0x18c, 0x20),
RSND_GEN_M_REG(CMD_CTRL, 0x190, 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_SWRSR, 0x200, 0x40),
RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
...@@ -403,6 +408,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev, ...@@ -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_IFSVR, 0x220, 0x40),
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
RSND_GEN_M_REG(SRC_MNFSR, 0x228, 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[] = { struct rsnd_regmap_field_conf conf_adg[] = {
RSND_GEN_S_REG(BRRA, 0x00), RSND_GEN_S_REG(BRRA, 0x00),
......
...@@ -44,6 +44,8 @@ enum rsnd_reg { ...@@ -44,6 +44,8 @@ enum rsnd_reg {
RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSCR,
RSND_REG_SRC_IFSVR, RSND_REG_SRC_IFSVR,
RSND_REG_SRC_SRCCR, RSND_REG_SRC_SRCCR,
RSND_REG_SCU_SYS_STATUS0,
RSND_REG_SCU_SYS_INT_EN0,
RSND_REG_CMD_ROUTE_SLCT, RSND_REG_CMD_ROUTE_SLCT,
RSND_REG_DVC_SWRSR, RSND_REG_DVC_SWRSR,
RSND_REG_DVC_DVUIR, RSND_REG_DVC_DVUIR,
...@@ -94,6 +96,9 @@ enum rsnd_reg { ...@@ -94,6 +96,9 @@ enum rsnd_reg {
RSND_REG_SHARE23, RSND_REG_SHARE23,
RSND_REG_SHARE24, RSND_REG_SHARE24,
RSND_REG_SHARE25, RSND_REG_SHARE25,
RSND_REG_SHARE26,
RSND_REG_SHARE27,
RSND_REG_SHARE28,
RSND_REG_MAX, RSND_REG_MAX,
}; };
...@@ -135,6 +140,9 @@ enum rsnd_reg { ...@@ -135,6 +140,9 @@ enum rsnd_reg {
#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23
#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24
#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 #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_of_data;
struct rsnd_priv; struct rsnd_priv;
......
...@@ -12,10 +12,18 @@ ...@@ -12,10 +12,18 @@
#define SRC_NAME "src" #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 {
struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_src_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod; struct rsnd_mod mod;
struct clk *clk; struct clk *clk;
int err;
}; };
#define RSND_SRC_NAME_SIZE 16 #define RSND_SRC_NAME_SIZE 16
...@@ -280,6 +288,8 @@ static int rsnd_src_init(struct rsnd_mod *mod, ...@@ -280,6 +288,8 @@ static int rsnd_src_init(struct rsnd_mod *mod,
clk_prepare_enable(src->clk); clk_prepare_enable(src->clk);
src->err = 0;
/* /*
* Initialize the operation of the SRC internal circuits * Initialize the operation of the SRC internal circuits
* see rsnd_src_start() * see rsnd_src_start()
...@@ -293,9 +303,14 @@ static int rsnd_src_quit(struct rsnd_mod *mod, ...@@ -293,9 +303,14 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_dai *rdai) struct rsnd_dai *rdai)
{ {
struct rsnd_src *src = rsnd_mod_to_src(mod); 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); clk_disable_unprepare(src->clk);
if (src->err)
dev_warn(dev, "src under/over flow err = %d\n", src->err);
return 0; return 0;
} }
...@@ -510,6 +525,110 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = { ...@@ -510,6 +525,110 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = {
/* /*
* Gen2 functions * 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, static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
struct rsnd_dai *rdai) struct rsnd_dai *rdai)
{ {
...@@ -588,20 +707,40 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, ...@@ -588,20 +707,40 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
int irq = src->info->irq;
int ret; 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, ret = rsnd_dma_init(priv,
rsnd_mod_to_dma(mod), rsnd_mod_to_dma(mod),
rsnd_info_is_playback(priv, src), rsnd_info_is_playback(priv, src),
src->info->dma_id); src->info->dma_id);
if (ret < 0) if (ret)
dev_err(dev, "%s[%d] (Gen2) failed\n", goto rsnd_src_probe_gen2_fail;
rsnd_mod_name(mod), rsnd_mod_id(mod));
else
dev_dbg(dev, "%s[%d] (Gen2) is probed\n", dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
rsnd_mod_name(mod), rsnd_mod_id(mod)); rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret; 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;
} }
static int rsnd_src_remove_gen2(struct rsnd_mod *mod, static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
...@@ -635,27 +774,21 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, ...@@ -635,27 +774,21 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
static int rsnd_src_start_gen2(struct rsnd_mod *mod, static int rsnd_src_start_gen2(struct rsnd_mod *mod,
struct rsnd_dai *rdai) struct rsnd_dai *rdai)
{ {
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); rsnd_dma_start(rsnd_mod_to_dma(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_mod_write(mod, SRC_CTRL, val); return _rsnd_src_start_gen2(mod);
return rsnd_src_start(mod);
} }
static int rsnd_src_stop_gen2(struct rsnd_mod *mod, static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
struct rsnd_dai *rdai) 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 = { static struct rsnd_mod_ops rsnd_src_gen2_ops = {
...@@ -681,10 +814,11 @@ static void rsnd_of_parse_src(struct platform_device *pdev, ...@@ -681,10 +814,11 @@ static void rsnd_of_parse_src(struct platform_device *pdev,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
struct device_node *src_node; struct device_node *src_node;
struct device_node *np;
struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct rsnd_src_platform_info *src_info; struct rsnd_src_platform_info *src_info;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int nr; int nr, i;
if (!of_data) if (!of_data)
return; return;
...@@ -708,6 +842,13 @@ static void rsnd_of_parse_src(struct platform_device *pdev, ...@@ -708,6 +842,13 @@ static void rsnd_of_parse_src(struct platform_device *pdev,
info->src_info = src_info; info->src_info = src_info;
info->src_info_nr = nr; 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: rsnd_of_parse_src_end:
of_node_put(src_node); of_node_put(src_node);
} }
......
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