From 678ea0037263bb8afb139c4035be5ad4bc610a2f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:55:16 +0000 Subject: [PATCH 01/74] ASoC: rsnd: remove unsed *parent Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 7ee89da4dd5f..df3ab74adf3e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -64,7 +64,6 @@ #define SSI_NAME "ssi" struct rsnd_ssi { - struct rsnd_ssi *parent; struct rsnd_mod mod; struct rsnd_mod *dma; From 7c89746eede3acbbb67f848a741f8bca412986af Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:55:39 +0000 Subject: [PATCH 02/74] ASoC: rsnd: use rsnd_mod_init() for ADG Current ADG doesn't use rsnd_mod_init(), but this limitation is no longer necessary. Let's use common rsnd_mod_init() Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 6d3ef366d536..19f5509f908d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -518,13 +518,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv) return -ENOMEM; } - /* - * ADG is special module. - * Use ADG mod without rsnd_mod_init() to make debug easy - * for rsnd_write/rsnd_read - */ - adg->mod.ops = &adg_ops; - adg->mod.priv = priv; + rsnd_mod_init(priv, &adg->mod, &adg_ops, + NULL, 0, 0); rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); From 4e880b2674a6d36d6c7b9741d88973e00a76922b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:55:57 +0000 Subject: [PATCH 03/74] ASoC: rsnd: add debug message for rsnd_mod_call() rsnd_mod_call() tries to call each IP's relevant function. But it is difficult to understand which function returned error. This patch adds debug message for this purpose Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ca05a0a95a4d..691e22381280 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -337,6 +337,9 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) *status, call ? #func : ""); \ if (call) \ ret = (mod)->ops->func(mod, io, param); \ + if (ret) \ + dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \ ret; \ }) From 6c001f3ab4ce20f01a1b3064b2bd1a9352c5d6b1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:56:18 +0000 Subject: [PATCH 04/74] ASoC: rsnd: don't update status if rsnd_mod_call() doesn't match If system uses CTU/MUX, and if probe error happened, it will try to call rsnd_dai_call(remove, ...). Then, MUX/DVC/SSIU/SSI might be called without calling rsnd_dai_call(probe, ...). Then, each mod status might be un-matched. It doesn't call un-matched remove function by this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 691e22381280..3eb7e9a7b5dc 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -330,8 +330,11 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ - *status = (*status & ~mask) + \ - (add << __rsnd_mod_shift_##func); \ + if (add == 0xF) \ + call = 0; \ + else \ + *status = (*status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), \ *status, call ? #func : ""); \ From bfa3119c38afb8177f8e403b9e9b2ecd0306bb26 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:56:36 +0000 Subject: [PATCH 05/74] ASoC: rsnd: try to connect connected mod is not error If system uses CTU/MUX, CTU/MUX/DVC will try to connect same CMD to system, but it is not error in this case. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 3eb7e9a7b5dc..ad97ce3ae844 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -369,6 +369,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod, if (!mod) return -EIO; + if (io->mod[type] == mod) + return 0; + if (io->mod[type]) return -EINVAL; From ea96380baa2794cb6e0d5bfbcca0c78b237a5f8a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:56:59 +0000 Subject: [PATCH 06/74] ASoC: rsnd: always call probe/remove for MUX CTU/MUX/DVC/SSIU/SSI/Audio-DMAC-periperi might be used under multipath. So, probe()/remove() need to be called multiple times. This patch allows it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 1 + sound/soc/sh/rcar/rsnd.h | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ad97ce3ae844..ed0918967def 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -163,6 +163,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod) { if (mod->clk) clk_unprepare(mod->clk); + mod->clk = NULL; } void rsnd_mod_interrupt(struct rsnd_mod *mod, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 317dd793149a..c7b2ba0daf2a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -237,29 +237,29 @@ struct rsnd_mod { /* * status * - * 0xH0000CBA + * 0xH0000CB0 * - * A 0: probe 1: remove * B 0: init 1: quit * C 0: start 1: stop * * H is always called (see __rsnd_mod_call) + * H 0: probe 1: remove * H 0: pcm_new * H 0: fallback * H 0: hw_params */ -#define __rsnd_mod_shift_probe 0 -#define __rsnd_mod_shift_remove 0 #define __rsnd_mod_shift_init 4 #define __rsnd_mod_shift_quit 4 #define __rsnd_mod_shift_start 8 #define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_probe 28 /* always called */ +#define __rsnd_mod_shift_remove 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ -#define __rsnd_mod_add_probe 1 -#define __rsnd_mod_add_remove -1 +#define __rsnd_mod_add_probe 0 +#define __rsnd_mod_add_remove 0 #define __rsnd_mod_add_init 1 #define __rsnd_mod_add_quit -1 #define __rsnd_mod_add_start 1 @@ -269,7 +269,7 @@ struct rsnd_mod { #define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 -#define __rsnd_mod_call_remove 1 +#define __rsnd_mod_call_remove 0 #define __rsnd_mod_call_init 0 #define __rsnd_mod_call_quit 1 #define __rsnd_mod_call_start 0 From 654a12b86305b1fd78279616f876b8d21d146a5e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:57:17 +0000 Subject: [PATCH 07/74] ASoC: rsnd: don't overwrite io on rsnd_cmd_init() Current rsnd_cmd_init() overwrites "io" which will be used end of this function. This patch solved this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/cmd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index cd1f064e63c4..e77e4935b4ea 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -38,6 +38,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, if (mix) { struct rsnd_dai *rdai; + struct rsnd_dai_stream *tio; int i; u32 path[] = { [0] = 0, @@ -55,12 +56,12 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, */ data = 0; for_each_rsnd_dai(rdai, priv, i) { - io = &rdai->playback; - if (mix == rsnd_io_to_mod_mix(io)) + tio = &rdai->playback; + if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; - io = &rdai->capture; - if (mix == rsnd_io_to_mod_mix(io)) + tio = &rdai->capture; + if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; } From 72154e50728e8a08940332db20884b63dfa6590d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:57:37 +0000 Subject: [PATCH 08/74] ASoC: rsnd: select each SRC correctly for CMD data path To select CMD data patch, it should use correct SRC from each stream in MUX case. But current code is selecting SRC from fixed stream. This patch solves this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/cmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index e77e4935b4ea..4b2d50d9a686 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -29,7 +29,6 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, { 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 device *dev = rsnd_priv_to_dev(priv); u32 data; @@ -38,6 +37,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, if (mix) { struct rsnd_dai *rdai; + struct rsnd_mod *src; struct rsnd_dai_stream *tio; int i; u32 path[] = { @@ -57,15 +57,19 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, data = 0; for_each_rsnd_dai(rdai, priv, i) { tio = &rdai->playback; + src = rsnd_io_to_mod_src(tio); if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; tio = &rdai->capture; + src = rsnd_io_to_mod_src(tio); if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; } } else { + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + u32 path[] = { [0] = 0x30000, [1] = 0x30001, From 5ba17b42e1755c3c5cfe96370cfd47f34d01f62c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:58:07 +0000 Subject: [PATCH 09/74] ASoC: rsnd: each mod has status again for CTU/MUX support SSI will be used as normal SSI or as clock parent SSI. Therefor, rsnd driver wants to control SSI and parent SSI separately. Otherwise it can't use Playback/Capture in the same time. And it has been done by c2dc47d5cf("ASoC: rsnd: rsnd_dai_stream has each mod's status insted of rsnd_mod") before. OTOH, rsnd driver doesn't want to control CTU/MUX/DVC/SSIU/SSI in separately. Otherwise, these will be re-initialized during playing if MUX merges 2 sounds. Because of these picky reasons, this patch re-defines status on each mod, and add new parent_ssi_status on rsnd_dai_stream. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 2 +- sound/soc/sh/rcar/cmd.c | 3 ++- sound/soc/sh/rcar/core.c | 21 ++++++++++++++++----- sound/soc/sh/rcar/ctu.c | 2 +- sound/soc/sh/rcar/dma.c | 2 +- sound/soc/sh/rcar/dvc.c | 2 +- sound/soc/sh/rcar/mix.c | 2 +- sound/soc/sh/rcar/rsnd.h | 21 ++++++++++++++++----- sound/soc/sh/rcar/src.c | 3 ++- sound/soc/sh/rcar/ssi.c | 37 ++++++++++++++++++++++++++++++++++++- sound/soc/sh/rcar/ssiu.c | 3 ++- 11 files changed, 79 insertions(+), 19 deletions(-) diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 19f5509f908d..d74e1ccc0f8f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -519,7 +519,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv) } rsnd_mod_init(priv, &adg->mod, &adg_ops, - NULL, 0, 0); + NULL, NULL, 0, 0); rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index 4b2d50d9a686..abb5eaac854a 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -157,7 +157,8 @@ int rsnd_cmd_probe(struct rsnd_priv *priv) for_each_rsnd_cmd(cmd, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), - &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i); + &rsnd_cmd_ops, NULL, + rsnd_mod_get_status, RSND_MOD_CMD, i); if (ret) return ret; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ed0918967def..b460d714d088 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + return &mod->status; +} + int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id) + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id) { int ret = clk_prepare(clk); @@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv, mod->type = type; mod->clk = clk; mod->priv = priv; + mod->get_status = get_status; return ret; } @@ -325,7 +336,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct rsnd_mod *mod = (io)->mod[idx]; \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 *status = (io)->mod_status + idx; \ + u32 *status = mod->get_status(io, mod, idx); \ u32 mask = 0xF << __rsnd_mod_shift_##func; \ u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index d53a225d19e9..109930a20401 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -129,7 +129,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, - clk, RSND_MOD_CTU, i); + clk, rsnd_mod_get_status, RSND_MOD_CTU, i); if (ret) goto rsnd_ctu_probe_done; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 418e6fdd06a3..d1cb3c177572 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -681,7 +681,7 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, dma_mod = rsnd_mod_get(dma); ret = rsnd_mod_init(priv, dma_mod, - ops, NULL, type, dma_id); + ops, NULL, rsnd_mod_get_status, type, dma_id); if (ret < 0) return ERR_PTR(ret); diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d45ffe496397..302c193f674d 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -373,7 +373,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, - clk, RSND_MOD_DVC, i); + clk, rsnd_mod_get_status, RSND_MOD_DVC, i); if (ret) goto rsnd_dvc_probe_done; diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 65542b6a89e9..e0e337ad4206 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -172,7 +172,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, - clk, RSND_MOD_MIX, i); + clk, rsnd_mod_get_status, RSND_MOD_MIX, i); if (ret) goto rsnd_mix_probe_done; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index c7b2ba0daf2a..28602607cf5e 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -233,6 +233,10 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_priv *priv; struct clk *clk; + u32 *(*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + u32 status; }; /* * status @@ -286,10 +290,13 @@ struct rsnd_mod { int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id); + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id); void rsnd_mod_quit(struct rsnd_mod *mod); char *rsnd_mod_name(struct rsnd_mod *mod); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, @@ -297,6 +304,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, void rsnd_mod_interrupt(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io)); +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), struct device_node *node, @@ -319,7 +330,7 @@ struct rsnd_dai_stream { struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai *rdai; - u32 mod_status[RSND_MOD_MAX]; + u32 parent_ssi_status; int byte_pos; int period_pos; int byte_per_period; diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index b438538a0a69..516b0c05451c 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -615,7 +615,8 @@ int rsnd_src_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(src), - &rsnd_src_ops, clk, RSND_MOD_SRC, i); + &rsnd_src_ops, clk, rsnd_mod_get_status, + RSND_MOD_SRC, i); if (ret) goto rsnd_src_probe_done; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index df3ab74adf3e..e68f3a1c9cb4 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -857,6 +857,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } +static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + /* + * SSIP (= SSI parent) needs to be special, otherwise, + * 2nd SSI might doesn't start. see also rsnd_mod_call() + * + * We can't include parent SSI status on SSI, because we don't know + * how many SSI requests parent SSI. Thus, it is localed on "io" now. + * ex) trouble case + * Playback: SSI0 + * Capture : SSI1 (needs SSI0) + * + * 1) start Capture -> SSI0/SSI1 are started. + * 2) start Playback -> SSI0 doesn't work, because it is already + * marked as "started" on 1) + * + * OTOH, using each mod's status is good for MUX case. + * It doesn't need to start in 2nd start + * ex) + * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 + * | + * IO-1: SRC1 -> CTU2 -+ + * + * 1) start IO-0 -> start SSI0 + * 2) start IO-1 -> SSI0 doesn't need to start, because it is + * already started on 1) + */ + if (type == RSND_MOD_SSIP) + return &io->parent_ssi_status; + + return rsnd_mod_get_status(io, mod, type); +} + int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; @@ -919,7 +954,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - RSND_MOD_SSI, i); + rsnd_ssi_get_status, RSND_MOD_SSI, i); if (ret) goto rsnd_ssi_probe_done; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 06d72828e5bc..11e55889b401 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -206,7 +206,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) for_each_rsnd_ssiu(ssiu, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), - ops, NULL, RSND_MOD_SSIU, i); + ops, NULL, rsnd_mod_get_status, + RSND_MOD_SSIU, i); if (ret) return ret; } From 355cb84fbe1f098f80c17dad9027ad2c6aec3fa0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:58:33 +0000 Subject: [PATCH 10/74] ASoC: rsnd: attach Audio-DMAC-periperi correctly SSI/SRC will try to attach DMAC as Audio-DMAC or Audio-DMAC-periperi. It is fixed IP, but will be attached to each streams as different module in case of MUX (= multi sound path will be merged). This patch solves this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/dma.c | 62 +++++++++++++++++++++------------------- sound/soc/sh/rcar/rsnd.h | 4 +-- sound/soc/sh/rcar/src.c | 4 +-- sound/soc/sh/rcar/ssi.c | 5 ++-- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index d1cb3c177572..7658e8fd7bdc 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -622,15 +622,13 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, } } -struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, int id) +int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod, int id) { - struct rsnd_mod *dma_mod; 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 rsnd_dma *dma; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; enum rsnd_mod_type type; @@ -646,17 +644,10 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, * rsnd_rdai_continuance_probe() */ if (!dmac) - return ERR_PTR(-EAGAIN); - - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return ERR_PTR(-ENOMEM); + return -EAGAIN; rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); - dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); - /* for Gen2 */ if (mod_from && mod_to) { ops = &rsnd_dmapp_ops; @@ -678,27 +669,38 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, type = RSND_MOD_AUDMA; } - dma_mod = rsnd_mod_get(dma); + if (!(*dma_mod)) { + struct rsnd_dma *dma; - ret = rsnd_mod_init(priv, dma_mod, - ops, NULL, rsnd_mod_get_status, type, dma_id); + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + *dma_mod = rsnd_mod_get(dma); + + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + rsnd_mod_get_status, type, dma_id); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", + rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), + rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + + ret = attach(io, dma, id, mod_from, mod_to); + if (ret < 0) + return ret; + } + + ret = rsnd_dai_connect(*dma_mod, io, type); if (ret < 0) - return ERR_PTR(ret); + return ret; - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod), - rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), - rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); - - ret = attach(io, dma, id, mod_from, mod_to); - if (ret < 0) - return ERR_PTR(ret); - - ret = rsnd_dai_connect(dma_mod, io, type); - if (ret < 0) - return ERR_PTR(ret); - - return rsnd_mod_get(dma); + return 0; } int rsnd_dma_probe(struct rsnd_priv *priv) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 28602607cf5e..90c732e265a2 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -166,8 +166,8 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); /* * R-Car DMA */ -struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, int id); +int rsnd_dma_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id); int rsnd_dma_probe(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 516b0c05451c..8dc9b483b5fa 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -495,9 +495,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, return ret; } - src->dma = rsnd_dma_attach(io, mod, 0); - if (IS_ERR(src->dma)) - return PTR_ERR(src->dma); + ret = rsnd_dma_attach(io, mod, &src->dma, 0); return ret; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index e68f3a1c9cb4..90674137aa90 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -704,9 +704,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, if (ret) return ret; - ssi->dma = rsnd_dma_attach(io, mod, dma_id); - if (IS_ERR(ssi->dma)) - return PTR_ERR(ssi->dma); + /* SSI probe might be called many times in MUX multi path */ + ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id); return ret; } From bd9a603fe78fa838a9c884b1e67749120a45508c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:58:53 +0000 Subject: [PATCH 11/74] ASoC: rsnd: ctu: add rsnd_mix_activation() Based on datasheet Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 9 +++++++++ sound/soc/sh/rcar/gen.c | 1 + sound/soc/sh/rcar/rsnd.h | 1 + 3 files changed, 11 insertions(+) diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 109930a20401..0dc451803eb6 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -24,6 +24,13 @@ struct rsnd_ctu { i++) #define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) + +static void rsnd_ctu_activation(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_SWRSR, 0); + rsnd_mod_write(mod, CTU_SWRSR, 1); +} + #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) @@ -44,6 +51,8 @@ static int rsnd_ctu_init(struct rsnd_mod *mod, { rsnd_mod_power_on(mod); + rsnd_ctu_activation(mod); + rsnd_ctu_initialize_lock(mod); rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index c7aee9e59e86..2f01e5eb462c 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -260,6 +260,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) 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_SWRSR, 0x500, 0x100), RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 90c732e265a2..12227f6b6221 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -86,6 +86,7 @@ enum rsnd_reg { RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */ RSND_REG_CMD_ROUTE_SLCT, RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */ + RSND_REG_CTU_SWRSR, RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, RSND_REG_MIX_SWRSR, From d0658b31c712327e04ddc2621742144de5562962 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:59:12 +0000 Subject: [PATCH 12/74] ASoC: rsnd: ctu: add rsnd_ctu_halt() Based on datasheet process Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 0dc451803eb6..7b482f36cb63 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -31,6 +31,12 @@ static void rsnd_ctu_activation(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 1); } +static void rsnd_ctu_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_CTUIR, 1); + rsnd_mod_write(mod, CTU_SWRSR, 0); +} + #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) @@ -66,6 +72,8 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + rsnd_ctu_halt(mod); + rsnd_mod_power_off(mod); return 0; From dc037afdbc4f285dc2f8107a0b8eaf89892e28ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:59:33 +0000 Subject: [PATCH 13/74] ASoC: rsnd: ctu: settings matches to datasheet Current CTU settings was rough. This patch makes it match to datasheet. But do nothing at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 61 ++++++++++++++++++++++++++++++++-------- sound/soc/sh/rcar/gen.c | 34 ++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 34 ++++++++++++++++++++++ 3 files changed, 117 insertions(+), 12 deletions(-) diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 7b482f36cb63..a10d0f7b73fa 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -37,13 +37,6 @@ static void rsnd_ctu_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 0); } -#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_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -51,6 +44,54 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod, return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4); } +static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_CTUIR, 1); + + rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + + rsnd_mod_write(mod, CTU_CPMDR, 0); + rsnd_mod_write(mod, CTU_SCMDR, 0); + rsnd_mod_write(mod, CTU_SV00R, 0); + rsnd_mod_write(mod, CTU_SV01R, 0); + rsnd_mod_write(mod, CTU_SV02R, 0); + rsnd_mod_write(mod, CTU_SV03R, 0); + rsnd_mod_write(mod, CTU_SV04R, 0); + rsnd_mod_write(mod, CTU_SV05R, 0); + rsnd_mod_write(mod, CTU_SV06R, 0); + rsnd_mod_write(mod, CTU_SV07R, 0); + + rsnd_mod_write(mod, CTU_SV10R, 0); + rsnd_mod_write(mod, CTU_SV11R, 0); + rsnd_mod_write(mod, CTU_SV12R, 0); + rsnd_mod_write(mod, CTU_SV13R, 0); + rsnd_mod_write(mod, CTU_SV14R, 0); + rsnd_mod_write(mod, CTU_SV15R, 0); + rsnd_mod_write(mod, CTU_SV16R, 0); + rsnd_mod_write(mod, CTU_SV17R, 0); + + rsnd_mod_write(mod, CTU_SV20R, 0); + rsnd_mod_write(mod, CTU_SV21R, 0); + rsnd_mod_write(mod, CTU_SV22R, 0); + rsnd_mod_write(mod, CTU_SV23R, 0); + rsnd_mod_write(mod, CTU_SV24R, 0); + rsnd_mod_write(mod, CTU_SV25R, 0); + rsnd_mod_write(mod, CTU_SV26R, 0); + rsnd_mod_write(mod, CTU_SV27R, 0); + + rsnd_mod_write(mod, CTU_SV30R, 0); + rsnd_mod_write(mod, CTU_SV31R, 0); + rsnd_mod_write(mod, CTU_SV32R, 0); + rsnd_mod_write(mod, CTU_SV33R, 0); + rsnd_mod_write(mod, CTU_SV34R, 0); + rsnd_mod_write(mod, CTU_SV35R, 0); + rsnd_mod_write(mod, CTU_SV36R, 0); + rsnd_mod_write(mod, CTU_SV37R, 0); + + rsnd_mod_write(mod, CTU_CTUIR, 0); +} + static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -59,11 +100,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod, rsnd_ctu_activation(mod); - rsnd_ctu_initialize_lock(mod); - - rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); - - rsnd_ctu_initialize_unlock(mod); + rsnd_ctu_value_init(io, mod); return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 2f01e5eb462c..b6e487e06019 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -263,6 +263,40 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100), RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), + RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100), + RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100), + RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100), + RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100), + RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100), + RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100), + RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100), + RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100), + RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100), + RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100), + RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100), + RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100), + RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100), + RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100), + RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100), + RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100), + RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100), + RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100), + RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100), + RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100), + RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100), + RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100), + RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100), + RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100), + RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100), + RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100), + RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100), + RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100), + RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100), + RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100), + RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100), + RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100), + RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100), + RSND_GEN_M_REG(CTU_SV37R, 0x594, 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), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 12227f6b6221..4974db6679c3 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -89,6 +89,40 @@ enum rsnd_reg { RSND_REG_CTU_SWRSR, RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, + RSND_REG_CTU_CPMDR, + RSND_REG_CTU_SCMDR, + RSND_REG_CTU_SV00R, + RSND_REG_CTU_SV01R, + RSND_REG_CTU_SV02R, + RSND_REG_CTU_SV03R, + RSND_REG_CTU_SV04R, + RSND_REG_CTU_SV05R, + RSND_REG_CTU_SV06R, + RSND_REG_CTU_SV07R, + RSND_REG_CTU_SV10R, + RSND_REG_CTU_SV11R, + RSND_REG_CTU_SV12R, + RSND_REG_CTU_SV13R, + RSND_REG_CTU_SV14R, + RSND_REG_CTU_SV15R, + RSND_REG_CTU_SV16R, + RSND_REG_CTU_SV17R, + RSND_REG_CTU_SV20R, + RSND_REG_CTU_SV21R, + RSND_REG_CTU_SV22R, + RSND_REG_CTU_SV23R, + RSND_REG_CTU_SV24R, + RSND_REG_CTU_SV25R, + RSND_REG_CTU_SV26R, + RSND_REG_CTU_SV27R, + RSND_REG_CTU_SV30R, + RSND_REG_CTU_SV31R, + RSND_REG_CTU_SV32R, + RSND_REG_CTU_SV33R, + RSND_REG_CTU_SV34R, + RSND_REG_CTU_SV35R, + RSND_REG_CTU_SV36R, + RSND_REG_CTU_SV37R, RSND_REG_MIX_SWRSR, RSND_REG_MIX_MIXIR, RSND_REG_MIX_ADINR, From 38587f4cb7937344e12a9eef1ccdc5b8a0e992ba Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 01:59:55 +0000 Subject: [PATCH 14/74] ASoC: rsnd: tidyup Playback/Capture sequence Based on datasheet sequence Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index b460d714d088..1fcefab03ad6 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -358,15 +358,51 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ret; \ }) +static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { + { + /* CAPTURE */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + RSND_MOD_SSIU, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + }, { + /* PLAYBACK */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + RSND_MOD_SSIU, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + }, +}; + #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ + int type, is_play = rsnd_io_is_play(io); \ int ret = 0, i; \ for (i = 0; i < RSND_MOD_MAX; i++) { \ - mod = (io)->mod[i]; \ + type = rsnd_mod_sequence[is_play][i]; \ + mod = (io)->mod[type]; \ if (!mod) \ continue; \ - ret |= rsnd_mod_call(i, io, fn, param); \ + ret |= rsnd_mod_call(type, io, fn, param); \ } \ ret; \ }) From 31739a689f828345a7ef52fd8dbbb200df1b6555 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 21 Jan 2016 02:00:13 +0000 Subject: [PATCH 15/74] ASoC: rsnd: disable SRC.out/in in same time commit b761bf272bce ("ASoC: rsnd: disable SRC.out only when stop timing") disabled SRC.out/in in different timing, but was based on picky HW information. Now, we have confirmed that we can disable both in the same time. This patch do it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 8dc9b483b5fa..7749615bd404 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -367,11 +367,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - /* - * stop SRC output only - * see rsnd_src_quit - */ - rsnd_mod_write(mod, SRC_CTRL, 0x01); + rsnd_mod_write(mod, SRC_CTRL, 0); return 0; } @@ -409,9 +405,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, rsnd_src_irq_disable(mod); - /* stop both out/in */ - rsnd_mod_write(mod, SRC_CTRL, 0); - rsnd_src_halt(mod); rsnd_mod_power_off(mod); From 6a25c8da00284f5612b404368bd07b69efd84aa2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 26 Jan 2016 04:56:14 +0000 Subject: [PATCH 16/74] ASoC: rsnd: don't auto-recover when under/over run error Renesas R-Car sound needs recovery (= restart) when under/over run error occurred, and current driver tries it on under/over run error handler automatically. But this recovery should be handled by userland, not kernel. This patch stops XRUN when under/over run error occur, and will leave the recovery of HW in userland. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 39 ++++------------ sound/soc/sh/rcar/ssi.c | 101 +++++++++------------------------------- 2 files changed, 31 insertions(+), 109 deletions(-) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 7749615bd404..cccca154e4c3 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -25,7 +25,6 @@ struct rsnd_src { struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ u32 convert_rate; /* sampling rate convert */ - int err; int irq; }; @@ -316,7 +315,7 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod) rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); } -static bool rsnd_src_record_error(struct rsnd_mod *mod) +static bool rsnd_src_error_occurred(struct rsnd_mod *mod) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val0, val1; @@ -333,12 +332,8 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod) 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++; + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) ret = true; - } return ret; } @@ -388,8 +383,6 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_irq_enable(mod); - src->err = 0; - /* reset sync convert_rate */ src->sync.val = 0; @@ -401,7 +394,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); rsnd_src_irq_disable(mod); @@ -409,10 +401,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, rsnd_mod_power_off(mod); - if (src->err) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); - src->convert_rate = 0; /* reset sync convert_rate */ @@ -425,8 +413,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); + bool stop = false; spin_lock(&priv->lock); @@ -434,26 +421,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_src_interrupt_out; - if (rsnd_src_record_error(mod)) { - - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - rsnd_src_stop(mod, io, priv); - rsnd_src_start(mod, io, priv); - } - - if (src->err > 1024) { - rsnd_src_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (rsnd_src_error_occurred(mod)) + stop = true; rsnd_src_status_clear(mod); rsnd_src_interrupt_out: spin_unlock(&priv->lock); + + if (stop) + snd_pcm_stop_xrun(io->substream); } static irqreturn_t rsnd_src_interrupt(int irq, void *data) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 90674137aa90..5870434bbc58 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -74,7 +74,6 @@ struct rsnd_ssi { u32 wsr; int chan; int rate; - int err; int irq; unsigned int usrcnt; }; @@ -385,8 +384,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (ret < 0) return ret; - ssi->err = -1; /* ignore 1st error */ - /* clear error status */ rsnd_ssi_status_clear(mod); @@ -409,13 +406,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, } if (!rsnd_ssi_is_parent(mod, io)) { - if (ssi->err > 0) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - ssi->err); - ssi->cr_own = 0; - ssi->err = 0; rsnd_ssi_irq_disable(mod); } @@ -455,21 +446,9 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, return 0; } -static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - u32 status = rsnd_ssi_status_get(mod); - - /* under/over flow error */ - if (status & (UIRQ | OIRQ)) - ssi->err++; - - return status; -} - -static int __rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr; @@ -491,26 +470,22 @@ static int __rsnd_ssi_start(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - /* - * no limit to start - * see also - * rsnd_ssi_stop - * rsnd_ssi_interrupt - */ - return __rsnd_ssi_start(mod, io, priv); -} - -static int __rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr; + /* + * don't stop if not last user + * see also + * rsnd_ssi_start + * rsnd_ssi_interrupt + */ + if (ssi->usrcnt > 1) + return 0; + /* * disable all IRQ, * and, wait all data was sent @@ -531,33 +506,14 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - /* - * don't stop if not last user - * see also - * rsnd_ssi_start - * rsnd_ssi_interrupt - */ - if (ssi->usrcnt > 1) - return 0; - - return __rsnd_ssi_stop(mod, io, priv); -} - static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; bool elapsed = false; + bool stop = false; spin_lock(&priv->lock); @@ -565,7 +521,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; - status = rsnd_ssi_record_error(ssi); + status = rsnd_ssi_status_get(mod); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -587,23 +543,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, } /* DMA only */ - if (is_dma && (status & (UIRQ | OIRQ))) { - /* - * restart SSI - */ - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - __rsnd_ssi_stop(mod, io, priv); - __rsnd_ssi_start(mod, io, priv); - } - - if (ssi->err > 1024) { - rsnd_ssi_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (is_dma && (status & (UIRQ | OIRQ))) + stop = true; rsnd_ssi_status_clear(mod); rsnd_ssi_interrupt_out: @@ -611,6 +552,10 @@ rsnd_ssi_interrupt_out: if (elapsed) rsnd_dai_period_elapsed(io); + + if (stop) + snd_pcm_stop_xrun(io->substream); + } static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) From b5b442abd9d5cfe4f04a1e83be9900c87444bd66 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 26 Jan 2016 04:56:57 +0000 Subject: [PATCH 17/74] ASoC: rsnd: add .irq callback Current rsnd driver has .init/.start/.stop/.quit callbacks, and it needs many IPs (SRC/CTU/MUX/DVC/CMD/SSIU/SSI). Because of these relationship, it might get unnecessary error IRQ when start/stop. This patch adds new .irq callback and control IRQ enable/disable timing to avoid it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 9 ++++++++- sound/soc/sh/rcar/rsnd.h | 6 ++++++ sound/soc/sh/rcar/src.c | 16 ++++++++-------- sound/soc/sh/rcar/ssi.c | 36 +++++++++++++----------------------- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 1fcefab03ad6..704ba7ae9eaf 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -568,9 +568,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = rsnd_dai_call(start, io, priv); if (ret < 0) goto dai_trigger_end; + + ret = rsnd_dai_call(irq, io, priv, 1); + if (ret < 0) + goto dai_trigger_end; + break; case SNDRV_PCM_TRIGGER_STOP: - ret = rsnd_dai_call(stop, io, priv); + ret = rsnd_dai_call(irq, io, priv, 0); + + ret |= rsnd_dai_call(stop, io, priv); ret |= rsnd_dai_call(quit, io, priv); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4974db6679c3..bbaf89b6de8a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -249,6 +249,9 @@ struct rsnd_mod_ops { int (*stop)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); + int (*irq)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, int enable); int (*pcm_new)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd); @@ -293,6 +296,7 @@ struct rsnd_mod { #define __rsnd_mod_shift_stop 8 #define __rsnd_mod_shift_probe 28 /* always called */ #define __rsnd_mod_shift_remove 28 /* always called */ +#define __rsnd_mod_shift_irq 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ @@ -303,6 +307,7 @@ struct rsnd_mod { #define __rsnd_mod_add_quit -1 #define __rsnd_mod_add_start 1 #define __rsnd_mod_add_stop -1 +#define __rsnd_mod_add_irq 0 #define __rsnd_mod_add_pcm_new 0 #define __rsnd_mod_add_fallback 0 #define __rsnd_mod_add_hw_params 0 @@ -313,6 +318,7 @@ struct rsnd_mod { #define __rsnd_mod_call_quit 1 #define __rsnd_mod_call_start 0 #define __rsnd_mod_call_stop 1 +#define __rsnd_mod_call_irq 0 #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index cccca154e4c3..ab5f13155055 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -271,9 +271,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_adg_set_convert_timing_gen2(mod, io); } -#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1) -#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0) -static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) +static int rsnd_src_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 sys_int_val, int_val, sys_int_mask; @@ -305,6 +306,8 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) 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); + + return 0; } static void rsnd_src_status_clear(struct rsnd_mod *mod) @@ -381,8 +384,6 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_status_clear(mod); - rsnd_src_irq_enable(mod); - /* reset sync convert_rate */ src->sync.val = 0; @@ -395,8 +396,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, { struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_src_irq_disable(mod); - rsnd_src_halt(mod); rsnd_mod_power_off(mod); @@ -455,7 +454,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, /* * IRQ is not supported on non-DT * see - * rsnd_src_irq_enable() + * rsnd_src_irq() */ ret = devm_request_irq(dev, irq, rsnd_src_interrupt, @@ -518,6 +517,7 @@ static struct rsnd_mod_ops rsnd_src_ops = { .quit = rsnd_src_quit, .start = rsnd_src_start, .stop = rsnd_src_stop, + .irq = rsnd_src_irq, .hw_params = rsnd_src_hw_params, .pcm_new = rsnd_src_pcm_new, }; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5870434bbc58..803e9ae65915 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -142,30 +142,24 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, dev_warn(dev, "status check failed\n"); } -static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod) +static int rsnd_ssi_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 val = 0; if (rsnd_is_gen1(priv)) return 0; - /* enable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, - rsnd_ssi_is_dma_mode(ssi_mod) ? - 0x0e000000 : 0x0f000000); - - return 0; -} - -static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - - if (rsnd_is_gen1(priv)) + if (ssi->usrcnt != 1) return 0; - /* disable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); + if (enable) + val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; + + rsnd_mod_write(mod, SSI_INT_ENABLE, val); return 0; } @@ -387,8 +381,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, /* clear error status */ rsnd_ssi_status_clear(mod); - rsnd_ssi_irq_enable(mod); - return 0; } @@ -405,12 +397,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return -EIO; } - if (!rsnd_ssi_is_parent(mod, io)) { + if (!rsnd_ssi_is_parent(mod, io)) ssi->cr_own = 0; - rsnd_ssi_irq_disable(mod); - } - rsnd_ssi_master_clk_stop(ssi, io); rsnd_mod_power_off(mod); @@ -627,6 +616,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, .hw_params = rsnd_ssi_hw_params, }; From 7315917f7ccdf5eb5758f663a781daa7599d63d7 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 25 Jan 2016 18:23:30 +0100 Subject: [PATCH 18/74] ASoC: rk3036: fix missing dependency on REGMAP_MMIO When SND_SOC_INNO_RK3036 is enabled but REGMAP_MMIO is not, the MODPOST step fails with this error: ... Kernel: arch/x86/boot/bzImage is ready (#3) Building modules, stage 2. MODPOST 3203 modules ERROR: "__devm_regmap_init_mmio_clk" [sound/soc/codecs/snd-soc-inno-rk3036.ko] undefined! Signed-off-by: Antonio Ospite Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..1f7eaa919088 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -497,6 +497,7 @@ config SND_SOC_ICS43432 config SND_SOC_INNO_RK3036 tristate "Inno codec driver for RK3036 SoC" + select REGMAP_MMIO config SND_SOC_ISABELLE tristate From dcaee813a64368e09878d0cee931d14c9603e925 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Thu, 28 Jan 2016 16:43:35 +0800 Subject: [PATCH 19/74] ASoC: rt5616: add mclk property for rt5616 document This patch add the mclk property for the CODEC driver, since sometime the CODEC driver needs the clock enabled. The system clock of ALC5616 can be selected from MCLK, That's also make as the codec master clock provider, Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5616.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/rt5616.txt b/Documentation/devicetree/bindings/sound/rt5616.txt index efc48c65198d..e41085818559 100644 --- a/Documentation/devicetree/bindings/sound/rt5616.txt +++ b/Documentation/devicetree/bindings/sound/rt5616.txt @@ -8,6 +8,12 @@ Required properties: - reg : The I2C address of the device. +Optional properties: + +- clocks: The phandle of the master clock to the CODEC. + +- clock-names: Should be "mclk". + Pins on the device (for linking into audio routes) for RT5616: * IN1P From 99081589c5ad590e9828ae9febc384612f241164 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Thu, 28 Jan 2016 16:43:36 +0800 Subject: [PATCH 20/74] ASoC: rt5616: trivial: fix the typo This patch try to fix the trivial typo. Run "scripts/checkpatch.pl -f --subjective xxx" The enable more subjective tests. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 376 +++++++++++++++++++------------------- 1 file changed, 191 insertions(+), 185 deletions(-) diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 1c10d8ed39d2..d4bdf9f6f0af 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -53,6 +53,7 @@ static const struct reg_sequence init_list[] = { {RT5616_PR_BASE + 0x21, 0x4040}, {RT5616_PR_BASE + 0x23, 0x0004}, }; + #define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt5616_reg[] = { @@ -162,9 +163,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg) for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { if (reg >= rt5616_ranges[i].range_min && - reg <= rt5616_ranges[i].range_max) { + reg <= rt5616_ranges[i].range_max) return true; - } } switch (reg) { @@ -190,9 +190,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg) for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { if (reg >= rt5616_ranges[i].range_min && - reg <= rt5616_ranges[i].range_max) { + reg <= rt5616_ranges[i].range_max) return true; - } } switch (reg) { @@ -307,45 +306,45 @@ static unsigned int bst_tlv[] = { static const struct snd_kcontrol_new rt5616_snd_controls[] = { /* Headphone Output Volume */ SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* OUTPUT Control */ SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1, - RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, - 175, 0, dac_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 175, 0, dac_vol_tlv), /* IN1/IN2 Control */ SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2, - RT5616_BST_SFT1, 8, 0, bst_tlv), + RT5616_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2, - RT5616_BST_SFT2, 8, 0, bst_tlv), + RT5616_BST_SFT2, 8, 0, bst_tlv), /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL, - RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT, - 31, 1, in_vol_tlv), + RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT, + 31, 1, in_vol_tlv), /* ADC Digital Volume Control */ SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL, - RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL, - RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, - 127, 0, adc_vol_tlv), + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 127, 0, adc_vol_tlv), /* ADC Boost Volume Control */ SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL, - RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT, - 3, 0, adc_bst_tlv), + RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), }; static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) + struct snd_soc_dapm_widget *sink) { unsigned int val; @@ -462,20 +461,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = { }; static int rt5616_adc_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); break; case SND_SOC_DAPM_POST_PMD: snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); break; default: @@ -486,7 +485,7 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w, } static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -494,54 +493,55 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* depop parameters */ snd_soc_update_bits(codec, RT5616_DEPOP_M2, - RT5616_DEPOP_MASK, RT5616_DEPOP_MAN); + RT5616_DEPOP_MASK, RT5616_DEPOP_MAN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | - RT5616_HP_CB_MASK, RT5616_HP_CP_PU | - RT5616_HP_SG_DIS | RT5616_HP_CB_PU); + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, RT5616_HP_CP_PU | + RT5616_HP_SG_DIS | RT5616_HP_CB_PU); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_HP_DCC_INT1, 0x9f00); + RT5616_HP_DCC_INT1, 0x9f00); /* headphone amp power on */ snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, 0); + RT5616_PWR_FV1 | RT5616_PWR_FV2, 0); snd_soc_update_bits(codec, RT5616_PWR_VOL, - RT5616_PWR_HV_L | RT5616_PWR_HV_R, - RT5616_PWR_HV_L | RT5616_PWR_HV_R); + RT5616_PWR_HV_L | RT5616_PWR_HV_R, + RT5616_PWR_HV_L | RT5616_PWR_HV_R); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_HP_L | RT5616_PWR_HP_R | - RT5616_PWR_HA, RT5616_PWR_HP_L | - RT5616_PWR_HP_R | RT5616_PWR_HA); + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, RT5616_PWR_HP_L | + RT5616_PWR_HP_R | RT5616_PWR_HA); msleep(50); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); snd_soc_update_bits(codec, RT5616_CHARGE_PUMP, - RT5616_PM_HP_MASK, RT5616_PM_HP_HV); + RT5616_PM_HP_MASK, RT5616_PM_HP_HV); snd_soc_update_bits(codec, RT5616_PR_BASE + - RT5616_CHOP_DAC_ADC, 0x0200, 0x0200); + RT5616_CHOP_DAC_ADC, 0x0200, 0x0200); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_CO_MASK | RT5616_HP_SG_MASK, - RT5616_HP_CO_EN | RT5616_HP_SG_EN); + RT5616_HP_CO_MASK | RT5616_HP_SG_MASK, + RT5616_HP_CO_EN | RT5616_HP_SG_EN); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PR_BASE + - RT5616_CHOP_DAC_ADC, 0x0200, 0x0); + RT5616_CHOP_DAC_ADC, 0x0200, 0x0); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | - RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); /* headphone amp power down */ snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK | - RT5616_HP_CO_MASK | RT5616_HP_CP_MASK | - RT5616_HP_SG_MASK | RT5616_HP_CB_MASK, - RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN | - RT5616_HP_CO_DIS | RT5616_HP_CP_PD | - RT5616_HP_SG_EN | RT5616_HP_CB_PD); + RT5616_SMT_TRIG_MASK | + RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK | + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, + RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN | + RT5616_HP_CO_DIS | RT5616_HP_CP_PD | + RT5616_HP_SG_EN | RT5616_HP_CB_PD); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_HP_L | RT5616_PWR_HP_R | - RT5616_PWR_HA, 0); + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, 0); break; default: return 0; @@ -551,7 +551,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, } static int rt5616_hp_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -559,57 +559,57 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* headphone unmute sequence */ snd_soc_update_bits(codec, RT5616_DEPOP_M3, - RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | - RT5616_CP_FQ3_MASK, - (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) | - (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) | - (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT)); + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_MAMP_INT_REG2, 0xfc00); + RT5616_MAMP_INT_REG2, 0xfc00); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN); + RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTN_MASK, RT5616_RSTN_EN); + RT5616_RSTN_MASK, RT5616_RSTN_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS | - RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); snd_soc_update_bits(codec, RT5616_HP_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); msleep(100); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | - RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); msleep(20); snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET, - RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN); + RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN); break; case SND_SOC_DAPM_PRE_PMD: /* headphone mute sequence */ snd_soc_update_bits(codec, RT5616_DEPOP_M3, - RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | - RT5616_CP_FQ3_MASK, - (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) | - (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) | - (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT)); + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT); snd_soc_write(codec, RT5616_PR_BASE + - RT5616_MAMP_INT_REG2, 0xfc00); + RT5616_MAMP_INT_REG2, 0xfc00); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_HP_SG_MASK, RT5616_HP_SG_EN); + RT5616_HP_SG_MASK, RT5616_HP_SG_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTP_MASK, RT5616_RSTP_EN); + RT5616_RSTP_MASK, RT5616_RSTP_EN); snd_soc_update_bits(codec, RT5616_DEPOP_M1, - RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK | - RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS | - RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET, - RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS); + RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS); msleep(90); snd_soc_update_bits(codec, RT5616_HP_VOL, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); msleep(30); break; @@ -621,24 +621,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w, } static int rt5616_lout_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_LM, RT5616_PWR_LM); + RT5616_PWR_LM, RT5616_PWR_LM); snd_soc_update_bits(codec, RT5616_LOUT_CTRL1, - RT5616_L_MUTE | RT5616_R_MUTE, 0); + RT5616_L_MUTE | RT5616_R_MUTE, 0); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_LOUT_CTRL1, - RT5616_L_MUTE | RT5616_R_MUTE, - RT5616_L_MUTE | RT5616_R_MUTE); + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_LM, 0); + RT5616_PWR_LM, 0); break; default: @@ -649,19 +649,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w, } static int rt5616_bst1_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2); + RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST1_OP2, 0); + RT5616_PWR_BST1_OP2, 0); break; default: @@ -672,19 +672,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w, } static int rt5616_bst2_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2); + RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2); break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, RT5616_PWR_ANLG2, - RT5616_PWR_BST2_OP2, 0); + RT5616_PWR_BST2_OP2, 0); break; default: @@ -696,13 +696,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2, - RT5616_PWR_PLL_BIT, 0, NULL, 0), + RT5616_PWR_PLL_BIT, 0, NULL, 0), /* Input Side */ /* micbias */ SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1, - RT5616_PWR_LDO_BIT, 0, NULL, 0), + RT5616_PWR_LDO_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2, - RT5616_PWR_MB1_BIT, 0, NULL, 0), + RT5616_PWR_MB1_BIT, 0, NULL, 0), /* Input Lines */ SND_SOC_DAPM_INPUT("MIC1"), @@ -714,45 +714,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { /* Boost */ SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2, - RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2, - RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), /* Input Volume */ SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL, - RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), /* REC Mixer */ SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0, - rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)), + rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)), SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0, - rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)), + rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)), /* ADCs */ SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1, - RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event, - SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1, - RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event, - SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), /* ADC Mixer */ SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2, - RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0), + RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)), + rt5616_sto1_adc_l_mix, + ARRAY_SIZE(rt5616_sto1_adc_l_mix)), SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)), + rt5616_sto1_adc_r_mix, + ARRAY_SIZE(rt5616_sto1_adc_r_mix)), /* Digital Interface */ SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1, - RT5616_PWR_I2S1_BIT, 0, NULL, 0), + RT5616_PWR_I2S1_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -770,68 +772,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { /* Output Side */ /* DAC mixer before sound effect */ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)), + rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)), SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)), + rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)), SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2, - RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0), + RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0), /* DAC Mixer */ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, - rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)), + rt5616_sto_dac_l_mix, + ARRAY_SIZE(rt5616_sto_dac_l_mix)), SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, - rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)), + rt5616_sto_dac_r_mix, + ARRAY_SIZE(rt5616_sto_dac_r_mix)), /* DACs */ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1, - RT5616_PWR_DAC_L1_BIT, 0), + RT5616_PWR_DAC_L1_BIT, 0), SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1, - RT5616_PWR_DAC_R1_BIT, 0), + RT5616_PWR_DAC_R1_BIT, 0), /* OUT Mixer */ SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT, - 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)), + 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)), SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT, - 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)), + 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)), /* Output Volume */ SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL, - RT5616_PWR_OV_L_BIT, 0, NULL, 0), + RT5616_PWR_OV_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL, - RT5616_PWR_OV_R_BIT, 0, NULL, 0), + RT5616_PWR_OV_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL, - RT5616_PWR_HV_L_BIT, 0, NULL, 0), + RT5616_PWR_HV_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL, - RT5616_PWR_HV_R_BIT, 0, NULL, 0), + RT5616_PWR_HV_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, - 0, 0, NULL, 0), + 0, 0, NULL, 0), SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL, - RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL, - RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL, - RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL, - RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), /* HPO/LOUT/Mono Mixer */ SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, - rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)), + rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)), SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, - rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)), + rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)), SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, - rt5616_hp_event, SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMU), + rt5616_hp_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, - rt5616_lout_event, SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMU), + rt5616_lout_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0, - rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), + rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), /* Output Lines */ SND_SOC_DAPM_OUTPUT("HPOL"), @@ -950,7 +954,8 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = { }; static int rt5616_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; @@ -977,7 +982,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", rt5616->bclk[dai->id], rt5616->lrck[dai->id]); dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", - bclk_ms, pre_div, dai->id); + bclk_ms, pre_div, dai->id); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -998,10 +1003,9 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream, mask_clk = RT5616_I2S_PD1_MASK; val_clk = pre_div << RT5616_I2S_PD1_SFT; snd_soc_update_bits(codec, RT5616_I2S1_SDP, - RT5616_I2S_DL_MASK, val_len); + RT5616_I2S_DL_MASK, val_len); snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk); - return 0; } @@ -1050,15 +1054,14 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } snd_soc_update_bits(codec, RT5616_I2S1_SDP, - RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK | - RT5616_I2S_DF_MASK, reg_val); - + RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK | + RT5616_I2S_DF_MASK, reg_val); return 0; } static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) + int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); @@ -1078,8 +1081,9 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); return -EINVAL; } + snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_SCLK_SRC_MASK, reg_val); + RT5616_SCLK_SRC_MASK, reg_val); rt5616->sysclk = freq; rt5616->sysclk_src = clk_id; @@ -1089,7 +1093,7 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, } static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, - unsigned int freq_in, unsigned int freq_out) + unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = dai->codec; struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); @@ -1106,19 +1110,22 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, rt5616->pll_in = 0; rt5616->pll_out = 0; snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK); + RT5616_SCLK_SRC_MASK, + RT5616_SCLK_SRC_MCLK); return 0; } switch (source) { case RT5616_PLL1_S_MCLK: snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK); + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_MCLK); break; case RT5616_PLL1_S_BCLK1: case RT5616_PLL1_S_BCLK2: snd_soc_update_bits(codec, RT5616_GLB_CLK, - RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1); + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_BCLK1); break; default: dev_err(codec->dev, "Unknown PLL source %d\n", source); @@ -1136,10 +1143,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, pll_code.n_code, pll_code.k_code); snd_soc_write(codec, RT5616_PLL_CTRL1, - pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code); + pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code); snd_soc_write(codec, RT5616_PLL_CTRL2, - (pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT | - pll_code.m_bp << RT5616_PLL_M_BP_SFT); + (pll_code.m_bp ? 0 : pll_code.m_code) << + RT5616_PLL_M_SFT | + pll_code.m_bp << RT5616_PLL_M_BP_SFT); rt5616->pll_in = freq_in; rt5616->pll_out = freq_out; @@ -1149,22 +1157,23 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, } static int rt5616_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) + enum snd_soc_bias_level level) { switch (level) { case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2); + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); mdelay(10); snd_soc_update_bits(codec, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); snd_soc_update_bits(codec, RT5616_D_MISC, - RT5616_D_GATE_EN, RT5616_D_GATE_EN); + RT5616_D_GATE_EN, + RT5616_D_GATE_EN); } break; @@ -1222,7 +1231,6 @@ static int rt5616_resume(struct snd_soc_codec *codec) #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) - struct snd_soc_dai_ops rt5616_aif_dai_ops = { .hw_params = rt5616_hw_params, .set_fmt = rt5616_set_dai_fmt, @@ -1296,15 +1304,15 @@ MODULE_DEVICE_TABLE(of, rt5616_of_match); #endif static int rt5616_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct rt5616_priv *rt5616; unsigned int val; int ret; rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv), - GFP_KERNEL); - if (rt5616 == NULL) + GFP_KERNEL); + if (!rt5616) return -ENOMEM; i2c_set_clientdata(i2c, rt5616); @@ -1326,14 +1334,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c, } regmap_write(rt5616->regmap, RT5616_RESET, 0); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2, - RT5616_PWR_VREF1 | RT5616_PWR_MB | - RT5616_PWR_BG | RT5616_PWR_VREF2); + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); mdelay(10); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_FV1 | RT5616_PWR_FV2, - RT5616_PWR_FV1 | RT5616_PWR_FV2); + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); ret = regmap_register_patch(rt5616->regmap, init_list, ARRAY_SIZE(init_list)); @@ -1341,11 +1349,10 @@ static int rt5616_i2c_probe(struct i2c_client *i2c, dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, - RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V); + RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V); return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616, - rt5616_dai, ARRAY_SIZE(rt5616_dai)); - + rt5616_dai, ARRAY_SIZE(rt5616_dai)); } static int rt5616_i2c_remove(struct i2c_client *i2c) @@ -1361,7 +1368,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client) regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8); regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8); - } static struct i2c_driver rt5616_i2c_driver = { From 76d3204eaa9364a662f0fe0f075dfe61e6bd14fe Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Thu, 28 Jan 2016 16:43:37 +0800 Subject: [PATCH 21/74] ASoC: rt5616: add the mclk for the codec driver This patch adds the code to enable the clock to the CODEC driver if it needs the clock enabled. In some case, We need to claim the clock which is driving the codec so that when we enable clock gating, we continue to clock the codec when needed. if mclk provided, to enable and disable the clock source. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index d4bdf9f6f0af..fdca63694b40 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,7 @@ struct rt5616_priv { struct snd_soc_codec *codec; struct delayed_work patch_work; struct regmap *regmap; + struct clk *mclk; int sysclk; int sysclk_src; @@ -1159,7 +1161,34 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, static int rt5616_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { + + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(rt5616->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5616->mclk); + } else { + ret = clk_prepare_enable(rt5616->mclk); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, RT5616_PWR_ANLG1, @@ -1198,6 +1227,11 @@ static int rt5616_probe(struct snd_soc_codec *codec) { struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); + /* Check if MCLK provided */ + rt5616->mclk = devm_clk_get(codec->dev, "mclk"); + if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + rt5616->codec = codec; return 0; From 4e26ad80cbd3a6b0d606201892e81a9a9c6864ce Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Fri, 29 Jan 2016 01:47:12 +0000 Subject: [PATCH 22/74] ASoC: rt5616: Add support sample rate to 192KHz Reference the TRM, the ALC5616 support one 24bit/8KHz ~ 192KHz I2S/PCM Interface for stereo DAC and stereo ADC. Signed-off-by: Xing Zheng Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index fdca63694b40..3704d0716c04 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1261,7 +1261,7 @@ static int rt5616_resume(struct snd_soc_codec *codec) #define rt5616_resume NULL #endif -#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000 #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) From 4a6180ea7399b945cd380dc63e2e8118f9b432d3 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 3 Feb 2016 19:53:24 +0800 Subject: [PATCH 23/74] ASoC: rt5514: add rt5514 codec driver This is the initial codec driver for rt5514. The codec includes a low power DSP for voice wake up. The register address is incremental by 4 in the DSP memory map. In order to recover the codec settings in the codec mode and manipulate the DSP mode for voice wake up, we use the multi-level register map. One is for ALSA API in codec mode that can be recovered by cache before recording. Another is for DSP related settings that can be accessed with 32bit address of the DSP in the application of voice wake up. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/rt5514.txt | 25 + sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5514.c | 982 ++++++++++++++++++ sound/soc/codecs/rt5514.h | 252 +++++ 5 files changed, 1267 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/rt5514.txt create mode 100644 sound/soc/codecs/rt5514.c create mode 100644 sound/soc/codecs/rt5514.h diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt new file mode 100644 index 000000000000..e24436fc5ea9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rt5514.txt @@ -0,0 +1,25 @@ +RT5514 audio CODEC + +This device supports I2C only. + +Required properties: + +- compatible : "realtek,rt5514". + +- reg : The I2C address of the device. + +Pins on the device (for linking into audio routes) for RT5514: + + * DMIC1L + * DMIC1R + * DMIC2L + * DMIC2R + * AMICL + * AMICR + +Example: + +codec: rt5514@57 { + compatible = "realtek,rt5514"; + reg = <0x57>; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..dabd479bf1f0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -95,6 +95,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C select SND_SOC_RT298 if I2C + select SND_SOC_RT5514 if I2C select SND_SOC_RT5616 if I2C select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C @@ -565,6 +566,7 @@ config SND_SOC_PCM512x_SPI config SND_SOC_RL6231 tristate + default y if SND_SOC_RT5514=y default y if SND_SOC_RT5616=y default y if SND_SOC_RT5640=y default y if SND_SOC_RT5645=y @@ -572,6 +574,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5659=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y + default m if SND_SOC_RT5514=m default m if SND_SOC_RT5616=m default m if SND_SOC_RT5640=m default m if SND_SOC_RT5645=m @@ -595,6 +598,9 @@ config SND_SOC_RT298 tristate depends on I2C +config SND_SOC_RT5514 + tristate + config SND_SOC_RT5616 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d44f7d347183..79f95dd4ed68 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -92,6 +92,7 @@ snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt286-objs := rt286.o snd-soc-rt298-objs := rt298.o +snd-soc-rt5514-objs := rt5514.o snd-soc-rt5616-objs := rt5616.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -296,6 +297,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o +obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c new file mode 100644 index 000000000000..879bf60f4965 --- /dev/null +++ b/sound/soc/codecs/rt5514.c @@ -0,0 +1,982 @@ +/* + * rt5514.c -- RT5514 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Oder Chiou + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5514.h" + +static const struct reg_sequence rt5514_i2c_patch[] = { + {0x1800101c, 0x00000000}, + {0x18001100, 0x0000031f}, + {0x18001104, 0x00000007}, + {0x18001108, 0x00000000}, + {0x1800110c, 0x00000000}, + {0x18001110, 0x00000000}, + {0x18001114, 0x00000001}, + {0x18001118, 0x00000000}, + {0x18002f08, 0x00000006}, + {0x18002f00, 0x00055149}, + {0x18002f00, 0x0005514b}, + {0x18002f00, 0x00055149}, + {0xfafafafa, 0x00000001}, + {0x18002f10, 0x00000001}, + {0x18002f10, 0x00000000}, + {0x18002f10, 0x00000001}, + {0xfafafafa, 0x00000001}, + {0x18002000, 0x000010ec}, + {0xfafafafa, 0x00000000}, +}; + +static const struct reg_sequence rt5514_patch[] = { + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, +}; + +static const struct reg_default rt5514_reg[] = { + {RT5514_RESET, 0x00000000}, + {RT5514_PWR_ANA1, 0x00808880}, + {RT5514_PWR_ANA2, 0x00220000}, + {RT5514_I2S_CTRL1, 0x00000330}, + {RT5514_I2S_CTRL2, 0x20000000}, + {RT5514_VAD_CTRL6, 0xc00007d2}, + {RT5514_EXT_VAD_CTRL, 0x80000080}, + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_PAD_CTRL1, 0x00804000}, + {RT5514_DMIC_DATA_CTRL, 0x00000005}, + {RT5514_DIG_SOURCE_CTRL, 0x00000002}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_DOWNFILTER2_CTRL1, 0x0000882f}, + {RT5514_PLL_SOURCE_CTRL, 0x00000004}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_CLK_CTRL2, 0x00000000}, + {RT5514_PLL3_CALIB_CTRL1, 0x00400200}, + {RT5514_PLL3_CALIB_CTRL5, 0x40220012}, + {RT5514_DELAY_BUF_CTRL1, 0x7fff006a}, + {RT5514_DELAY_BUF_CTRL3, 0x00000000}, + {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL3, 0x00000362}, + {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL3, 0x00000362}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_LDO18_16, 0x02000345}, + {RT5514_ANA_CTRL_ADC12, 0x0000a2a8}, + {RT5514_ANA_CTRL_ADC21, 0x00001180}, + {RT5514_ANA_CTRL_ADC22, 0x0000aaa8}, + {RT5514_ANA_CTRL_ADC23, 0x00151427}, + {RT5514_ANA_CTRL_MICBST, 0x00002000}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, + {RT5514_ANA_CTRL_INBUF, 0x00000143}, + {RT5514_ANA_CTRL_VREF, 0x00008d50}, + {RT5514_ANA_CTRL_PLL3, 0x0000000e}, + {RT5514_ANA_CTRL_PLL1_1, 0x00000000}, + {RT5514_ANA_CTRL_PLL1_2, 0x00030220}, + {RT5514_DMIC_LP_CTRL, 0x00000000}, + {RT5514_MISC_CTRL_DSP, 0x00000000}, + {RT5514_DSP_CTRL1, 0x00055149}, + {RT5514_DSP_CTRL3, 0x00000006}, + {RT5514_DSP_CTRL4, 0x00000001}, + {RT5514_VENDOR_ID1, 0x00000001}, + {RT5514_VENDOR_ID2, 0x10ec5514}, +}; + +static bool rt5514_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_RESET: + case RT5514_PWR_ANA1: + case RT5514_PWR_ANA2: + case RT5514_I2S_CTRL1: + case RT5514_I2S_CTRL2: + case RT5514_VAD_CTRL6: + case RT5514_EXT_VAD_CTRL: + case RT5514_DIG_IO_CTRL: + case RT5514_PAD_CTRL1: + case RT5514_DMIC_DATA_CTRL: + case RT5514_DIG_SOURCE_CTRL: + case RT5514_SRC_CTRL: + case RT5514_DOWNFILTER2_CTRL1: + case RT5514_PLL_SOURCE_CTRL: + case RT5514_CLK_CTRL1: + case RT5514_CLK_CTRL2: + case RT5514_PLL3_CALIB_CTRL1: + case RT5514_PLL3_CALIB_CTRL5: + case RT5514_DELAY_BUF_CTRL1: + case RT5514_DELAY_BUF_CTRL3: + case RT5514_DOWNFILTER0_CTRL1: + case RT5514_DOWNFILTER0_CTRL2: + case RT5514_DOWNFILTER0_CTRL3: + case RT5514_DOWNFILTER1_CTRL1: + case RT5514_DOWNFILTER1_CTRL2: + case RT5514_DOWNFILTER1_CTRL3: + case RT5514_ANA_CTRL_LDO10: + case RT5514_ANA_CTRL_LDO18_16: + case RT5514_ANA_CTRL_ADC12: + case RT5514_ANA_CTRL_ADC21: + case RT5514_ANA_CTRL_ADC22: + case RT5514_ANA_CTRL_ADC23: + case RT5514_ANA_CTRL_MICBST: + case RT5514_ANA_CTRL_ADCFED: + case RT5514_ANA_CTRL_INBUF: + case RT5514_ANA_CTRL_VREF: + case RT5514_ANA_CTRL_PLL3: + case RT5514_ANA_CTRL_PLL1_1: + case RT5514_ANA_CTRL_PLL1_2: + case RT5514_DMIC_LP_CTRL: + case RT5514_MISC_CTRL_DSP: + case RT5514_DSP_CTRL1: + case RT5514_DSP_CTRL3: + case RT5514_DSP_CTRL4: + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_i2c_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case RT5514_DSP_MAPPING | RT5514_RESET: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA1: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA2: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2: + case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6: + case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL: + case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_SRC_CTRL: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2: + case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL: + case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0), + 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0), + 4, 4, TLV_DB_SCALE_ITEM(750, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(950, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0) +); + +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); + +static const struct snd_kcontrol_new rt5514_snd_controls[] = { + SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, + RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), + SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1, + RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + adc_vol_tlv), + SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1, + RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + adc_vol_tlv), +}; + +/* ADC Mixer*/ +static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +/* DMIC Source */ +static const char * const rt5514_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum); + +/** + * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic. + * + * @rate: base clock rate. + * + * Choose divider parameter that gives the highest possible DMIC frequency in + * 1MHz - 3MHz range. + */ +static int rt5514_calc_dmic_clk(struct snd_soc_codec *codec, int rate) +{ + int div[] = {2, 3, 4, 8, 12, 16, 24, 32}; + int i; + + if (rate < 1000000 * div[0]) { + pr_warn("Base clock rate %d is too low\n", rate); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(div); i++) { + /* find divider that gives DMIC frequency below 3.072MHz */ + if (3072000 * div[i] >= rate) + return i; + } + + dev_warn(codec->dev, "Base clock rate %d is too high\n", rate); + return -EINVAL; +} + +static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + int idx; + + idx = rt5514_calc_dmic_clk(codec, rt5514->sysclk); + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1, + RT5514_CLK_DMIC_OUT_SEL_MASK, + idx << RT5514_CLK_DMIC_OUT_SEL_SFT); + + return idx; +} + +static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + + if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1) + return 1; + else + return 0; +} + +static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1L"), + SND_SOC_DAPM_INPUT("DMIC1R"), + SND_SOC_DAPM_INPUT("DMIC2L"), + SND_SOC_DAPM_INPUT("DMIC2R"), + + SND_SOC_DAPM_INPUT("AMICL"), + SND_SOC_DAPM_INPUT("AMICR"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1, + RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1, + RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO21_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2, + RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2, + RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2, + RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0, + NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto2_dmic_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD0_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)), + + SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt5514_dapm_routes[] = { + { "DMIC1", NULL, "DMIC1L" }, + { "DMIC1", NULL, "DMIC1R" }, + { "DMIC2", NULL, "DMIC2L" }, + { "DMIC2", NULL, "DMIC2R" }, + + { "DMIC1L", NULL, "DMIC CLK" }, + { "DMIC1R", NULL, "DMIC CLK" }, + { "DMIC2L", NULL, "DMIC CLK" }, + { "DMIC2R", NULL, "DMIC CLK" }, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXR", "ADC Switch", "AMICR" }, + + { "ADC Power", NULL, "LDO18 IN" }, + { "ADC Power", NULL, "LDO18 ADC" }, + { "ADC Power", NULL, "LDO21" }, + { "ADC Power", NULL, "BG LDO18 IN" }, + { "ADC Power", NULL, "BG LDO21" }, + { "ADC Power", NULL, "BG MBIAS" }, + { "ADC Power", NULL, "MBIAS" }, + { "ADC Power", NULL, "VREF2" }, + { "ADC Power", NULL, "VREF1" }, + + { "ADCL Power", NULL, "LDO16L" }, + { "ADCL Power", NULL, "ADC1L" }, + { "ADCL Power", NULL, "BSTL2" }, + { "ADCL Power", NULL, "BSTL" }, + { "ADCL Power", NULL, "ADCFEDL" }, + + { "ADCR Power", NULL, "LDO16R" }, + { "ADCR Power", NULL, "ADC1R" }, + { "ADCR Power", NULL, "BSTR2" }, + { "ADCR Power", NULL, "BSTR" }, + { "ADCR Power", NULL, "ADCFEDR" }, + + { "AMICL", NULL, "ADC CLK" }, + { "AMICL", NULL, "ADC Power" }, + { "AMICL", NULL, "ADCL Power" }, + { "AMICR", NULL, "ADC CLK" }, + { "AMICR", NULL, "ADC Power" }, + { "AMICR", NULL, "ADCR Power" }, + + { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" }, + { "PLL1", NULL, "PLL1 LDO" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" }, + { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + + { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXR", "ADC Switch", "AMICR" }, + + { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" }, + { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, + + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" }, + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" }, + { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" }, + { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + + { "AIF1TX", NULL, "Stereo1 ADC MIX"}, + { "AIF1TX", NULL, "Stereo2 ADC MIX"}, +}; + +static int rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0; + + rt5514->lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt5514->bclk = rt5514->lrck * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5514->bclk, rt5514->lrck); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len = RT5514_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len = RT5514_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S8: + val_len = RT5514_I2S_DL_8; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK, + val_len); + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK, + pre_div << RT5514_CLK_SYS_DIV_OUT_SFT | + pre_div << RT5514_SEL_ADC_OSR_SFT); + + return 0; +} + +static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_NB_IF: + reg_val |= RT5514_I2S_LR_INV; + break; + + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5514_I2S_BP_INV; + break; + + case SND_SOC_DAIFMT_IB_IF: + reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV; + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5514_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5514_I2S_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5514_I2S_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, + RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK, + reg_val); + + return 0; +} + +static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src) + return 0; + + switch (clk_id) { + case RT5514_SCLK_S_MCLK: + reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK; + break; + + case RT5514_SCLK_S_PLL1: + reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL; + break; + + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, reg_val); + + rt5514->sysclk = freq; + rt5514->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5514->pll_in = 0; + rt5514->pll_out = 0; + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, + RT5514_CLK_SYS_PRE_SEL_MCLK); + + return 0; + } + + if (source == rt5514->pll_src && freq_in == rt5514->pll_in && + freq_out == rt5514->pll_out) + return 0; + + switch (source) { + case RT5514_PLL1_S_MCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK); + break; + + case RT5514_PLL1_S_BCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK); + break; + + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1, + pll_code.k_code << RT5514_PLL_K_SFT | + pll_code.n_code << RT5514_PLL_N_SFT | + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT); + regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2, + RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT); + + rt5514->pll_in = freq_in; + rt5514->pll_out = freq_out; + rt5514->pll_src = source; + + return 0; +} + +static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= RT5514_TDM_MODE; + + if (slots == 4) + val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH; + + + switch (slot_width) { + case 20: + val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20; + break; + + case 24: + val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24; + break; + + case 32: + val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32; + break; + + case 16: + default: + break; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE | + RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK | + RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val); + + return 0; +} + +static int rt5514_probe(struct snd_soc_codec *codec) +{ + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + + rt5514->codec = codec; + + return 0; +} + +static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5514_aif_dai_ops = { + .hw_params = rt5514_hw_params, + .set_fmt = rt5514_set_dai_fmt, + .set_sysclk = rt5514_set_dai_sysclk, + .set_pll = rt5514_set_dai_pll, + .set_tdm_slot = rt5514_set_tdm_slot, +}; + +struct snd_soc_dai_driver rt5514_dai[] = { + { + .name = "rt5514-aif1", + .id = 0, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT5514_STEREO_RATES, + .formats = RT5514_FORMATS, + }, + .ops = &rt5514_aif_dai_ops, + } +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5514 = { + .probe = rt5514_probe, + .idle_bias_off = true, + .controls = rt5514_snd_controls, + .num_controls = ARRAY_SIZE(rt5514_snd_controls), + .dapm_widgets = rt5514_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets), + .dapm_routes = rt5514_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes), +}; + +static const struct regmap_config rt5514_i2c_regmap = { + .name = "i2c", + .reg_bits = 32, + .val_bits = 32, + + .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2, + .readable_reg = rt5514_i2c_readable_register, + + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_config rt5514_regmap = { + .reg_bits = 16, + .val_bits = 32, + + .max_register = RT5514_VENDOR_ID2, + .volatile_reg = rt5514_volatile_register, + .readable_reg = rt5514_readable_register, + .reg_read = rt5514_i2c_read, + .reg_write = rt5514_i2c_write, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5514_reg, + .num_reg_defaults = ARRAY_SIZE(rt5514_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5514_i2c_id[] = { + { "rt5514", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5514_of_match[] = { + { .compatible = "realtek,rt5514", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5514_of_match); +#endif + +static int rt5514_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5514_priv *rt5514; + int ret; + unsigned int val; + + rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv), + GFP_KERNEL); + if (rt5514 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5514); + + rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap); + if (IS_ERR(rt5514->i2c_regmap)) { + ret = PTR_ERR(rt5514->i2c_regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap); + if (IS_ERR(rt5514->regmap)) { + ret = PTR_ERR(rt5514->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val); + if (val != RT5514_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5514\n", val); + return -ENODEV; + } + + ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch, + ARRAY_SIZE(rt5514_i2c_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n", + ret); + + ret = regmap_register_patch(rt5514->regmap, rt5514_patch, + ARRAY_SIZE(rt5514_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5514, + rt5514_dai, ARRAY_SIZE(rt5514_dai)); +} + +static int rt5514_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +struct i2c_driver rt5514_i2c_driver = { + .driver = { + .name = "rt5514", + .of_match_table = of_match_ptr(rt5514_of_match), + }, + .probe = rt5514_i2c_probe, + .remove = rt5514_i2c_remove, + .id_table = rt5514_i2c_id, +}; +module_i2c_driver(rt5514_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5514 driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h new file mode 100644 index 000000000000..6ad8a612f659 --- /dev/null +++ b/sound/soc/codecs/rt5514.h @@ -0,0 +1,252 @@ +/* + * rt5514.h -- RT5514 ALSA SoC audio driver + * + * Copyright 2015 Realtek Microelectronics + * Author: Oder Chiou + * + * 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. + */ + +#ifndef __RT5514_H__ +#define __RT5514_H__ + +#define RT5514_DEVICE_ID 0x10ec5514 + +#define RT5514_RESET 0x2000 +#define RT5514_PWR_ANA1 0x2004 +#define RT5514_PWR_ANA2 0x2008 +#define RT5514_I2S_CTRL1 0x2010 +#define RT5514_I2S_CTRL2 0x2014 +#define RT5514_VAD_CTRL6 0x2030 +#define RT5514_EXT_VAD_CTRL 0x206c +#define RT5514_DIG_IO_CTRL 0x2070 +#define RT5514_PAD_CTRL1 0x2080 +#define RT5514_DMIC_DATA_CTRL 0x20a0 +#define RT5514_DIG_SOURCE_CTRL 0x20a4 +#define RT5514_SRC_CTRL 0x20ac +#define RT5514_DOWNFILTER2_CTRL1 0x20d0 +#define RT5514_PLL_SOURCE_CTRL 0x2100 +#define RT5514_CLK_CTRL1 0x2104 +#define RT5514_CLK_CTRL2 0x2108 +#define RT5514_PLL3_CALIB_CTRL1 0x2110 +#define RT5514_PLL3_CALIB_CTRL5 0x2124 +#define RT5514_DELAY_BUF_CTRL1 0x2140 +#define RT5514_DELAY_BUF_CTRL3 0x2148 +#define RT5514_DOWNFILTER0_CTRL1 0x2190 +#define RT5514_DOWNFILTER0_CTRL2 0x2194 +#define RT5514_DOWNFILTER0_CTRL3 0x2198 +#define RT5514_DOWNFILTER1_CTRL1 0x21a0 +#define RT5514_DOWNFILTER1_CTRL2 0x21a4 +#define RT5514_DOWNFILTER1_CTRL3 0x21a8 +#define RT5514_ANA_CTRL_LDO10 0x2200 +#define RT5514_ANA_CTRL_LDO18_16 0x2204 +#define RT5514_ANA_CTRL_ADC12 0x2210 +#define RT5514_ANA_CTRL_ADC21 0x2214 +#define RT5514_ANA_CTRL_ADC22 0x2218 +#define RT5514_ANA_CTRL_ADC23 0x221c +#define RT5514_ANA_CTRL_MICBST 0x2220 +#define RT5514_ANA_CTRL_ADCFED 0x2224 +#define RT5514_ANA_CTRL_INBUF 0x2228 +#define RT5514_ANA_CTRL_VREF 0x222c +#define RT5514_ANA_CTRL_PLL3 0x2240 +#define RT5514_ANA_CTRL_PLL1_1 0x2260 +#define RT5514_ANA_CTRL_PLL1_2 0x2264 +#define RT5514_DMIC_LP_CTRL 0x2e00 +#define RT5514_MISC_CTRL_DSP 0x2e04 +#define RT5514_DSP_CTRL1 0x2f00 +#define RT5514_DSP_CTRL3 0x2f08 +#define RT5514_DSP_CTRL4 0x2f10 +#define RT5514_VENDOR_ID1 0x2ff0 +#define RT5514_VENDOR_ID2 0x2ff4 + +#define RT5514_DSP_MAPPING 0x18000000 + +/* RT5514_PWR_ANA1 (0x2004) */ +#define RT5514_POW_LDO18_IN (0x1 << 5) +#define RT5514_POW_LDO18_IN_BIT 5 +#define RT5514_POW_LDO18_ADC (0x1 << 4) +#define RT5514_POW_LDO18_ADC_BIT 4 +#define RT5514_POW_LDO21 (0x1 << 3) +#define RT5514_POW_LDO21_BIT 3 +#define RT5514_POW_BG_LDO18_IN (0x1 << 2) +#define RT5514_POW_BG_LDO18_IN_BIT 2 +#define RT5514_POW_BG_LDO21 (0x1 << 1) +#define RT5514_POW_BG_LDO21_BIT 1 + +/* RT5514_PWR_ANA2 (0x2008) */ +#define RT5514_POW_PLL1 (0x1 << 18) +#define RT5514_POW_PLL1_BIT 18 +#define RT5514_POW_PLL1_LDO (0x1 << 16) +#define RT5514_POW_PLL1_LDO_BIT 16 +#define RT5514_POW_BG_MBIAS (0x1 << 15) +#define RT5514_POW_BG_MBIAS_BIT 15 +#define RT5514_POW_MBIAS (0x1 << 14) +#define RT5514_POW_MBIAS_BIT 14 +#define RT5514_POW_VREF2 (0x1 << 13) +#define RT5514_POW_VREF2_BIT 13 +#define RT5514_POW_VREF1 (0x1 << 12) +#define RT5514_POW_VREF1_BIT 12 +#define RT5514_POWR_LDO16 (0x1 << 11) +#define RT5514_POWR_LDO16_BIT 11 +#define RT5514_POWL_LDO16 (0x1 << 10) +#define RT5514_POWL_LDO16_BIT 10 +#define RT5514_POW_ADC2 (0x1 << 9) +#define RT5514_POW_ADC2_BIT 9 +#define RT5514_POW_INPUT_BUF (0x1 << 8) +#define RT5514_POW_INPUT_BUF_BIT 8 +#define RT5514_POW_ADC1_R (0x1 << 7) +#define RT5514_POW_ADC1_R_BIT 7 +#define RT5514_POW_ADC1_L (0x1 << 6) +#define RT5514_POW_ADC1_L_BIT 6 +#define RT5514_POW2_BSTR (0x1 << 5) +#define RT5514_POW2_BSTR_BIT 5 +#define RT5514_POW2_BSTL (0x1 << 4) +#define RT5514_POW2_BSTL_BIT 4 +#define RT5514_POW_BSTR (0x1 << 3) +#define RT5514_POW_BSTR_BIT 3 +#define RT5514_POW_BSTL (0x1 << 2) +#define RT5514_POW_BSTL_BIT 2 +#define RT5514_POW_ADCFEDR (0x1 << 1) +#define RT5514_POW_ADCFEDR_BIT 1 +#define RT5514_POW_ADCFEDL (0x1 << 0) +#define RT5514_POW_ADCFEDL_BIT 0 + +/* RT5514_I2S_CTRL1 (0x2010) */ +#define RT5514_TDM_MODE (0x1 << 28) +#define RT5514_TDM_MODE_SFT 28 +#define RT5514_I2S_LR_MASK (0x1 << 26) +#define RT5514_I2S_LR_SFT 26 +#define RT5514_I2S_LR_NOR (0x0 << 26) +#define RT5514_I2S_LR_INV (0x1 << 26) +#define RT5514_I2S_BP_MASK (0x1 << 25) +#define RT5514_I2S_BP_SFT 25 +#define RT5514_I2S_BP_NOR (0x0 << 25) +#define RT5514_I2S_BP_INV (0x1 << 25) +#define RT5514_I2S_DF_MASK (0x7 << 16) +#define RT5514_I2S_DF_SFT 16 +#define RT5514_I2S_DF_I2S (0x0 << 16) +#define RT5514_I2S_DF_LEFT (0x1 << 16) +#define RT5514_I2S_DF_PCM_A (0x2 << 16) +#define RT5514_I2S_DF_PCM_B (0x3 << 16) +#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10) +#define RT5514_TDMSLOT_SEL_RX_SFT 10 +#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10) +#define RT5514_CH_LEN_RX_MASK (0x3 << 8) +#define RT5514_CH_LEN_RX_SFT 8 +#define RT5514_CH_LEN_RX_16 (0x0 << 8) +#define RT5514_CH_LEN_RX_20 (0x1 << 8) +#define RT5514_CH_LEN_RX_24 (0x2 << 8) +#define RT5514_CH_LEN_RX_32 (0x3 << 8) +#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6) +#define RT5514_TDMSLOT_SEL_TX_SFT 6 +#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6) +#define RT5514_CH_LEN_TX_MASK (0x3 << 4) +#define RT5514_CH_LEN_TX_SFT 4 +#define RT5514_CH_LEN_TX_16 (0x0 << 4) +#define RT5514_CH_LEN_TX_20 (0x1 << 4) +#define RT5514_CH_LEN_TX_24 (0x2 << 4) +#define RT5514_CH_LEN_TX_32 (0x3 << 4) +#define RT5514_I2S_DL_MASK (0x3 << 0) +#define RT5514_I2S_DL_SFT 0 +#define RT5514_I2S_DL_16 (0x0 << 0) +#define RT5514_I2S_DL_20 (0x1 << 0) +#define RT5514_I2S_DL_24 (0x2 << 0) +#define RT5514_I2S_DL_8 (0x3 << 0) + +/* RT5514_DIG_SOURCE_CTRL (0x20a4) */ +#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1) +#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1 +#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0) +#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0 + +/* RT5514_PLL_SOURCE_CTRL (0x2100) */ +#define RT5514_PLL_1_SEL_MASK (0x7 << 12) +#define RT5514_PLL_1_SEL_SFT 12 +#define RT5514_PLL_1_SEL_SCLK (0x3 << 12) +#define RT5514_PLL_1_SEL_MCLK (0x4 << 12) + +/* RT5514_CLK_CTRL1 (0x2104) */ +#define RT5514_CLK_AD_ANA1_EN (0x1 << 31) +#define RT5514_CLK_AD_ANA1_EN_BIT 31 +#define RT5514_CLK_AD1_EN (0x1 << 24) +#define RT5514_CLK_AD1_EN_BIT 24 +#define RT5514_CLK_AD0_EN (0x1 << 23) +#define RT5514_CLK_AD0_EN_BIT 23 +#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8) +#define RT5514_CLK_DMIC_OUT_SEL_SFT 8 + +/* RT5514_CLK_CTRL2 (0x2108) */ +#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8) +#define RT5514_CLK_SYS_DIV_OUT_SFT 8 +#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4) +#define RT5514_SEL_ADC_OSR_SFT 4 +#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0) +#define RT5514_CLK_SYS_PRE_SEL_SFT 0 +#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0) +#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0) + +/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */ +#define RT5514_AD_DMIC_MIX (0x1 << 11) +#define RT5514_AD_DMIC_MIX_BIT 11 +#define RT5514_AD_AD_MIX (0x1 << 10) +#define RT5514_AD_AD_MIX_BIT 10 +#define RT5514_AD_AD_MUTE (0x1 << 7) +#define RT5514_AD_AD_MUTE_BIT 7 +#define RT5514_AD_GAIN_MASK (0x7f << 0) +#define RT5514_AD_GAIN_SFT 0 + +/* RT5514_ANA_CTRL_MICBST (0x2220) */ +#define RT5514_SEL_BSTL_MASK (0xf << 4) +#define RT5514_SEL_BSTL_SFT 4 +#define RT5514_SEL_BSTR_MASK (0xf << 0) +#define RT5514_SEL_BSTR_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */ +#define RT5514_PLL_K_MAX 0x1f +#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16) +#define RT5514_PLL_K_SFT 16 +#define RT5514_PLL_N_MAX 0x1ff +#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7) +#define RT5514_PLL_N_SFT 4 +#define RT5514_PLL_M_MAX 0xf +#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0) +#define RT5514_PLL_M_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */ +#define RT5514_PLL_M_BP (0x1 << 2) +#define RT5514_PLL_M_BP_SFT 2 +#define RT5514_PLL_K_BP (0x1 << 1) +#define RT5514_PLL_K_BP_SFT 1 +#define RT5514_EN_LDO_PLL1 (0x1 << 0) +#define RT5514_EN_LDO_PLL1_BIT 0 + +#define RT5514_PLL_INP_MAX 40000000 +#define RT5514_PLL_INP_MIN 256000 + +/* System Clock Source */ +enum { + RT5514_SCLK_S_MCLK, + RT5514_SCLK_S_PLL1, +}; + +/* PLL1 Source */ +enum { + RT5514_PLL1_S_MCLK, + RT5514_PLL1_S_BCLK, +}; + +struct rt5514_priv { + struct snd_soc_codec *codec; + struct regmap *i2c_regmap, *regmap; + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int pll_src; + int pll_in; + int pll_out; +}; + +#endif /* __RT5514_H__ */ From c308abe45e2a7bc58e22d9b6d04dccf76a1b2012 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 9 Feb 2016 07:04:09 +0000 Subject: [PATCH 24/74] ASoC: rsnd: rsnd_ssi_is_multi_slave() macro uses rsnd_ssi_multi_slaves() b4c83b171 ("ASoC: rsnd: add Multi channel support") added Multi channel support, and current rsnd_ssi_is_multi_slave()'s check method is !SSI equals SSIM1/2/3. But, SSI parent also hit to this macro. Because of this reason, some stream which needs SSI parent clock can't work correctly. This patch uses rsnd_ssi_multi_slaves() to solve this issue. This issue was reported by Dung. Reported-by: Nguyen Viet Dung Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 803e9ae65915..592505a4bc13 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -94,7 +94,8 @@ struct rsnd_ssi { #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_ssi_mode_flags(p) ((p)->flags) #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) -#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io)) +#define rsnd_ssi_is_multi_slave(mod, io) \ + (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod))) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -167,9 +168,6 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod, u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) { struct rsnd_mod *mod; - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); enum rsnd_mod_type types[] = { RSND_MOD_SSIM1, RSND_MOD_SSIM2, @@ -177,16 +175,6 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) }; int i, mask; - switch (runtime->channels) { - case 2: /* Multi channel is not needed for Stereo */ - return 0; - case 6: - break; - default: - dev_err(dev, "unsupported channel\n"); - return 0; - } - mask = 0; for (i = 0; i < ARRAY_SIZE(types); i++) { mod = rsnd_io_to_mod(io, types[i]); From d2240f0dad488c66e14c45762bd23999901f57a1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:13:13 +0000 Subject: [PATCH 25/74] ASoC: rsnd: rename RSND_DVC_CHANNELS to RSND_MAX_CHANNELS The channels number is not only for DVC. Let's rename it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 2 +- sound/soc/sh/rcar/dvc.c | 6 +++--- sound/soc/sh/rcar/rsnd.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 704ba7ae9eaf..f86d62730614 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -988,7 +988,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, int ch_size, u32 max) { - if (ch_size > RSND_DVC_CHANNELS) + if (ch_size > RSND_MAX_CHANNELS) return -EINVAL; _cfg->cfg.max = max; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 302c193f674d..d757f1316385 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -83,15 +83,15 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u32 val[RSND_DVC_CHANNELS]; + u32 val[RSND_MAX_CHANNELS]; int i; /* Enable Ramp */ if (dvc->ren.val) - for (i = 0; i < RSND_DVC_CHANNELS; i++) + for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.cfg.max; else - for (i = 0; i < RSND_DVC_CHANNELS; i++) + for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.val[i]; /* Enable Digital Volume */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index bbaf89b6de8a..61cb4aefdfd9 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -550,10 +550,10 @@ struct rsnd_kctrl_cfg { struct snd_kcontrol *kctrl; }; -#define RSND_DVC_CHANNELS 8 +#define RSND_MAX_CHANNELS 8 struct rsnd_kctrl_cfg_m { struct rsnd_kctrl_cfg cfg; - u32 val[RSND_DVC_CHANNELS]; + u32 val[RSND_MAX_CHANNELS]; }; struct rsnd_kctrl_cfg_s { From 0fbab951db17085fbb521b3a50550990b763bdf4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:13:45 +0000 Subject: [PATCH 26/74] ASoC: rsnd: setup SRC_ROUTE_MODE0 before SRC_SRCIR SRC_ROUTE_MODE0 determines whether to use SRC. Thus, it should be setup before SRC_SRCIR. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index ab5f13155055..dab0954196b7 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -249,6 +249,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, break; } + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ rsnd_mod_write(mod, SRC_ADINR, adinr); rsnd_mod_write(mod, SRC_IFSCR, ifscr); @@ -258,7 +260,6 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_BSISR, bsisr); rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ - rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); From 1120dbff2abd3dd9ca3f0736d0690b9592cdadb3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:14:09 +0000 Subject: [PATCH 27/74] ASoC: rsnd: indicates status failed SSI Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 592505a4bc13..a72ce284e34b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -140,7 +140,8 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, udelay(50); } - dev_warn(dev, "status check failed\n"); + dev_warn(dev, "%s[%d] status check failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); } static int rsnd_ssi_irq(struct rsnd_mod *mod, From 26d34b11af6a344da6191beca2e2883f65d2597a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:14:37 +0000 Subject: [PATCH 28/74] ASoC: rsnd: SSI function parameter uses "mod" instead of "ssi" To reduce confusion, SSI uses "mod" instead of "ssi" as function parameter Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index a72ce284e34b..c5c4510afb2d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -188,14 +188,14 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) return mask; } -static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, +static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); int slots = rsnd_get_slot_width(io); int j, ret; @@ -255,11 +255,11 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, return -EIO; } -static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, +static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); if (!rsnd_rdai_is_clk_master(rdai)) @@ -277,11 +277,12 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, rsnd_adg_ssi_clk_stop(mod); } -static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, +static int rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr_own; u32 cr_mode; u32 wsr; @@ -317,7 +318,7 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, return -EINVAL; } - if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) { + if (rsnd_ssi_is_dma_mode(mod)) { cr_mode = UIEN | OIEN | /* over/under run */ DMEN; /* DMA : enable DMA */ } else { @@ -356,14 +357,14 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, rsnd_mod_power_on(mod); - ret = rsnd_ssi_master_clk_start(ssi, io); + ret = rsnd_ssi_master_clk_start(mod, io); if (ret < 0) return ret; if (rsnd_ssi_is_parent(mod, io)) return 0; - ret = rsnd_ssi_config_init(ssi, io); + ret = rsnd_ssi_config_init(mod, io); if (ret < 0) return ret; @@ -389,7 +390,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, if (!rsnd_ssi_is_parent(mod, io)) ssi->cr_own = 0; - rsnd_ssi_master_clk_stop(ssi, io); + rsnd_ssi_master_clk_stop(mod, io); rsnd_mod_power_off(mod); From 5bf5d8fc7f5a8a1e75413939e4bdb00ebc2d5610 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:16:04 +0000 Subject: [PATCH 29/74] ASoC: rsnd: fixup forever loop bug on SSI commit b5b442abd9 ("ASoC: rsnd: add .irq callback") added .irq support, and it cares both parent SSI and normal SSI. But it should care only normal SSI. Otherwise SSI might be forever loop if SSI is used as both parent SSI and normal SSI (= 2 users), and if under/over run error happen. Because irq disable do nothing in such case. This patch solve this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index c5c4510afb2d..90c3f58821db 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -149,13 +149,12 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod, struct rsnd_priv *priv, int enable) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 val = 0; if (rsnd_is_gen1(priv)) return 0; - if (ssi->usrcnt != 1) + if (rsnd_ssi_is_parent(mod, io)) return 0; if (enable) From c8e969a85ecb982dccf2ba13ba9aff9f1a68eab2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:16:43 +0000 Subject: [PATCH 30/74] ASoC: rsnd: add missing .irq callback for DMA commit b5b442abd9d5 ("ASoC: rsnd: add .irq callback") added .irq callback but SSI DMA is missing it. This patch adds it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 90c3f58821db..b1a29e28b251 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -694,6 +694,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; From 0dc6bf75023a42895962800020583c19e0b87159 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:17:18 +0000 Subject: [PATCH 31/74] ASoC: rsnd: tidyup SSI init/start sequence SSI want to have SSIWSR settings and SSICR settings without EN bit when init, and SSICR EN bit only when start timing. Otherwise, SSI output signal might be unstable. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index b1a29e28b251..120587270fe7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -276,7 +276,7 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, rsnd_adg_ssi_clk_stop(mod); } -static int rsnd_ssi_config_init(struct rsnd_mod *mod, +static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); @@ -313,8 +313,6 @@ static int rsnd_ssi_config_init(struct rsnd_mod *mod, case 32: cr_own |= DWL_24; break; - default: - return -EINVAL; } if (rsnd_ssi_is_dma_mode(mod)) { @@ -338,8 +336,16 @@ static int rsnd_ssi_config_init(struct rsnd_mod *mod, ssi->cr_own = cr_own; ssi->cr_mode = cr_mode; ssi->wsr = wsr; +} - return 0; +static void rsnd_ssi_register_setup(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + rsnd_mod_write(mod, SSIWSR, ssi->wsr); + rsnd_mod_write(mod, SSICR, ssi->cr_own | + ssi->cr_clk | + ssi->cr_mode); /* without EN */ } /* @@ -360,12 +366,10 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (ret < 0) return ret; - if (rsnd_ssi_is_parent(mod, io)) - return 0; + if (!rsnd_ssi_is_parent(mod, io)) + rsnd_ssi_config_init(mod, io); - ret = rsnd_ssi_config_init(mod, io); - if (ret < 0) - return ret; + rsnd_ssi_register_setup(mod); /* clear error status */ rsnd_ssi_status_clear(mod); @@ -428,22 +432,14 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr; - - cr = ssi->cr_own | - ssi->cr_clk | - ssi->cr_mode; - /* * EN will be set via SSIU :: SSI_CONTROL * if Multi channel mode */ - if (!rsnd_ssi_multi_slaves(io)) - cr |= EN; + if (rsnd_ssi_multi_slaves(io)) + return 0; - rsnd_mod_write(mod, SSICR, cr); - rsnd_mod_write(mod, SSIWSR, ssi->wsr); + rsnd_mod_bset(mod, SSICR, EN, EN); return 0; } From 098bd8911a5eacc3b70fdc09fa4084657511c584 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:17:52 +0000 Subject: [PATCH 32/74] ASoC: rsnd: Parent SSI attach is not needed if not clock master Parent SSI is needed if it is PIN sharing and clock master, otherwise, not needed. But, whether clockk master is judged on .set_fmt, thus, it can't call rsnd_ssi_parent_attach() on .probe. Now, .pcm_new will be called after .set_fmt, so this patch reuses it at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 120587270fe7..b5c6f0c274c3 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -545,12 +545,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) * SSI PIO */ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) + struct rsnd_dai_stream *io) { + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + if (!__rsnd_ssi_is_pin_sharing(mod)) return; + if (!rsnd_rdai_is_clk_master(rdai)) + return; + switch (rsnd_mod_id(mod)) { case 1: case 2: @@ -565,6 +570,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, } } +static int rsnd_ssi_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + /* + * rsnd_rdai_is_clk_master() will be enabled after set_fmt, + * and, pcm_new will be called after it. + * This function reuse pcm_new at this point. + */ + rsnd_ssi_parent_attach(mod, io); + + return 0; +} + static int rsnd_ssi_common_probe(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -580,7 +599,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, if (rsnd_ssi_is_multi_slave(mod, io)) return 0; - rsnd_ssi_parent_attach(mod, io, priv); + /* + * It can't judge ssi parent at this point + * see rsnd_ssi_pcm_new() + */ ret = rsnd_ssiu_attach(io, mod); if (ret < 0) @@ -602,6 +624,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .hw_params = rsnd_ssi_hw_params, }; @@ -691,6 +714,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; From 615fb6c7b13b7f142f5f8e23e5f8593dd1e7b319 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:18:16 +0000 Subject: [PATCH 33/74] ASoC: rsnd: move rsnd_ssi_irq() position prepare for runtime judging for SSI work Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index b5c6f0c274c3..d46bc08ad977 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -144,27 +144,6 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, rsnd_mod_name(mod), rsnd_mod_id(mod)); } -static int rsnd_ssi_irq(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv, - int enable) -{ - u32 val = 0; - - if (rsnd_is_gen1(priv)) - return 0; - - if (rsnd_ssi_is_parent(mod, io)) - return 0; - - if (enable) - val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; - - rsnd_mod_write(mod, SSI_INT_ENABLE, val); - - return 0; -} - u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) { struct rsnd_mod *mod; @@ -480,6 +459,27 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, return 0; } +static int rsnd_ssi_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) +{ + u32 val = 0; + + if (rsnd_is_gen1(priv)) + return 0; + + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + if (enable) + val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; + + rsnd_mod_write(mod, SSI_INT_ENABLE, val); + + return 0; +} + static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { From 4f5c634d58e71963d3c34a0a4af9ec71785f094f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:18:54 +0000 Subject: [PATCH 34/74] ASoC: rsnd: judge multi SSI in runtime Current rsnd supports multi SSI (maximum 4 SSI for 8ch), and, it should determine whether using each SSI or not in runtime. Current judgement is vague, and had broken by c308abe45e2("ASoC: rsnd: rsnd_ssi_is_multi_slave() macro uses rsnd_ssi_multi_slaves()") This patch makes clean it, and solve this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 2 +- sound/soc/sh/rcar/rsnd.h | 2 +- sound/soc/sh/rcar/ssi.c | 15 +++++++++++++-- sound/soc/sh/rcar/ssiu.c | 6 +++--- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f86d62730614..7bc5b724fbf5 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -230,7 +230,7 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io) int chan = runtime->channels; /* Multi channel Mode */ - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) chan /= rsnd_get_slot_num(io); /* TDM Extend Mode needs 8ch */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 61cb4aefdfd9..5f613eb42614 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -599,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); -u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io); +u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d46bc08ad977..32f1f5fb82b6 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -144,7 +144,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, rsnd_mod_name(mod), rsnd_mod_id(mod)); } -u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) +static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) { struct rsnd_mod *mod; enum rsnd_mod_type types[] = { @@ -166,6 +166,17 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) return mask; } +u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 mask = rsnd_ssi_multi_slaves(io); + + if (mask && (runtime->channels >= 6)) + return mask; + + return 0; +} + static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { @@ -415,7 +426,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, * EN will be set via SSIU :: SSI_CONTROL * if Multi channel mode */ - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) return 0; rsnd_mod_bset(mod, SSICR, EN, EN); diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 11e55889b401..1b8ea0e09bc2 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -27,7 +27,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io); + u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io); int use_busif = rsnd_ssi_use_busif(io); int id = rsnd_mod_id(mod); u32 mask1, val1; @@ -136,7 +136,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SSI_CTRL, 0x1); - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) rsnd_mod_write(mod, SSI_CONTROL, 0x1); return 0; @@ -151,7 +151,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SSI_CTRL, 0); - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) rsnd_mod_write(mod, SSI_CONTROL, 0); return 0; From fd9adcfdc1434fdd4d0a14ddfe686449a6ffeeb3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 18 Feb 2016 08:19:12 +0000 Subject: [PATCH 35/74] ASoC: rsnd: judge work SSI in runtime Current rsnd supports multi SSI (maximum 4 SSI for 8ch), and, it should determine whether using each SSI or not in runtime. All SSIs are not used even if there are 4 SSI in case of stereo. Current driver setups un-used SSI in such case. It is no problem, but not needed. This patch judges it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 32f1f5fb82b6..0979db8af8f9 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -96,6 +96,8 @@ struct rsnd_ssi { #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) #define rsnd_ssi_is_multi_slave(mod, io) \ (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod))) +#define rsnd_ssi_is_run_mods(mod, io) \ + (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -166,6 +168,16 @@ static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) return mask; } +static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + + return rsnd_ssi_multi_slaves_runtime(io) | + 1 << rsnd_mod_id(ssi_mod) | + 1 << rsnd_mod_id(ssi_parent_mod); +} + u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); @@ -348,6 +360,9 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + ssi->usrcnt++; rsnd_mod_power_on(mod); @@ -374,6 +389,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + if (!ssi->usrcnt) { dev_err(dev, "%s[%d] usrcnt error\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); @@ -422,6 +440,9 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + /* * EN will be set via SSIU :: SSI_CONTROL * if Multi channel mode @@ -441,6 +462,9 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + /* * don't stop if not last user * see also @@ -483,6 +507,9 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod, if (rsnd_ssi_is_parent(mod, io)) return 0; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + if (enable) val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; From 91d31b9f8e7662726f273fc32b25f4099d78de4a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:22:18 +0000 Subject: [PATCH 36/74] regmap: add regmap_update_bits_base() Current regmap has many similar update functions like below, but the difference is very few. regmap_update_bits() regmap_update_bits_async() regmap_update_bits_check() regmap_update_bits_check_async() Furthermore, we can add *force* write option too in the future. This patch adds new regmap_update_bits_base() which is feature merged function. Above functions can be merged into it by macro. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 40 ++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 11 ++++++++++ 2 files changed, 51 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee54e841de4a..4e35b2f41304 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2647,6 +2647,46 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, return ret; } +/** + * regmap_update_bits_base: + * Perform a read/modify/write cycle on the + * register map with change, async, force option + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * @change: Boolean indicating if a write was done + * @async: Boolean indicating asynchronously + * @force: Boolean indicating use force update + * + * if async was true, + * With most buses the read must be done synchronously so this is most + * useful for devices with a cache which do not need to interact with + * the hardware to determine the current register value. + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits_base(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + int ret; + + map->lock(map->lock_arg); + + map->async = async; + + ret = _regmap_update_bits(map, reg, mask, val, change, force); + + map->async = false; + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_update_bits_base); + /** * regmap_update_bits: Perform a read/modify/write cycle on the register map * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 18394343f489..28e50a3d2872 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -691,6 +691,9 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count); +int regmap_update_bits_base(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); int regmap_write_bits(struct regmap *map, unsigned int reg, @@ -937,6 +940,14 @@ static inline int regmap_bulk_read(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { From ca7a94464b5457a8dc5add19f6fc3bea59d6193f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:22:42 +0000 Subject: [PATCH 37/74] regmap: merge regmap_update_bits() into macro Current regmap has many similar update functions like below, but the difference is very few. regmap_update_bits() regmap_update_bits_async() regmap_update_bits_check() regmap_update_bits_check_async() Furthermore, we can add *force* write option too in the future. This patch merges regmap_update_bits() into macro by using regmap_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 23 ----------------------- include/linux/regmap.h | 12 +++--------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4e35b2f41304..281898a97e8f 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2687,29 +2687,6 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_base); -/** - * regmap_update_bits: Perform a read/modify/write cycle on the register map - * - * @map: Register map to update - * @reg: Register to update - * @mask: Bitmask to change - * @val: New value for bitmask - * - * Returns zero for success, a negative number on error. - */ -int regmap_update_bits(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val) -{ - int ret; - - map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, NULL, false); - map->unlock(map->lock_arg); - - return ret; -} -EXPORT_SYMBOL_GPL(regmap_update_bits); - /** * regmap_write_bits: Perform a read/modify/write cycle on the register map * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 28e50a3d2872..500b36cbc7aa 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -65,6 +65,9 @@ struct reg_sequence { unsigned int delay_us; }; +#define regmap_update_bits(map, reg, mask, val) \ + regmap_update_bits_base(map, reg, mask, val, NULL, false, false) + #ifdef CONFIG_REGMAP enum regmap_endian { @@ -694,8 +697,6 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, int regmap_update_bits_base(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change, bool async, bool force); -int regmap_update_bits(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val); int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); int regmap_update_bits_async(struct regmap *map, unsigned int reg, @@ -948,13 +949,6 @@ static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg, return -EINVAL; } -static inline int regmap_update_bits(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val) -{ - WARN_ONCE(1, "regmap API is disabled"); - return -EINVAL; -} - static inline int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { From 30ed9cb7a49b499ebc6061e4ff38e88cb4857cad Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:23:01 +0000 Subject: [PATCH 38/74] regmap: merge regmap_update_bits_async() into macro Current regmap has many similar update functions like below, but the difference is very few. regmap_update_bits() regmap_update_bits_async() regmap_update_bits_check() regmap_update_bits_check_async() Furthermore, we can add *force* write option too in the future. This patch merges regmap_update_bits_async() into macro by using regmap_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 34 ---------------------------------- include/linux/regmap.h | 12 ++---------- 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 281898a97e8f..c2255f604c03 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2710,40 +2710,6 @@ int regmap_write_bits(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_write_bits); -/** - * regmap_update_bits_async: Perform a read/modify/write cycle on the register - * map asynchronously - * - * @map: Register map to update - * @reg: Register to update - * @mask: Bitmask to change - * @val: New value for bitmask - * - * With most buses the read must be done synchronously so this is most - * useful for devices with a cache which do not need to interact with - * the hardware to determine the current register value. - * - * Returns zero for success, a negative number on error. - */ -int regmap_update_bits_async(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val) -{ - int ret; - - map->lock(map->lock_arg); - - map->async = true; - - ret = _regmap_update_bits(map, reg, mask, val, NULL, false); - - map->async = false; - - map->unlock(map->lock_arg); - - return ret; -} -EXPORT_SYMBOL_GPL(regmap_update_bits_async); - /** * regmap_update_bits_check: Perform a read/modify/write cycle on the * register map and report if updated diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 500b36cbc7aa..90c8b0e99f9d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -67,6 +67,8 @@ struct reg_sequence { #define regmap_update_bits(map, reg, mask, val) \ regmap_update_bits_base(map, reg, mask, val, NULL, false, false) +#define regmap_update_bits_async(map, reg, mask, val)\ + regmap_update_bits_base(map, reg, mask, val, NULL, true, false) #ifdef CONFIG_REGMAP @@ -699,8 +701,6 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg, bool *change, bool async, bool force); int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); -int regmap_update_bits_async(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val); int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); @@ -956,14 +956,6 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg, return -EINVAL; } -static inline int regmap_update_bits_async(struct regmap *map, - unsigned int reg, - unsigned int mask, unsigned int val) -{ - WARN_ONCE(1, "regmap API is disabled"); - return -EINVAL; -} - static inline int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, From 98c2dc48694a47109fff430a216fc13a9b45a4a1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:23:17 +0000 Subject: [PATCH 39/74] regmap: merge regmap_update_bits_check() into macro Current regmap has many similar update functions like below, but the difference is very few. regmap_update_bits() regmap_update_bits_async() regmap_update_bits_check() regmap_update_bits_check_async() Furthermore, we can add *force* write option too in the future. This patch merges regmap_update_bits_check() into macro by using regmap_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 25 ------------------------- include/linux/regmap.h | 14 ++------------ 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index c2255f604c03..ce24e9688b05 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2710,31 +2710,6 @@ int regmap_write_bits(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_write_bits); -/** - * regmap_update_bits_check: Perform a read/modify/write cycle on the - * register map and report if updated - * - * @map: Register map to update - * @reg: Register to update - * @mask: Bitmask to change - * @val: New value for bitmask - * @change: Boolean indicating if a write was done - * - * Returns zero for success, a negative number on error. - */ -int regmap_update_bits_check(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change) -{ - int ret; - - map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, change, false); - map->unlock(map->lock_arg); - return ret; -} -EXPORT_SYMBOL_GPL(regmap_update_bits_check); - /** * regmap_update_bits_check_async: Perform a read/modify/write cycle on the * register map asynchronously and report if diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 90c8b0e99f9d..dd227dd5e5f8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -69,6 +69,8 @@ struct reg_sequence { regmap_update_bits_base(map, reg, mask, val, NULL, false, false) #define regmap_update_bits_async(map, reg, mask, val)\ regmap_update_bits_base(map, reg, mask, val, NULL, true, false) +#define regmap_update_bits_check(map, reg, mask, val, change)\ + regmap_update_bits_base(map, reg, mask, val, change, false, false) #ifdef CONFIG_REGMAP @@ -701,9 +703,6 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg, bool *change, bool async, bool force); int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); -int regmap_update_bits_check(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change); int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); @@ -956,15 +955,6 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg, return -EINVAL; } -static inline int regmap_update_bits_check(struct regmap *map, - unsigned int reg, - unsigned int mask, unsigned int val, - bool *change) -{ - WARN_ONCE(1, "regmap API is disabled"); - return -EINVAL; -} - static inline int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, unsigned int mask, From 89d8d4b833b0b29e0e95bd0cd51e80f5ee7a6b0a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:23:37 +0000 Subject: [PATCH 40/74] regmap: merge regmap_update_bits_check_async() into macro Current regmap has many similar update functions like below, but the difference is very few. regmap_update_bits() regmap_update_bits_async() regmap_update_bits_check() regmap_update_bits_check_async() Furthermore, we can add *force* write option too in the future. This patch merges regmap_update_bits_check_async() into macro by using regmap_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 37 ------------------------------------ include/linux/regmap.h | 15 ++------------- 2 files changed, 2 insertions(+), 50 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ce24e9688b05..015135a656b7 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2710,43 +2710,6 @@ int regmap_write_bits(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_write_bits); -/** - * regmap_update_bits_check_async: Perform a read/modify/write cycle on the - * register map asynchronously and report if - * updated - * - * @map: Register map to update - * @reg: Register to update - * @mask: Bitmask to change - * @val: New value for bitmask - * @change: Boolean indicating if a write was done - * - * With most buses the read must be done synchronously so this is most - * useful for devices with a cache which do not need to interact with - * the hardware to determine the current register value. - * - * Returns zero for success, a negative number on error. - */ -int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change) -{ - int ret; - - map->lock(map->lock_arg); - - map->async = true; - - ret = _regmap_update_bits(map, reg, mask, val, change, false); - - map->async = false; - - map->unlock(map->lock_arg); - - return ret; -} -EXPORT_SYMBOL_GPL(regmap_update_bits_check_async); - void regmap_async_complete_cb(struct regmap_async *async, int ret) { struct regmap *map = async->map; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index dd227dd5e5f8..4d8e1edb4407 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -71,6 +71,8 @@ struct reg_sequence { regmap_update_bits_base(map, reg, mask, val, NULL, true, false) #define regmap_update_bits_check(map, reg, mask, val, change)\ regmap_update_bits_base(map, reg, mask, val, change, false, false) +#define regmap_update_bits_check_async(map, reg, mask, val, change)\ + regmap_update_bits_base(map, reg, mask, val, change, true, false) #ifdef CONFIG_REGMAP @@ -703,9 +705,6 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg, bool *change, bool async, bool force); int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); -int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val, - bool *change); int regmap_get_val_bytes(struct regmap *map); int regmap_get_max_register(struct regmap *map); int regmap_get_reg_stride(struct regmap *map); @@ -955,16 +954,6 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg, return -EINVAL; } -static inline int regmap_update_bits_check_async(struct regmap *map, - unsigned int reg, - unsigned int mask, - unsigned int val, - bool *change) -{ - WARN_ONCE(1, "regmap API is disabled"); - return -EINVAL; -} - static inline int regmap_get_val_bytes(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); From 28972eaa34f384eef5e33f36e00d8fa21ca44375 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:23:55 +0000 Subject: [PATCH 41/74] regmap: add regmap_field_update_bits_base() This patch adds new regmap_field_update_bits_base() which is using regmap_update_bits_base(). Current regmap_field_xxx() can be merged into it by macro. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 27 +++++++++++++++++++++++++++ include/linux/regmap.h | 12 +++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 015135a656b7..e534105f47f6 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1689,6 +1689,33 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_raw_write); +/** + * regmap_field_update_bits_base(): + * Perform a read/modify/write cycle on the register field + * with change, async, force option + * + * @field: Register field to write to + * @mask: Bitmask to change + * @val: Value to be written + * @change: Boolean indicating if a write was done + * @async: Boolean indicating asynchronously + * @force: Boolean indicating use force update + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits_base(field->regmap, field->reg, + mask, val << field->shift, + change, async, force); +} +EXPORT_SYMBOL_GPL(regmap_field_update_bits_base); + /** * regmap_field_write(): Write a value to a single register field * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4d8e1edb4407..23bf7657e485 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -773,9 +773,11 @@ void devm_regmap_field_free(struct device *dev, struct regmap_field *field); int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_write(struct regmap_field *field, unsigned int val); +int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val); - int regmap_fields_write(struct regmap_field *field, unsigned int id, unsigned int val); int regmap_fields_force_write(struct regmap_field *field, unsigned int id, @@ -954,6 +956,14 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_get_val_bytes(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); From 3674124b35894631f8f4d33ab041e713078bfd4b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:24:15 +0000 Subject: [PATCH 42/74] regmap: merge regmap_field_write() into macro This patch merges regmap_field_write() into macro by using regmap_field_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 16 ---------------- include/linux/regmap.h | 4 +++- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e534105f47f6..228dce237658 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1716,22 +1716,6 @@ int regmap_field_update_bits_base(struct regmap_field *field, } EXPORT_SYMBOL_GPL(regmap_field_update_bits_base); -/** - * regmap_field_write(): Write a value to a single register field - * - * @field: Register field to write to - * @val: Value to be written - * - * A value of zero will be returned on success, a negative errno will - * be returned in error cases. - */ -int regmap_field_write(struct regmap_field *field, unsigned int val) -{ - return regmap_update_bits(field->regmap, field->reg, - field->mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_field_write); - /** * regmap_field_update_bits(): Perform a read/modify/write cycle * on the register field diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 23bf7657e485..13e9ebdea1c7 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -74,6 +74,9 @@ struct reg_sequence { #define regmap_update_bits_check_async(map, reg, mask, val, change)\ regmap_update_bits_base(map, reg, mask, val, change, true, false) +#define regmap_field_write(field, val) \ + regmap_field_update_bits_base(field, ~0, val, NULL, false, false) + #ifdef CONFIG_REGMAP enum regmap_endian { @@ -772,7 +775,6 @@ struct regmap_field *devm_regmap_field_alloc(struct device *dev, void devm_regmap_field_free(struct device *dev, struct regmap_field *field); int regmap_field_read(struct regmap_field *field, unsigned int *val); -int regmap_field_write(struct regmap_field *field, unsigned int val); int regmap_field_update_bits_base(struct regmap_field *field, unsigned int mask, unsigned int val, bool *change, bool async, bool force); From 721ed64dda3774c619874866ca4f9a38ae6750af Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:24:33 +0000 Subject: [PATCH 43/74] regmap: merge regmap_field_update_bits() into macro This patch merges regmap_field_update_bits() into macro by using regmap_field_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 20 -------------------- include/linux/regmap.h | 4 ++-- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 228dce237658..606c9b53526b 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1716,26 +1716,6 @@ int regmap_field_update_bits_base(struct regmap_field *field, } EXPORT_SYMBOL_GPL(regmap_field_update_bits_base); -/** - * regmap_field_update_bits(): Perform a read/modify/write cycle - * on the register field - * - * @field: Register field to write to - * @mask: Bitmask to change - * @val: Value to be written - * - * A value of zero will be returned on success, a negative errno will - * be returned in error cases. - */ -int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val) -{ - mask = (mask << field->shift) & field->mask; - - return regmap_update_bits(field->regmap, field->reg, - mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_field_update_bits); - /** * regmap_fields_write(): Write a value to a single register field with port ID * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 13e9ebdea1c7..e525beeaa2c6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -76,6 +76,8 @@ struct reg_sequence { #define regmap_field_write(field, val) \ regmap_field_update_bits_base(field, ~0, val, NULL, false, false) +#define regmap_field_update_bits(field, mask, val)\ + regmap_field_update_bits_base(field, mask, val, NULL, false, false) #ifdef CONFIG_REGMAP @@ -778,8 +780,6 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_update_bits_base(struct regmap_field *field, unsigned int mask, unsigned int val, bool *change, bool async, bool force); -int regmap_field_update_bits(struct regmap_field *field, - unsigned int mask, unsigned int val); int regmap_fields_write(struct regmap_field *field, unsigned int id, unsigned int val); int regmap_fields_force_write(struct regmap_field *field, unsigned int id, From e126edec184ea3049cc1f8b652c6eeb06aa65fda Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:24:51 +0000 Subject: [PATCH 44/74] regmap: add regmap_fields_update_bits_base() This patch adds new regmap_fields_update_bits_base() which is using regmap_update_bits_base(). Current regmap_fields_xxx() can be merged into it by macro. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 32 ++++++++++++++++++++++++++++++++ include/linux/regmap.h | 12 ++++++++++++ 2 files changed, 44 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 606c9b53526b..0c7773fadd48 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1716,6 +1716,38 @@ int regmap_field_update_bits_base(struct regmap_field *field, } EXPORT_SYMBOL_GPL(regmap_field_update_bits_base); +/** + * regmap_fields_update_bits_base(): + * Perform a read/modify/write cycle on the register field + * with change, async, force option + * + * @field: Register field to write to + * @id: port ID + * @mask: Bitmask to change + * @val: Value to be written + * @change: Boolean indicating if a write was done + * @async: Boolean indicating asynchronously + * @force: Boolean indicating use force update + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + if (id >= field->id_size) + return -EINVAL; + + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits_base(field->regmap, + field->reg + (field->id_offset * id), + mask, val << field->shift, + change, async, force); +} +EXPORT_SYMBOL_GPL(regmap_fields_update_bits_base); + /** * regmap_fields_write(): Write a value to a single register field with port ID * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index e525beeaa2c6..2735a3df7eab 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -788,6 +788,9 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id, unsigned int *val); int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, unsigned int mask, unsigned int val); +int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); /** * Description of an IRQ for the generic regmap irq_chip. @@ -966,6 +969,15 @@ static inline int regmap_field_update_bits_base(struct regmap_field *field, return -EINVAL; } +static inline int regmap_fields_update_bits_base(struct regmap_field *field, + unsigned int id, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_get_val_bytes(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); From bbf2c46f46e23a496337e143cd012c013c6c7910 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:25:15 +0000 Subject: [PATCH 45/74] regmap: merge regmap_fields_write() into macro This patch merges regmap_fields_write() into macro by using regmap_fields_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 22 ---------------------- include/linux/regmap.h | 5 +++-- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0c7773fadd48..4b14745249ba 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1748,28 +1748,6 @@ int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, } EXPORT_SYMBOL_GPL(regmap_fields_update_bits_base); -/** - * regmap_fields_write(): Write a value to a single register field with port ID - * - * @field: Register field to write to - * @id: port ID - * @val: Value to be written - * - * A value of zero will be returned on success, a negative errno will - * be returned in error cases. - */ -int regmap_fields_write(struct regmap_field *field, unsigned int id, - unsigned int val) -{ - if (id >= field->id_size) - return -EINVAL; - - return regmap_update_bits(field->regmap, - field->reg + (field->id_offset * id), - field->mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_fields_write); - int regmap_fields_force_write(struct regmap_field *field, unsigned int id, unsigned int val) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 2735a3df7eab..5f438a4df5e6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -79,6 +79,9 @@ struct reg_sequence { #define regmap_field_update_bits(field, mask, val)\ regmap_field_update_bits_base(field, mask, val, NULL, false, false) +#define regmap_fields_write(field, id, val) \ + regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, false) + #ifdef CONFIG_REGMAP enum regmap_endian { @@ -780,8 +783,6 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_update_bits_base(struct regmap_field *field, unsigned int mask, unsigned int val, bool *change, bool async, bool force); -int regmap_fields_write(struct regmap_field *field, unsigned int id, - unsigned int val); int regmap_fields_force_write(struct regmap_field *field, unsigned int id, unsigned int val); int regmap_fields_read(struct regmap_field *field, unsigned int id, From 48138609135fc9c363f034596e14bff5dbf9f33f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:25:32 +0000 Subject: [PATCH 46/74] regmap: merge regmap_fields_update_bits() into macro This patch merges regmap_fields_update_bits() into macro by using regmap_field_update_bits_base(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 26 -------------------------- include/linux/regmap.h | 4 ++-- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4b14745249ba..79d7f51019d7 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1760,32 +1760,6 @@ int regmap_fields_force_write(struct regmap_field *field, unsigned int id, } EXPORT_SYMBOL_GPL(regmap_fields_force_write); -/** - * regmap_fields_update_bits(): Perform a read/modify/write cycle - * on the register field - * - * @field: Register field to write to - * @id: port ID - * @mask: Bitmask to change - * @val: Value to be written - * - * A value of zero will be returned on success, a negative errno will - * be returned in error cases. - */ -int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, - unsigned int mask, unsigned int val) -{ - if (id >= field->id_size) - return -EINVAL; - - mask = (mask << field->shift) & field->mask; - - return regmap_update_bits(field->regmap, - field->reg + (field->id_offset * id), - mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_fields_update_bits); - /* * regmap_bulk_write(): Write multiple registers to the device * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 5f438a4df5e6..7d3d498fd3e8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -81,6 +81,8 @@ struct reg_sequence { #define regmap_fields_write(field, id, val) \ regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, false) +#define regmap_fields_update_bits(field, id, mask, val)\ + regmap_fields_update_bits_base(field, id, mask, val, NULL, false, false) #ifdef CONFIG_REGMAP @@ -787,8 +789,6 @@ int regmap_fields_force_write(struct regmap_field *field, unsigned int id, unsigned int val); int regmap_fields_read(struct regmap_field *field, unsigned int id, unsigned int *val); -int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, - unsigned int mask, unsigned int val); int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, unsigned int mask, unsigned int val, bool *change, bool async, bool force); From 255edcdfab3d1a45b78d1dacd845d6da5fc64b03 Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Mon, 22 Feb 2016 16:32:05 +0800 Subject: [PATCH 47/74] ASoC: rockchip: add bindings for rk3399 i2s Add devicetree bindings for i2s controller found on rk3399 processors from rockchip. It's helpful to add full set of compatible strings for serials of Rockchip SoCs (rk3066, rk3188, rk3288, rk3399). Acked-by: Rob Herring Signed-off-by: Jianqun Xu Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rockchip-i2s.txt | 1 + sound/soc/rockchip/rockchip_i2s.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt index b7f3a9325ebd..6e86d8aa29b4 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt @@ -9,6 +9,7 @@ Required properties: - "rockchip,rk3066-i2s": for rk3066 - "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188 - "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288 + - "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399 - reg: physical base address of the controller and length of memory mapped region. - interrupts: should contain the I2S interrupt. diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 6561c4cc2edd..531a6d201d2e 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -575,6 +575,9 @@ static int rockchip_i2s_remove(struct platform_device *pdev) static const struct of_device_id rockchip_i2s_match[] = { { .compatible = "rockchip,rk3066-i2s", }, + { .compatible = "rockchip,rk3188-i2s", }, + { .compatible = "rockchip,rk3288-i2s", }, + { .compatible = "rockchip,rk3399-i2s", }, {}, }; From ea2e5b96bd6e835052981b777253def60cfd905f Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 22 Feb 2016 15:56:54 +0800 Subject: [PATCH 48/74] ASoC: rockchip: i2s: add default values for registers this patch add default values for registers according description from TRM. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 531a6d201d2e..2f8e20416bd3 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -440,11 +440,21 @@ static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg) } } +static const struct reg_default rockchip_i2s_reg_defaults[] = { + {0x00, 0x0000000f}, + {0x04, 0x0000000f}, + {0x08, 0x00071f1f}, + {0x10, 0x001f0000}, + {0x14, 0x01f00000}, +}; + static const struct regmap_config rockchip_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = I2S_RXDR, + .reg_defaults = rockchip_i2s_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults), .writeable_reg = rockchip_i2s_wr_reg, .readable_reg = rockchip_i2s_rd_reg, .volatile_reg = rockchip_i2s_volatile_reg, From 4b2fe3822ae16436ed0aba379c500558f7846ce1 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 24 Feb 2016 15:51:26 +0800 Subject: [PATCH 49/74] ASoC: rt298: enable IRQ for jack detection There are some registers needed for enabling rt298 IRQ missed on current driver. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 6 ++++++ sound/soc/codecs/rt298.h | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 30c6de62ae6c..e67ef5f8d4f3 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1225,6 +1225,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt298->regmap, RT298_WIND_FILTER_CTRL, 0x0082, 0x0082); regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); + + regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81); + regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82); + regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84); + regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2); + rt298->is_hp_in = -1; if (rt298->i2c->irq) { diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h index 31da16265f2b..d66f8847b676 100644 --- a/sound/soc/codecs/rt298.h +++ b/sound/soc/codecs/rt298.h @@ -34,6 +34,7 @@ #define RT298_HP_OUT 0x21 #define RT298_MIXER_IN1 0x22 #define RT298_MIXER_IN2 0x23 +#define RT298_INLINE_CMD 0x55 #define RT298_SET_PIN_SFT 6 #define RT298_SET_PIN_ENABLE 0x40 @@ -124,6 +125,12 @@ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0) #define RT298_PROC_COEF\ VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0) +#define RT298_UNSOLICITED_INLINE_CMD\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0) +#define RT298_UNSOLICITED_HP_OUT\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0) +#define RT298_UNSOLICITED_MIC1\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0) /* Index registers */ #define RT298_A_BIAS_CTRL1 0x01 @@ -148,6 +155,7 @@ #define RT298_DEPOP_CTRL2 0x67 #define RT298_DEPOP_CTRL3 0x68 #define RT298_DEPOP_CTRL4 0x69 +#define RT298_IRQ_FLAG_CTRL 0x7c /* SPDIF (0x06) */ #define RT298_SPDIF_SEL_SFT 0 From 0ce58cd6ce5e706c70c7b038ef5ddde0cc7d7385 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 24 Feb 2016 15:51:27 +0800 Subject: [PATCH 50/74] ASoC: rt298: Don't enable IRQ in i2c_probe The IRQ function should not be enabled before irq handler is registered. In fact, it is done in rt298_probe. So remove it from rt298_i2c_probe. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index e67ef5f8d4f3..f0e6c06e89ac 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1224,7 +1224,6 @@ static int rt298_i2c_probe(struct i2c_client *i2c, regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000); regmap_update_bits(rt298->regmap, RT298_WIND_FILTER_CTRL, 0x0082, 0x0082); - regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81); regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82); From 489061bba6c655a2f98d39be17df118c0fd09d57 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:25:54 +0000 Subject: [PATCH 51/74] regmap: add regmap_field_force_xxx() macros Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/linux/regmap.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 7d3d498fd3e8..d36ea89adc50 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -76,8 +76,12 @@ struct reg_sequence { #define regmap_field_write(field, val) \ regmap_field_update_bits_base(field, ~0, val, NULL, false, false) +#define regmap_field_force_write(field, val) \ + regmap_field_update_bits_base(field, ~0, val, NULL, false, true) #define regmap_field_update_bits(field, mask, val)\ regmap_field_update_bits_base(field, mask, val, NULL, false, false) +#define regmap_field_force_update_bits(field, mask, val) \ + regmap_field_update_bits_base(field, mask, val, NULL, false, true) #define regmap_fields_write(field, id, val) \ regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, false) From e6ef243fa4660f3206137bd5f3e69b13a9b7c28a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:26:14 +0000 Subject: [PATCH 52/74] regmap: add regmap_fields_force_xxx() macros Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 12 ------------ include/linux/regmap.h | 6 ++++-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 79d7f51019d7..c7d4a636778d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1748,18 +1748,6 @@ int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, } EXPORT_SYMBOL_GPL(regmap_fields_update_bits_base); -int regmap_fields_force_write(struct regmap_field *field, unsigned int id, - unsigned int val) -{ - if (id >= field->id_size) - return -EINVAL; - - return regmap_write_bits(field->regmap, - field->reg + (field->id_offset * id), - field->mask, val << field->shift); -} -EXPORT_SYMBOL_GPL(regmap_fields_force_write); - /* * regmap_bulk_write(): Write multiple registers to the device * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index d36ea89adc50..e0960b3ff290 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -85,8 +85,12 @@ struct reg_sequence { #define regmap_fields_write(field, id, val) \ regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, false) +#define regmap_fields_force_write(field, id, val) \ + regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, true) #define regmap_fields_update_bits(field, id, mask, val)\ regmap_fields_update_bits_base(field, id, mask, val, NULL, false, false) +#define regmap_fields_force_update_bits(field, id, mask, val) \ + regmap_fields_update_bits_base(field, id, mask, val, NULL, false, true) #ifdef CONFIG_REGMAP @@ -789,8 +793,6 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_update_bits_base(struct regmap_field *field, unsigned int mask, unsigned int val, bool *change, bool async, bool force); -int regmap_fields_force_write(struct regmap_field *field, unsigned int id, - unsigned int val); int regmap_fields_read(struct regmap_field *field, unsigned int id, unsigned int *val); int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, From b3fc95ad025683180628f6896d1f82d2cf5d0266 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Feb 2016 05:26:51 +0000 Subject: [PATCH 53/74] ASoC: rsnd: rsnd_write() / rsnd_bset() uses regmap _force_ function Some R-Car sound requests picky register access which needs *force* register write. Some status register needs to set 1 to clear status, but we might read 1 from its register. In such case, current regmap does nothing and driver will be forever loop To reduce code complexity, this patch uses regmap _force_ function for all register access. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/gen.c | 21 ++------------------- sound/soc/sh/rcar/rsnd.h | 2 -- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 271d29adac68..46c0ba7b6414 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -101,23 +101,6 @@ void rsnd_write(struct rsnd_priv *priv, 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; - - regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); - - dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - rsnd_reg_name(gen, reg), reg, 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; @@ -137,8 +120,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), - mask, data); + regmap_fields_force_update_bits(gen->regs[reg], + rsnd_mod_id(mod), mask, data); dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 5f613eb42614..305cc086a0bc 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -182,8 +182,6 @@ 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) From 6d8044b4ff305238503edca7d75f3ab7410396ba Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 1 Mar 2016 17:39:19 +0100 Subject: [PATCH 54/74] ASoC: sh: rcar: core: don't open code of_device_get_match_data() This change will also make Coverity happy by avoiding a theoretical NULL pointer dereference; yet another reason is to use the above helper function to tighten the code and make it more readable. Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 21e13b3a356f..ba37b0d55b21 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1119,7 +1119,6 @@ static int rsnd_probe(struct platform_device *pdev) struct rsnd_priv *priv; struct device *dev = &pdev->dev; struct rsnd_dai *rdai; - const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev); int (*probe_func[])(struct rsnd_priv *priv) = { rsnd_gen_probe, rsnd_dma_probe, @@ -1145,7 +1144,7 @@ static int rsnd_probe(struct platform_device *pdev) } priv->pdev = pdev; - priv->flags = (unsigned long)of_id->data; + priv->flags = (unsigned long)of_device_get_match_data(dev); spin_lock_init(&priv->lock); /* From 6a16c1765120abc8f58b2c5f275c95a33bb25572 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 1 Mar 2016 17:39:20 +0100 Subject: [PATCH 55/74] ASoC: sh: rcar: rsrc-card: don't open code of_device_get_match_data() This change will also make Coverity happy by avoiding a theoretical NULL pointer dereference; yet another reason is to use the above helper function to tighten the code and make it more readable. Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- sound/soc/sh/rcar/rsrc-card.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 8a357fdf1077..545e94245e62 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -71,7 +71,6 @@ struct rsrc_card_priv { #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) #define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) #define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) static int rsrc_card_startup(struct snd_pcm_substream *substream) { @@ -246,7 +245,7 @@ static int rsrc_card_parse_links(struct device_node *np, struct device *dev = rsrc_priv_to_dev(priv); const struct rsrc_card_of_data *of_data; - of_data = rsrc_dev_to_of_data(dev); + of_data = of_device_get_match_data(dev); /* FE is dummy */ dai_link->cpu_of_node = NULL; @@ -396,7 +395,7 @@ static int rsrc_card_parse_of(struct device_node *node, struct rsrc_card_priv *priv, struct device *dev) { - const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev); struct rsrc_card_dai *props; struct snd_soc_dai_link *links; int ret; From 288bc356a881fcdce05f9782736a6a3d9db359a9 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 4 Mar 2016 14:54:50 +0800 Subject: [PATCH 56/74] ASoC: rt5616: allow to build with CONFIG_SND_SOC_RT5616 This patch fixes that hasn't built the rt5616 driver with 'CONFIG_SND_SOC_RT5616=y' in .config. The tristate is the prompt on the 'make menuconfig', in other words, that can't show the prompt and select it if we don't say what's the tristate. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50693c867e71..1de259860bc9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -596,7 +596,7 @@ config SND_SOC_RT298 depends on I2C config SND_SOC_RT5616 - tristate + tristate "Realtek RT5616 CODEC" config SND_SOC_RT5631 tristate "Realtek ALC5631/RT5631 CODEC" From d8fc2198aab117a4bc16ee305caef19c4c7e7f5c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Mar 2016 16:59:06 +0100 Subject: [PATCH 57/74] ASoC: rockchip: use __maybe_unused to hide st_irq_syscfg_resume The rockchip spdif driver uses SIMPLE_DEV_PM_OPS to conditionally set its power management functions, but we get a warning about rk_spdif_runtime_resume being unused when CONFIG_PM is not set: sound/soc/rockchip/rockchip_spdif.c:67:12: error: 'rk_spdif_runtime_resume' defined but not used [-Werror=unused-function] This adds a __maybe_unused annotation so the compiler knows it can silently drop it instead of warning. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 5a806da89f42..274599d4caeb 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -54,7 +54,7 @@ static const struct of_device_id rk_spdif_match[] = { }; MODULE_DEVICE_TABLE(of, rk_spdif_match); -static int rk_spdif_runtime_suspend(struct device *dev) +static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); @@ -64,7 +64,7 @@ static int rk_spdif_runtime_suspend(struct device *dev) return 0; } -static int rk_spdif_runtime_resume(struct device *dev) +static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); int ret; From ab8773943ab3126d39eb41531c8e06aea2107aa1 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 4 Mar 2016 18:31:54 +0800 Subject: [PATCH 58/74] ASoC: rockchip: add bindings for spdif controller this patch add compatible for rk3366/rk3368/rk3399 spdif, these three spdifs share the same type. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/rockchip-spdif.txt | 8 ++++++-- sound/soc/rockchip/rockchip_spdif.c | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt index e64dbdea7db9..11046429a118 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt @@ -7,8 +7,12 @@ a fibre cable. Required properties: - compatible: should be one of the following: - - "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or - "rockchip,rk3066-spdif" + - "rockchip,rk3066-spdif" + - "rockchip,rk3188-spdif" + - "rockchip,rk3288-spdif" + - "rockchip,rk3366-spdif" + - "rockchip,rk3368-spdif" + - "rockchip,rk3399-spdif" - reg: physical base address of the controller and length of memory mapped region. - interrupts: should contain the SPDIF interrupt. diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 274599d4caeb..100781e37848 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -28,6 +28,7 @@ enum rk_spdif_type { RK_SPDIF_RK3066, RK_SPDIF_RK3188, RK_SPDIF_RK3288, + RK_SPDIF_RK3366, }; #define RK3288_GRF_SOC_CON2 0x24c @@ -45,11 +46,17 @@ struct rk_spdif_dev { static const struct of_device_id rk_spdif_match[] = { { .compatible = "rockchip,rk3066-spdif", - .data = (void *) RK_SPDIF_RK3066 }, + .data = (void *)RK_SPDIF_RK3066 }, { .compatible = "rockchip,rk3188-spdif", - .data = (void *) RK_SPDIF_RK3188 }, + .data = (void *)RK_SPDIF_RK3188 }, { .compatible = "rockchip,rk3288-spdif", - .data = (void *) RK_SPDIF_RK3288 }, + .data = (void *)RK_SPDIF_RK3288 }, + { .compatible = "rockchip,rk3366-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3368-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3399-spdif", + .data = (void *)RK_SPDIF_RK3366 }, {}, }; MODULE_DEVICE_TABLE(of, rk_spdif_match); From d7289565483c65094d0473555625a4acd89567d3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:51:12 +0000 Subject: [PATCH 59/74] ASoC: rsnd: don't call update callback if it was NULL Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ba37b0d55b21..3a3dc2ff18c9 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -927,7 +927,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, } } - if (change) + if (change && cfg->update) cfg->update(cfg->io, mod); return change; From f90432fc6ec41c5bb1d7b75edc1f2bc58799ece3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:51:44 +0000 Subject: [PATCH 60/74] ASoC: rsrc-card: add convert channels support Renesas sound device has CTU (= Channel Transfer Unit), and sound card needs its support. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../bindings/sound/renesas,rsrc-card.txt | 1 + sound/soc/sh/rcar/rsrc-card.c | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt index 2b2caa281ce3..5abebf7898d9 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt @@ -30,6 +30,7 @@ Optional subnode properties: - frame-inversion : bool property. Add this if the dai-link uses frame clock inversion. - convert-rate : platform specified sampling rate convert +- convert-channels : platform specified channel size convert - audio-prefix : see audio-routing - audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 545e94245e62..1bc7ecfc42a9 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -66,6 +66,7 @@ struct rsrc_card_priv { struct snd_soc_dai_link *dai_link; int dai_num; u32 convert_rate; + u32 convert_channels; }; #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) @@ -144,11 +145,16 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); - if (!priv->convert_rate) - return 0; + if (priv->convert_rate) + rate->min = + rate->max = priv->convert_rate; - rate->min = rate->max = priv->convert_rate; + if (priv->convert_channels) + channels->min = + channels->max = priv->convert_channels; return 0; } @@ -436,9 +442,13 @@ static int rsrc_card_parse_of(struct device_node *node, /* sampling rate convert */ of_property_read_u32(node, "convert-rate", &priv->convert_rate); - dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n", - priv->snd_card.name ? priv->snd_card.name : "", - priv->convert_rate); + /* channels transfer */ + of_property_read_u32(node, "convert-channels", &priv->convert_channels); + + dev_dbg(dev, "New rsrc-audio-card: %s\n", + priv->snd_card.name ? priv->snd_card.name : ""); + dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate); + dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels); ret = rsrc_card_dai_link_of(node, priv); if (ret < 0) From 135bb7d5c7b3fa40cd0559b5500a50d4e3663f6e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:52:13 +0000 Subject: [PATCH 61/74] ASoC: rsnd: tidyup rsnd_ssiu_init_gen2() remove unnecessary variable Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssiu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 1b8ea0e09bc2..0d964a0a3e31 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -115,13 +115,12 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, } if (rsnd_ssi_use_busif(io)) { - u32 val = rsnd_get_dalign(mod, io); - rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr_bit(mod, io) | rsnd_get_adinr_chan(mod, io)); rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); - rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val); + rsnd_mod_write(mod, SSI_BUSIF_DALIGN, + rsnd_get_dalign(mod, io)); } return 0; From 8a3a2211e97395694827f12552bbad7f2caf11ef Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:53:00 +0000 Subject: [PATCH 62/74] ASoC: rsnd: add CTU basic support This patch adds Renesas R-Car sound CTU (= Channel Transfer Unit) very basic support, but not yet enough feature at this point. Because CTU support needs more complex channel function for each modules. To avoid complex patch reviewing, this patch picked up very basic part only. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 49 ++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 1 + 2 files changed, 50 insertions(+) diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index a10d0f7b73fa..784515afefaf 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -14,6 +14,7 @@ struct rsnd_ctu { struct rsnd_mod mod; + int channels; }; #define rsnd_ctu_nr(priv) ((priv)->ctu_nr) @@ -23,6 +24,9 @@ struct rsnd_ctu { ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ i++) +#define rsnd_mod_to_ctu(_mod) \ + container_of((_mod), struct rsnd_ctu, mod) + #define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) static void rsnd_ctu_activation(struct rsnd_mod *mod) @@ -37,6 +41,13 @@ static void rsnd_ctu_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 0); } +int rsnd_ctu_converted_channel(struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + + return ctu->channels; +} + static int rsnd_ctu_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -116,11 +127,49 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, return 0; } +static int rsnd_ctu_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *fe_params) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + struct snd_soc_pcm_runtime *fe = substream->private_data; + + /* + * CTU assumes that it is used under DPCM if user want to use + * channel transfer. Then, CTU should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + ctu->channels = 0; + if (fe->dai_link->dynamic) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_soc_dpcm *dpcm; + struct snd_pcm_hw_params *be_params; + int stream = substream->stream; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + be_params = &dpcm->hw_params; + if (params_channels(fe_params) != params_channels(be_params)) + ctu->channels = params_channels(be_params); + } + + dev_dbg(dev, "CTU convert channels %d\n", ctu->channels); + } + + return 0; +} + static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, + .hw_params = rsnd_ctu_hw_params, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 305cc086a0bc..bbc89bd1e918 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -638,6 +638,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, */ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); +int rsnd_ctu_converted_channel(struct rsnd_mod *mod); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_ctu_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu") From eed76bb811cd143119b4bdeca88606685222e687 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:54:58 +0000 Subject: [PATCH 63/74] ASoC: rsnd: add rsnd_runtime_channel_xxx() Current SSI is supporting Normal SSI/Multi mode SSI/TDM mode SSI and its behavior is based on input channels. This input channel might be converted by CTU, and SSI needs to be Multi SSI mode / TDM SSI mode if 6ch input EX) 6ch input, CTU for 2ch, playback 6ch 6ch 2ch 2ch 2ch 2ch -> SRC -> CTU -> MIX -> DVC -> SSIU -> SSI EX) 6ch input, no CTU, Multi SSI, playback 6ch 6ch 6ch 6ch 6ch 2ch -> SRC -> CTU -> MIX -> DVC -> SSIU -> SSI0/SSI1/SSI2 Current driver is using rsnd_get_adinr_chan() / rsnd_get_slot_width() for this purpose, but it is complicated enough without meaning. This patch adds new rsnd_runtime_channel_xxx() which is caring CTU/Multi SSI. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 69 ++++++++++++++++++++++++---------------- sound/soc/sh/rcar/ctu.c | 2 +- sound/soc/sh/rcar/dvc.c | 2 +- sound/soc/sh/rcar/mix.c | 2 +- sound/soc/sh/rcar/rsnd.h | 8 +++-- sound/soc/sh/rcar/src.c | 2 +- sound/soc/sh/rcar/ssi.c | 15 ++++----- sound/soc/sh/rcar/ssiu.c | 6 ++-- 8 files changed, 62 insertions(+), 44 deletions(-) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 3a3dc2ff18c9..3351a701c60e 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -224,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io) return rdai->slots_num; } -int rsnd_get_slot_width(struct rsnd_dai_stream *io) +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - int chan = runtime->channels; - /* Multi channel Mode */ - if (rsnd_ssi_multi_slaves_runtime(io)) + return runtime->channels; +} + +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) +{ + int chan = rsnd_runtime_channel_original(io); + struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); + + if (ctu_mod) { + u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod); + + if (converted_chan) + return converted_chan; + } + + return chan; +} + +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) +{ + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + /* Use Multi SSI */ + if (rsnd_runtime_is_ssi_multi(io)) chan /= rsnd_get_slot_num(io); /* TDM Extend Mode needs 8ch */ @@ -240,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io) return chan; } +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) +{ + int slots = rsnd_get_slot_num(io); + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + return (chan >= 6) && (slots > 1); +} + +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) +{ + return rsnd_runtime_channel_for_ssi(io) >= 6; +} + /* * ADINR function */ @@ -261,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) return 0; } -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 */ diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 784515afefaf..b326966ea407 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -60,7 +60,7 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, { rsnd_mod_write(mod, CTU_CTUIR, 1); - rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); rsnd_mod_write(mod, CTU_CPMDR, 0); rsnd_mod_write(mod, CTU_SCMDR, 0); diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d757f1316385..93b11e1c5d7f 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -116,7 +116,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, u32 vrdbr = 0; adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io); + rsnd_runtime_channel_after_ctu(io); /* Enable Digital Volume, Zero Cross Mute Mode */ dvucr |= 0x101; diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index e0e337ad4206..195fc7bb22af 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -51,7 +51,7 @@ static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, rsnd_mod_write(mod, MIX_MIXIR, 1); /* General Information */ - rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io)); + rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); /* volume step */ rsnd_mod_write(mod, MIX_MIXMR, 0); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index bbc89bd1e918..ff53f96e5006 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -193,7 +193,6 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); 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); /* @@ -356,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, void rsnd_set_slot(struct rsnd_dai *rdai, int slots, int slots_total); int rsnd_get_slot(struct rsnd_dai_stream *io); -int rsnd_get_slot_width(struct rsnd_dai_stream *io); int rsnd_get_slot_num(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); + /* * R-Car sound DAI */ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 03c6314871ff..8e1177aea6b1 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -205,7 +205,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, * SRC_ADINR */ adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io); + rsnd_runtime_channel_original(io); /* * SRC_IFSCR / SRC_IFSVR diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 0979db8af8f9..540489755367 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -180,11 +180,8 @@ static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) { - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 mask = rsnd_ssi_multi_slaves(io); - - if (mask && (runtime->channels >= 6)) - return mask; + if (rsnd_runtime_is_ssi_multi(io)) + return rsnd_ssi_multi_slaves(io); return 0; } @@ -198,7 +195,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); - int slots = rsnd_get_slot_width(io); + int chan = rsnd_runtime_channel_for_ssi(io); int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, @@ -231,10 +228,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, /* * this driver is assuming that - * system word is 32bit x slots + * system word is 32bit x chan * see rsnd_ssi_init() */ - main_rate = rate * 32 * slots * ssi_clk_mul_table[j]; + main_rate = rate * 32 * chan * ssi_clk_mul_table[j]; ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (0 == ret) { @@ -289,7 +286,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, u32 wsr; int is_tdm; - is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0; + is_tdm = rsnd_runtime_is_ssi_tdm(io); /* * always use 32bit system word. diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 0d964a0a3e31..6f9b388ec5a8 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -105,7 +105,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, if (ret < 0) return ret; - if (rsnd_get_slot_width(io) >= 6) { + if (rsnd_runtime_is_ssi_tdm(io)) { /* * TDM Extend Mode * see @@ -117,7 +117,9 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, if (rsnd_ssi_use_busif(io)) { rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io)); + (rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io))); rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(mod, SSI_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); From ec2ac01afe001360b911e27c915579fd003339e8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 25 Feb 2016 05:55:23 +0000 Subject: [PATCH 64/74] ASoC: rsnd: add CTU support This patch adds CTU (= Channel Transfer Unit) support on Renesas R-Car sound driver. It can Down/Up mixing and splitter. You need to check R-Car datasheet especially CTUn_CPMDR/CTUn_SV0xR/CTUn_SV1xR/CTUn_SV2xR/CTUn_SV3xR for setting parameter. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 230 ++++++++++++++++++++++++++++++++++------ 1 file changed, 195 insertions(+), 35 deletions(-) diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index b326966ea407..9dcc1f9db026 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -12,8 +12,74 @@ #define CTU_NAME_SIZE 16 #define CTU_NAME "ctu" +/* + * User needs to setup CTU by amixer, and its settings are + * based on below registers + * + * CTUn_CPMDR : amixser set "CTU Pass" + * CTUn_SV0xR : amixser set "CTU SV0" + * CTUn_SV1xR : amixser set "CTU SV1" + * CTUn_SV2xR : amixser set "CTU SV2" + * CTUn_SV3xR : amixser set "CTU SV3" + * + * [CTU Pass] + * 0000: default + * 0001: Connect input data of channel 0 + * 0010: Connect input data of channel 1 + * 0011: Connect input data of channel 2 + * 0100: Connect input data of channel 3 + * 0101: Connect input data of channel 4 + * 0110: Connect input data of channel 5 + * 0111: Connect input data of channel 6 + * 1000: Connect input data of channel 7 + * 1001: Connect calculated data by scale values of matrix row 0 + * 1010: Connect calculated data by scale values of matrix row 1 + * 1011: Connect calculated data by scale values of matrix row 2 + * 1100: Connect calculated data by scale values of matrix row 3 + * + * [CTU SVx] + * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07] + * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17] + * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27] + * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37] + * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * + * [SVxx] + * Plus Minus + * value time dB value time dB + * ----------------------------------------------------------------------- + * H'7F_FFFF 2 6 H'80_0000 2 6 + * ... + * H'40_0000 1 0 H'C0_0000 1 0 + * ... + * H'00_0001 2.38 x 10^-7 -132 + * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132 + * + * + * Ex) Input ch -> Output ch + * 1ch -> 0ch + * 0ch -> 1ch + * + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 9,10 + * amixer set "CTU SV0" 0,4194304 + * amixer set "CTU SV1" 4194304,0 + * or + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 2,1 + */ + struct rsnd_ctu { struct rsnd_mod mod; + struct rsnd_kctrl_cfg_m pass; + struct rsnd_kctrl_cfg_m sv0; + struct rsnd_kctrl_cfg_m sv1; + struct rsnd_kctrl_cfg_m sv2; + struct rsnd_kctrl_cfg_m sv3; + struct rsnd_kctrl_cfg_s reset; int channels; }; @@ -58,51 +124,91 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod, static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + u32 cpmdr = 0; + u32 scmdr = 0; + int i; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + u32 val = ctu->pass.val[i]; + + cpmdr |= val << (28 - (i * 4)); + + if ((val > 0x8) && (scmdr < (val - 0x8))) + scmdr = val - 0x8; + } + rsnd_mod_write(mod, CTU_CTUIR, 1); rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); - rsnd_mod_write(mod, CTU_CPMDR, 0); - rsnd_mod_write(mod, CTU_SCMDR, 0); - rsnd_mod_write(mod, CTU_SV00R, 0); - rsnd_mod_write(mod, CTU_SV01R, 0); - rsnd_mod_write(mod, CTU_SV02R, 0); - rsnd_mod_write(mod, CTU_SV03R, 0); - rsnd_mod_write(mod, CTU_SV04R, 0); - rsnd_mod_write(mod, CTU_SV05R, 0); - rsnd_mod_write(mod, CTU_SV06R, 0); - rsnd_mod_write(mod, CTU_SV07R, 0); + rsnd_mod_write(mod, CTU_CPMDR, cpmdr); - rsnd_mod_write(mod, CTU_SV10R, 0); - rsnd_mod_write(mod, CTU_SV11R, 0); - rsnd_mod_write(mod, CTU_SV12R, 0); - rsnd_mod_write(mod, CTU_SV13R, 0); - rsnd_mod_write(mod, CTU_SV14R, 0); - rsnd_mod_write(mod, CTU_SV15R, 0); - rsnd_mod_write(mod, CTU_SV16R, 0); - rsnd_mod_write(mod, CTU_SV17R, 0); + rsnd_mod_write(mod, CTU_SCMDR, scmdr); - rsnd_mod_write(mod, CTU_SV20R, 0); - rsnd_mod_write(mod, CTU_SV21R, 0); - rsnd_mod_write(mod, CTU_SV22R, 0); - rsnd_mod_write(mod, CTU_SV23R, 0); - rsnd_mod_write(mod, CTU_SV24R, 0); - rsnd_mod_write(mod, CTU_SV25R, 0); - rsnd_mod_write(mod, CTU_SV26R, 0); - rsnd_mod_write(mod, CTU_SV27R, 0); - - rsnd_mod_write(mod, CTU_SV30R, 0); - rsnd_mod_write(mod, CTU_SV31R, 0); - rsnd_mod_write(mod, CTU_SV32R, 0); - rsnd_mod_write(mod, CTU_SV33R, 0); - rsnd_mod_write(mod, CTU_SV34R, 0); - rsnd_mod_write(mod, CTU_SV35R, 0); - rsnd_mod_write(mod, CTU_SV36R, 0); - rsnd_mod_write(mod, CTU_SV37R, 0); + if (scmdr > 0) { + rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]); + rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]); + rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]); + rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]); + rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]); + rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]); + rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]); + rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]); + } + if (scmdr > 1) { + rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]); + rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]); + rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]); + rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]); + rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]); + rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]); + rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]); + rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]); + } + if (scmdr > 2) { + rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]); + rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]); + rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]); + rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]); + rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]); + rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]); + rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]); + rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]); + } + if (scmdr > 3) { + rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]); + rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]); + rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]); + rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]); + rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]); + rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]); + rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]); + rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]); + } rsnd_mod_write(mod, CTU_CTUIR, 0); } +static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int i; + + if (!ctu->reset.val) + return; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + ctu->pass.val[i] = 0; + ctu->sv0.val[i] = 0; + ctu->sv1.val[i] = 0; + ctu->sv2.val[i] = 0; + ctu->sv3.val[i] = 0; + } + ctu->reset.val = 0; +} + static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -164,12 +270,66 @@ static int rsnd_ctu_hw_params(struct rsnd_mod *mod, return 0; } +static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int ret; + + /* CTU Pass */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", + NULL, + &ctu->pass, RSND_MAX_CHANNELS, + 0xC); + + /* ROW0 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", + NULL, + &ctu->sv0, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW1 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", + NULL, + &ctu->sv1, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW2 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", + NULL, + &ctu->sv2, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW3 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", + NULL, + &ctu->sv3, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* Reset */ + ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", + rsnd_ctu_value_reset, + &ctu->reset, 1); + + return ret; +} + static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, .hw_params = rsnd_ctu_hw_params, + .pcm_new = rsnd_ctu_pcm_new, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) From d7fcd13663fe43ad938fec1acd46ff196dddf914 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 7 Mar 2016 12:12:11 +0800 Subject: [PATCH 65/74] ASoC: rt5616: add missing mute control for HPVOL Add missing kcontrol for HPVOL mute control. Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 3704d0716c04..f527b5b2817b 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -309,6 +309,8 @@ static const struct snd_kcontrol_new rt5616_snd_controls[] = { /* Headphone Output Volume */ SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL, RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL, + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL, RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), /* OUTPUT Control */ From 74e26809fb47e072d0838f74a08a0a5aa428848f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 04:57:55 +0000 Subject: [PATCH 66/74] ASoC: rsrc-card: tidyup convert-channels explain Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt index 5abebf7898d9..255ece3043ad 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt @@ -30,7 +30,7 @@ Optional subnode properties: - frame-inversion : bool property. Add this if the dai-link uses frame clock inversion. - convert-rate : platform specified sampling rate convert -- convert-channels : platform specified channel size convert +- convert-channels : platform specified converted channel size (2 - 8 ch) - audio-prefix : see audio-routing - audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, From 61a219fe192412bca6c26657a4b0c99c67a3e7fc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:06:17 +0000 Subject: [PATCH 67/74] ASoC: rsnd: don't enable SRC Sync mode when Capture with CMD It can't use SRC Synchronous convert when Capture if it uses CMD, because no one provide out side clocks. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 8e1177aea6b1..541a478cf157 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -475,7 +475,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -490,9 +489,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, return 0; /* - * SRC In doesn't work if DVC was enabled + * It can't use SRC Synchronous convert + * when Capture if it uses CMD */ - if (dvc && !rsnd_io_is_play(io)) + if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) return 0; /* From b5ebbb3a16c631c776f6619dff8ee86da21cca6f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:06:46 +0000 Subject: [PATCH 68/74] ASoC: rsnd: SRC sync mode is not related to clock master mode SRC sync mode needs to control its clock (= for in/out). 1st but codec side clock Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 541a478cf157..ab974afe79eb 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -474,7 +474,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -482,12 +481,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, * enable SRC sync convert if possible */ - /* - * SRC sync convert needs clock master - */ - if (!rsnd_rdai_is_clk_master(rdai)) - return 0; - /* * It can't use SRC Synchronous convert * when Capture if it uses CMD From 1a9be9ee194131465d945981969b85045f20e6af Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:07:29 +0000 Subject: [PATCH 69/74] ASoC: rsnd: SRC function parameter uses "mod" instead of "src" To reduce confusion, SRC uses "mod" instead of "src" as function parameter Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index ab974afe79eb..d520690409a2 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -93,9 +93,10 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, } static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, - struct rsnd_src *src) + struct rsnd_mod *mod) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate; if (!runtime) @@ -120,18 +121,14 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct snd_pcm_runtime *runtime) { struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct rsnd_src *src; unsigned int rate = 0; - if (src_mod) { - src = rsnd_mod_to_src(src_mod); - - /* - * return convert rate if SRC is used, - * otherwise, return runtime->rate as usual - */ - rate = rsnd_src_convert_rate(io, src); - } + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + if (src_mod) + rate = rsnd_src_convert_rate(io, src_mod); if (!rate) rate = runtime->rate; @@ -179,7 +176,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); + u32 convert_rate = rsnd_src_convert_rate(io, mod); u32 ifscr, fsrate, adinr; u32 cr, route; u32 bsdsr, bsisr; From ab2049f9de72c901c07eec25eff74dae2e82df7e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:07:57 +0000 Subject: [PATCH 70/74] ASoC: rsnd: rename rsnd_enable_sync_convert() to rsnd_src_sync_is_enabled() rsnd_enable_sync_convert() is for checking, not for setting. In order to avoid confusion, this patch renamed it to rsnd_src_sync_is_enabled() Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index d520690409a2..1d5aedb50213 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -33,7 +33,7 @@ struct rsnd_src { #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) #define rsnd_src_to_dma(src) ((src)->dma) #define rsnd_src_nr(priv) ((priv)->src_nr) -#define rsnd_enable_sync_convert(src) ((src)->sen.val) +#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) @@ -102,7 +102,7 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, if (!runtime) return 0; - if (!rsnd_enable_sync_convert(src)) + if (!rsnd_src_sync_is_enabled(mod)) return src->convert_rate; convert_rate = src->sync.val; @@ -175,7 +175,6 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(io, mod); u32 ifscr, fsrate, adinr; u32 cr, route; @@ -222,7 +221,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, if (convert_rate) { route = 0x1; - if (rsnd_enable_sync_convert(src)) { + if (rsnd_src_sync_is_enabled(mod)) { cr |= 0x1; route |= rsnd_io_is_play(io) ? (0x1 << 24) : (0x1 << 25); @@ -296,9 +295,9 @@ static int rsnd_src_irq(struct rsnd_mod *mod, /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) sys_int_val = sys_int_val & 0xffff; rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); @@ -318,7 +317,6 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod) static bool rsnd_src_error_occurred(struct rsnd_mod *mod) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val0, val1; bool ret = false; @@ -327,9 +325,9 @@ static bool rsnd_src_error_occurred(struct rsnd_mod *mod) /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) val0 = val0 & 0xffff; if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || @@ -343,7 +341,6 @@ static int rsnd_src_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val; /* @@ -351,7 +348,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, * * 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)) ? + val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); From cbf1494fbcc80d363477af1efefb2380e7660a24 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:08:33 +0000 Subject: [PATCH 71/74] ASoC: rsnd: add rsnd_src_get_in/out_rate() SRC will convert rate, and then, CMD and SSI want to know its rate (= SRC.in / SRC.out) for each purpose. Current driver is supporting only Playback, but SRC+Capture support needs more flexibility. This patch adds rsnd_src_get_in/out_rate() for it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/rsnd.h | 10 +++++++--- sound/soc/sh/rcar/src.c | 20 +++++++++++++++++--- sound/soc/sh/rcar/ssi.c | 5 +++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index ff53f96e5006..4b77f33358fb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -627,9 +627,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv); int rsnd_src_probe(struct rsnd_priv *priv); void rsnd_src_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime); + +#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) +#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in); + #define rsnd_src_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") #define rsnd_parse_connect_src(rdai, playback, capture) \ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 1d5aedb50213..d1a8741cc446 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -116,12 +116,26 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, return convert_rate; } -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in) { struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); unsigned int rate = 0; + int is_play = rsnd_io_is_play(io); + + /* + * + * Playback + * runtime_rate -> [SRC] -> convert_rate + * + * Capture + * convert_rate -> [SRC] -> runtime_rate + */ + + if (is_play == is_in) + return runtime->rate; /* * return convert rate if SRC is used, diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 540489755367..5f848f054745 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -190,7 +190,6 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -201,7 +200,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, 1, 2, 4, 8, 16, 6, 12, }; unsigned int main_rate; - unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); + unsigned int rate = rsnd_io_is_play(io) ? + rsnd_src_get_out_rate(priv, io) : + rsnd_src_get_in_rate(priv, io); if (!rsnd_rdai_is_clk_master(rdai)) return 0; From 0102eed57c47371023c03b3b0c564f33d5e94570 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:09:14 +0000 Subject: [PATCH 72/74] ASoC: rsnd: SRC TIMSEL support for Capture SRC has Sync/Async mode, and it can't use Sync mode when Capture with CMD. In Async mode, it needs to care about in/out SRC rate for settings, but current driver supporting Playback case only. This patch supports Capture case. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 204 ++++++++++++++++++++++----------------- sound/soc/sh/rcar/rsnd.h | 8 +- sound/soc/sh/rcar/src.c | 26 +++-- 3 files changed, 128 insertions(+), 110 deletions(-) diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d74e1ccc0f8f..f7e164c89f33 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) return (0x6 + ws) << 8; } +static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int target_rate, + unsigned int *target_val, + unsigned int *target_en) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int idx, sel, div, step; + unsigned int val, en; + unsigned int min, diff; + unsigned int sel_rate[] = { + clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ + clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ + clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ + adg->rbga_rate_for_441khz, /* 0011: RBGA */ + adg->rbgb_rate_for_48khz, /* 0100: RBGB */ + }; + + min = ~0; + val = 0; + en = 0; + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + idx = 0; + step = 2; + + if (!sel_rate[sel]) + continue; + + for (div = 2; div <= 98304; div += step) { + diff = abs(target_rate - sel_rate[sel] / div); + if (min > diff) { + val = (sel << 8) | idx; + min = diff; + en = 1 << (sel + 1); /* fixme */ + } + + /* + * step of 0_0000 / 0_0001 / 0_1101 + * are out of order + */ + if ((idx > 2) && (idx % 2)) + step *= 2; + if (idx == 0x1c) { + div += step; + step *= 2; + } + idx++; + } + } + + if (min == ~0) { + dev_err(dev, "no Input clock\n"); + return; + } + + *target_val = val; + if (target_en) + *target_en = en; +} + +static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate, + u32 *in, u32 *out, u32 *en) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + unsigned int target_rate; + u32 *target_val; + u32 _in; + u32 _out; + u32 _en; + + /* default = SSI WS */ + _in = + _out = rsnd_adg_ssi_ws_timing_gen2(io); + + target_rate = 0; + target_val = NULL; + _en = 0; + if (runtime->rate != in_rate) { + target_rate = out_rate; + target_val = &_out; + } else if (runtime->rate != out_rate) { + target_rate = in_rate; + target_val = &_in; + } + + if (target_rate) + __rsnd_adg_get_timesel_ratio(priv, io, + target_rate, + target_val, &_en); + + if (in) + *in = _in; + if (out) + *out = _out; + if (en) + *en = _en; +} + int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, struct rsnd_dai_stream *io) { @@ -110,25 +212,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, return 0; } -static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - u32 timsel) +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int is_play = rsnd_io_is_play(io); + u32 in, out; + u32 mask, en; int id = rsnd_mod_id(src_mod); int shift = (id % 2) ? 16 : 0; - u32 mask, ws; - u32 in, out; rsnd_mod_confirm_src(src_mod); - ws = rsnd_adg_ssi_ws_timing_gen2(io); - - in = (is_play) ? timsel : ws; - out = (is_play) ? ws : timsel; + rsnd_adg_get_timesel_ratio(priv, io, + in_rate, out_rate, + &in, &out, &en); in = in << shift; out = out << shift; @@ -157,91 +258,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, break; } - return 0; -} - -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - struct device *dev = rsnd_priv_to_dev(priv); - int idx, sel, div, step, ret; - u32 val, en; - unsigned int min, diff; - unsigned int sel_rate [] = { - clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ - clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ - clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ - adg->rbga_rate_for_441khz, /* 0011: RBGA */ - adg->rbgb_rate_for_48khz, /* 0100: RBGB */ - }; - - rsnd_mod_confirm_src(src_mod); - - min = ~0; - val = 0; - en = 0; - for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { - idx = 0; - step = 2; - - if (!sel_rate[sel]) - continue; - - for (div = 2; div <= 98304; div += step) { - diff = abs(src_rate - sel_rate[sel] / div); - if (min > diff) { - val = (sel << 8) | idx; - min = diff; - en = 1 << (sel + 1); /* fixme */ - } - - /* - * step of 0_0000 / 0_0001 / 0_1101 - * are out of order - */ - if ((idx > 2) && (idx % 2)) - step *= 2; - if (idx == 0x1c) { - div += step; - step *= 2; - } - idx++; - } - } - - if (min == ~0) { - dev_err(dev, "no Input clock\n"); - return -EIO; - } - - ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val); - if (ret < 0) { - dev_err(dev, "timsel error\n"); - return ret; - } - - rsnd_mod_bset(adg_mod, DIV_EN, en, en); - - dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); + if (en) + rsnd_mod_bset(adg_mod, DIV_EN, en, en); return 0; } -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io) -{ - u32 val = rsnd_adg_ssi_ws_timing_gen2(io); - - rsnd_mod_confirm_src(src_mod); - - return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); -} - static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4b77f33358fb..fc89a67258ca 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -446,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); int rsnd_adg_probe(struct rsnd_priv *priv); void rsnd_adg_remove(struct rsnd_priv *priv); -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate); -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io); + unsigned int in_rate, + unsigned int out_rate); int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index d1a8741cc446..15d6ffe8be74 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -189,7 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 convert_rate = rsnd_src_convert_rate(io, mod); + u32 fin, fout; u32 ifscr, fsrate, adinr; u32 cr, route; u32 bsdsr, bsisr; @@ -198,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, if (!runtime) return; + fin = rsnd_src_get_in_rate(priv, io); + fout = rsnd_src_get_out_rate(priv, io); + /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (!convert_rate) + if (fin == fout) ratio = 0; - else if (convert_rate > runtime->rate) - ratio = 100 * convert_rate / runtime->rate; + else if (fin > fout) + ratio = 100 * fin / fout; else - ratio = 100 * runtime->rate / convert_rate; + ratio = 100 * fout / fin; if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n"); @@ -222,9 +225,9 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ ifscr = 0; fsrate = 0; - if (convert_rate) { + if (fin != fout) { ifscr = 1; - fsrate = 0x0400000 / convert_rate * runtime->rate; + fsrate = 0x0400000 / fout * fin; } /* @@ -232,7 +235,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ cr = 0x00011110; route = 0x0; - if (convert_rate) { + if (fin != fout) { route = 0x1; if (rsnd_src_sync_is_enabled(mod)) { @@ -274,12 +277,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); - if (convert_rate) - rsnd_adg_set_convert_clk_gen2(mod, io, - runtime->rate, - convert_rate); - else - rsnd_adg_set_convert_timing_gen2(mod, io); + rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); } static int rsnd_src_irq(struct rsnd_mod *mod, From 09e59075496517206351cc28226ad7263a1a5c4f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 7 Mar 2016 05:09:34 +0000 Subject: [PATCH 73/74] ASoC: rsnd: CMD TIMSEL support for Capture CMD.out should use same as SRC.out for TIMSEL settings, but it cares Playback case only. This patch fixup it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index f7e164c89f33..606399de684d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -202,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, int shift = (id % 2) ? 16 : 0; u32 mask, val; - val = rsnd_adg_ssi_ws_timing_gen2(io); + rsnd_adg_get_timesel_ratio(priv, io, + rsnd_src_get_in_rate(priv, io), + rsnd_src_get_out_rate(priv, io), + NULL, &val, NULL); val = val << shift; mask = 0xffff << shift; From f1511a14a4a94d063cf8f8f12f36cf2ebb0cc8a8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Mar 2016 05:29:21 +0000 Subject: [PATCH 74/74] ASoC: rsnd: add simplified module explanation Renesas sound driver user needs to read its datasheet when create DT. But it is difficult to understand, because it has many modules (SRC/CTU/MIX/DVC/SSIU/SSI/AudioDMAC/AudioDMACperiperi), and many features (Asynchronous/Synchronous mode on SRC, CTU matrix, DVC volume settings feature, Multi-SSI/TDM-SSI, etc). This patch adds simplified explanation to help setting/understanding. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../bindings/sound/renesas,rsnd.txt | 340 ++++++++++++++++++ sound/soc/sh/rcar/dvc.c | 23 ++ 2 files changed, 363 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 8ee0fa91e4a0..c7b29df4a963 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -1,6 +1,337 @@ Renesas R-Car sound +============================================= +* Modules +============================================= + +Renesas R-Car sound is constructed from below modules +(for Gen2 or later) + + SCU : Sampling Rate Converter Unit + - SRC : Sampling Rate Converter + - CMD + - CTU : Channel Transfer Unit + - MIX : Mixer + - DVC : Digital Volume and Mute Function + SSIU : Serial Sound Interface Unit + SSI : Serial Sound Interface + +See detail of each module's channels, connection, limitation on datasheet + +============================================= +* Multi channel +============================================= + +Multi channel is supported by Multi-SSI, or TDM-SSI. + + Multi-SSI : 6ch case, you can use stereo x 3 SSI + TDM-SSI : 6ch case, you can use TDM + +============================================= +* Enable/Disable each modules +============================================= + +See datasheet to check SRC/CTU/MIX/DVC connect-limitation. +DT controls enabling/disabling module. +${LINUX}/arch/arm/boot/dts/r8a7790-lager.dts can be good example. +This is example of + +Playback: [MEM] -> [SRC2] -> [DVC0] -> [SSIU0/SSI0] -> [codec] +Capture: [MEM] <- [DVC1] <- [SRC3] <- [SSIU1/SSI1] <- [codec] + + &rcar_sound { + ... + rcar_sound,dai { + dai0 { + playback = <&ssi0 &src2 &dvc0>; + capture = <&ssi1 &src3 &dvc1>; + }; + }; + }; + +You can use below. +${LINUX}/arch/arm/boot/dts/r8a7790.dts can be good example. + + &src0 &ctu00 &mix0 &dvc0 &ssi0 + &src1 &ctu01 &mix1 &dvc1 &ssi1 + &src2 &ctu02 &ssi2 + &src3 &ctu03 &ssi3 + &src4 &ssi4 + &src5 &ctu10 &ssi5 + &src6 &ctu11 &ssi6 + &src7 &ctu12 &ssi7 + &src8 &ctu13 &ssi8 + &src9 &ssi9 + +============================================= +* SRC (Sampling Rate Converter) +============================================= + + [xx]Hz [yy]Hz + ------> [SRC] ------> + +SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes + + Asynchronous mode: input data / output data are based on different clocks. + you can use this mode on Playback / Capture + Synchronous mode: input data / output data are based on same clocks. + This mode will be used if system doesn't have its input clock, + for example digital TV case. + you can use this mode on Playback + +------------------ +** Asynchronous mode +------------------ + +You need to use "renesas,rsrc-card" sound card for it. +example) + + sound { + compatible = "renesas,rsrc-card"; + ... + /* + * SRC Asynchronous mode setting + * Playback: + * All input data will be converted to 48kHz + * Capture: + * Inputed 48kHz data will be converted to + * system specified Hz + */ + convert-rate = <48000>; + ... + cpu { + sound-dai = <&rcar_sound>; + }; + codec { + ... + }; + }; + +------------------ +** Synchronous mode +------------------ + + > amixer set "SRC Out Rate" on + > aplay xxxx.wav + > amixer set "SRC Out Rate" 48000 + > amixer set "SRC Out Rate" 44100 + +============================================= +* CTU (Channel Transfer Unit) +============================================= + + [xx]ch [yy]ch + ------> [CTU] --------> + +CTU can convert [xx]ch to [yy]ch, or exchange outputed channel. +CTU conversion needs matrix settings. +For more detail information, see below + + Renesas R-Car datasheet + - Sampling Rate Converter Unit (SCU) + - SCU Operation + - CMD Block + - Functional Blocks in CMD + + Renesas R-Car datasheet + - Sampling Rate Converter Unit (SCU) + - Register Description + - CTUn Scale Value exx Register (CTUn_SVxxR) + + ${LINUX}/sound/soc/sh/rcar/ctu.c + - comment of header + +You need to use "renesas,rsrc-card" sound card for it. +example) + + sound { + compatible = "renesas,rsrc-card"; + ... + /* + * CTU setting + * All input data will be converted to 2ch + * as output data + */ + convert-channels = <2>; + ... + cpu { + sound-dai = <&rcar_sound>; + }; + codec { + ... + }; + }; + +Ex) Exchange output channel + Input -> Output + 1ch -> 0ch + 0ch -> 1ch + + example of using matrix + output 0ch = (input 0ch x 0) + (input 1ch x 1) + output 1ch = (input 0ch x 1) + (input 1ch x 0) + + amixer set "CTU Reset" on + amixer set "CTU Pass" 9,10 + amixer set "CTU SV0" 0,4194304 + amixer set "CTU SV1" 4194304,0 + + example of changing connection + amixer set "CTU Reset" on + amixer set "CTU Pass" 2,1 + +============================================= +* MIX (Mixer) +============================================= + +MIX merges 2 sounds path. You can see 2 sound interface on system, +and these sounds will be merged by MIX. + + aplay -D plughw:0,0 xxxx.wav & + aplay -D plughw:0,1 yyyy.wav + +You need to use "renesas,rsrc-card" sound card for it. +Ex) + [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0] + | + [MEM] -> [SRC2] -> [CTU03] -+ + + sound { + compatible = "renesas,rsrc-card"; + ... + cpu@0 { + sound-dai = <&rcar_sound 0>; + }; + cpu@1 { + sound-dai = <&rcar_sound 1>; + }; + codec { + ... + }; + }; + + &rcar_sound { + ... + rcar_sound,dai { + dai0 { + playback = <&src1 &ctu02 &mix0 &dvc0 &ssi0>; + }; + dai1 { + playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>; + }; + }; + }; + +============================================= +* DVC (Digital Volume and Mute Function) +============================================= + +DVC controls Playback/Capture volume. + +Playback Volume + amixer set "DVC Out" 100% + +Capture Volume + amixer set "DVC In" 100% + +Playback Mute + amixer set "DVC Out Mute" on + +Capture Mute + amixer set "DVC In Mute" on + +Volume Ramp + amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" + amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" + amixer set "DVC Out Ramp" on + aplay xxx.wav & + amixer set "DVC Out" 80% // Volume Down + amixer set "DVC Out" 100% // Volume Up + +============================================= +* SSIU (Serial Sound Interface Unit) +============================================= + +There is no DT settings for SSIU, because SSIU will be automatically +selected via SSI. +SSIU can avoid some under/over run error, because it has some buffer. +But you can't use it if SSI was PIO mode. +In DMA mode, you can select not to use SSIU by using "no-busif" on DT. + + &ssi0 { + no-busif; + }; + +============================================= +* SSI (Serial Sound Interface) +============================================= + +** PIO mode + +You can use PIO mode which is for connection check by using. +Note: The system will drop non-SSI modules in PIO mode +even though if DT is selecting other modules. + + &ssi0 { + pio-transfer + }; + +** DMA mode without SSIU + +You can use DMA without SSIU. +Note: under/over run, or noise are likely to occur + + &ssi0 { + no-busif; + }; + +** PIN sharing + +Each SSI can share WS pin. It is based on platform. +This is example if SSI1 want to share WS pin with SSI0 + + &ssi1 { + shared-pin; + }; + +** Multi-SSI + +You can use Multi-SSI. +This is example of SSI0/SSI1/SSI2 (= for 6ch) + + &rcar_sound { + ... + rcar_sound,dai { + dai0 { + playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>; + }; + }; + }; + +** TDM-SSI + +You can use TDM with SSI. +This is example of TDM 6ch. +Driver can automatically switches TDM <-> stereo mode in this case. + + rsnd_tdm: sound { + compatible = "simple-audio-card"; + ... + simple-audio-card,cpu { + /* system can use TDM 6ch */ + dai-tdm-slot-num = <6>; + sound-dai = <&rcar_sound>; + }; + simple-audio-card,codec { + ... + }; + }; + + +============================================= Required properties: +============================================= + - compatible : "renesas,rcar_sound-", fallbacks "renesas,rcar_sound-gen1" if generation1, and "renesas,rcar_sound-gen2" if generation2 @@ -64,7 +395,10 @@ DAI subnode properties: - playback : list of playback modules - capture : list of capture modules + +============================================= Example: +============================================= rcar_sound: sound@ec500000 { #sound-dai-cells = <1>; @@ -250,7 +584,9 @@ rcar_sound: sound@ec500000 { }; }; +============================================= Example: simple sound card +============================================= rsnd_ak4643: sound { compatible = "simple-audio-card"; @@ -290,7 +626,9 @@ Example: simple sound card shared-pin; }; +============================================= Example: simple sound card for TDM +============================================= rsnd_tdm: sound { compatible = "simple-audio-card"; @@ -309,7 +647,9 @@ Example: simple sound card for TDM }; }; +============================================= Example: simple sound card for Multi channel +============================================= &rcar_sound { pinctrl-0 = <&sound_pins &sound_clk_pins>; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 93b11e1c5d7f..02d971f69eff 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -8,6 +8,29 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +/* + * Playback Volume + * amixer set "DVC Out" 100% + * + * Capture Volume + * amixer set "DVC In" 100% + * + * Playback Mute + * amixer set "DVC Out Mute" on + * + * Capture Mute + * amixer set "DVC In Mute" on + * + * Volume Ramp + * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" + * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" + * amixer set "DVC Out Ramp" on + * aplay xxx.wav & + * amixer set "DVC Out" 80% // Volume Down + * amixer set "DVC Out" 100% // Volume Up + */ + #include "rsnd.h" #define RSND_DVC_NAME_SIZE 16