From 8d91c1e411f55d7ea91b1183a2e9f8088fb4d5be Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Tue, 15 Dec 2015 14:52:16 -0300 Subject: Linux-libre 4.3.2-gnu --- sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/core.c | 195 +++++++++++++++++++++++++++++++++++----- sound/soc/sh/rcar/ctu.c | 171 ++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/dma.c | 128 ++++++++++++++++++--------- sound/soc/sh/rcar/dvc.c | 73 +++++++-------- sound/soc/sh/rcar/gen.c | 33 ++++++- sound/soc/sh/rcar/mix.c | 200 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 111 ++++++++++++++++------- sound/soc/sh/rcar/rsrc-card.c | 22 ++++- sound/soc/sh/rcar/src.c | 117 +++++++++++++----------- sound/soc/sh/rcar/ssi.c | 4 +- 11 files changed, 858 insertions(+), 198 deletions(-) create mode 100644 sound/soc/sh/rcar/ctu.c create mode 100644 sound/soc/sh/rcar/mix.c (limited to 'sound/soc/sh/rcar') diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index f1b445173..8b258501a 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 src.o adg.o ssi.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 diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f1e592065..f3feed5ce 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -203,9 +203,9 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) } /* - * settting function + * ADINR function */ -u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); @@ -227,6 +227,64 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) return adinr; } +u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct device *dev = rsnd_priv_to_dev(priv); + u32 chan = runtime->channels; + + switch (chan) { + case 1: + case 2: + case 4: + case 6: + case 8: + break; + default: + dev_warn(dev, "not supported channel\n"); + chan = 0; + break; + } + + return chan; +} + +/* + * DALIGN function + */ +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *target = src ? src : ssi; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 val = 0x76543210; + u32 mask = ~0; + + mask <<= runtime->channels * 4; + val = val & mask; + + switch (runtime->sample_bits) { + case 16: + val |= 0x67452301 & ~mask; + break; + case 32: + val |= 0x76543210 & ~mask; + break; + } + + /* + * exchange channeles on SRC if possible, + * otherwise, R/L volume settings on DVC + * changes inverted channels + */ + if (mod == target) + return val; + else + return 0x76543210; +} + /* * rsnd_dai functions */ @@ -242,9 +300,9 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) if (val == __rsnd_mod_call_##func) { \ called = 1; \ ret = (mod)->ops->func(mod, io, param); \ - mod->status = (mod->status & ~mask) + \ - (add << __rsnd_mod_shift_##func); \ } \ + mod->status = (mod->status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d] 0x%08x %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \ called ? #func : ""); \ @@ -274,21 +332,21 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) static int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { + struct rsnd_priv *priv; + struct device *dev; + if (!mod) return -EIO; - if (io->mod[mod->type]) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_err(dev, "%s[%d] is not empty\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod)); - return -EIO; - } + priv = rsnd_mod_to_priv(mod); + dev = rsnd_priv_to_dev(priv); io->mod[mod->type] = mod; + dev_dbg(dev, "%s[%d] is connected to io (%s)\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_io_is_play(io) ? "Playback" : "Capture"); + return 0; } @@ -517,7 +575,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .set_fmt = rsnd_soc_dai_set_fmt, }; -#define rsnd_path_parse(priv, io, type) \ +#define rsnd_path_add(priv, io, type) \ ({ \ struct rsnd_mod *mod; \ int ret = 0; \ @@ -533,7 +591,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { ret; \ }) -#define rsnd_path_break(priv, io, type) \ +#define rsnd_path_remove(priv, io, type) \ { \ struct rsnd_mod *mod; \ int id = -1; \ @@ -547,6 +605,79 @@ 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 *dvc = rsnd_io_to_mod_dvc(io); + 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; + + 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, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -564,18 +695,28 @@ static int rsnd_path_init(struct rsnd_priv *priv, * using fixed path. */ + /* SSI */ + ret = rsnd_path_add(priv, io, ssi); + if (ret < 0) + return ret; + /* SRC */ - ret = rsnd_path_parse(priv, io, src); + ret = rsnd_path_add(priv, io, src); if (ret < 0) return ret; - /* SSI */ - ret = rsnd_path_parse(priv, io, ssi); + /* CTU */ + ret = rsnd_path_add(priv, io, ctu); + if (ret < 0) + return ret; + + /* MIX */ + ret = rsnd_path_add(priv, io, mix); if (ret < 0) return ret; /* DVC */ - ret = rsnd_path_parse(priv, io, dvc); + ret = rsnd_path_add(priv, io, dvc); if (ret < 0) return ret; @@ -589,13 +730,15 @@ 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 *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, dvc_i; + int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i; if (!of_data) return; @@ -621,6 +764,8 @@ 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) \ @@ -657,6 +802,8 @@ if (name##_node) { \ mod_parse(ssi); mod_parse(src); + mod_parse(ctu); + mod_parse(mix); mod_parse(dvc); of_node_put(playback); @@ -1033,8 +1180,8 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, /* * remove SRC/DVC from DAI, */ - rsnd_path_break(priv, io, src); - rsnd_path_break(priv, io, dvc); + rsnd_path_remove(priv, io, src); + rsnd_path_remove(priv, io, dvc); /* * fallback @@ -1069,6 +1216,8 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_dma_probe, rsnd_ssi_probe, rsnd_src_probe, + rsnd_ctu_probe, + rsnd_mix_probe, rsnd_dvc_probe, rsnd_adg_probe, rsnd_dai_probe, @@ -1164,6 +1313,8 @@ static int rsnd_remove(struct platform_device *pdev) struct rsnd_priv *priv) = { rsnd_ssi_remove, rsnd_src_remove, + rsnd_ctu_remove, + rsnd_mix_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 000000000..05498bba5 --- /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; +} + +static 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 d306e298c..bfbb8a5e9 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -27,6 +27,15 @@ struct rsnd_dma_ctrl { int dmapp_num; }; +struct rsnd_dma_ops { + char *name; + void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); + void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); +}; + #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) /* @@ -168,7 +177,7 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io, dma_cap_set(DMA_SLAVE, mask); dmaen->chan = dma_request_channel(mask, shdma_chan_filter, - (void *)id); + (void *)(uintptr_t)id); } if (IS_ERR_OR_NULL(dmaen->chan)) { dmaen->chan = NULL; @@ -182,7 +191,8 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io, cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dev_dbg(dev, "dma : %pad -> %pad\n", + dev_dbg(dev, "%s %pad -> %pad\n", + dma->ops->name, &cfg.src_addr, &cfg.dst_addr); ret = dmaengine_slave_config(dmaen->chan, &cfg); @@ -215,6 +225,7 @@ static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) } static struct rsnd_dma_ops rsnd_dmaen_ops = { + .name = "audmac", .start = rsnd_dmaen_start, .stop = rsnd_dmaen_stop, .init = rsnd_dmaen_init, @@ -360,6 +371,7 @@ static int rsnd_dmapp_init(struct rsnd_dai_stream *io, } static struct rsnd_dma_ops rsnd_dmapp_ops = { + .name = "audmac-pp", .start = rsnd_dmapp_start, .stop = rsnd_dmapp_stop, .init = rsnd_dmapp_init, @@ -414,7 +426,9 @@ 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_mix(io) || + !!rsnd_io_to_mod_ctu(io); int id = rsnd_mod_id(mod); struct dma_addr { dma_addr_t out_addr; @@ -452,7 +466,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 ? */ @@ -460,8 +474,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, @@ -482,7 +496,7 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, return rsnd_gen2_dma_addr(io, mod, is_play, is_from); } -#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ +#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ static void rsnd_dma_of_path(struct rsnd_dma *dma, struct rsnd_dai_stream *io, int is_play, @@ -492,55 +506,81 @@ 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 *mix = rsnd_io_to_mod_mix(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_mod *mod[MOD_MAX]; - int i, index; + struct rsnd_mod *mod_start, *mod_end; + struct rsnd_priv *priv = rsnd_mod_to_priv(this); + struct device *dev = rsnd_priv_to_dev(priv); + int nr, i; + if (!ssi) + return; - for (i = 0; i < MOD_MAX; i++) + nr = 0; + for (i = 0; i < MOD_MAX; i++) { mod[i] = NULL; + nr += !!rsnd_io_to_mod(io, i); + } /* - * in play case... + * [S] -*-> [E] + * [S] -*-> SRC -o-> [E] + * [S] -*-> SRC -> DVC -o-> [E] + * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E] * - * src -> dst + * playback [S] = mem + * [E] = SSI * - * mem -> SSI - * mem -> SRC -> SSI - * mem -> SRC -> DVC -> SSI + * capture [S] = SSI + * [E] = mem + * + * -*-> Audio DMAC + * -o-> Audio DMAC peri peri */ - mod[0] = NULL; /* for "mem" */ - index = 1; - for (i = 1; i < MOD_MAX; i++) { - if (!src) { - mod[i] = ssi; - } else if (!dvc) { - mod[i] = src; - src = NULL; - } else { - if ((!is_play) && (this == src)) - this = dvc; + mod_start = (is_play) ? NULL : ssi; + mod_end = (is_play) ? ssi : NULL; - mod[i] = (is_play) ? src : dvc; - i++; - mod[i] = (is_play) ? dvc : src; + mod[0] = mod_start; + for (i = 1; i < nr; i++) { + if (src) { + mod[i] = src; src = NULL; + } else if (ctu) { + mod[i] = ctu; + ctu = NULL; + } else if (mix) { + mod[i] = mix; + mix = NULL; + } else if (dvc) { + mod[i] = dvc; dvc = NULL; } - - if (mod[i] == this) - index = i; - - if (mod[i] == ssi) - break; } + mod[i] = mod_end; - if (is_play) { - *mod_from = mod[index - 1]; - *mod_to = mod[index]; + /* + * | SSI | SRC | + * -------------+-----+-----+ + * is_play | o | * | + * !is_play | * | o | + */ + if ((this == ssi) == (is_play)) { + *mod_from = mod[nr - 1]; + *mod_to = mod[nr]; } else { - *mod_from = mod[index]; - *mod_to = mod[index - 1]; + *mod_from = mod[0]; + *mod_to = mod[1]; + } + + dev_dbg(dev, "module connection (this is %s[%d])\n", + rsnd_mod_name(this), rsnd_mod_id(this)); + for (i = 0; i <= nr; i++) { + dev_dbg(dev, " %s[%d]%s\n", + rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]), + (mod[i] == *mod_from) ? " from" : + (mod[i] == *mod_to) ? " to" : ""); } } @@ -568,10 +608,11 @@ void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) { - struct rsnd_mod *mod_from; - struct rsnd_mod *mod_to; + struct rsnd_mod *mod_from = NULL; + struct rsnd_mod *mod_to = NULL; struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); int is_play = rsnd_io_is_play(io); /* @@ -598,6 +639,11 @@ int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) if (rsnd_is_gen1(priv)) dma->ops = &rsnd_dmaen_ops; + dev_dbg(dev, "%s %s[%d] -> %s[%d]\n", + dma->ops->name, + rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), + rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + return dma->ops->init(io, dma, id, mod_from, mod_to); } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 36fc020cb..57796387d 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -24,6 +24,7 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ }; +#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) #define rsnd_dvc_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") @@ -63,6 +64,19 @@ static const char * const dvc_ramp_rate[] = { "0.125 dB/8192 steps", /* 10111 */ }; +static void rsnd_dvc_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, DVC_SWRSR, 0); + rsnd_mod_write(mod, DVC_SWRSR, 1); +} + +#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1) +#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0) +static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, DVC_DVUIR, enable); +} + static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -135,49 +149,24 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, return 0; } -static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, +static int rsnd_dvc_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct device *dev = rsnd_priv_to_dev(priv); - int dvc_id = rsnd_mod_id(dvc_mod); - int src_id = rsnd_mod_id(src_mod); - u32 route[] = { - [0] = 0x30000, - [1] = 0x30001, - [2] = 0x40000, - [3] = 0x10000, - [4] = 0x20000, - [5] = 0x40100 - }; - - if (src_id >= ARRAY_SIZE(route)) { - dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id); - return -EINVAL; - } - - rsnd_mod_hw_start(dvc_mod); + rsnd_mod_hw_start(mod); - /* - * fixme - * it doesn't support CTU/MIX - */ - rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]); + rsnd_dvc_soft_reset(mod); - rsnd_mod_write(dvc_mod, DVC_SWRSR, 0); - rsnd_mod_write(dvc_mod, DVC_SWRSR, 1); + rsnd_dvc_initialize_lock(mod); - rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); + rsnd_path_parse(priv, io); - rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod, io)); + rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io)); /* ch0/ch1 Volume */ - rsnd_dvc_volume_update(io, dvc_mod); + rsnd_dvc_volume_update(io, mod); - rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); - - rsnd_adg_set_cmd_timsel_gen2(dvc_mod, io); + rsnd_adg_set_cmd_timsel_gen2(mod, io); return 0; } @@ -195,6 +184,8 @@ static int rsnd_dvc_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + rsnd_dvc_initialize_unlock(mod); + rsnd_mod_write(mod, CMD_CTRL, 0x10); return 0; @@ -341,23 +332,21 @@ int rsnd_dvc_probe(struct platform_device *pdev, char name[RSND_DVC_NAME_SIZE]; int i, nr, ret; - rsnd_of_parse_dvc(pdev, of_data, priv); - - nr = info->dvc_info_nr; - if (!nr) - return 0; - /* This driver doesn't support Gen1 at this point */ if (rsnd_is_gen1(priv)) { dev_warn(dev, "CMD is not supported on Gen1\n"); return -EINVAL; } + rsnd_of_parse_dvc(pdev, of_data, priv); + + nr = info->dvc_info_nr; + if (!nr) + return 0; + dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); - if (!dvc) { - dev_err(dev, "CMD allocate failed\n"); + if (!dvc) return -ENOMEM; - } priv->dvc_nr = nr; priv->dvc = dvc; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 8c7dc51b1..f04d17bc6 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -103,6 +103,22 @@ void rsnd_write(struct rsnd_priv *priv, regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); } +void rsnd_force_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return; + + dev_dbg(dev, "w %s[%d] - %4d : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data); + + regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); +} + void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data) { @@ -200,12 +216,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, /* FIXME: it needs SSI_MODE2/3 in the future */ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80), - RSND_GEN_M_REG(BUSIF_DALIGN, 0x8, 0x80), + RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80), RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), - RSND_GEN_M_REG(INT_ENABLE, 0x18, 0x80), + RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), }; struct rsnd_regmap_field_conf conf_scu[] = { RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), + RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), @@ -223,6 +240,18 @@ 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(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), diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c new file mode 100644 index 000000000..0d5c102db --- /dev/null +++ b/sound/soc/sh/rcar/mix.c @@ -0,0 +1,200 @@ +/* + * mix.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 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); + } +} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 09fcc54a8..7a0e52b46 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -47,6 +47,18 @@ 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_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, @@ -99,6 +111,7 @@ enum rsnd_reg { RSND_REG_SHARE26, RSND_REG_SHARE27, RSND_REG_SHARE28, + RSND_REG_SHARE29, RSND_REG_MAX, }; @@ -119,7 +132,7 @@ enum rsnd_reg { #define RSND_REG_SSI_CTRL RSND_REG_SHARE02 #define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03 #define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04 -#define RSND_REG_INT_ENABLE RSND_REG_SHARE05 +#define RSND_REG_SSI_INT_ENABLE RSND_REG_SHARE05 #define RSND_REG_SRC_BSDSR RSND_REG_SHARE06 #define RSND_REG_SRC_BSISR RSND_REG_SHARE07 #define RSND_REG_DIV_EN RSND_REG_SHARE08 @@ -136,13 +149,14 @@ enum rsnd_reg { #define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19 #define RSND_REG_CMD_CTRL RSND_REG_SHARE20 #define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21 -#define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22 +#define RSND_REG_SSI_BUSIF_DALIGN RSND_REG_SHARE22 #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 #define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 #define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 #define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28 +#define RSND_REG_SRC_BUSIF_DALIGN RSND_REG_SHARE29 struct rsnd_of_data; struct rsnd_priv; @@ -157,27 +171,28 @@ struct rsnd_dai_stream; rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) #define rsnd_mod_write(m, r, d) \ rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_force_write(m, r, d) \ + rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) #define rsnd_mod_bset(m, r, s, d) \ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); +void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); -u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +void rsnd_path_parse(struct rsnd_priv *priv, + struct rsnd_dai_stream *io); /* * R-Car DMA */ struct rsnd_dma; -struct rsnd_dma_ops { - void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); - void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); - int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); - void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); -}; struct rsnd_dmaen { struct dma_chan *chan; @@ -217,6 +232,8 @@ 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, RSND_MOD_MAX, @@ -312,7 +329,7 @@ struct rsnd_mod { #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) -#define rsnd_mod_id(mod) ((mod)->id) +#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) #define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) @@ -345,9 +362,12 @@ struct rsnd_dai_stream { int byte_per_period; int next_period_byte; }; -#define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI]) -#define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC]) -#define rsnd_io_to_mod_dvc(io) ((io)->mod[RSND_MOD_DVC]) +#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_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))) #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) @@ -436,12 +456,6 @@ struct rsnd_priv { */ void *gen; - /* - * below value will be filled on rsnd_src_probe() - */ - void *src; - int src_nr; - /* * below value will be filled on rsnd_adg_probe() */ @@ -458,6 +472,24 @@ struct rsnd_priv { void *ssi; int ssi_nr; + /* + * below value will be filled on rsnd_src_probe() + */ + 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_mix_probe() + */ + void *mix; + int mix_nr; + /* * below value will be filled on rsnd_dvc_probe() */ @@ -530,6 +562,19 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, const char * const *texts, u32 max); +/* + * R-Car SSI + */ +int rsnd_ssi_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); + /* * R-Car SRC */ @@ -550,20 +595,27 @@ 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); -#define rsnd_src_nr(priv) ((priv)->src_nr) +/* + * 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 SSI + * R-Car MIX */ -int rsnd_ssi_probe(struct platform_device *pdev, +int rsnd_mix_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); -void rsnd_ssi_remove(struct platform_device *pdev, + +void rsnd_mix_remove(struct platform_device *pdev, struct rsnd_priv *priv); -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); +struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); /* * R-Car DVC @@ -575,7 +627,4 @@ void rsnd_dvc_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) - - #endif diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 84e935711..d61db9c38 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -41,6 +41,7 @@ static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { static const struct of_device_id rsrc_card_of_match[] = { { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, + { .compatible = "renesas,rsrc-card", }, {}, }; MODULE_DEVICE_TABLE(of, rsrc_card_of_match); @@ -242,8 +243,15 @@ static int rsrc_card_parse_links(struct device_node *np, snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); /* additional name prefix */ - priv->codec_conf.of_node = dai_link->codec_of_node; - priv->codec_conf.name_prefix = of_data->prefix; + if (of_data) { + priv->codec_conf.of_node = dai_link->codec_of_node; + priv->codec_conf.name_prefix = of_data->prefix; + } else { + snd_soc_of_parse_audio_prefix(&priv->snd_card, + &priv->codec_conf, + dai_link->codec_of_node, + "audio-prefix"); + } /* set dai_name */ snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s", @@ -361,8 +369,14 @@ static int rsrc_card_parse_of(struct device_node *node, priv->snd_card.num_links = num; priv->snd_card.codec_conf = &priv->codec_conf; priv->snd_card.num_configs = 1; - priv->snd_card.of_dapm_routes = of_data->routes; - priv->snd_card.num_of_dapm_routes = of_data->num_routes; + + if (of_data) { + priv->snd_card.of_dapm_routes = of_data->routes; + priv->snd_card.num_of_dapm_routes = of_data->num_routes; + } else { + snd_soc_of_parse_audio_routing(&priv->snd_card, + "audio-routing"); + } /* Parse the card name from DT */ snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index c61c17180..89a18e102 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -30,6 +30,7 @@ struct rsnd_src { #define RSND_SRC_NAME_SIZE 16 +#define rsnd_src_nr(priv) ((priv)->src_nr) #define rsnd_enable_sync_convert(src) ((src)->sen.val) #define rsnd_src_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") @@ -117,6 +118,20 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ +static void rsnd_src_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SRC_SWRSR, 0); + rsnd_mod_write(mod, SRC_SWRSR, 1); +} + + +#define rsnd_src_initialize_lock(mod) __rsnd_src_initialize_lock(mod, 1) +#define rsnd_src_initialize_unlock(mod) __rsnd_src_initialize_lock(mod, 0) +static void __rsnd_src_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, SRC_SRCIR, enable); +} + static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -133,7 +148,6 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, int use_busif) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); int ssi_id = rsnd_mod_id(ssi_mod); /* @@ -170,27 +184,14 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, * DMA settings for SSIU */ if (use_busif) { - u32 val = 0x76543210; - u32 mask = ~0; + u32 val = rsnd_get_dalign(ssi_mod, io); rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, - rsnd_get_adinr(ssi_mod, io)); + rsnd_get_adinr_bit(ssi_mod, io)); rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); - mask <<= runtime->channels * 4; - val = val & mask; - - switch (runtime->sample_bits) { - case 16: - val |= 0x67452301 & ~mask; - break; - case 32: - val |= 0x76543210 & ~mask; - break; - } - rsnd_mod_write(ssi_mod, BUSIF_DALIGN, val); - + rsnd_mod_write(ssi_mod, SSI_BUSIF_DALIGN, val); } return 0; @@ -215,10 +216,9 @@ int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod) return 0; /* enable SSI interrupt if Gen2 */ - if (rsnd_ssi_is_dma_mode(ssi_mod)) - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000); - else - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, + rsnd_ssi_is_dma_mode(ssi_mod) ? + 0x0e000000 : 0x0f000000); return 0; } @@ -231,7 +231,7 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) return 0; /* disable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000); + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); return 0; } @@ -294,12 +294,8 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, if (convert_rate) fsrate = 0x0400000 / convert_rate * runtime->rate; - /* set/clear soft reset */ - rsnd_mod_write(mod, SRC_SWRSR, 0); - rsnd_mod_write(mod, SRC_SWRSR, 1); - /* Set channel number and output bit length */ - rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod, io)); + rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr_bit(mod, io)); /* Enable the initial value of IFS */ if (fsrate) { @@ -358,17 +354,15 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_mod_hw_start(mod); + rsnd_src_soft_reset(mod); + + rsnd_src_initialize_lock(mod); + src->err = 0; /* reset sync convert_rate */ src->sync.val = 0; - /* - * Initialize the operation of the SRC internal circuits - * see rsnd_src_start() - */ - rsnd_mod_write(mod, SRC_SRCIR, 1); - return 0; } @@ -395,11 +389,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, static int rsnd_src_start(struct rsnd_mod *mod) { - /* - * Cancel the initialization and operate the SRC function - * see rsnd_src_init() - */ - rsnd_mod_write(mod, SRC_SRCIR, 0); + rsnd_src_initialize_unlock(mod); return 0; } @@ -617,6 +607,14 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) int_val = 0; } + /* + * WORKAROUND + * + * ignore over flow error when rsnd_enable_sync_convert() + */ + if (rsnd_enable_sync_convert(src)) + sys_int_val = sys_int_val & 0xffff; + rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); @@ -632,11 +630,22 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) { - u32 val = OUF_SRC(rsnd_mod_id(mod)); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 val0, val1; bool ret = false; - if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) || - (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) { + val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); + + /* + * WORKAROUND + * + * ignore over flow error when rsnd_enable_sync_convert() + */ + if (rsnd_enable_sync_convert(src)) + val0 = val0 & 0xffff; + + if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) { struct rsnd_src *src = rsnd_mod_to_src(mod); src->err++; @@ -652,7 +661,20 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) static int _rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 val; + + val = rsnd_get_dalign(mod, io); + + rsnd_mod_write(mod, SRC_BUSIF_DALIGN, val); + + /* + * WORKAROUND + * + * Enable SRC output if you want to use sync convert together with DVC + */ + val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ? + 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); @@ -921,13 +943,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, if (!rsnd_rdai_is_clk_master(rdai)) return 0; - /* - * We can't use SRC sync convert - * if it has DVC - */ - if (rsnd_io_to_mod_dvc(io)) - return 0; - /* * enable sync convert */ @@ -1047,10 +1062,8 @@ int rsnd_src_probe(struct platform_device *pdev, return 0; src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); - if (!src) { - dev_err(dev, "SRC allocate failed\n"); + if (!src) return -ENOMEM; - } priv->src_nr = nr; priv->src = src; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 2fbe59f7f..d45b9a7e3 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -770,10 +770,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, */ nr = info->ssi_info_nr; ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); - if (!ssi) { - dev_err(dev, "SSI allocate failed\n"); + if (!ssi) return -ENOMEM; - } priv->ssi = ssi; priv->ssi_nr = nr; -- cgit v1.2.3-54-g00ecf