| From 6af0566e6b321dbf32134a6b8663ba571db6b9eb Mon Sep 17 00:00:00 2001 |
| From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Date: Sun, 21 Jul 2013 21:36:21 -0700 |
| Subject: ASoC: add Renesas R-Car Generation feature |
| |
| 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> |
| Signed-off-by: Mark Brown <broonie@linaro.org> |
| (cherry picked from commit 3337744ac41bee00b0068ad5f926dd9c27540809) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| 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 7272b2ea7108..14942a827fe5 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 cd8089f20c94..b2d313b1eb94 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 a47fda2aa600..bb8959f93a7d 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 000000000000..ec67a796eca2 |
| --- /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 65d3835cffbc..8cc36416da25 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; |
| -- |
| 1.8.5.rc3 |
| |