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
@ -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);
|
||||
|
@ -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
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
@ -172,6 +176,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()
|
||||
*/
|
||||
@ -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
|
||||
|
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…
Reference in New Issue
Block a user