blob: a75be9bcfa82721f08813101bf074e53dda2af99 [file] [log] [blame]
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