diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index b6b3a786855f..278607de05de 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -18,6 +18,9 @@ Required properties: - 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. @@ -90,6 +93,17 @@ rcar_sound: sound@ec500000 { }; }; + 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>; diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 4cecd0c175f6..8f9303093ab9 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -61,6 +61,10 @@ struct rsnd_src_platform_info { /* * flags */ +struct rsnd_ctu_platform_info { + u32 flags; +}; + struct rsnd_dvc_platform_info { u32 flags; }; @@ -68,6 +72,7 @@ struct rsnd_dvc_platform_info { 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; }; @@ -93,6 +98,8 @@ struct rcar_snd_info { 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; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 3a274fd3593c..7c4730a81c4a 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,4 +1,4 @@ -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 diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index e20d8ea0aafe..63ae7bbfd1dc 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -651,6 +651,11 @@ static int rsnd_path_init(struct rsnd_priv *priv, 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) @@ -666,13 +671,14 @@ static void rsnd_of_parse_dai(struct platform_device *pdev, 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; @@ -698,6 +704,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"); dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc"); #define mod_parse(name) \ @@ -734,6 +741,7 @@ if (name##_node) { \ mod_parse(ssi); mod_parse(src); + mod_parse(ctu); mod_parse(dvc); of_node_put(playback); @@ -1146,6 +1154,7 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_dma_probe, rsnd_ssi_probe, rsnd_src_probe, + rsnd_ctu_probe, rsnd_dvc_probe, rsnd_adg_probe, rsnd_dai_probe, @@ -1241,6 +1250,7 @@ static int rsnd_remove(struct platform_device *pdev) struct rsnd_priv *priv) = { rsnd_ssi_remove, rsnd_src_remove, + rsnd_ctu_remove, rsnd_dvc_remove, }; int ret = 0, i; diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c new file mode 100644 index 000000000000..05edd2009b81 --- /dev/null +++ b/sound/soc/sh/rcar/ctu.c @@ -0,0 +1,171 @@ +/* + * ctu.c + * + * Copyright (c) 2015 Kuninori Morimoto + * + * 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); + } +} diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 23282f48f71f..229b68d2cf70 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -426,7 +426,8 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, 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; @@ -464,7 +465,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, }; /* 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 ? */ @@ -472,8 +473,8 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, 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, @@ -504,6 +505,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, 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; @@ -543,6 +545,9 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, if (src) { mod[i] = src; src = NULL; + } else if (ctu) { + mod[i] = ctu; + ctu = NULL; } else if (dvc) { mod[i] = dvc; dvc = NULL; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index a2d5df4d5d17..41b75cd4e09b 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -240,6 +240,8 @@ static int rsnd_gen2_probe(struct platform_device *pdev, 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), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 7fee2079ba5a..f2128a7cf259 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -47,6 +47,8 @@ enum rsnd_reg { 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, @@ -220,6 +222,7 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, */ enum rsnd_mod_type { RSND_MOD_DVC = 0, + RSND_MOD_CTU, RSND_MOD_SRC, RSND_MOD_SSI, RSND_MOD_MAX, @@ -351,6 +354,7 @@ struct rsnd_dai_stream { #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))) @@ -462,6 +466,12 @@ struct rsnd_priv { void *src; 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() */ @@ -567,6 +577,17 @@ int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod); 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 */