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>
This commit is contained in:
parent
3337744ac4
commit
07539c1de8
6 changed files with 258 additions and 3 deletions
|
@ -14,8 +14,15 @@
|
||||||
|
|
||||||
#include <linux/sh_clk.h>
|
#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 {
|
struct rsnd_dai_platform_info {
|
||||||
int ssi_id_playback;
|
int ssi_id_playback;
|
||||||
|
@ -34,6 +41,8 @@ struct rsnd_dai_platform_info {
|
||||||
|
|
||||||
struct rcar_snd_info {
|
struct rcar_snd_info {
|
||||||
u32 flags;
|
u32 flags;
|
||||||
|
struct rsnd_scu_platform_info *scu_info;
|
||||||
|
int scu_info_nr;
|
||||||
struct rsnd_dai_platform_info *dai_info;
|
struct rsnd_dai_platform_info *dai_info;
|
||||||
int dai_info_nr;
|
int dai_info_nr;
|
||||||
int (*start)(int id);
|
int (*start)(int id);
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
snd-soc-rcar-objs := core.o gen.o
|
snd-soc-rcar-objs := core.o gen.o scu.o
|
||||||
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
|
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
|
|
@ -631,6 +631,10 @@ static int rsnd_probe(struct platform_device *pdev)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = rsnd_scu_probe(pdev, info, priv);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* asoc register
|
* asoc register
|
||||||
*/
|
*/
|
||||||
|
@ -669,6 +673,7 @@ static int rsnd_remove(struct platform_device *pdev)
|
||||||
/*
|
/*
|
||||||
* remove each module
|
* remove each module
|
||||||
*/
|
*/
|
||||||
|
rsnd_scu_remove(pdev, priv);
|
||||||
rsnd_dai_remove(pdev, priv);
|
rsnd_dai_remove(pdev, priv);
|
||||||
rsnd_gen_remove(pdev, priv);
|
rsnd_gen_remove(pdev, priv);
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,105 @@ struct rsnd_gen {
|
||||||
/*
|
/*
|
||||||
* Gen1
|
* 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,
|
static int rsnd_gen1_probe(struct platform_device *pdev,
|
||||||
struct rcar_snd_info *info,
|
struct rcar_snd_info *info,
|
||||||
struct rsnd_priv *priv)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,10 @@
|
||||||
* see gen1/gen2 for detail
|
* see gen1/gen2 for detail
|
||||||
*/
|
*/
|
||||||
enum rsnd_reg {
|
enum rsnd_reg {
|
||||||
|
/* SRU/SCU */
|
||||||
|
RSND_REG_SSI_MODE0,
|
||||||
|
RSND_REG_SSI_MODE1,
|
||||||
|
|
||||||
RSND_REG_MAX,
|
RSND_REG_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -172,6 +176,12 @@ struct rsnd_priv {
|
||||||
*/
|
*/
|
||||||
void *gen;
|
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()
|
* below value will be filled on rsnd_dai_probe()
|
||||||
*/
|
*/
|
||||||
|
@ -184,4 +194,15 @@ struct rsnd_priv {
|
||||||
#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
|
#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
|
||||||
#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&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
|
#endif
|
||||||
|
|
125
sound/soc/sh/rcar/scu.c
Normal file
125
sound/soc/sh/rcar/scu.c
Normal file
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue