summaryrefslogtreecommitdiff
path: root/sound/soc/sh/rcar/ssiu.c
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2018-11-06 08:21:08 +0300
committerMark Brown <broonie@kernel.org>2018-11-06 20:40:10 +0300
commit4e7788fb8018afbb2e34e9d59b59baea30f5b045 (patch)
tree63a350884c53d551f7f7b692668b7f035261b7ca /sound/soc/sh/rcar/ssiu.c
parentda48a6eb82ea2eaa26464f863f0f39908727bfdb (diff)
downloadlinux-4e7788fb8018afbb2e34e9d59b59baea30f5b045.tar.xz
ASoC: rsnd: add SSIU BUSIF support
Gen2 has BUSIF0-3, Gen3 has BUSIF0-7 on some SSIU. Current driver is assuming it is using BUSIF0 as default. Thus, SSI is attaching SSIU (with BUSIF0) by using rsnd_ssiu_attach(). But, TDM split mode also needs other BUSIF to use it. This patch adds missing SSIU BUSIFx support. BUSIF is handled by SSIU instead of SSI anymore. Thus, its settings no longer needed on SSI node on DT. This patch removes its settings from Document, but driver is still keeping compatibility. Thus, old DT style is still working. But, to avoid confusing, it doesn't indicate old compatibility things on Document. New SoC should have SSIU on DT from this patch. 1) old style DT is still supported (= no rcar_sound,ssiu node on DT) 2) If ssiu is not indicated on playback/capture, BUSIF0 will be used as default playback = <&ssi3>; /* ssiu30 will be selected */ 3) you can select own ssiu playback = <&ssi32 &ssi3>; /* ssiu32 will be selected */ Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/sh/rcar/ssiu.c')
-rw-r--r--sound/soc/sh/rcar/ssiu.c178
1 files changed, 166 insertions, 12 deletions
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 4f591008cfe5..0609a0c5f9f9 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -12,6 +12,8 @@ struct rsnd_ssiu {
struct rsnd_mod mod;
u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */
unsigned int usrcnt;
+ int id;
+ int id_sub;
};
#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
@@ -22,12 +24,29 @@ struct rsnd_ssiu {
((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \
i++)
+/*
+ * SSI Gen2 Gen3
+ * 0 BUSIF0-3 BUSIF0-7
+ * 1 BUSIF0-3 BUSIF0-7
+ * 2 BUSIF0-3 BUSIF0-7
+ * 3 BUSIF0 BUSIF0-7
+ * 4 BUSIF0 BUSIF0-7
+ * 5 BUSIF0 BUSIF0
+ * 6 BUSIF0 BUSIF0
+ * 7 BUSIF0 BUSIF0
+ * 8 BUSIF0 BUSIF0
+ * 9 BUSIF0-3 BUSIF0-7
+ * total 22 52
+ */
+static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 };
+static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
+
static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
enum rsnd_mod_type type)
{
struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
return &ssiu->busif_status[busif];
}
@@ -159,7 +178,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
if (rsnd_ssi_use_busif(io)) {
int id = rsnd_mod_id(mod);
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
/*
* FIXME
@@ -252,7 +271,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
if (!rsnd_ssi_use_busif(io))
return 0;
@@ -270,7 +289,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
if (!rsnd_ssi_use_busif(io))
return 0;
@@ -286,8 +305,49 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
return 0;
}
+static int rsnd_ssiu_id(struct rsnd_mod *mod)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+
+ /* see rsnd_ssiu_probe() */
+ return ssiu->id;
+}
+
+static int rsnd_ssiu_id_sub(struct rsnd_mod *mod)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+
+ /* see rsnd_ssiu_probe() */
+ return ssiu->id_sub;
+}
+
+static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ int is_play = rsnd_io_is_play(io);
+ char *name;
+
+ /*
+ * It should use "rcar_sound,ssiu" on DT.
+ * But, we need to keep compatibility for old version.
+ *
+ * If it has "rcar_sound.ssiu", it will be used.
+ * If not, "rcar_sound.ssi" will be used.
+ * see
+ * rsnd_ssi_dma_req()
+ * rsnd_dma_of_path()
+ */
+
+ name = is_play ? "rx" : "tx";
+
+ return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
+ mod, name);
+}
+
static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
.name = SSIU_NAME,
+ .dma_req = rsnd_ssiu_dma_req,
.init = rsnd_ssiu_init_gen2,
.start = rsnd_ssiu_start_gen2,
.stop = rsnd_ssiu_stop_gen2,
@@ -302,26 +362,83 @@ static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
}
-int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *ssi_mod)
+static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *mod;
+ struct rsnd_ssiu *ssiu;
+ int i;
+
+ if (!ssi_mod)
+ return;
+
+ /* select BUSIF0 */
+ for_each_rsnd_ssiu(ssiu, priv, i) {
+ mod = rsnd_mod_get(ssiu);
+
+ if ((rsnd_mod_id(ssi_mod) == rsnd_ssiu_id(mod)) &&
+ (rsnd_mod_id_sub(mod) == 0))
+ rsnd_dai_connect(mod, io, mod->type);
+ }
+}
+
+void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
+ struct device_node *playback,
+ struct device_node *capture)
{
- struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod));
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device_node *node = rsnd_ssiu_of_node(priv);
+ struct device_node *np;
+ struct rsnd_mod *mod;
+ struct rsnd_dai_stream *io_p = &rdai->playback;
+ struct rsnd_dai_stream *io_c = &rdai->capture;
+ int i;
- rsnd_mod_confirm_ssi(ssi_mod);
+ /* use rcar_sound,ssiu if exist */
+ if (node) {
+ i = 0;
+ for_each_child_of_node(node, np) {
+ mod = rsnd_ssiu_mod_get(priv, i);
+ if (np == playback)
+ rsnd_dai_connect(mod, io_p, mod->type);
+ if (np == capture)
+ rsnd_dai_connect(mod, io_c, mod->type);
+ i++;
+ }
- return rsnd_dai_connect(mod, io, mod->type);
+ of_node_put(node);
+ }
+
+ /* Keep DT compatibility */
+ if (!rsnd_io_to_mod_ssiu(io_p))
+ rsnd_parse_connect_ssiu_compatible(priv, io_p);
+ if (!rsnd_io_to_mod_ssiu(io_c))
+ rsnd_parse_connect_ssiu_compatible(priv, io_c);
}
int rsnd_ssiu_probe(struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *node;
struct rsnd_ssiu *ssiu;
struct rsnd_mod_ops *ops;
+ const int *list = NULL;
int i, nr, ret;
- /* same number to SSI */
- nr = priv->ssi_nr;
+ /*
+ * Keep DT compatibility.
+ * if it has "rcar_sound,ssiu", use it.
+ * if not, use "rcar_sound,ssi"
+ * see
+ * rsnd_ssiu_bufsif_to_id()
+ */
+ node = rsnd_ssiu_of_node(priv);
+ if (node)
+ nr = of_get_child_count(node);
+ else
+ nr = priv->ssi_nr;
+
ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
if (!ssiu)
return -ENOMEM;
@@ -334,7 +451,44 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
else
ops = &rsnd_ssiu_ops_gen2;
+ /* Keep compatibility */
+ nr = 0;
+ if ((node) &&
+ (ops == &rsnd_ssiu_ops_gen2)) {
+ ops->id = rsnd_ssiu_id;
+ ops->id_sub = rsnd_ssiu_id_sub;
+
+ if (rsnd_is_gen2(priv)) {
+ list = gen2_id;
+ nr = ARRAY_SIZE(gen2_id);
+ } else if (rsnd_is_gen3(priv)) {
+ list = gen3_id;
+ nr = ARRAY_SIZE(gen3_id);
+ } else {
+ dev_err(dev, "unknown SSIU\n");
+ return -ENODEV;
+ }
+ }
+
for_each_rsnd_ssiu(ssiu, priv, i) {
+ if (node) {
+ int j;
+
+ /*
+ * see
+ * rsnd_ssiu_get_id()
+ * rsnd_ssiu_get_id_sub()
+ */
+ for (j = 0; j < nr; j++) {
+ if (list[j] > i)
+ break;
+ ssiu->id = j;
+ ssiu->id_sub = i - list[ssiu->id];
+ }
+ } else {
+ ssiu->id = i;
+ }
+
ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
ops, NULL, RSND_MOD_SSIU, i);
if (ret)