diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2013-07-22 08:36:35 +0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-07-28 22:34:09 +0400 |
commit | 07539c1de82cdc0ecbe72b413762b2e920407227 (patch) | |
tree | fce4f8c1d8f1cdee3b2479b6c1c2ab666074059f | |
parent | 3337744ac41bee00b0068ad5f926dd9c27540809 (diff) | |
download | linux-07539c1de82cdc0ecbe72b413762b2e920407227.tar.xz |
ASoC: add Renesas R-Car SCU 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)
This patch adds SCU feature on this driver.
But, it defines SCU style only, does nothing at this point.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r-- | include/sound/rcar_snd.h | 11 | ||||
-rw-r--r-- | sound/soc/sh/rcar/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/sh/rcar/core.c | 5 | ||||
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 95 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 21 | ||||
-rw-r--r-- | sound/soc/sh/rcar/scu.c | 125 |
6 files changed, 258 insertions, 3 deletions
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 14942a827fe5..01f2e453dcbf 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -14,8 +14,15 @@ #include <linux/sh_clk.h> +#define RSND_GEN1_SRU 0 -#define RSND_BASE_MAX 0 +#define RSND_GEN2_SRU 0 + +#define RSND_BASE_MAX 1 + +struct rsnd_scu_platform_info { + u32 flags; +}; struct rsnd_dai_platform_info { int ssi_id_playback; @@ -34,6 +41,8 @@ struct rsnd_dai_platform_info { struct rcar_snd_info { u32 flags; + struct rsnd_scu_platform_info *scu_info; + int scu_info_nr; struct rsnd_dai_platform_info *dai_info; int dai_info_nr; int (*start)(int id); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index b2d313b1eb94..112b2cfd793b 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o -obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o +snd-soc-rcar-objs := core.o gen.o scu.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
\ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index bb8959f93a7d..02d736bb4f54 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -631,6 +631,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_scu_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -669,6 +673,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); rsnd_gen_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ec67a796eca2..2934c0d731c8 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -45,10 +45,105 @@ struct rsnd_gen { /* * Gen1 */ +static int rsnd_gen1_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); + struct rsnd_mod *mod; + int ret; + int id; + + /* + * Gen1 is created by SRU/SSI, and this SRU is base module of + * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) + * + * Easy image is.. + * Gen1 SRU = Gen2 SCU + SSIU + etc + * + * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is + * using fixed path. + * + * Then, SSI id = SCU id here + */ + + if (rsnd_dai_is_play(rdai, io)) + id = info->ssi_id_playback; + else + id = info->ssi_id_capture; + + /* SCU */ + mod = rsnd_scu_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + + return ret; +} + +static int rsnd_gen1_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod, *n; + int ret = 0; + + /* + * remove all mod from rdai + */ + for_each_rsnd_mod(mod, n, io) + ret |= rsnd_dai_disconnect(mod); + + return ret; +} + +static struct rsnd_gen_ops rsnd_gen1_ops = { + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + +#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ + do { \ + (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ + (g)->reg_map[RSND_REG_##i].offset_id = oi; \ + (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ + } while (0) + +static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +{ + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); +} + static int rsnd_gen1_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 = rsnd_priv_to_gen(priv); + struct resource *sru_res; + + /* + * map address + */ + sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); + if (!sru_res) { + dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); + return -ENODEV; + } + + gen->ops = &rsnd_gen1_ops; + + gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); + if (!gen->base[RSND_GEN1_SRU]) { + dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); + return -ENODEV; + } + + rsnd_gen1_reg_map_init(gen); + + dev_dbg(dev, "Gen1 device probed\n"); + dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, + gen->base[RSND_GEN1_SRU]); + return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 8cc36416da25..95a391ff0627 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -28,6 +28,10 @@ * see gen1/gen2 for detail */ enum rsnd_reg { + /* SRU/SCU */ + RSND_REG_SSI_MODE0, + RSND_REG_SSI_MODE1, + RSND_REG_MAX, }; @@ -173,6 +177,12 @@ struct rsnd_priv { void *gen; /* + * below value will be filled on rsnd_scu_probe() + */ + void *scu; + int scu_nr; + + /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv; @@ -184,4 +194,15 @@ struct rsnd_priv { #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +/* + * R-Car SCU + */ +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_scu_nr(priv) ((priv)->scu_nr) + #endif diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c new file mode 100644 index 000000000000..c12e65f240a1 --- /dev/null +++ b/sound/soc/sh/rcar/scu.c @@ -0,0 +1,125 @@ +/* + * Renesas R-Car SCU 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_scu { + struct rsnd_scu_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_mod_to_scu(_mod) \ + container_of((_mod), struct rsnd_scu, mod) + +#define for_each_rsnd_scu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_scu_nr(priv)) && \ + ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ + i++) + +static int rsnd_scu_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static struct rsnd_mod_ops rsnd_scu_ops = { + .name = "scu", + .init = rsnd_scu_init, + .quit = rsnd_scu_quit, + .start = rsnd_scu_start, + .stop = rsnd_scu_stop, +}; + +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); + + return &((struct rsnd_scu *)(priv->scu) + id)->mod; +} + +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_scu *scu; + int i, nr; + + /* + * init SCU + */ + nr = info->scu_info_nr; + scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); + if (!scu) { + dev_err(dev, "SCU allocate failed\n"); + return -ENOMEM; + } + + priv->scu_nr = nr; + priv->scu = scu; + + for_each_rsnd_scu(scu, priv, i) { + rsnd_mod_init(priv, &scu->mod, + &rsnd_scu_ops, i); + scu->info = &info->scu_info[i]; + } + + dev_dbg(dev, "scu probed\n"); + + return 0; +} + +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} |