This patch adds CTU (Channel Transfer Unit) support for rsnd driver.
But, it does nothing to data at this point, but is required for MIX
support.
CTU design is a little different from other IPs (CTU0 is including
CTU00 - CTU03, and CTU1 is including CTU10 - CTU13, these have different
register mapping) We need to care about it on this driver.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tested-by: Keita Kobayashi <keita.kobayashi.ym@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
- rcar_sound,src : Should contain SRC feature.
The number of SRC subnode should be same as HW.
see below for detail.
+- rcar_sound,ctu : Should contain CTU feature.
+ The number of CTU 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.
};
};
+ rcar_sound,ctu {
+ ctu00: ctu@0 { };
+ ctu01: ctu@1 { };
+ ctu02: ctu@2 { };
+ ctu03: ctu@3 { };
+ ctu10: ctu@4 { };
+ ctu11: ctu@5 { };
+ ctu12: ctu@6 { };
+ ctu13: ctu@7 { };
+ };
+
rcar_sound,src {
src0: src@0 {
interrupts = <0 352 IRQ_TYPE_LEVEL_HIGH>;
/*
* flags
*/
+struct rsnd_ctu_platform_info {
+ u32 flags;
+};
+
struct rsnd_dvc_platform_info {
u32 flags;
};
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_dvc_platform_info *dvc;
};
int ssi_info_nr;
struct rsnd_src_platform_info *src_info;
int src_info_nr;
+ struct rsnd_ctu_platform_info *ctu_info;
+ int ctu_info_nr;
struct rsnd_dvc_platform_info *dvc_info;
int dvc_info_nr;
struct rsnd_dai_platform_info *dai_info;
-snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o dvc.o
+snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o dvc.o
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
snd-soc-rsrc-card-objs := rsrc-card.o
if (ret < 0)
return ret;
+ /* CTU */
+ ret = rsnd_path_add(priv, io, ctu);
+ if (ret < 0)
+ return ret;
+
/* DVC */
ret = rsnd_path_add(priv, io, dvc);
if (ret < 0)
struct device_node *dai_node, *dai_np;
struct device_node *ssi_node, *ssi_np;
struct device_node *src_node, *src_np;
+ struct device_node *ctu_node, *ctu_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, dvc_i;
+ int dai_i, ssi_i, src_i, ctu_i, dvc_i;
if (!of_data)
return;
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");
dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
#define mod_parse(name) \
mod_parse(ssi);
mod_parse(src);
+ mod_parse(ctu);
mod_parse(dvc);
of_node_put(playback);
rsnd_dma_probe,
rsnd_ssi_probe,
rsnd_src_probe,
+ rsnd_ctu_probe,
rsnd_dvc_probe,
rsnd_adg_probe,
rsnd_dai_probe,
struct rsnd_priv *priv) = {
rsnd_ssi_remove,
rsnd_src_remove,
+ rsnd_ctu_remove,
rsnd_dvc_remove,
};
int ret = 0, i;
--- /dev/null
+/*
+ * ctu.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 CTU_NAME_SIZE 16
+#define CTU_NAME "ctu"
+
+struct rsnd_ctu {
+ struct rsnd_ctu_platform_info *info; /* rcar_snd.h */
+ struct rsnd_mod mod;
+};
+
+#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
+#define for_each_rsnd_ctu(pos, priv, i) \
+ for ((i) = 0; \
+ ((i) < rsnd_ctu_nr(priv)) && \
+ ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
+ i++)
+
+#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1)
+#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
+static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
+{
+ rsnd_mod_write(mod, CTU_CTUIR, enable);
+}
+
+static int rsnd_ctu_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ rsnd_mod_hw_start(mod);
+
+ rsnd_ctu_initialize_lock(mod);
+
+ rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io));
+
+ rsnd_ctu_initialize_unlock(mod);
+
+ return 0;
+}
+
+static int rsnd_ctu_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_ctu_ops = {
+ .name = CTU_NAME,
+ .init = rsnd_ctu_init,
+ .quit = rsnd_ctu_quit,
+};
+
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
+{
+ if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
+ id = 0;
+
+ return &((struct rsnd_ctu *)(priv->ctu) + id)->mod;
+}
+
+void rsnd_of_parse_ctu(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct rsnd_ctu_platform_info *ctu_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,ctu");
+ if (!node)
+ return;
+
+ nr = of_get_child_count(node);
+ if (!nr)
+ goto rsnd_of_parse_ctu_end;
+
+ ctu_info = devm_kzalloc(dev,
+ sizeof(struct rsnd_ctu_platform_info) * nr,
+ GFP_KERNEL);
+ if (!ctu_info) {
+ dev_err(dev, "ctu info allocation error\n");
+ goto rsnd_of_parse_ctu_end;
+ }
+
+ info->ctu_info = ctu_info;
+ info->ctu_info_nr = nr;
+
+rsnd_of_parse_ctu_end:
+ of_node_put(node);
+
+}
+
+int rsnd_ctu_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_ctu *ctu;
+ struct clk *clk;
+ char name[CTU_NAME_SIZE];
+ int i, nr, ret;
+
+ /* This driver doesn't support Gen1 at this point */
+ if (rsnd_is_gen1(priv)) {
+ dev_warn(dev, "CTU is not supported on Gen1\n");
+ return -EINVAL;
+ }
+
+ rsnd_of_parse_ctu(pdev, of_data, priv);
+
+ nr = info->ctu_info_nr;
+ if (!nr)
+ return 0;
+
+ ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL);
+ if (!ctu)
+ return -ENOMEM;
+
+ priv->ctu_nr = nr;
+ priv->ctu = ctu;
+
+ for_each_rsnd_ctu(ctu, priv, i) {
+ /*
+ * CTU00, CTU01, CTU02, CTU03 => CTU0
+ * CTU10, CTU11, CTU12, CTU13 => CTU1
+ */
+ snprintf(name, CTU_NAME_SIZE, "%s.%d",
+ CTU_NAME, i / 4);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ctu->info = &info->ctu_info[i];
+
+ ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops,
+ clk, RSND_MOD_CTU, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void rsnd_ctu_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ctu *ctu;
+ int i;
+
+ for_each_rsnd_ctu(ctu, priv, i) {
+ rsnd_mod_quit(&ctu->mod);
+ }
+}
phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
int use_src = !!rsnd_io_to_mod_src(io);
- int use_dvc = !!rsnd_io_to_mod_dvc(io);
+ int use_cmd = !!rsnd_io_to_mod_dvc(io) ||
+ !!rsnd_io_to_mod_ctu(io);
int id = rsnd_mod_id(mod);
struct dma_addr {
dma_addr_t out_addr;
};
/* it shouldn't happen */
- if (use_dvc && !use_src)
+ if (use_cmd && !use_src)
dev_err(dev, "DVC is selected without SRC\n");
/* use SSIU or SSI ? */
is_ssi++;
return (is_from) ?
- dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr :
- dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
+ dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr :
+ dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr;
}
static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
struct rsnd_mod *this = rsnd_dma_to_mod(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 *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mod[MOD_MAX];
struct rsnd_mod *mod_start, *mod_end;
if (src) {
mod[i] = src;
src = NULL;
+ } else if (ctu) {
+ mod[i] = ctu;
+ ctu = NULL;
} else if (dvc) {
mod[i] = dvc;
dvc = NULL;
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
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(DVC_SWRSR, 0xe00, 0x100),
RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100),
RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
RSND_REG_SCU_SYS_STATUS0,
RSND_REG_SCU_SYS_INT_EN0,
RSND_REG_CMD_ROUTE_SLCT,
+ RSND_REG_CTU_CTUIR,
+ RSND_REG_CTU_ADINR,
RSND_REG_DVC_SWRSR,
RSND_REG_DVC_DVUIR,
RSND_REG_DVC_ADINR,
*/
enum rsnd_mod_type {
RSND_MOD_DVC = 0,
+ RSND_MOD_CTU,
RSND_MOD_SRC,
RSND_MOD_SSI,
RSND_MOD_MAX,
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
#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_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)))
int src_nr;
/*
+ * below value will be filled on rsnd_ctu_probe()
+ */
+ void *ctu;
+ int ctu_nr;
+
+ /*
* below value will be filled on rsnd_dvc_probe()
*/
void *dvc;
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod);
/*
+ * R-Car CTU
+ */
+int rsnd_ctu_probe(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv);
+
+void rsnd_ctu_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
+
+/*
* R-Car DVC
*/
int rsnd_dvc_probe(struct platform_device *pdev,