Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips)
The main difference between Gen1 and Gen2 are 1) register offset, 2) data path
In order to control Gen1/Gen2 by same method, this patch adds gen.c.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v1 -> v2
- checkpatch - rsnd_bset has dev_dbg() - removed unused gen2 functions
include/sound/rcar_snd.h | 10 +++ sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/core.c | 58 ++++++++++++++++- sound/soc/sh/rcar/gen.c | 154 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 47 ++++++++++++++ 5 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sh/rcar/gen.c
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 7272b2e..14942a8 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -22,6 +22,16 @@ struct rsnd_dai_platform_info { int ssi_id_capture; };
+/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + struct rcar_snd_info { u32 flags; struct rsnd_dai_platform_info *dai_info; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index cd8089f..b2d313b 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o +snd-soc-rcar-objs := core.o gen.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a47fda2..bb8959f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -108,6 +108,50 @@
/* + * basic function + */ +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + + BUG_ON(!base); + + return ioread32(base); +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + + BUG_ON(!base); + + dev_dbg(dev, "w %p : %08x\n", base, data); + + iowrite32(data, base); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + u32 val; + + BUG_ON(!base); + + val = ioread32(base); + val &= ~mask; + val |= data & mask; + iowrite32(val, base); + + dev_dbg(dev, "s %p : %08x\n", base, val); +} + +/* * rsnd_mod functions */ char *rsnd_mod_name(struct rsnd_mod *mod) @@ -289,6 +333,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end;
+ ret = rsnd_gen_path_init(priv, rdai, io); + if (ret < 0) + goto dai_trigger_end; + ret = rsnd_dai_call(rdai, io, init); if (ret < 0) goto dai_trigger_end; @@ -306,10 +354,13 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end;
- ret = rsnd_platform_call(priv, dai, stop, ssi_id); + ret = rsnd_gen_path_exit(priv, rdai, io); if (ret < 0) goto dai_trigger_end;
+ ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; break; default: ret = -EINVAL; @@ -572,6 +623,10 @@ static int rsnd_probe(struct platform_device *pdev) /* * init each module */ + ret = rsnd_gen_probe(pdev, info, priv); + if (ret < 0) + return ret; + ret = rsnd_dai_probe(pdev, info, priv); if (ret < 0) return ret; @@ -615,6 +670,7 @@ static int rsnd_remove(struct platform_device *pdev) * remove each module */ rsnd_dai_remove(pdev, priv); + rsnd_gen_remove(pdev, priv);
return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c new file mode 100644 index 0000000..ec67a79 --- /dev/null +++ b/sound/soc/sh/rcar/gen.c @@ -0,0 +1,154 @@ +/* + * Renesas R-Car Gen1 SRU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto kuninori.morimoto.gx@renesas.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +struct rsnd_gen_ops { + int (*path_init)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*path_exit)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +}; + +struct rsnd_gen_reg_map { + int index; /* -1 : not supported */ + u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ + u32 offset_adr; /* offset of SSICR, SSISR, ... */ +}; + +struct rsnd_gen { + void __iomem *base[RSND_BASE_MAX]; + + struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; + struct rsnd_gen_ops *ops; +}; + +#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) + +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) + +/* + * Gen2 + * will be filled in the future + */ + +/* + * Gen1 + */ +static int rsnd_gen1_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + return 0; +} + +static void rsnd_gen1_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * Gen + */ +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_init(priv, rdai, io); +} + +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_exit(priv, rdai, io); +} + +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int index; + u32 offset_id, offset_adr; + + if (reg >= RSND_REG_MAX) { + dev_err(dev, "rsnd_reg reg error\n"); + return NULL; + } + + index = gen->reg_map[reg].index; + offset_id = gen->reg_map[reg].offset_id; + offset_adr = gen->reg_map[reg].offset_adr; + + if (index < 0) { + dev_err(dev, "unsupported reg access %d\n", reg); + return NULL; + } + + if (offset_id && mod) + offset_id *= rsnd_mod_id(mod); + + /* + * index/offset were set on gen1/gen2 + */ + + return gen->base[index] + offset_id + offset_adr; +} + +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen; + int i; + + gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); + if (!gen) { + dev_err(dev, "GEN allocate failed\n"); + return -ENOMEM; + } + + priv->gen = gen; + + /* + * see + * rsnd_reg_get() + * rsnd_gen_probe() + */ + for (i = 0; i < RSND_REG_MAX; i++) + gen->reg_map[i].index = -1; + + /* + * init each module + */ + if (rsnd_is_gen1(priv)) + return rsnd_gen1_probe(pdev, info, priv); + + dev_err(dev, "unknown generation R-Car sound device\n"); + + return -ENODEV; +} + +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + if (rsnd_is_gen1(priv)) + rsnd_gen1_remove(pdev, priv); +} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 65d3835..8cc3641 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -27,12 +27,36 @@ * This driver uses pseudo register in order to hide it. * see gen1/gen2 for detail */ +enum rsnd_reg { + RSND_REG_MAX, +}; + struct rsnd_priv; struct rsnd_mod; struct rsnd_dai; struct rsnd_dai_stream;
/* + * R-Car basic functions + */ +#define rsnd_mod_read(m, r) \ + rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) +#define rsnd_mod_write(m, r, d) \ + rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_bset(m, r, s, d) \ + rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) + +#define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) +#define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) +#define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) + +u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, + u32 mask, u32 data); + +/* * R-Car sound mod */
@@ -117,6 +141,24 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
/* + * R-Car Gen1/Gen2 + */ +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg); + +/* * R-Car sound priv */ struct rsnd_priv { @@ -126,6 +168,11 @@ struct rsnd_priv { spinlock_t lock;
/* + * below value will be filled on rsnd_gen_probe() + */ + void *gen; + + /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv;