@@ -21,6 +21,9 @@ Required properties:
- rcar_sound,ctu : Should contain CTU feature.
The number of CTU subnode should be same as HW.
see below for detail.
+- rcar_sound,mix : Should contain MIX feature.
+ The number of MIX subnode should be same as HW.
+ see below for detail.
- rcar_sound,dvc : Should contain DVC feature.
The number of DVC subnode should be same as HW.
see below for detail.
@@ -93,6 +96,11 @@ rcar_sound: sound@ec500000 {
};
};
+ rcar_sound,mix {
+ mix0: mix@0 { };
+ mix1: mix@1 { };
+ };
+
rcar_sound,ctu {
ctu00: ctu@0 { };
ctu01: ctu@1 { };
@@ -65,6 +65,10 @@ struct rsnd_ctu_platform_info {
u32 flags;
};
+struct rsnd_mix_platform_info {
+ u32 flags;
+};
+
struct rsnd_dvc_platform_info {
u32 flags;
};
@@ -73,6 +77,7 @@ struct rsnd_dai_path_info {
struct rsnd_ssi_platform_info *ssi;
struct rsnd_src_platform_info *src;
struct rsnd_ctu_platform_info *ctu;
+ struct rsnd_mix_platform_info *mix;
struct rsnd_dvc_platform_info *dvc;
};
@@ -100,6 +105,8 @@ struct rcar_snd_info {
int src_info_nr;
struct rsnd_ctu_platform_info *ctu_info;
int ctu_info_nr;
+ struct rsnd_mix_platform_info *mix_info;
+ int mix_info_nr;
struct rsnd_dvc_platform_info *dvc_info;
int dvc_info_nr;
struct rsnd_dai_platform_info *dai_info;
@@ -1,4 +1,4 @@
-snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o dvc.o
+snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
snd-soc-rsrc-card-objs := rsrc-card.o
@@ -605,23 +605,74 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
void rsnd_path_parse(struct rsnd_priv *priv,
struct rsnd_dai_stream *io)
{
- struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
- int src_id = rsnd_mod_id(src);
- u32 path[] = {
- [0] = 0x30000,
- [1] = 0x30001,
- [2] = 0x40000,
- [3] = 0x10000,
- [4] = 0x20000,
- [5] = 0x40100
- };
+ struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
+ struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+ struct rsnd_mod *cmd;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ u32 data;
/* Gen1 is not supported */
if (rsnd_is_gen1(priv))
return;
- rsnd_mod_write(dvc, CMD_ROUTE_SLCT, path[src_id]);
+ if (!mix && !dvc)
+ return;
+
+ if (mix) {
+ struct rsnd_dai *rdai;
+ int i;
+ u32 path[] = {
+ [0] = 0,
+ [1] = 1 << 0,
+ [2] = 0,
+ [3] = 0,
+ [4] = 0,
+ [5] = 1 << 8
+ };
+
+ /*
+ * it is assuming that integrater is well understanding about
+ * data path. Here doesn't check impossible connection,
+ * like src2 + src5
+ */
+ data = 0;
+ for_each_rsnd_dai(rdai, priv, i) {
+ io = &rdai->playback;
+ if (mix == rsnd_io_to_mod_mix(io))
+ data |= path[rsnd_mod_id(src)];
+
+ io = &rdai->capture;
+ if (mix == rsnd_io_to_mod_mix(io))
+ data |= path[rsnd_mod_id(src)];
+ }
+
+ /*
+ * We can't use ctu = rsnd_io_ctu() here.
+ * Since, ID of dvc/mix are 0 or 1 (= same as CMD number)
+ * but ctu IDs are 0 - 7 (= CTU00 - CTU13)
+ */
+ cmd = mix;
+ } else {
+ u32 path[] = {
+ [0] = 0x30000,
+ [1] = 0x30001,
+ [2] = 0x40000,
+ [3] = 0x10000,
+ [4] = 0x20000,
+ [5] = 0x40100
+ };
+
+ data = path[rsnd_mod_id(src)];
+
+ cmd = dvc;
+ }
+
+ dev_dbg(dev, "ctu/mix path = 0x%08x", data);
+
+ rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data);
+
+ rsnd_mod_write(cmd, CMD_CTRL, 0x10);
}
static int rsnd_path_init(struct rsnd_priv *priv,
@@ -656,6 +707,11 @@ static int rsnd_path_init(struct rsnd_priv *priv,
if (ret < 0)
return ret;
+ /* MIX */
+ ret = rsnd_path_add(priv, io, mix);
+ if (ret < 0)
+ return ret;
+
/* DVC */
ret = rsnd_path_add(priv, io, dvc);
if (ret < 0)
@@ -672,13 +728,14 @@ static void rsnd_of_parse_dai(struct platform_device *pdev,
struct device_node *ssi_node, *ssi_np;
struct device_node *src_node, *src_np;
struct device_node *ctu_node, *ctu_np;
+ struct device_node *mix_node, *mix_np;
struct device_node *dvc_node, *dvc_np;
struct device_node *playback, *capture;
struct rsnd_dai_platform_info *dai_info;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = &pdev->dev;
int nr, i;
- int dai_i, ssi_i, src_i, ctu_i, dvc_i;
+ int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i;
if (!of_data)
return;
@@ -705,6 +762,7 @@ static void rsnd_of_parse_dai(struct platform_device *pdev,
ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
+ mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
#define mod_parse(name) \
@@ -742,6 +800,7 @@ if (name##_node) { \
mod_parse(ssi);
mod_parse(src);
mod_parse(ctu);
+ mod_parse(mix);
mod_parse(dvc);
of_node_put(playback);
@@ -1155,6 +1214,7 @@ static int rsnd_probe(struct platform_device *pdev)
rsnd_ssi_probe,
rsnd_src_probe,
rsnd_ctu_probe,
+ rsnd_mix_probe,
rsnd_dvc_probe,
rsnd_adg_probe,
rsnd_dai_probe,
@@ -1251,6 +1311,7 @@ static int rsnd_remove(struct platform_device *pdev)
rsnd_ssi_remove,
rsnd_src_remove,
rsnd_ctu_remove,
+ rsnd_mix_remove,
rsnd_dvc_remove,
};
int ret = 0, i;
@@ -427,6 +427,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
int use_src = !!rsnd_io_to_mod_src(io);
int use_cmd = !!rsnd_io_to_mod_dvc(io) ||
+ !!rsnd_io_to_mod_mix(io) ||
!!rsnd_io_to_mod_ctu(io);
int id = rsnd_mod_id(mod);
struct dma_addr {
@@ -506,6 +507,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
+ struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mod[MOD_MAX];
struct rsnd_mod *mod_start, *mod_end;
@@ -548,6 +550,9 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
} else if (ctu) {
mod[i] = ctu;
ctu = NULL;
+ } else if (mix) {
+ mod[i] = mix;
+ mix = NULL;
} else if (dvc) {
mod[i] = dvc;
dvc = NULL;
@@ -242,6 +242,16 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100),
RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100),
+ RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40),
+ RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40),
+ RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40),
+ RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40),
+ RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40),
+ RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40),
+ RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40),
+ RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40),
+ RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40),
+ RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40),
RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100),
RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100),
RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
new file mode 100644
@@ -0,0 +1,200 @@
+/*
+ * mix.c
+ *
+ * Copyright (c) 2015 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"
+
+#define MIX_NAME_SIZE 16
+#define MIX_NAME "mix"
+
+struct rsnd_mix {
+ struct rsnd_mix_platform_info *info; /* rcar_snd.h */
+ struct rsnd_mod mod;
+};
+
+#define rsnd_mix_nr(priv) ((priv)->mix_nr)
+#define for_each_rsnd_mix(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_mix_nr(priv)) && \
+ ((pos) = (struct rsnd_mix *)(priv)->mix + i); \
+ i++)
+
+
+static void rsnd_mix_soft_reset(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, MIX_SWRSR, 0);
+ rsnd_mod_write(mod, MIX_SWRSR, 1);
+}
+
+#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1)
+#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0)
+static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable)
+{
+ rsnd_mod_write(mod, MIX_MIXIR, enable);
+}
+
+static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+
+ /* Disable MIX dB setting */
+ rsnd_mod_write(mod, MIX_MDBER, 0);
+
+ rsnd_mod_write(mod, MIX_MDBAR, 0);
+ rsnd_mod_write(mod, MIX_MDBBR, 0);
+ rsnd_mod_write(mod, MIX_MDBCR, 0);
+ rsnd_mod_write(mod, MIX_MDBDR, 0);
+
+ /* Enable MIX dB setting */
+ rsnd_mod_write(mod, MIX_MDBER, 1);
+}
+
+static int rsnd_mix_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_hw_start(mod);
+
+ rsnd_mix_soft_reset(mod);
+
+ rsnd_mix_initialize_lock(mod);
+
+ rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
+
+ rsnd_path_parse(priv, io);
+
+ /* volume step */
+ rsnd_mod_write(mod, MIX_MIXMR, 0);
+ rsnd_mod_write(mod, MIX_MVPDR, 0);
+
+ rsnd_mix_volume_update(io, mod);
+
+ rsnd_mix_initialize_unlock(mod);
+
+ return 0;
+}
+
+static int rsnd_mix_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_hw_stop(mod);
+
+ return 0;
+}
+
+static struct rsnd_mod_ops rsnd_mix_ops = {
+ .name = MIX_NAME,
+ .init = rsnd_mix_init,
+ .quit = rsnd_mix_quit,
+};
+
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
+ id = 0;
+
+ return &((struct rsnd_mix *)(priv->mix) + id)->mod;
+}
+
+static void rsnd_of_parse_mix(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct rsnd_mix_platform_info *mix_info;
+ struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+ struct device *dev = &pdev->dev;
+ int nr;
+
+ if (!of_data)
+ return;
+
+ node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
+ if (!node)
+ return;
+
+ nr = of_get_child_count(node);
+ if (!nr)
+ goto rsnd_of_parse_mix_end;
+
+ mix_info = devm_kzalloc(dev,
+ sizeof(struct rsnd_mix_platform_info) * nr,
+ GFP_KERNEL);
+ if (!mix_info) {
+ dev_err(dev, "mix info allocation error\n");
+ goto rsnd_of_parse_mix_end;
+ }
+
+ info->mix_info = mix_info;
+ info->mix_info_nr = nr;
+
+rsnd_of_parse_mix_end:
+ of_node_put(node);
+
+}
+
+int rsnd_mix_probe(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mix *mix;
+ struct clk *clk;
+ char name[MIX_NAME_SIZE];
+ int i, nr, ret;
+
+ /* This driver doesn't support Gen1 at this point */
+ if (rsnd_is_gen1(priv)) {
+ dev_warn(dev, "MIX is not supported on Gen1\n");
+ return -EINVAL;
+ }
+
+ rsnd_of_parse_mix(pdev, of_data, priv);
+
+ nr = info->mix_info_nr;
+ if (!nr)
+ return 0;
+
+ mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
+ if (!mix)
+ return -ENOMEM;
+
+ priv->mix_nr = nr;
+ priv->mix = mix;
+
+ for_each_rsnd_mix(mix, priv, i) {
+ snprintf(name, MIX_NAME_SIZE, "%s.%d",
+ MIX_NAME, i);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ mix->info = &info->mix_info[i];
+
+ ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops,
+ clk, RSND_MOD_MIX, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void rsnd_mix_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_mix *mix;
+ int i;
+
+ for_each_rsnd_mix(mix, priv, i) {
+ rsnd_mod_quit(&mix->mod);
+ }
+}
@@ -49,6 +49,16 @@ enum rsnd_reg {
RSND_REG_CMD_ROUTE_SLCT,
RSND_REG_CTU_CTUIR,
RSND_REG_CTU_ADINR,
+ RSND_REG_MIX_SWRSR,
+ RSND_REG_MIX_MIXIR,
+ RSND_REG_MIX_ADINR,
+ RSND_REG_MIX_MIXMR,
+ RSND_REG_MIX_MVPDR,
+ RSND_REG_MIX_MDBAR,
+ RSND_REG_MIX_MDBBR,
+ RSND_REG_MIX_MDBCR,
+ RSND_REG_MIX_MDBDR,
+ RSND_REG_MIX_MDBER,
RSND_REG_DVC_SWRSR,
RSND_REG_DVC_DVUIR,
RSND_REG_DVC_ADINR,
@@ -222,6 +232,7 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
*/
enum rsnd_mod_type {
RSND_MOD_DVC = 0,
+ RSND_MOD_MIX,
RSND_MOD_CTU,
RSND_MOD_SRC,
RSND_MOD_SSI,
@@ -355,6 +366,7 @@ struct rsnd_dai_stream {
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
+#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX)
#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC)
#define rsnd_io_to_rdai(io) ((io)->rdai)
#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
@@ -473,6 +485,12 @@ struct rsnd_priv {
int ctu_nr;
/*
+ * below value will be filled on rsnd_mix_probe()
+ */
+ void *mix;
+ int mix_nr;
+
+ /*
* below value will be filled on rsnd_dvc_probe()
*/
void *dvc;
@@ -589,6 +607,17 @@ void rsnd_ctu_remove(struct platform_device *pdev,
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
/*
+ * R-Car MIX
+ */
+int rsnd_mix_probe(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv);
+
+void rsnd_mix_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
+
+/*
* R-Car DVC
*/
int rsnd_dvc_probe(struct platform_device *pdev,