| From 5cfb0bb00963de809a1881cfd17161b5d2fd0448 Mon Sep 17 00:00:00 2001 |
| From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Date: Sun, 21 Jul 2013 21:36:35 -0700 |
| Subject: 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> |
| (cherry picked from commit 07539c1de82cdc0ecbe72b413762b2e920407227) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| include/sound/rcar_snd.h | 11 +++- |
| sound/soc/sh/rcar/Makefile | 4 +- |
| sound/soc/sh/rcar/core.c | 5 ++ |
| sound/soc/sh/rcar/gen.c | 95 ++++++++++++++++++++++++++++++++++ |
| sound/soc/sh/rcar/rsnd.h | 21 ++++++++ |
| sound/soc/sh/rcar/scu.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ |
| 6 files changed, 258 insertions(+), 3 deletions(-) |
| create mode 100644 sound/soc/sh/rcar/scu.c |
| |
| 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) |
| +{ |
| +} |
| -- |
| 1.8.5.rc3 |
| |