diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-11 10:41:07 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-13 08:17:18 +0300 |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/sound/soc/sh/rcar | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/sound/soc/sh/rcar')
-rw-r--r-- | kernel/sound/soc/sh/rcar/Makefile | 2 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/adg.c | 306 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/core.c | 372 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/ctu.c | 169 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/dma.c | 241 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/dvc.c | 103 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/gen.c | 49 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/mix.c | 198 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/rcar_snd.h | 117 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/rsnd.h | 235 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/rsrc-card.c | 457 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/src.c | 267 | ||||
-rw-r--r-- | kernel/sound/soc/sh/rcar/ssi.c | 265 |
13 files changed, 1943 insertions, 838 deletions
diff --git a/kernel/sound/soc/sh/rcar/Makefile b/kernel/sound/soc/sh/rcar/Makefile index f1b445173..8b258501a 100644 --- a/kernel/sound/soc/sh/rcar/Makefile +++ b/kernel/sound/soc/sh/rcar/Makefile @@ -1,4 +1,4 @@ -snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o +snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o snd-soc-rsrc-card-objs := rsrc-card.o diff --git a/kernel/sound/soc/sh/rcar/adg.c b/kernel/sound/soc/sh/rcar/adg.c index fefc881db..2a5b3a293 100644 --- a/kernel/sound/soc/sh/rcar/adg.c +++ b/kernel/sound/soc/sh/rcar/adg.c @@ -7,7 +7,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#include <linux/sh_clk.h> +#include <linux/clk-provider.h> #include "rsnd.h" #define CLKA 0 @@ -16,12 +16,26 @@ #define CLKI 3 #define CLKMAX 4 +#define CLKOUT 0 +#define CLKOUT1 1 +#define CLKOUT2 2 +#define CLKOUT3 3 +#define CLKOUTMAX 4 + +#define BRRx_MASK(x) (0x3FF & x) + +static struct rsnd_mod_ops adg_ops = { + .name = "adg", +}; + struct rsnd_adg { struct clk *clk[CLKMAX]; + struct clk *clkout[CLKOUTMAX]; + struct clk_onecell_data onecell; + struct rsnd_mod mod; - int rbga_rate_for_441khz_div_6; /* RBGA */ - int rbgb_rate_for_48khz_div_6; /* RBGB */ - u32 ckr; + int rbga_rate_for_441khz; /* RBGA */ + int rbgb_rate_for_48khz; /* RBGB */ }; #define for_each_rsnd_clk(pos, adg, i) \ @@ -29,17 +43,36 @@ struct rsnd_adg { (i < CLKMAX) && \ ((pos) = adg->clk[i]); \ i++) +#define for_each_rsnd_clkout(pos, adg, i) \ + for (i = 0; \ + (i < CLKOUTMAX) && \ + ((pos) = adg->clkout[i]); \ + i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) +static u32 rsnd_adg_calculate_rbgx(unsigned long div) +{ + int i, ratio; + + if (!div) + return 0; + + for (i = 3; i >= 0; i--) { + ratio = 2 << (i * 2); + if (0 == (div % ratio)) + return (u32)((i << 8) | ((div / ratio) - 1)); + } + + return ~0; +} static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); int id = rsnd_mod_id(mod); int ws = id; - if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { + if (rsnd_ssi_is_pin_sharing(io)) { switch (id) { case 1: case 2: @@ -60,6 +93,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int id = rsnd_mod_id(mod); int shift = (id % 2) ? 16 : 0; u32 mask, val; @@ -69,21 +105,26 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, val = val << shift; mask = 0xffff << shift; - rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val); + rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val); return 0; } -static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, +static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, u32 timsel) { + 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); - int id = rsnd_mod_id(mod); + 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; @@ -95,37 +136,38 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, switch (id / 2) { case 0: - rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out); break; case 1: - rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out); break; case 2: - rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out); break; case 3: - rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out); break; case 4: - rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out); break; } return 0; } -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +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(mod); + 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; @@ -134,10 +176,12 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, 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_div_6,/* 0011: RBGA */ - adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ + 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; @@ -175,25 +219,27 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, return -EIO; } - ret = rsnd_adg_set_src_timsel_gen2(mod, io, val); + 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(mod, DIV_EN, en, en); + rsnd_mod_bset(adg_mod, DIV_EN, en, en); dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); return 0; } -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, +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); - return rsnd_adg_set_src_timsel_gen2(mod, io, val); + rsnd_mod_confirm_src(src_mod); + + return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); } int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, @@ -202,6 +248,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, unsigned int dst_rate) { 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, shift; u32 mask, val; @@ -211,8 +258,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */ clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */ 0, /* 011: MLBCLK (not used) */ - adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */ - adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */ + adg->rbga_rate_for_441khz, /* 100: RBGA */ + adg->rbgb_rate_for_48khz, /* 101: RBGB */ }; /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */ @@ -238,13 +285,13 @@ find_rate: switch (id / 4) { case 0: - rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val); break; case 1: - rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val); break; case 2: - rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val); break; } @@ -257,12 +304,17 @@ find_rate: return 0; } -static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) +static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { - int id = rsnd_mod_id(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + int id = rsnd_mod_id(ssi_mod); int shift = (id % 4) * 8; u32 mask = 0xFF << shift; + rsnd_mod_confirm_ssi(ssi_mod); + val = val << shift; /* @@ -274,13 +326,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) switch (id / 4) { case 0: - rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val); break; case 1: - rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val); break; case 2: - rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val); break; } } @@ -326,14 +378,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) } /* - * find 1/6 clock from BRGA/BRGB + * find divided clock from BRGA/BRGB */ - if (rate == adg->rbga_rate_for_441khz_div_6) { + if (rate == adg->rbga_rate_for_441khz) { data = 0x10; goto found_clock; } - if (rate == adg->rbgb_rate_for_48khz_div_6) { + if (rate == adg->rbgb_rate_for_48khz) { data = 0x20; goto found_clock; } @@ -342,29 +394,60 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) found_clock: - /* see rsnd_adg_ssi_clk_init() */ - rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr); - rsnd_mod_write(mod, BRRA, 0x00000002); /* 1/6 */ - rsnd_mod_write(mod, BRRB, 0x00000002); /* 1/6 */ - /* * This "mod" = "ssi" here. * we can get "ssi id" from mod */ rsnd_adg_set_ssi_clk(mod, data); - dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", - rsnd_mod_id(mod), i, rate); + dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + data, rate); return 0; } -static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) +static void rsnd_adg_get_clkin(struct rsnd_priv *priv, + struct rsnd_adg *adg) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + static const char * const clk_name[] = { + [CLKA] = "clk_a", + [CLKB] = "clk_b", + [CLKC] = "clk_c", + [CLKI] = "clk_i", + }; + int i; + + for (i = 0; i < CLKMAX; i++) { + clk = devm_clk_get(dev, clk_name[i]); + adg->clk[i] = IS_ERR(clk) ? NULL : clk; + } + + for_each_rsnd_clk(clk, adg, i) + dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); +} + +static void rsnd_adg_get_clkout(struct rsnd_priv *priv, + struct rsnd_adg *adg) { struct clk *clk; - unsigned long rate; - u32 ckr; + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; + u32 ckr, rbgx, rbga, rbgb; + u32 rate, req_rate, div; + uint32_t count = 0; + unsigned long req_48kHz_rate, req_441kHz_rate; int i; + const char *parent_clk_name = NULL; + static const char * const clkout_name[] = { + [CLKOUT] = "audio_clkout", + [CLKOUT1] = "audio_clkout1", + [CLKOUT2] = "audio_clkout2", + [CLKOUT3] = "audio_clkout3", + }; int brg_table[] = { [CLKA] = 0x0, [CLKB] = 0x1, @@ -372,19 +455,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) [CLKI] = 0x2, }; + of_property_read_u32(np, "#clock-cells", &count); + + /* + * ADG supports BRRA/BRRB output only + * this means all clkout0/1/2/3 will be same rate + */ + of_property_read_u32(np, "clock-frequency", &req_rate); + req_48kHz_rate = 0; + req_441kHz_rate = 0; + if (0 == (req_rate % 44100)) + req_441kHz_rate = req_rate; + if (0 == (req_rate % 48000)) + req_48kHz_rate = req_rate; + /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC * have 44.1kHz or 48kHz base clocks for now. * * SSI itself can divide parent clock by 1/1 - 1/16 - * So, BRGA outputs 44.1kHz base parent clock 1/32, - * and, BRGB outputs 48.0kHz base parent clock 1/32 here. * see * rsnd_adg_ssi_clk_try_start() + * rsnd_ssi_master_clk_start() */ ckr = 0; - adg->rbga_rate_for_441khz_div_6 = 0; - adg->rbgb_rate_for_48khz_div_6 = 0; + rbga = 2; /* default 1/6 */ + rbgb = 2; /* default 1/6 */ + adg->rbga_rate_for_441khz = 0; + adg->rbgb_rate_for_48khz = 0; for_each_rsnd_clk(clk, adg, i) { rate = clk_get_rate(clk); @@ -392,19 +490,86 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) continue; /* RBGA */ - if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) { - adg->rbga_rate_for_441khz_div_6 = rate / 6; - ckr |= brg_table[i] << 20; + if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) { + div = 6; + if (req_441kHz_rate) + div = rate / req_441kHz_rate; + rbgx = rsnd_adg_calculate_rbgx(div); + if (BRRx_MASK(rbgx) == rbgx) { + rbga = rbgx; + adg->rbga_rate_for_441khz = rate / div; + ckr |= brg_table[i] << 20; + if (req_441kHz_rate) + parent_clk_name = __clk_get_name(clk); + } } /* RBGB */ - if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) { - adg->rbgb_rate_for_48khz_div_6 = rate / 6; - ckr |= brg_table[i] << 16; + if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) { + div = 6; + if (req_48kHz_rate) + div = rate / req_48kHz_rate; + rbgx = rsnd_adg_calculate_rbgx(div); + if (BRRx_MASK(rbgx) == rbgx) { + rbgb = rbgx; + adg->rbgb_rate_for_48khz = rate / div; + ckr |= brg_table[i] << 16; + if (req_48kHz_rate) { + parent_clk_name = __clk_get_name(clk); + ckr |= 0x80000000; + } + } } } - adg->ckr = ckr; + /* + * ADG supports BRRA/BRRB output only. + * this means all clkout0/1/2/3 will be * same rate + */ + + /* + * for clkout + */ + if (!count) { + clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT], + parent_clk_name, + (parent_clk_name) ? + 0 : CLK_IS_ROOT, req_rate); + if (!IS_ERR(clk)) { + adg->clkout[CLKOUT] = clk; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + } + } + /* + * for clkout0/1/2/3 + */ + else { + for (i = 0; i < CLKOUTMAX; i++) { + clk = clk_register_fixed_rate(dev, clkout_name[i], + parent_clk_name, + (parent_clk_name) ? + 0 : CLK_IS_ROOT, + req_rate); + if (!IS_ERR(clk)) { + adg->onecell.clks = adg->clkout; + adg->onecell.clk_num = CLKOUTMAX; + + adg->clkout[i] = clk; + + of_clk_add_provider(np, of_clk_src_onecell_get, + &adg->onecell); + } + } + } + + rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr); + rsnd_mod_write(adg_mod, BRRA, rbga); + rsnd_mod_write(adg_mod, BRRB, rbgb); + + for_each_rsnd_clkout(clk, adg, i) + dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk)); + dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", + ckr, rbga, rbgb); } int rsnd_adg_probe(struct platform_device *pdev, @@ -413,8 +578,6 @@ int rsnd_adg_probe(struct platform_device *pdev, { struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); - struct clk *clk; - int i; adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); if (!adg) { @@ -422,15 +585,16 @@ int rsnd_adg_probe(struct platform_device *pdev, return -ENOMEM; } - adg->clk[CLKA] = devm_clk_get(dev, "clk_a"); - adg->clk[CLKB] = devm_clk_get(dev, "clk_b"); - adg->clk[CLKC] = devm_clk_get(dev, "clk_c"); - adg->clk[CLKI] = devm_clk_get(dev, "clk_i"); - - for_each_rsnd_clk(clk, adg, i) - dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); + /* + * 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_adg_ssi_clk_init(priv, adg); + rsnd_adg_get_clkin(priv, adg); + rsnd_adg_get_clkout(priv, adg); priv->adg = adg; diff --git a/kernel/sound/soc/sh/rcar/core.c b/kernel/sound/soc/sh/rcar/core.c index 9f48d75fa..deed48ef2 100644 --- a/kernel/sound/soc/sh/rcar/core.c +++ b/kernel/sound/soc/sh/rcar/core.c @@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = { static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, + { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */ {}, }; MODULE_DEVICE_TABLE(of, rsnd_of_match); @@ -126,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match); #define rsnd_info_id(priv, io, name) \ ((io)->info->name - priv->info->name##_info) +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) +{ + if (mod->type != type) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "%s[%d] is not your expected module\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } +} + /* * rsnd_mod functions */ @@ -137,15 +149,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod) return mod->ops->name; } -struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod) +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { if (!mod || !mod->ops || !mod->ops->dma_req) return NULL; - return mod->ops->dma_req(mod); + return mod->ops->dma_req(io, mod); } -int rsnd_mod_init(struct rsnd_mod *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, @@ -160,6 +174,7 @@ int rsnd_mod_init(struct rsnd_mod *mod, mod->ops = ops; mod->type = type; mod->clk = clk; + mod->priv = priv; return ret; } @@ -170,13 +185,41 @@ void rsnd_mod_quit(struct rsnd_mod *mod) clk_unprepare(mod->clk); } +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io; + struct rsnd_dai *rdai; + int i, j; + + for_each_rsnd_dai(rdai, priv, j) { + + for (i = 0; i < RSND_MOD_MAX; i++) { + io = &rdai->playback; + if (mod == io->mod[i]) + callback(mod, io); + + io = &rdai->capture; + if (mod == io->mod[i]) + callback(mod, io); + } + } +} + +int rsnd_io_is_working(struct rsnd_dai_stream *io) +{ + /* see rsnd_dai_stream_init/quit() */ + return !!io->substream; +} + /* - * settting function + * ADINR function */ -u32 rsnd_get_adinr(struct rsnd_mod *mod) +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); u32 adinr = runtime->channels; @@ -196,30 +239,86 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) return adinr; } +u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct device *dev = rsnd_priv_to_dev(priv); + u32 chan = runtime->channels; + + switch (chan) { + case 1: + case 2: + case 4: + case 6: + case 8: + break; + default: + dev_warn(dev, "not supported channel\n"); + chan = 0; + break; + } + + return chan; +} + +/* + * DALIGN function + */ +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *target = src ? src : ssi; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 val = 0x76543210; + u32 mask = ~0; + + mask <<= runtime->channels * 4; + val = val & mask; + + switch (runtime->sample_bits) { + case 16: + val |= 0x67452301 & ~mask; + break; + case 32: + val |= 0x76543210 & ~mask; + break; + } + + /* + * exchange channeles on SRC if possible, + * otherwise, R/L volume settings on DVC + * changes inverted channels + */ + if (mod == target) + return val; + else + return 0x76543210; +} + /* * rsnd_dai functions */ -#define __rsnd_mod_call(mod, func, param...) \ +#define rsnd_mod_call(mod, io, func, param...) \ ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ - u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ + u32 mask = 0xF << __rsnd_mod_shift_##func; \ + u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ + u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - if ((mod->status & mask) == call) { \ - dev_dbg(dev, "%s[%d] %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ - ret = (mod)->ops->func(mod, param); \ - mod->status = (mod->status & ~mask) | (~call & mask); \ - } \ + int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ + mod->status = (mod->status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ + dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), \ + mod->status, call ? #func : ""); \ + if (call) \ + ret = (mod)->ops->func(mod, io, param); \ ret; \ }) -#define rsnd_mod_call(mod, func, param...) \ - (!(mod) ? -ENODEV : \ - !((mod)->ops->func) ? 0 : \ - __rsnd_mod_call(mod, func, param)) - #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ @@ -228,9 +327,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) mod = (io)->mod[i]; \ if (!mod) \ continue; \ - ret = rsnd_mod_call(mod, fn, param); \ - if (ret < 0) \ - break; \ + ret |= rsnd_mod_call(mod, io, fn, param); \ } \ ret; \ }) @@ -238,21 +335,20 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) static int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { + struct rsnd_priv *priv; + struct device *dev; + if (!mod) return -EIO; - if (io->mod[mod->type]) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_err(dev, "%s[%d] is not empty\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod)); - return -EIO; - } + priv = rsnd_mod_to_priv(mod); + dev = rsnd_priv_to_dev(priv); io->mod[mod->type] = mod; - mod->io = io; + + dev_dbg(dev, "%s[%d] is connected to io (%s)\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_io_is_play(io) ? "Playback" : "Capture"); return 0; } @@ -260,7 +356,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, static void rsnd_dai_disconnect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - mod->io = NULL; io->mod[mod->type] = NULL; } @@ -272,9 +367,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) return priv->rdai + id; } +#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai) static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) { - struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); return rsnd_rdai_get(priv, dai->id); } @@ -293,7 +389,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) return pos; } -void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) +bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) { io->byte_pos += byte; @@ -310,11 +406,27 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) io->next_period_byte = io->byte_per_period; } - snd_pcm_period_elapsed(substream); + return true; } + + return false; } -static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, +void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) +{ + struct snd_pcm_substream *substream = io->substream; + + /* + * this function should be called... + * + * - if rsnd_dai_pointer_update() returns true + * - without spin lock + */ + + snd_pcm_period_elapsed(substream); +} + +static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -326,8 +438,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, runtime->channels * samples_to_bytes(runtime, 1); io->next_period_byte = io->byte_per_period; +} - return 0; +static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io) +{ + io->substream = NULL; } static @@ -351,20 +466,18 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io)); int ret; unsigned long flags; - rsnd_lock(priv, flags); + spin_lock_irqsave(&priv->lock, flags); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - ret = rsnd_dai_stream_init(io, substream); - if (ret < 0) - goto dai_trigger_end; + rsnd_dai_stream_init(io, substream); ret = rsnd_platform_call(priv, dai, start, ssi_id); if (ret < 0) @@ -380,23 +493,19 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, break; case SNDRV_PCM_TRIGGER_STOP: ret = rsnd_dai_call(stop, io, priv); - if (ret < 0) - goto dai_trigger_end; - ret = rsnd_dai_call(quit, io, priv); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_dai_call(quit, io, priv); - ret = rsnd_platform_call(priv, dai, stop, ssi_id); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_platform_call(priv, dai, stop, ssi_id); + + rsnd_dai_stream_quit(io); break; default: ret = -EINVAL; } dai_trigger_end: - rsnd_unlock(priv, flags); + spin_unlock_irqrestore(&priv->lock, flags); return ret; } @@ -463,7 +572,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .set_fmt = rsnd_soc_dai_set_fmt, }; -#define rsnd_path_parse(priv, io, type) \ +#define rsnd_path_add(priv, io, type) \ ({ \ struct rsnd_mod *mod; \ int ret = 0; \ @@ -479,7 +588,7 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { ret; \ }) -#define rsnd_path_break(priv, io, type) \ +#define rsnd_path_remove(priv, io, type) \ { \ struct rsnd_mod *mod; \ int id = -1; \ @@ -493,6 +602,79 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { } \ } +void rsnd_path_parse(struct rsnd_priv *priv, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *cmd; + struct device *dev = rsnd_priv_to_dev(priv); + u32 data; + + /* Gen1 is not supported */ + if (rsnd_is_gen1(priv)) + return; + + if (!mix && !dvc) + return; + + if (mix) { + struct rsnd_dai *rdai; + int i; + u32 path[] = { + [0] = 0, + [1] = 1 << 0, + [2] = 0, + [3] = 0, + [4] = 0, + [5] = 1 << 8 + }; + + /* + * it is assuming that integrater is well understanding about + * data path. Here doesn't check impossible connection, + * like src2 + src5 + */ + data = 0; + for_each_rsnd_dai(rdai, priv, i) { + io = &rdai->playback; + if (mix == rsnd_io_to_mod_mix(io)) + data |= path[rsnd_mod_id(src)]; + + io = &rdai->capture; + if (mix == rsnd_io_to_mod_mix(io)) + data |= path[rsnd_mod_id(src)]; + } + + /* + * We can't use ctu = rsnd_io_ctu() here. + * Since, ID of dvc/mix are 0 or 1 (= same as CMD number) + * but ctu IDs are 0 - 7 (= CTU00 - CTU13) + */ + cmd = mix; + } else { + u32 path[] = { + [0] = 0x30000, + [1] = 0x30001, + [2] = 0x40000, + [3] = 0x10000, + [4] = 0x20000, + [5] = 0x40100 + }; + + data = path[rsnd_mod_id(src)]; + + cmd = dvc; + } + + dev_dbg(dev, "ctu/mix path = 0x%08x", data); + + rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data); + + rsnd_mod_write(cmd, CMD_CTRL, 0x10); +} + static int rsnd_path_init(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -510,18 +692,28 @@ static int rsnd_path_init(struct rsnd_priv *priv, * using fixed path. */ + /* SSI */ + ret = rsnd_path_add(priv, io, ssi); + if (ret < 0) + return ret; + /* SRC */ - ret = rsnd_path_parse(priv, io, src); + ret = rsnd_path_add(priv, io, src); if (ret < 0) return ret; - /* SSI */ - ret = rsnd_path_parse(priv, io, ssi); + /* CTU */ + ret = rsnd_path_add(priv, io, ctu); + if (ret < 0) + return ret; + + /* MIX */ + ret = rsnd_path_add(priv, io, mix); if (ret < 0) return ret; /* DVC */ - ret = rsnd_path_parse(priv, io, dvc); + ret = rsnd_path_add(priv, io, dvc); if (ret < 0) return ret; @@ -535,13 +727,15 @@ static void rsnd_of_parse_dai(struct platform_device *pdev, struct device_node *dai_node, *dai_np; struct device_node *ssi_node, *ssi_np; struct device_node *src_node, *src_np; + struct device_node *ctu_node, *ctu_np; + struct device_node *mix_node, *mix_np; struct device_node *dvc_node, *dvc_np; struct device_node *playback, *capture; struct rsnd_dai_platform_info *dai_info; struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct device *dev = &pdev->dev; int nr, i; - int dai_i, ssi_i, src_i, dvc_i; + int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i; if (!of_data) return; @@ -567,6 +761,8 @@ static void rsnd_of_parse_dai(struct platform_device *pdev, ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); + ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); + mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix"); dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc"); #define mod_parse(name) \ @@ -603,6 +799,8 @@ if (name##_node) { \ mod_parse(ssi); mod_parse(src); + mod_parse(ctu); + mod_parse(mix); mod_parse(dvc); of_node_put(playback); @@ -822,23 +1020,27 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, } if (change) - cfg->update(mod); + cfg->update(cfg->io, mod); return change; } static int __rsnd_kctrl_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg *cfg, - void (*update)(struct rsnd_mod *mod)) + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod)) { + struct snd_soc_card *soc_card = rtd->card; struct snd_card *card = rtd->card->snd_card; struct snd_kcontrol *kctrl; struct snd_kcontrol_new knew = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = name, .info = rsnd_kctrl_info, + .index = rtd - soc_card->rtd, .get = rsnd_kctrl_get, .put = rsnd_kctrl_put, .private_value = (unsigned long)cfg, @@ -858,6 +1060,7 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod, cfg->update = update; cfg->card = card; cfg->kctrl = kctrl; + cfg->io = io; return 0; } @@ -868,36 +1071,42 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg) } int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, u32 max) { _cfg->cfg.max = max; _cfg->cfg.size = RSND_DVC_CHANNELS; _cfg->cfg.val = _cfg->val; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_s *_cfg, u32 max) { _cfg->cfg.max = max; _cfg->cfg.size = 1; _cfg->cfg.val = &_cfg->val; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg_s *_cfg, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), const char * const *texts, u32 max) { @@ -905,7 +1114,7 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, _cfg->cfg.size = 1; _cfg->cfg.val = &_cfg->val; _cfg->cfg.texts = texts; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } /* @@ -968,8 +1177,8 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, /* * remove SRC/DVC from DAI, */ - rsnd_path_break(priv, io, src); - rsnd_path_break(priv, io, dvc); + rsnd_path_remove(priv, io, src); + rsnd_path_remove(priv, io, dvc); /* * fallback @@ -1004,26 +1213,19 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_dma_probe, rsnd_ssi_probe, rsnd_src_probe, + rsnd_ctu_probe, + rsnd_mix_probe, rsnd_dvc_probe, rsnd_adg_probe, rsnd_dai_probe, }; int ret, i; - info = NULL; - of_data = NULL; - if (of_id) { - info = devm_kzalloc(&pdev->dev, - sizeof(struct rcar_snd_info), GFP_KERNEL); - of_data = of_id->data; - } else { - info = pdev->dev.platform_data; - } - - if (!info) { - dev_err(dev, "driver needs R-Car sound information\n"); - return -ENODEV; - } + info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + of_data = of_id->data; /* * init priv data @@ -1099,6 +1301,8 @@ static int rsnd_remove(struct platform_device *pdev) struct rsnd_priv *priv) = { rsnd_ssi_remove, rsnd_src_remove, + rsnd_ctu_remove, + rsnd_mix_remove, rsnd_dvc_remove, }; int ret = 0, i; diff --git a/kernel/sound/soc/sh/rcar/ctu.c b/kernel/sound/soc/sh/rcar/ctu.c new file mode 100644 index 000000000..3cb214ab8 --- /dev/null +++ b/kernel/sound/soc/sh/rcar/ctu.c @@ -0,0 +1,169 @@ +/* + * ctu.c + * + * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +#define CTU_NAME_SIZE 16 +#define CTU_NAME "ctu" + +struct rsnd_ctu { + struct rsnd_ctu_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_ctu_nr(priv) ((priv)->ctu_nr) +#define for_each_rsnd_ctu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_ctu_nr(priv)) && \ + ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ + i++) + +#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) +#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) +static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, CTU_CTUIR, enable); +} + +static int rsnd_ctu_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_power_on(mod); + + rsnd_ctu_initialize_lock(mod); + + rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + + rsnd_ctu_initialize_unlock(mod); + + return 0; +} + +static int rsnd_ctu_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_power_off(mod); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ctu_ops = { + .name = CTU_NAME, + .init = rsnd_ctu_init, + .quit = rsnd_ctu_quit, +}; + +struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) + id = 0; + + return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id); +} + +static void rsnd_of_parse_ctu(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct device_node *node; + struct rsnd_ctu_platform_info *ctu_info; + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = &pdev->dev; + int nr; + + if (!of_data) + return; + + node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); + if (!node) + return; + + nr = of_get_child_count(node); + if (!nr) + goto rsnd_of_parse_ctu_end; + + ctu_info = devm_kzalloc(dev, + sizeof(struct rsnd_ctu_platform_info) * nr, + GFP_KERNEL); + if (!ctu_info) { + dev_err(dev, "ctu info allocation error\n"); + goto rsnd_of_parse_ctu_end; + } + + info->ctu_info = ctu_info; + info->ctu_info_nr = nr; + +rsnd_of_parse_ctu_end: + of_node_put(node); + +} + +int rsnd_ctu_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ctu *ctu; + struct clk *clk; + char name[CTU_NAME_SIZE]; + int i, nr, ret; + + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) + return 0; + + rsnd_of_parse_ctu(pdev, of_data, priv); + + nr = info->ctu_info_nr; + if (!nr) + return 0; + + ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL); + if (!ctu) + return -ENOMEM; + + priv->ctu_nr = nr; + priv->ctu = ctu; + + for_each_rsnd_ctu(ctu, priv, i) { + /* + * CTU00, CTU01, CTU02, CTU03 => CTU0 + * CTU10, CTU11, CTU12, CTU13 => CTU1 + */ + snprintf(name, CTU_NAME_SIZE, "%s.%d", + CTU_NAME, i / 4); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ctu->info = &info->ctu_info[i]; + + ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, + clk, RSND_MOD_CTU, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_ctu_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ctu *ctu; + int i; + + for_each_rsnd_ctu(ctu, priv, i) { + rsnd_mod_quit(rsnd_mod_get(ctu)); + } +} diff --git a/kernel/sound/soc/sh/rcar/dma.c b/kernel/sound/soc/sh/rcar/dma.c index 144308f15..5d084d040 100644 --- a/kernel/sound/soc/sh/rcar/dma.c +++ b/kernel/sound/soc/sh/rcar/dma.c @@ -27,16 +27,26 @@ struct rsnd_dma_ctrl { int dmapp_num; }; +struct rsnd_dma_ops { + char *name; + void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); + void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); +}; + #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) /* * Audio DMAC */ -static void rsnd_dmaen_complete(void *data) +static void __rsnd_dmaen_complete(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dma *dma = (struct rsnd_dma *)data; - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + bool elapsed = false; + unsigned long flags; /* * Renesas sound Gen1 needs 1 DMAC, @@ -49,23 +59,36 @@ static void rsnd_dmaen_complete(void *data) * rsnd_dai_pointer_update() will be called twice, * ant it will breaks io->byte_pos */ + spin_lock_irqsave(&priv->lock, flags); + + if (rsnd_io_is_working(io)) + elapsed = rsnd_dai_pointer_update(io, io->byte_per_period); + + spin_unlock_irqrestore(&priv->lock, flags); + + if (elapsed) + rsnd_dai_period_elapsed(io); +} + +static void rsnd_dmaen_complete(void *data) +{ + struct rsnd_mod *mod = data; - rsnd_dai_pointer_update(io, io->byte_per_period); + rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); } -static void rsnd_dmaen_stop(struct rsnd_dma *dma) +static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); dmaengine_terminate_all(dmaen->chan); } -static void rsnd_dmaen_start(struct rsnd_dma *dma) +static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_substream *substream = io->substream; struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; @@ -84,7 +107,7 @@ static void rsnd_dmaen_start(struct rsnd_dma *dma) } desc->callback = rsnd_dmaen_complete; - desc->callback_param = dma; + desc->callback_param = mod; if (dmaengine_submit(desc) < 0) { dev_err(dev, "dmaengine_submit() fail\n"); @@ -115,7 +138,8 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, return chan; } -static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, +static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { if ((!mod_from && !mod_to) || @@ -123,19 +147,19 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, return NULL; if (mod_from) - return rsnd_mod_dma_req(mod_from); + return rsnd_mod_dma_req(io, mod_from); else - return rsnd_mod_dma_req(mod_to); + return rsnd_mod_dma_req(io, mod_to); } -static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, +static int rsnd_dmaen_init(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, int id, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct device *dev = rsnd_priv_to_dev(priv); struct dma_slave_config cfg = {}; - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); int ret; @@ -145,7 +169,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, } if (dev->of_node) { - dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to); + dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); } else { dma_cap_mask_t mask; @@ -153,7 +177,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, dma_cap_set(DMA_SLAVE, mask); dmaen->chan = dma_request_channel(mask, shdma_chan_filter, - (void *)id); + (void *)(uintptr_t)id); } if (IS_ERR_OR_NULL(dmaen->chan)) { dmaen->chan = NULL; @@ -167,7 +191,8 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dev_dbg(dev, "dma : %pad -> %pad\n", + dev_dbg(dev, "%s %pad -> %pad\n", + dma->ops->name, &cfg.src_addr, &cfg.dst_addr); ret = dmaengine_slave_config(dmaen->chan, &cfg); @@ -177,7 +202,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, return 0; rsnd_dma_init_err: - rsnd_dma_quit(dma); + rsnd_dma_quit(io, dma); rsnd_dma_channel_err: /* @@ -189,7 +214,7 @@ rsnd_dma_channel_err: return -EAGAIN; } -static void rsnd_dmaen_quit(struct rsnd_dma *dma) +static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); @@ -200,6 +225,7 @@ static void rsnd_dmaen_quit(struct rsnd_dma *dma) } static struct rsnd_dma_ops rsnd_dmaen_ops = { + .name = "audmac", .start = rsnd_dmaen_start, .stop = rsnd_dmaen_stop, .init = rsnd_dmaen_init, @@ -238,9 +264,9 @@ static const u8 gen2_id_table_cmd[] = { 0x38, /* SCU_CMD1 */ }; -static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) +static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); @@ -268,11 +294,12 @@ static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) return entry[id]; } -static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from, +static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { - return (rsnd_dmapp_get_id(mod_from) << 24) + - (rsnd_dmapp_get_id(mod_to) << 16); + return (rsnd_dmapp_get_id(io, mod_from) << 24) + + (rsnd_dmapp_get_id(io, mod_to) << 16); } #define rsnd_dmapp_addr(dmac, dma, reg) \ @@ -299,7 +326,7 @@ static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); } -static void rsnd_dmapp_stop(struct rsnd_dma *dma) +static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { int i; @@ -312,7 +339,7 @@ static void rsnd_dmapp_stop(struct rsnd_dma *dma) } } -static void rsnd_dmapp_start(struct rsnd_dma *dma) +static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); @@ -321,19 +348,21 @@ static void rsnd_dmapp_start(struct rsnd_dma *dma) rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); } -static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, +static int rsnd_dmapp_init(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, int id, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct device *dev = rsnd_priv_to_dev(priv); dmapp->dmapp_id = dmac->dmapp_num; - dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE; + dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE; dmac->dmapp_num++; - rsnd_dmapp_stop(dma); + rsnd_dmapp_stop(io, dma); dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); @@ -342,6 +371,7 @@ static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, } static struct rsnd_dma_ops rsnd_dmapp_ops = { + .name = "audmac-pp", .start = rsnd_dmapp_start, .stop = rsnd_dmapp_stop, .init = rsnd_dmapp_init, @@ -386,17 +416,19 @@ static struct rsnd_dma_ops rsnd_dmapp_ops = { #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) static dma_addr_t -rsnd_gen2_dma_addr(struct rsnd_priv *priv, +rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, int is_play, int is_from) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); int use_src = !!rsnd_io_to_mod_src(io); - int use_dvc = !!rsnd_io_to_mod_dvc(io); + int use_cmd = !!rsnd_io_to_mod_dvc(io) || + !!rsnd_io_to_mod_mix(io) || + !!rsnd_io_to_mod_ctu(io); int id = rsnd_mod_id(mod); struct dma_addr { dma_addr_t out_addr; @@ -434,22 +466,24 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv, }; /* it shouldn't happen */ - if (use_dvc && !use_src) + if (use_cmd && !use_src) dev_err(dev, "DVC is selected without SRC\n"); /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(mod)) + if (is_ssi && rsnd_ssi_use_busif(io)) is_ssi++; return (is_from) ? - dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : - dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; + dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr : + dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr; } -static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, +static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, int is_play, int is_from) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + /* * gen1 uses default DMA addr */ @@ -459,82 +493,108 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, if (!mod) return 0; - return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); + return rsnd_gen2_dma_addr(io, mod, is_play, is_from); } -#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ +#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ static void rsnd_dma_of_path(struct rsnd_dma *dma, + struct rsnd_dai_stream *io, int is_play, struct rsnd_mod **mod_from, struct rsnd_mod **mod_to) { struct rsnd_mod *this = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(this); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); + struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_mod *mod[MOD_MAX]; - int i, index; + struct rsnd_mod *mod_start, *mod_end; + struct rsnd_priv *priv = rsnd_mod_to_priv(this); + struct device *dev = rsnd_priv_to_dev(priv); + int nr, i; + if (!ssi) + return; - for (i = 0; i < MOD_MAX; i++) + nr = 0; + for (i = 0; i < MOD_MAX; i++) { mod[i] = NULL; + nr += !!rsnd_io_to_mod(io, i); + } /* - * in play case... + * [S] -*-> [E] + * [S] -*-> SRC -o-> [E] + * [S] -*-> SRC -> DVC -o-> [E] + * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E] + * + * playback [S] = mem + * [E] = SSI * - * src -> dst + * capture [S] = SSI + * [E] = mem * - * mem -> SSI - * mem -> SRC -> SSI - * mem -> SRC -> DVC -> SSI + * -*-> Audio DMAC + * -o-> Audio DMAC peri peri */ - mod[0] = NULL; /* for "mem" */ - index = 1; - for (i = 1; i < MOD_MAX; i++) { - if (!src) { - mod[i] = ssi; - } else if (!dvc) { - mod[i] = src; - src = NULL; - } else { - if ((!is_play) && (this == src)) - this = dvc; + mod_start = (is_play) ? NULL : ssi; + mod_end = (is_play) ? ssi : NULL; - mod[i] = (is_play) ? src : dvc; - i++; - mod[i] = (is_play) ? dvc : src; + mod[0] = mod_start; + for (i = 1; i < nr; i++) { + if (src) { + mod[i] = src; src = NULL; + } else if (ctu) { + mod[i] = ctu; + ctu = NULL; + } else if (mix) { + mod[i] = mix; + mix = NULL; + } else if (dvc) { + mod[i] = dvc; dvc = NULL; } - - if (mod[i] == this) - index = i; - - if (mod[i] == ssi) - break; } + mod[i] = mod_end; - if (is_play) { - *mod_from = mod[index - 1]; - *mod_to = mod[index]; + /* + * | SSI | SRC | + * -------------+-----+-----+ + * is_play | o | * | + * !is_play | * | o | + */ + if ((this == ssi) == (is_play)) { + *mod_from = mod[nr - 1]; + *mod_to = mod[nr]; } else { - *mod_from = mod[index]; - *mod_to = mod[index - 1]; + *mod_from = mod[0]; + *mod_to = mod[1]; + } + + dev_dbg(dev, "module connection (this is %s[%d])\n", + rsnd_mod_name(this), rsnd_mod_id(this)); + for (i = 0; i <= nr; i++) { + dev_dbg(dev, " %s[%d]%s\n", + rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]), + (mod[i] == *mod_from) ? " from" : + (mod[i] == *mod_to) ? " to" : ""); } } -void rsnd_dma_stop(struct rsnd_dma *dma) +void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { - dma->ops->stop(dma); + dma->ops->stop(io, dma); } -void rsnd_dma_start(struct rsnd_dma *dma) +void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { - dma->ops->start(dma); + dma->ops->start(io, dma); } -void rsnd_dma_quit(struct rsnd_dma *dma) +void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); @@ -543,16 +603,16 @@ void rsnd_dma_quit(struct rsnd_dma *dma) if (!dmac) return; - dma->ops->quit(dma); + dma->ops->quit(io, dma); } -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) +int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) { - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_mod *mod_from; - struct rsnd_mod *mod_to; - struct rsnd_dai_stream *io = rsnd_mod_to_io(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 device *dev = rsnd_priv_to_dev(priv); int is_play = rsnd_io_is_play(io); /* @@ -564,10 +624,10 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) if (!dmac) return -EAGAIN; - rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); + rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to); - dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0); + 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) @@ -579,7 +639,12 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) if (rsnd_is_gen1(priv)) dma->ops = &rsnd_dmaen_ops; - return dma->ops->init(priv, dma, id, mod_from, mod_to); + dev_dbg(dev, "%s %s[%d] -> %s[%d]\n", + dma->ops->name, + rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), + rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + + return dma->ops->init(io, dma, id, mod_from, mod_to); } int rsnd_dma_probe(struct platform_device *pdev, diff --git a/kernel/sound/soc/sh/rcar/dvc.c b/kernel/sound/soc/sh/rcar/dvc.c index e5fcb062a..58f690900 100644 --- a/kernel/sound/soc/sh/rcar/dvc.c +++ b/kernel/sound/soc/sh/rcar/dvc.c @@ -24,6 +24,7 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ }; +#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) #define rsnd_dvc_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") @@ -63,7 +64,21 @@ static const char * const dvc_ramp_rate[] = { "0.125 dB/8192 steps", /* 10111 */ }; -static void rsnd_dvc_volume_update(struct rsnd_mod *mod) +static void rsnd_dvc_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, DVC_SWRSR, 0); + rsnd_mod_write(mod, DVC_SWRSR, 1); +} + +#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1) +#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0) +static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, DVC_DVUIR, enable); +} + +static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); u32 val[RSND_DVC_CHANNELS]; @@ -120,6 +135,7 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) } static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); @@ -133,70 +149,50 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, return 0; } -static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, +static int rsnd_dvc_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod); - struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct device *dev = rsnd_priv_to_dev(priv); - int dvc_id = rsnd_mod_id(dvc_mod); - int src_id = rsnd_mod_id(src_mod); - u32 route[] = { - [0] = 0x30000, - [1] = 0x30001, - [2] = 0x40000, - [3] = 0x10000, - [4] = 0x20000, - [5] = 0x40100 - }; - - if (src_id >= ARRAY_SIZE(route)) { - dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id); - return -EINVAL; - } - - rsnd_mod_hw_start(dvc_mod); + rsnd_mod_power_on(mod); - /* - * fixme - * it doesn't support CTU/MIX - */ - rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]); + rsnd_dvc_soft_reset(mod); - rsnd_mod_write(dvc_mod, DVC_SWRSR, 0); - rsnd_mod_write(dvc_mod, DVC_SWRSR, 1); + rsnd_dvc_initialize_lock(mod); - rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); + rsnd_path_parse(priv, io); - rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod)); + rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io)); /* ch0/ch1 Volume */ - rsnd_dvc_volume_update(dvc_mod); + rsnd_dvc_volume_update(io, mod); - rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); - - rsnd_adg_set_cmd_timsel_gen2(dvc_mod, io); + rsnd_adg_set_cmd_timsel_gen2(mod, io); return 0; } static int rsnd_dvc_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } static int rsnd_dvc_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + rsnd_dvc_initialize_unlock(mod); + rsnd_mod_write(mod, CMD_CTRL, 0x10); return 0; } static int rsnd_dvc_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_write(mod, CMD_CTRL, 0); @@ -205,15 +201,15 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod, } static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); int is_play = rsnd_io_is_play(io); int ret; /* Volume */ - ret = rsnd_kctrl_new_m(mod, rtd, + ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Playback Volume" : "DVC In Capture Volume", rsnd_dvc_volume_update, @@ -222,7 +218,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return ret; /* Mute */ - ret = rsnd_kctrl_new_m(mod, rtd, + ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Mute Switch" : "DVC In Mute Switch", rsnd_dvc_volume_update, @@ -231,7 +227,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return ret; /* Ramp */ - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, is_play ? "DVC Out Ramp Switch" : "DVC In Ramp Switch", rsnd_dvc_volume_update, @@ -239,7 +235,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_e(mod, rtd, + ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", &dvc->rup, @@ -248,7 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_e(mod, rtd, + ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", &dvc->rdown, @@ -261,7 +257,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return 0; } -static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); @@ -285,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) id = 0; - return &((struct rsnd_dvc *)(priv->dvc) + id)->mod; + return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id); } static void rsnd_of_parse_dvc(struct platform_device *pdev, @@ -335,23 +332,19 @@ int rsnd_dvc_probe(struct platform_device *pdev, char name[RSND_DVC_NAME_SIZE]; int i, nr, ret; + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) + return 0; + rsnd_of_parse_dvc(pdev, of_data, priv); nr = info->dvc_info_nr; if (!nr) return 0; - /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "CMD is not supported on Gen1\n"); - return -EINVAL; - } - dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); - if (!dvc) { - dev_err(dev, "CMD allocate failed\n"); + if (!dvc) return -ENOMEM; - } priv->dvc_nr = nr; priv->dvc = dvc; @@ -366,7 +359,7 @@ int rsnd_dvc_probe(struct platform_device *pdev, dvc->info = &info->dvc_info[i]; - ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops, + ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); if (ret) return ret; @@ -382,6 +375,6 @@ void rsnd_dvc_remove(struct platform_device *pdev, int i; for_each_rsnd_dvc(dvc, priv, i) { - rsnd_mod_quit(&dvc->mod); + rsnd_mod_quit(rsnd_mod_get(dvc)); } } diff --git a/kernel/sound/soc/sh/rcar/gen.c b/kernel/sound/soc/sh/rcar/gen.c index 8c7dc51b1..edcf4cc2e 100644 --- a/kernel/sound/soc/sh/rcar/gen.c +++ b/kernel/sound/soc/sh/rcar/gen.c @@ -22,13 +22,15 @@ #include "rsnd.h" struct rsnd_gen { - void __iomem *base[RSND_BASE_MAX]; - struct rsnd_gen_ops *ops; + /* RSND_BASE_MAX base */ + void __iomem *base[RSND_BASE_MAX]; + phys_addr_t res[RSND_BASE_MAX]; struct regmap *regmap[RSND_BASE_MAX]; + + /* RSND_REG_MAX base */ struct regmap_field *regs[RSND_REG_MAX]; - phys_addr_t res[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -79,11 +81,11 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + dev_dbg(dev, "r %s[%d] - %4d : %08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); - regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); - return val; } @@ -103,6 +105,22 @@ void rsnd_write(struct rsnd_priv *priv, regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); } +void rsnd_force_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return; + + dev_dbg(dev, "w %s[%d] - %4d : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data); + + regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); +} + void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data) { @@ -166,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regmap)) return PTR_ERR(regmap); + /* RSND_BASE_MAX base */ gen->base[reg_id] = base; gen->regmap[reg_id] = regmap; gen->res[reg_id] = res->start; @@ -182,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regs)) return PTR_ERR(regs); + /* RSND_REG_MAX base */ gen->regs[conf[i].idx] = regs; } @@ -200,12 +220,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, /* FIXME: it needs SSI_MODE2/3 in the future */ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80), - RSND_GEN_M_REG(BUSIF_DALIGN, 0x8, 0x80), + RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80), RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), - RSND_GEN_M_REG(INT_ENABLE, 0x18, 0x80), + RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), }; struct rsnd_regmap_field_conf conf_scu[] = { RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), + RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), @@ -214,7 +235,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), - RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4), RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), @@ -223,6 +244,18 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40), RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40), + RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), + RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), + RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), + RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40), + RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40), + RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40), + RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40), + RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40), + RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40), + RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40), + RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40), + RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40), RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100), RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100), RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100), diff --git a/kernel/sound/soc/sh/rcar/mix.c b/kernel/sound/soc/sh/rcar/mix.c new file mode 100644 index 000000000..953dd0be9 --- /dev/null +++ b/kernel/sound/soc/sh/rcar/mix.c @@ -0,0 +1,198 @@ +/* + * mix.c + * + * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +#define MIX_NAME_SIZE 16 +#define MIX_NAME "mix" + +struct rsnd_mix { + struct rsnd_mix_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_mix_nr(priv) ((priv)->mix_nr) +#define for_each_rsnd_mix(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_mix_nr(priv)) && \ + ((pos) = (struct rsnd_mix *)(priv)->mix + i); \ + i++) + + +static void rsnd_mix_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, MIX_SWRSR, 0); + rsnd_mod_write(mod, MIX_SWRSR, 1); +} + +#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1) +#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0) +static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, MIX_MIXIR, enable); +} + +static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + + /* Disable MIX dB setting */ + rsnd_mod_write(mod, MIX_MDBER, 0); + + rsnd_mod_write(mod, MIX_MDBAR, 0); + rsnd_mod_write(mod, MIX_MDBBR, 0); + rsnd_mod_write(mod, MIX_MDBCR, 0); + rsnd_mod_write(mod, MIX_MDBDR, 0); + + /* Enable MIX dB setting */ + rsnd_mod_write(mod, MIX_MDBER, 1); +} + +static int rsnd_mix_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_power_on(mod); + + rsnd_mix_soft_reset(mod); + + rsnd_mix_initialize_lock(mod); + + rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io)); + + rsnd_path_parse(priv, io); + + /* volume step */ + rsnd_mod_write(mod, MIX_MIXMR, 0); + rsnd_mod_write(mod, MIX_MVPDR, 0); + + rsnd_mix_volume_update(io, mod); + + rsnd_mix_initialize_unlock(mod); + + return 0; +} + +static int rsnd_mix_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_power_off(mod); + + return 0; +} + +static struct rsnd_mod_ops rsnd_mix_ops = { + .name = MIX_NAME, + .init = rsnd_mix_init, + .quit = rsnd_mix_quit, +}; + +struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) + id = 0; + + return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id); +} + +static void rsnd_of_parse_mix(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct device_node *node; + struct rsnd_mix_platform_info *mix_info; + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = &pdev->dev; + int nr; + + if (!of_data) + return; + + node = of_get_child_by_name(dev->of_node, "rcar_sound,mix"); + if (!node) + return; + + nr = of_get_child_count(node); + if (!nr) + goto rsnd_of_parse_mix_end; + + mix_info = devm_kzalloc(dev, + sizeof(struct rsnd_mix_platform_info) * nr, + GFP_KERNEL); + if (!mix_info) { + dev_err(dev, "mix info allocation error\n"); + goto rsnd_of_parse_mix_end; + } + + info->mix_info = mix_info; + info->mix_info_nr = nr; + +rsnd_of_parse_mix_end: + of_node_put(node); + +} + +int rsnd_mix_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix; + struct clk *clk; + char name[MIX_NAME_SIZE]; + int i, nr, ret; + + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) + return 0; + + rsnd_of_parse_mix(pdev, of_data, priv); + + nr = info->mix_info_nr; + if (!nr) + return 0; + + mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL); + if (!mix) + return -ENOMEM; + + priv->mix_nr = nr; + priv->mix = mix; + + for_each_rsnd_mix(mix, priv, i) { + snprintf(name, MIX_NAME_SIZE, "%s.%d", + MIX_NAME, i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + mix->info = &info->mix_info[i]; + + ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, + clk, RSND_MOD_MIX, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_mix_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_mix *mix; + int i; + + for_each_rsnd_mix(mix, priv, i) { + rsnd_mod_quit(rsnd_mod_get(mix)); + } +} diff --git a/kernel/sound/soc/sh/rcar/rcar_snd.h b/kernel/sound/soc/sh/rcar/rcar_snd.h new file mode 100644 index 000000000..d8e33d38d --- /dev/null +++ b/kernel/sound/soc/sh/rcar/rcar_snd.h @@ -0,0 +1,117 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RCAR_SND_H +#define RCAR_SND_H + + +#define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 + +#define RSND_GEN2_SCU 0 +#define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 + +#define RSND_BASE_MAX 4 + +/* + * flags + * + * 0xAB000000 + * + * A : clock sharing settings + * B : SSI direction + */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */ + +#define RSND_SSI(_dma_id, _irq, _flags) \ +{ .dma_id = _dma_id, .irq = _irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dma_id = -1, .irq = -1, .flags = 0 } + +struct rsnd_ssi_platform_info { + int dma_id; + int irq; + u32 flags; +}; + +#define RSND_SRC(rate, _dma_id) \ +{ .convert_rate = rate, .dma_id = _dma_id, } +#define RSND_SRC_UNUSED \ +{ .convert_rate = 0, .dma_id = -1, } + +struct rsnd_src_platform_info { + u32 convert_rate; /* sampling rate convert */ + int dma_id; /* for Gen2 SCU */ + int irq; +}; + +/* + * flags + */ +struct rsnd_ctu_platform_info { + u32 flags; +}; + +struct rsnd_mix_platform_info { + u32 flags; +}; + +struct rsnd_dvc_platform_info { + u32 flags; +}; + +struct rsnd_dai_path_info { + struct rsnd_ssi_platform_info *ssi; + struct rsnd_src_platform_info *src; + struct rsnd_ctu_platform_info *ctu; + struct rsnd_mix_platform_info *mix; + struct rsnd_dvc_platform_info *dvc; +}; + +struct rsnd_dai_platform_info { + struct rsnd_dai_path_info playback; + struct rsnd_dai_path_info capture; +}; + +/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN_MASK (0xF << 0) +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + +struct rcar_snd_info { + u32 flags; + struct rsnd_ssi_platform_info *ssi_info; + int ssi_info_nr; + struct rsnd_src_platform_info *src_info; + int src_info_nr; + struct rsnd_ctu_platform_info *ctu_info; + int ctu_info_nr; + struct rsnd_mix_platform_info *mix_info; + int mix_info_nr; + struct rsnd_dvc_platform_info *dvc_info; + int dvc_info_nr; + struct rsnd_dai_platform_info *dai_info; + int dai_info_nr; + int (*start)(int id); + int (*stop)(int id); +}; + +#endif diff --git a/kernel/sound/soc/sh/rcar/rsnd.h b/kernel/sound/soc/sh/rcar/rsnd.h index 4e6de6804..085329878 100644 --- a/kernel/sound/soc/sh/rcar/rsnd.h +++ b/kernel/sound/soc/sh/rcar/rsnd.h @@ -21,10 +21,11 @@ #include <linux/of_irq.h> #include <linux/sh_dma.h> #include <linux/workqueue.h> -#include <sound/rcar_snd.h> #include <sound/soc.h> #include <sound/pcm_params.h> +#include "rcar_snd.h" + /* * pseudo register * @@ -47,6 +48,18 @@ enum rsnd_reg { RSND_REG_SCU_SYS_STATUS0, RSND_REG_SCU_SYS_INT_EN0, RSND_REG_CMD_ROUTE_SLCT, + RSND_REG_CTU_CTUIR, + RSND_REG_CTU_ADINR, + RSND_REG_MIX_SWRSR, + RSND_REG_MIX_MIXIR, + RSND_REG_MIX_ADINR, + RSND_REG_MIX_MIXMR, + RSND_REG_MIX_MVPDR, + RSND_REG_MIX_MDBAR, + RSND_REG_MIX_MDBBR, + RSND_REG_MIX_MDBCR, + RSND_REG_MIX_MDBDR, + RSND_REG_MIX_MDBER, RSND_REG_DVC_SWRSR, RSND_REG_DVC_DVUIR, RSND_REG_DVC_ADINR, @@ -99,6 +112,7 @@ enum rsnd_reg { RSND_REG_SHARE26, RSND_REG_SHARE27, RSND_REG_SHARE28, + RSND_REG_SHARE29, RSND_REG_MAX, }; @@ -119,7 +133,7 @@ enum rsnd_reg { #define RSND_REG_SSI_CTRL RSND_REG_SHARE02 #define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03 #define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04 -#define RSND_REG_INT_ENABLE RSND_REG_SHARE05 +#define RSND_REG_SSI_INT_ENABLE RSND_REG_SHARE05 #define RSND_REG_SRC_BSDSR RSND_REG_SHARE06 #define RSND_REG_SRC_BSISR RSND_REG_SHARE07 #define RSND_REG_DIV_EN RSND_REG_SHARE08 @@ -136,13 +150,14 @@ enum rsnd_reg { #define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19 #define RSND_REG_CMD_CTRL RSND_REG_SHARE20 #define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21 -#define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22 +#define RSND_REG_SSI_BUSIF_DALIGN RSND_REG_SHARE22 #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 #define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 #define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 #define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28 +#define RSND_REG_SRC_BUSIF_DALIGN RSND_REG_SHARE29 struct rsnd_of_data; struct rsnd_priv; @@ -157,27 +172,28 @@ struct rsnd_dai_stream; rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) #define rsnd_mod_write(m, r, d) \ rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_force_write(m, r, d) \ + rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) #define rsnd_mod_bset(m, r, s, d) \ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); +void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); -u32 rsnd_get_adinr(struct rsnd_mod *mod); +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +void rsnd_path_parse(struct rsnd_priv *priv, + struct rsnd_dai_stream *io); /* * R-Car DMA */ struct rsnd_dma; -struct rsnd_dma_ops { - void (*start)(struct rsnd_dma *dma); - void (*stop)(struct rsnd_dma *dma); - int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); - void (*quit)(struct rsnd_dma *dma); -}; struct rsnd_dmaen { struct dma_chan *chan; @@ -199,24 +215,25 @@ struct rsnd_dma { }; #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) -void rsnd_dma_start(struct rsnd_dma *dma); -void rsnd_dma_stop(struct rsnd_dma *dma); -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id); -void rsnd_dma_quit(struct rsnd_dma *dma); +void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma); +void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma); +int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id); +void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma); int rsnd_dma_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); -#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) - /* * R-Car sound mod */ enum rsnd_mod_type { RSND_MOD_DVC = 0, + RSND_MOD_MIX, + RSND_MOD_CTU, RSND_MOD_SRC, RSND_MOD_SSI, RSND_MOD_MAX, @@ -224,25 +241,35 @@ enum rsnd_mod_type { struct rsnd_mod_ops { char *name; - struct dma_chan* (*dma_req)(struct rsnd_mod *mod); + struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); int (*probe)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*remove)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*init)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*quit)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*start)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*stop)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*pcm_new)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd); int (*hw_params)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); int (*fallback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); }; @@ -252,32 +279,43 @@ struct rsnd_mod { enum rsnd_mod_type type; struct rsnd_mod_ops *ops; struct rsnd_dma dma; - struct rsnd_dai_stream *io; + struct rsnd_priv *priv; struct clk *clk; u32 status; }; /* * status * - * bit - * 0 0: probe 1: remove - * 1 0: init 1: quit - * 2 0: start 1: stop - * 3 0: pcm_new - * 4 0: fallback + * 0xH0000CBA + * + * A 0: probe 1: remove + * B 0: init 1: quit + * C 0: start 1: stop * - * 31 bit is always called (see __rsnd_mod_call) - * 31 0: hw_params + * H is always called (see __rsnd_mod_call) + * 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 1 -#define __rsnd_mod_shift_quit 1 -#define __rsnd_mod_shift_start 2 -#define __rsnd_mod_shift_stop 2 -#define __rsnd_mod_shift_pcm_new 3 -#define __rsnd_mod_shift_fallback 4 -#define __rsnd_mod_shift_hw_params 31 /* always called */ +#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_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_init 1 +#define __rsnd_mod_add_quit -1 +#define __rsnd_mod_add_start 1 +#define __rsnd_mod_add_stop -1 +#define __rsnd_mod_add_pcm_new 0 +#define __rsnd_mod_add_fallback 0 +#define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_remove 1 @@ -289,21 +327,26 @@ struct rsnd_mod { #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 -#define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) +#define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) -#define rsnd_mod_to_io(mod) ((mod)->io) -#define rsnd_mod_id(mod) ((mod)->id) -#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) -#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) +#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) +#define rsnd_mod_power_on(mod) clk_enable((mod)->clk) +#define rsnd_mod_power_off(mod) clk_disable((mod)->clk) +#define rsnd_mod_get(ip) (&(ip)->mod) -int rsnd_mod_init(struct rsnd_mod *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); 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_mod *mod); +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)); /* * R-Car sound DAI @@ -320,15 +363,18 @@ struct rsnd_dai_stream { int byte_per_period; int next_period_byte; }; -#define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI]) -#define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC]) -#define rsnd_io_to_mod_dvc(io) ((io)->mod[RSND_MOD_DVC]) +#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) +#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) +#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) +#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) +#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX) +#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC) #define rsnd_io_to_rdai(io) ((io)->rdai) #define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io))) #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) #define rsnd_io_to_runtime(io) ((io)->substream ? \ (io)->substream->runtime : NULL) - +int rsnd_io_is_working(struct rsnd_dai_stream *io); struct rsnd_dai { char name[RSND_DAI_NAME_SIZE]; @@ -354,7 +400,8 @@ struct rsnd_dai { struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); -void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); /* @@ -411,12 +458,6 @@ struct rsnd_priv { void *gen; /* - * below value will be filled on rsnd_src_probe() - */ - void *src; - int src_nr; - - /* * below value will be filled on rsnd_adg_probe() */ void *adg; @@ -433,6 +474,24 @@ struct rsnd_priv { int ssi_nr; /* + * below value will be filled on rsnd_src_probe() + */ + void *src; + int src_nr; + + /* + * below value will be filled on rsnd_ctu_probe() + */ + void *ctu; + int ctu_nr; + + /* + * below value will be filled on rsnd_mix_probe() + */ + void *mix; + int mix_nr; + + /* * below value will be filled on rsnd_dvc_probe() */ void *dvc; @@ -449,8 +508,6 @@ struct rsnd_priv { #define rsnd_priv_to_pdev(priv) ((priv)->pdev) #define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev)) #define rsnd_priv_to_info(priv) ((priv)->info) -#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) -#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) /* * rsnd_kctrl @@ -460,7 +517,8 @@ struct rsnd_kctrl_cfg { unsigned int size; u32 *val; const char * const *texts; - void (*update)(struct rsnd_mod *mod); + void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); + struct rsnd_dai_stream *io; struct snd_card *card; struct snd_kcontrol *kctrl; }; @@ -480,26 +538,48 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg); #define rsnd_kctrl_remove(_cfg) _rsnd_kctrl_remove(&((_cfg).cfg)) int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, u32 max); int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_s *_cfg, u32 max); int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg_s *_cfg, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), const char * const *texts, u32 max); /* + * R-Car SSI + */ +int rsnd_ssi_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); + +#define rsnd_ssi_is_pin_sharing(io) \ + __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); + +/* * R-Car SRC */ int rsnd_src_probe(struct platform_device *pdev, @@ -512,25 +592,34 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime); int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io, int use_busif); -int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod); +int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io); int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod); int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod); -#define rsnd_src_nr(priv) ((priv)->src_nr) +/* + * R-Car CTU + */ +int rsnd_ctu_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); + +void rsnd_ctu_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); /* - * R-Car SSI + * R-Car MIX */ -int rsnd_ssi_probe(struct platform_device *pdev, +int rsnd_mix_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); -void rsnd_ssi_remove(struct platform_device *pdev, + +void rsnd_mix_remove(struct platform_device *pdev, struct rsnd_priv *priv); -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_mod *mod); +struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); /* * R-Car DVC @@ -542,7 +631,15 @@ void rsnd_dvc_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) - +#ifdef DEBUG +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); +#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI) +#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC) +#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC) +#else +#define rsnd_mod_confirm_ssi(mssi) +#define rsnd_mod_confirm_src(msrc) +#define rsnd_mod_confirm_dvc(mdvc) +#endif #endif diff --git a/kernel/sound/soc/sh/rcar/rsrc-card.c b/kernel/sound/soc/sh/rcar/rsrc-card.c index a68517afe..d61db9c38 100644 --- a/kernel/sound/soc/sh/rcar/rsrc-card.c +++ b/kernel/sound/soc/sh/rcar/rsrc-card.c @@ -41,65 +41,53 @@ static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { static const struct of_device_id rsrc_card_of_match[] = { { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, + { .compatible = "renesas,rsrc-card", }, {}, }; MODULE_DEVICE_TABLE(of, rsrc_card_of_match); +#define DAI_NAME_NUM 32 struct rsrc_card_dai { - const char *name; unsigned int fmt; unsigned int sysclk; struct clk *clk; + char dai_name[DAI_NAME_NUM]; }; -#define RSRC_FB_NUM 2 /* FE/BE */ #define IDX_CPU 0 #define IDX_CODEC 1 struct rsrc_card_priv { struct snd_soc_card snd_card; - struct rsrc_card_dai_props { - struct rsrc_card_dai cpu_dai; - struct rsrc_card_dai codec_dai; - } dai_props[RSRC_FB_NUM]; struct snd_soc_codec_conf codec_conf; - struct snd_soc_dai_link dai_link[RSRC_FB_NUM]; + struct rsrc_card_dai *dai_props; + struct snd_soc_dai_link *dai_link; + int dai_num; u32 convert_rate; }; #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_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) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct rsrc_card_dai_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; - int ret; - - ret = clk_prepare_enable(dai_props->cpu_dai.clk); - if (ret) - return ret; + struct rsrc_card_dai *dai_props = + rsrc_priv_to_props(priv, rtd - rtd->card->rtd); - ret = clk_prepare_enable(dai_props->codec_dai.clk); - if (ret) - clk_disable_unprepare(dai_props->cpu_dai.clk); - - return ret; + return clk_prepare_enable(dai_props->clk); } static void rsrc_card_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct rsrc_card_dai_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; - - clk_disable_unprepare(dai_props->cpu_dai.clk); + struct rsrc_card_dai *dai_props = + rsrc_priv_to_props(priv, rtd - rtd->card->rtd); - clk_disable_unprepare(dai_props->codec_dai.clk); + clk_disable_unprepare(dai_props->clk); } static struct snd_soc_ops rsrc_card_ops = { @@ -107,21 +95,31 @@ static struct snd_soc_ops rsrc_card_ops = { .shutdown = rsrc_card_shutdown, }; -static int __rsrc_card_dai_init(struct snd_soc_dai *dai, - struct rsrc_card_dai *set) +static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) { + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai; + struct snd_soc_dai_link *dai_link; + struct rsrc_card_dai *dai_props; + int num = rtd - rtd->card->rtd; int ret; - if (set->fmt) { - ret = snd_soc_dai_set_fmt(dai, set->fmt); + dai_link = rsrc_priv_to_link(priv, num); + dai_props = rsrc_priv_to_props(priv, num); + dai = dai_link->dynamic ? + rtd->cpu_dai : + rtd->codec_dai; + + if (dai_props->fmt) { + ret = snd_soc_dai_set_fmt(dai, dai_props->fmt); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "set_fmt error\n"); goto err; } } - if (set->sysclk) { - ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + if (dai_props->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "set_sysclk error\n"); goto err; @@ -134,27 +132,6 @@ err: return ret; } -static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct rsrc_card_dai_props *dai_props; - int num, ret; - - num = rtd - rtd->card->rtd; - dai_props = &priv->dai_props[num]; - ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai); - if (ret < 0) - return ret; - - ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai); - if (ret < 0) - return ret; - - return 0; -} - static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -170,40 +147,47 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int -rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, - struct device_node *np, - struct rsrc_card_dai *dai, - struct snd_soc_dai_link *dai_link, - int *args_count) +static int rsrc_card_parse_daifmt(struct device_node *node, + struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) { - struct device *dev = rsrc_priv_to_dev(priv); - const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); - struct of_phandle_args args; - struct device_node **p_node; - struct clk *clk; - const char **dai_name; - const char **name; - u32 val; - int ret; + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + struct device_node *codec = is_fe ? NULL : np; + unsigned int daifmt; - if (args_count) { - p_node = &dai_link->cpu_of_node; - dai_name = &dai_link->cpu_dai_name; - name = &dai_link->cpu_name; - } else { - p_node = &dai_link->codec_of_node; - dai_name = &dai_link->codec_dai_name; - name = &dai_link->codec_name; - } + daifmt = snd_soc_of_parse_daifmt(node, NULL, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - if (!np) { - /* use snd-soc-dummy */ - *p_node = NULL; - *dai_name = "snd-soc-dummy-dai"; - *name = "snd-soc-dummy"; - return 0; - } + if (!bitclkmaster && !framemaster) + return -EINVAL; + + if (codec == bitclkmaster) + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; + else + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; + + dai_props->fmt = daifmt; + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return 0; +} + +static int rsrc_card_parse_links(struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) +{ + struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + struct of_phandle_args args; + int ret; /* * Get node via "sound-dai = <&phandle port>" @@ -214,29 +198,88 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, if (ret) return ret; - *p_node = args.np; + if (is_fe) { + /* BE is dummy */ + dai_link->codec_of_node = NULL; + dai_link->codec_dai_name = "snd-soc-dummy-dai"; + dai_link->codec_name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + dai_link->cpu_of_node = args.np; + snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name); + + /* set dai_name */ + snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s", + dai_link->cpu_dai_name); + + /* + * In soc_bind_dai_link() will check cpu name after + * of_node matching if dai_link has cpu_dai_name. + * but, it will never match if name was created by + * fmt_single_name() remove cpu_dai_name if cpu_args + * was 0. See: + * fmt_single_name() + * fmt_multiple_name() + */ + if (!args.args_count) + dai_link->cpu_dai_name = NULL; + } else { + struct device *dev = rsrc_priv_to_dev(priv); + const struct rsrc_card_of_data *of_data; + + of_data = rsrc_dev_to_of_data(dev); + + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; + dai_link->codec_of_node = args.np; + snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); + + /* additional name prefix */ + if (of_data) { + priv->codec_conf.of_node = dai_link->codec_of_node; + priv->codec_conf.name_prefix = of_data->prefix; + } else { + snd_soc_of_parse_audio_prefix(&priv->snd_card, + &priv->codec_conf, + dai_link->codec_of_node, + "audio-prefix"); + } - /* Get dai->name */ - ret = snd_soc_of_get_dai_name(np, dai_name); - if (ret < 0) - return ret; + /* set dai_name */ + snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s", + dai_link->codec_dai_name); + } - /* - * FIXME - * - * rsrc assumes DPCM playback/capture - */ - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; + /* Simple Card assumes platform == cpu */ + dai_link->platform_of_node = dai_link->cpu_of_node; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->name = dai_props->dai_name; + dai_link->stream_name = dai_props->dai_name; + dai_link->ops = &rsrc_card_ops; + dai_link->init = rsrc_card_dai_init; - if (args_count) { - *args_count = args.args_count; - dai_link->dynamic = 1; - } else { - dai_link->no_pcm = 1; - priv->codec_conf.of_node = (*p_node); - priv->codec_conf.name_prefix = of_data->prefix; - } + return 0; +} + +static int rsrc_card_parse_clk(struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) +{ + struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + struct clk *clk; + struct device_node *of_np = is_fe ? dai_link->cpu_of_node : + dai_link->codec_of_node; + u32 val; /* * Parse dai->sysclk come from "clocks = <&xxx>" @@ -246,173 +289,98 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, */ if (of_property_read_bool(np, "clocks")) { clk = of_clk_get(np, 0); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - return ret; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); - dai->sysclk = clk_get_rate(clk); - dai->clk = clk; + dai_props->sysclk = clk_get_rate(clk); + dai_props->clk = clk; } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { - dai->sysclk = val; + dai_props->sysclk = val; } else { - clk = of_clk_get(args.np, 0); + clk = of_clk_get(of_np, 0); if (!IS_ERR(clk)) - dai->sysclk = clk_get_rate(clk); + dai_props->sysclk = clk_get_rate(clk); } return 0; } -static int rsrc_card_parse_daifmt(struct device_node *node, - struct rsrc_card_priv *priv, - struct device_node *codec, - int idx) -{ - struct device_node *bitclkmaster = NULL; - struct device_node *framemaster = NULL; - struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); - struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai; - struct rsrc_card_dai *codec_dai = &dai_props->codec_dai; - unsigned int daifmt; - - daifmt = snd_soc_of_parse_daifmt(node, NULL, - &bitclkmaster, &framemaster); - daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - - if (!bitclkmaster && !framemaster) - return -EINVAL; - - if (codec == bitclkmaster) - daifmt |= (codec == framemaster) ? - SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; - else - daifmt |= (codec == framemaster) ? - SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - - cpu_dai->fmt = daifmt; - codec_dai->fmt = daifmt; - - of_node_put(bitclkmaster); - of_node_put(framemaster); - - return 0; -} - static int rsrc_card_dai_link_of(struct device_node *node, + struct device_node *np, struct rsrc_card_priv *priv, int idx) { struct device *dev = rsrc_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); - struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); - struct device_node *cpu = NULL; - struct device_node *codec = NULL; - char *name; - char prop[128]; - int ret, cpu_args; - - cpu = of_get_child_by_name(node, "cpu"); - codec = of_get_child_by_name(node, "codec"); - - if (!cpu || !codec) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + bool is_fe = false; + int ret; - ret = rsrc_card_parse_daifmt(node, priv, codec, idx); - if (ret < 0) - goto dai_link_of_err; + if (0 == strcmp(np->name, "cpu")) + is_fe = true; - ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL, - &dai_props->cpu_dai, - dai_link, - &cpu_args); + ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe); if (ret < 0) - goto dai_link_of_err; + return ret; - ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL, - &dai_props->codec_dai, - dai_link, - NULL); + ret = rsrc_card_parse_links(np, priv, idx, is_fe); if (ret < 0) - goto dai_link_of_err; - - if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { - ret = -EINVAL; - goto dai_link_of_err; - } - - /* Simple Card assumes platform == cpu */ - dai_link->platform_of_node = dai_link->cpu_of_node; - - /* DAI link name is created from CPU/CODEC dai name */ - name = devm_kzalloc(dev, - strlen(dai_link->cpu_dai_name) + - strlen(dai_link->codec_dai_name) + 2, - GFP_KERNEL); - if (!name) { - ret = -ENOMEM; - goto dai_link_of_err; - } - - sprintf(name, "%s-%s", dai_link->cpu_dai_name, - dai_link->codec_dai_name); - dai_link->name = dai_link->stream_name = name; - dai_link->ops = &rsrc_card_ops; - dai_link->init = rsrc_card_dai_init; - - if (idx == IDX_CODEC) - dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; - - dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tcpu : %s / %04x / %d\n", - dai_link->cpu_dai_name, - dai_props->cpu_dai.fmt, - dai_props->cpu_dai.sysclk); - dev_dbg(dev, "\tcodec : %s / %04x / %d\n", - dai_link->codec_dai_name, - dai_props->codec_dai.fmt, - dai_props->codec_dai.sysclk); + return ret; - /* - * In soc_bind_dai_link() will check cpu name after - * of_node matching if dai_link has cpu_dai_name. - * but, it will never match if name was created by - * fmt_single_name() remove cpu_dai_name if cpu_args - * was 0. See: - * fmt_single_name() - * fmt_multiple_name() - */ - if (!cpu_args) - dai_link->cpu_dai_name = NULL; + ret = rsrc_card_parse_clk(np, priv, idx, is_fe); + if (ret < 0) + return ret; -dai_link_of_err: - of_node_put(cpu); - of_node_put(codec); + dev_dbg(dev, "\t%s / %04x / %d\n", + dai_props->dai_name, + dai_props->fmt, + dai_props->sysclk); return ret; } static int rsrc_card_parse_of(struct device_node *node, - struct rsrc_card_priv *priv) + struct rsrc_card_priv *priv, + struct device *dev) { - struct device *dev = rsrc_priv_to_dev(priv); const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + struct rsrc_card_dai *props; + struct snd_soc_dai_link *links; + struct device_node *np; int ret; - int i; + int i, num; if (!node) return -EINVAL; + num = of_get_child_count(node); + props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL); + links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL); + if (!props || !links) + return -ENOMEM; + + priv->dai_props = props; + priv->dai_link = links; + priv->dai_num = num; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + priv->snd_card.codec_conf = &priv->codec_conf; + priv->snd_card.num_configs = 1; + + if (of_data) { + priv->snd_card.of_dapm_routes = of_data->routes; + priv->snd_card.num_of_dapm_routes = of_data->num_routes; + } else { + snd_soc_of_parse_audio_routing(&priv->snd_card, + "audio-routing"); + } + /* Parse the card name from DT */ snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); - /* DAPM routes */ - priv->snd_card.of_dapm_routes = of_data->routes; - priv->snd_card.num_of_dapm_routes = of_data->num_routes; - /* sampling rate convert */ of_property_read_u32(node, "convert-rate", &priv->convert_rate); @@ -420,11 +388,12 @@ static int rsrc_card_parse_of(struct device_node *node, priv->snd_card.name ? priv->snd_card.name : "", priv->convert_rate); - /* FE/BE */ - for (i = 0; i < RSRC_FB_NUM; i++) { - ret = rsrc_card_dai_link_of(node, priv, i); + i = 0; + for_each_child_of_node(node, np) { + ret = rsrc_card_dai_link_of(node, np, priv, i); if (ret < 0) return ret; + i++; } if (!priv->snd_card.name) @@ -451,7 +420,6 @@ static int rsrc_card_unref(struct snd_soc_card *card) static int rsrc_card_probe(struct platform_device *pdev) { struct rsrc_card_priv *priv; - struct snd_soc_dai_link *dai_link; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; @@ -461,16 +429,7 @@ static int rsrc_card_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - /* Init snd_soc_card */ - priv->snd_card.owner = THIS_MODULE; - priv->snd_card.dev = dev; - dai_link = priv->dai_link; - priv->snd_card.dai_link = dai_link; - priv->snd_card.num_links = RSRC_FB_NUM; - priv->snd_card.codec_conf = &priv->codec_conf; - priv->snd_card.num_configs = 1; - - ret = rsrc_card_parse_of(np, priv); + ret = rsrc_card_parse_of(np, priv, dev); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); diff --git a/kernel/sound/soc/sh/rcar/src.c b/kernel/sound/soc/sh/rcar/src.c index 3beb32eb4..68b439ed2 100644 --- a/kernel/sound/soc/sh/rcar/src.c +++ b/kernel/sound/soc/sh/rcar/src.c @@ -30,6 +30,7 @@ struct rsnd_src { #define RSND_SRC_NAME_SIZE 16 +#define rsnd_src_nr(priv) ((priv)->src_nr) #define rsnd_enable_sync_convert(src) ((src)->sen.val) #define rsnd_src_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") @@ -117,10 +118,24 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ -static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) +static void rsnd_src_soft_reset(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SRC_SWRSR, 0); + rsnd_mod_write(mod, SRC_SWRSR, 1); +} + + +#define rsnd_src_initialize_lock(mod) __rsnd_src_initialize_lock(mod, 1) +#define rsnd_src_initialize_unlock(mod) __rsnd_src_initialize_lock(mod, 0) +static void __rsnd_src_initialize_lock(struct rsnd_mod *mod, u32 enable) +{ + rsnd_mod_write(mod, SRC_SRCIR, enable); +} + +static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); return rsnd_dma_request_channel(rsnd_src_of_node(priv), @@ -129,11 +144,10 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) } int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io, int use_busif) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); int ssi_id = rsnd_mod_id(ssi_mod); /* @@ -145,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, /* * SSI_MODE1 */ - if (rsnd_ssi_is_pin_sharing(ssi_mod)) { + if (rsnd_ssi_is_pin_sharing(io)) { int shift = -1; switch (ssi_id) { case 1: @@ -170,33 +184,21 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, * DMA settings for SSIU */ if (use_busif) { - u32 val = 0x76543210; - u32 mask = ~0; + u32 val = rsnd_get_dalign(ssi_mod, io); rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, - rsnd_get_adinr(ssi_mod)); + rsnd_get_adinr_bit(ssi_mod, io)); rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); - mask <<= runtime->channels * 4; - val = val & mask; - - switch (runtime->sample_bits) { - case 16: - val |= 0x67452301 & ~mask; - break; - case 32: - val |= 0x76543210 & ~mask; - break; - } - rsnd_mod_write(ssi_mod, BUSIF_DALIGN, val); - + rsnd_mod_write(ssi_mod, SSI_BUSIF_DALIGN, val); } return 0; } -int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod) +int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io) { /* * DMA settings for SSIU @@ -214,10 +216,9 @@ int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod) return 0; /* enable SSI interrupt if Gen2 */ - if (rsnd_ssi_is_dma_mode(ssi_mod)) - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000); - else - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, + rsnd_ssi_is_dma_mode(ssi_mod) ? + 0x0e000000 : 0x0f000000); return 0; } @@ -230,15 +231,14 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) return 0; /* disable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000); + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); return 0; } -static u32 rsnd_src_convert_rate(struct rsnd_src *src) +static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_src *src) { - struct rsnd_mod *mod = &src->mod; - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 convert_rate; @@ -274,7 +274,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, * return convert rate if SRC is used, * otherwise, return runtime->rate as usual */ - rate = rsnd_src_convert_rate(src); + rate = rsnd_src_convert_rate(io, src); } if (!rate) @@ -283,23 +283,19 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, return rate; } -static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); 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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 fsrate = 0; if (convert_rate) fsrate = 0x0400000 / convert_rate * runtime->rate; - /* set/clear soft reset */ - rsnd_mod_write(mod, SRC_SWRSR, 0); - rsnd_mod_write(mod, SRC_SWRSR, 1); - /* Set channel number and output bit length */ - rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod)); + rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr_bit(mod, io)); /* Enable the initial value of IFS */ if (fsrate) { @@ -316,6 +312,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) } static int rsnd_src_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *fe_params) { @@ -355,29 +352,28 @@ static int rsnd_src_init(struct rsnd_mod *mod, { struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); + + rsnd_src_soft_reset(mod); + + rsnd_src_initialize_lock(mod); src->err = 0; /* reset sync convert_rate */ src->sync.val = 0; - /* - * Initialize the operation of the SRC internal circuits - * see rsnd_src_start() - */ - rsnd_mod_write(mod, SRC_SRCIR, 1); - return 0; } static int rsnd_src_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); if (src->err) dev_warn(dev, "%s[%d] under/over flow err = %d\n", @@ -393,11 +389,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, static int rsnd_src_start(struct rsnd_mod *mod) { - /* - * Cancel the initialization and operate the SRC function - * see rsnd_src_init() - */ - rsnd_mod_write(mod, SRC_SRCIR, 0); + rsnd_src_initialize_unlock(mod); return 0; } @@ -411,9 +403,9 @@ static int rsnd_src_stop(struct rsnd_mod *mod) /* * Gen1 functions */ -static int rsnd_src_set_route_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct src_route_config { u32 mask; int shift; @@ -448,13 +440,13 @@ static int rsnd_src_set_route_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 convert_rate = rsnd_src_convert_rate(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 mask; u32 val; int shift; @@ -506,12 +498,13 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; - ret = rsnd_src_set_convert_rate(mod); + ret = rsnd_src_set_convert_rate(mod, io); if (ret < 0) return ret; @@ -523,7 +516,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); /* Gen1/Gen2 are not compatible */ - if (rsnd_src_convert_rate(src)) + if (rsnd_src_convert_rate(io, src)) rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ @@ -532,6 +525,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) } static int rsnd_src_init_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; @@ -540,15 +534,15 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_src_set_route_gen1(mod); + ret = rsnd_src_set_route_gen1(io, mod); if (ret < 0) return ret; - ret = rsnd_src_set_convert_rate_gen1(mod); + ret = rsnd_src_set_convert_rate_gen1(mod, io); if (ret < 0) return ret; - ret = rsnd_src_set_convert_timing_gen1(mod); + ret = rsnd_src_set_convert_timing_gen1(io, mod); if (ret < 0) return ret; @@ -556,6 +550,7 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod, } static int rsnd_src_start_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int id = rsnd_mod_id(mod); @@ -566,6 +561,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod, } static int rsnd_src_stop_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int id = rsnd_mod_id(mod); @@ -611,6 +607,14 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) int_val = 0; } + /* + * WORKAROUND + * + * ignore over flow error when rsnd_enable_sync_convert() + */ + if (rsnd_enable_sync_convert(src)) + sys_int_val = sys_int_val & 0xffff; + rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); @@ -626,11 +630,22 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) { - u32 val = OUF_SRC(rsnd_mod_id(mod)); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 val0, val1; bool ret = false; - if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) || - (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) { + val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); + + /* + * WORKAROUND + * + * ignore over flow error when rsnd_enable_sync_convert() + */ + if (rsnd_enable_sync_convert(src)) + val0 = val0 & 0xffff; + + if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) { struct rsnd_src *src = rsnd_mod_to_src(mod); src->err++; @@ -643,10 +658,23 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) return ret; } -static int _rsnd_src_start_gen2(struct rsnd_mod *mod) +static int _rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 val; + + val = rsnd_get_dalign(mod, io); + + rsnd_mod_write(mod, SRC_BUSIF_DALIGN, val); + + /* + * WORKAROUND + * + * Enable SRC output if you want to use sync convert together with DVC + */ + val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ? + 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); @@ -670,13 +698,16 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) return rsnd_src_stop(mod); } -static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_mod *mod = data; - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - if (!io) - return IRQ_NONE; + spin_lock(&priv->lock); + + /* ignore all cases if not working */ + if (!rsnd_io_is_working(io)) + goto rsnd_src_interrupt_gen2_out; if (rsnd_src_error_record_gen2(mod)) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); @@ -688,22 +719,32 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) _rsnd_src_stop_gen2(mod); if (src->err < 1024) - _rsnd_src_start_gen2(mod); + _rsnd_src_start_gen2(mod, io); else dev_warn(dev, "no more SRC restart\n"); } +rsnd_src_interrupt_gen2_out: + spin_unlock(&priv->lock); +} + +static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2); + return IRQ_HANDLED; } -static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); 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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 cr, route; uint ratio; int ret; @@ -721,7 +762,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) return -EINVAL; } - ret = rsnd_src_set_convert_rate(mod); + ret = rsnd_src_set_convert_rate(mod, io); if (ret < 0) return ret; @@ -757,12 +798,12 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod) +static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); 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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); int ret; if (convert_rate) @@ -776,6 +817,7 @@ static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod) } static int rsnd_src_probe_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); @@ -797,7 +839,7 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, return ret; } - ret = rsnd_dma_init(priv, + ret = rsnd_dma_init(io, rsnd_mod_to_dma(mod), src->info->dma_id); @@ -805,14 +847,16 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, } static int rsnd_src_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_dma_quit(rsnd_mod_to_dma(mod)); + rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); return 0; } static int rsnd_src_init_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; @@ -821,11 +865,11 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_src_set_convert_rate_gen2(mod); + ret = rsnd_src_set_convert_rate_gen2(mod, io); if (ret < 0) return ret; - ret = rsnd_src_set_convert_timing_gen2(mod); + ret = rsnd_src_set_convert_timing_gen2(io, mod); if (ret < 0) return ret; @@ -833,31 +877,33 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, } static int rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_dma_start(rsnd_mod_to_dma(mod)); + rsnd_dma_start(io, rsnd_mod_to_dma(mod)); - return _rsnd_src_start_gen2(mod); + return _rsnd_src_start_gen2(mod, io); } static int rsnd_src_stop_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; ret = _rsnd_src_stop_gen2(mod); - rsnd_dma_stop(rsnd_mod_to_dma(mod)); + rsnd_dma_stop(io, rsnd_mod_to_dma(mod)); return ret; } -static void rsnd_src_reconvert_update(struct rsnd_mod *mod) +static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); 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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 fsrate; if (!runtime) @@ -872,12 +918,12 @@ static void rsnd_src_reconvert_update(struct rsnd_mod *mod) rsnd_mod_write(mod, SRC_IFSVR, fsrate); } -static int rsnd_src_pcm_new(struct rsnd_mod *mod, +static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); 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; @@ -886,28 +932,21 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, */ /* - * Gen1 is not supported - */ - if (rsnd_is_gen1(priv)) - return 0; - - /* * SRC sync convert needs clock master */ if (!rsnd_rdai_is_clk_master(rdai)) return 0; /* - * We can't use SRC sync convert - * if it has DVC + * SRC In doesn't work if DVC was enabled */ - if (rsnd_io_to_mod_dvc(io)) + if (dvc && !rsnd_io_is_play(io)) return 0; /* * enable sync convert */ - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate Switch" : "SRC In Rate Switch", @@ -916,7 +955,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", @@ -936,7 +975,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = { .start = rsnd_src_start_gen2, .stop = rsnd_src_stop_gen2, .hw_params = rsnd_src_hw_params, - .pcm_new = rsnd_src_pcm_new, + .pcm_new = rsnd_src_pcm_new_gen2, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) @@ -944,7 +983,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) id = 0; - return &((struct rsnd_src *)(priv->src) + id)->mod; + return rsnd_mod_get((struct rsnd_src *)(priv->src) + id); } static void rsnd_of_parse_src(struct platform_device *pdev, @@ -1004,8 +1043,10 @@ int rsnd_src_probe(struct platform_device *pdev, int i, nr, ret; ops = NULL; - if (rsnd_is_gen1(priv)) + if (rsnd_is_gen1(priv)) { ops = &rsnd_src_gen1_ops; + dev_warn(dev, "Gen1 support will be removed soon\n"); + } if (rsnd_is_gen2(priv)) ops = &rsnd_src_gen2_ops; if (!ops) { @@ -1023,10 +1064,8 @@ int rsnd_src_probe(struct platform_device *pdev, return 0; src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); - if (!src) { - dev_err(dev, "SRC allocate failed\n"); + if (!src) return -ENOMEM; - } priv->src_nr = nr; priv->src = src; @@ -1041,7 +1080,7 @@ int rsnd_src_probe(struct platform_device *pdev, src->info = &info->src_info[i]; - ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); + ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i); if (ret) return ret; } @@ -1056,6 +1095,6 @@ void rsnd_src_remove(struct platform_device *pdev, int i; for_each_rsnd_src(src, priv, i) { - rsnd_mod_quit(&src->mod); + rsnd_mod_quit(rsnd_mod_get(src)); } } diff --git a/kernel/sound/soc/sh/rcar/ssi.c b/kernel/sound/soc/sh/rcar/ssi.c index 7bb9c087f..1427ec21b 100644 --- a/kernel/sound/soc/sh/rcar/ssi.c +++ b/kernel/sound/soc/sh/rcar/ssi.c @@ -66,6 +66,7 @@ struct rsnd_ssi { u32 cr_own; u32 cr_clk; + int chan; int err; unsigned int usrcnt; }; @@ -78,18 +79,17 @@ struct rsnd_ssi { #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) -#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) +#define rsnd_ssi_parent(ssi) ((ssi)->parent) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) #define rsnd_ssi_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") -int rsnd_ssi_use_busif(struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int use_busif = 0; if (!rsnd_ssi_is_dma_mode(mod)) @@ -128,10 +128,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, 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); - int i, j, ret; - int adg_clk_div_table[] = { - 1, 6, /* see adg.c */ - }; + struct rsnd_mod *mod = rsnd_mod_get(ssi); + int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, }; @@ -141,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, /* * Find best clock, and try to start ADG */ - for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { - for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { - - /* - * this driver is assuming that - * system word is 64fs (= 2 x 32bit) - * see rsnd_ssi_init() - */ - main_rate = rate / adg_clk_div_table[i] - * 32 * 2 * ssi_clk_mul_table[j]; - - ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); - if (0 == ret) { - ssi->cr_clk = FORCE | SWL_32 | - SCKD | SWSD | CKDV(j); - - dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(&ssi->mod), - rsnd_mod_id(&ssi->mod), rate); - - return 0; - } + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * this driver is assuming that + * system word is 64fs (= 2 x 32bit) + * see rsnd_ssi_init() + */ + main_rate = rate * 32 * 2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); + if (0 == ret) { + ssi->cr_clk = FORCE | SWL_32 | + SCKD | SWSD | CKDV(j); + + dev_dbg(dev, "%s[%d] outputs %u Hz\n", + rsnd_mod_name(mod), + rsnd_mod_id(mod), rate); + + return 0; } } @@ -172,8 +167,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) { + struct rsnd_mod *mod = rsnd_mod_get(ssi); + ssi->cr_clk = 0; - rsnd_adg_ssi_clk_stop(&ssi->mod); + rsnd_adg_ssi_clk_stop(mod); } static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, @@ -182,55 +179,62 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *mod = rsnd_mod_get(ssi); u32 cr_mode; u32 cr; if (0 == ssi->usrcnt) { - rsnd_mod_hw_start(&ssi->mod); + rsnd_mod_power_on(mod); if (rsnd_rdai_is_clk_master(rdai)) { - if (rsnd_ssi_clk_from_parent(ssi)) - rsnd_ssi_hw_start(ssi->parent, io); + struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + + if (ssi_parent) + rsnd_ssi_hw_start(ssi_parent, io); else rsnd_ssi_master_clk_start(ssi, io); } } - cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? - DMEN : /* DMA : enable DMA */ - DIEN; /* PIO : enable Data interrupt */ - + if (rsnd_ssi_is_dma_mode(mod)) { + cr_mode = UIEN | OIEN | /* over/under run */ + DMEN; /* DMA : enable DMA */ + } else { + cr_mode = DIEN; /* PIO : enable Data interrupt */ + } cr = ssi->cr_own | ssi->cr_clk | cr_mode | - UIEN | OIEN | EN; + EN; - rsnd_mod_write(&ssi->mod, SSICR, cr); + rsnd_mod_write(mod, SSICR, cr); /* enable WS continue */ if (rsnd_rdai_is_clk_master(rdai)) - rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + rsnd_mod_write(mod, SSIWSR, CONT); /* clear error status */ - rsnd_mod_write(&ssi->mod, SSISR, 0); + rsnd_mod_write(mod, SSISR, 0); ssi->usrcnt++; dev_dbg(dev, "%s[%d] hw started\n", - rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); + rsnd_mod_name(mod), rsnd_mod_id(mod)); } -static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) +static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) { - struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(&ssi->mod); + struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); u32 cr; - if (0 == ssi->usrcnt) /* stop might be called without start */ + if (0 == ssi->usrcnt) { + dev_err(dev, "%s called without starting\n", __func__); return; + } ssi->usrcnt--; @@ -242,38 +246,42 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) cr = ssi->cr_own | ssi->cr_clk; - rsnd_mod_write(&ssi->mod, SSICR, cr | EN); - rsnd_ssi_status_check(&ssi->mod, DIRQ); + rsnd_mod_write(mod, SSICR, cr | EN); + rsnd_ssi_status_check(mod, DIRQ); /* * disable SSI, * and, wait idle state */ - rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ - rsnd_ssi_status_check(&ssi->mod, IIRQ); + rsnd_mod_write(mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(mod, IIRQ); if (rsnd_rdai_is_clk_master(rdai)) { - if (rsnd_ssi_clk_from_parent(ssi)) - rsnd_ssi_hw_stop(ssi->parent); + struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + + if (ssi_parent) + rsnd_ssi_hw_stop(io, ssi_parent); else rsnd_ssi_master_clk_stop(ssi); } - rsnd_mod_hw_stop(&ssi->mod); + rsnd_mod_power_off(mod); + + ssi->chan = 0; } dev_dbg(dev, "%s[%d] hw stopped\n", - rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); + rsnd_mod_name(mod), rsnd_mod_id(mod)); } /* * SSI mod common functions */ static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 cr; @@ -321,6 +329,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, } static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -336,24 +345,57 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return 0; } +static int rsnd_ssi_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + int chan = params_channels(params); + + /* + * Already working. + * It will happen if SSI has parent/child connection. + */ + if (ssi->usrcnt) { + /* + * it is error if child <-> parent SSI uses + * different channels. + */ + if (ssi->chan != chan) + return -EIO; + } + + /* It will be removed on rsnd_ssi_hw_stop */ + ssi->chan = chan; + if (ssi_parent) + return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io, + substream, params); + + return 0; +} + static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) { + struct rsnd_mod *mod = rsnd_mod_get(ssi); + /* under/over flow error */ if (status & (UIRQ | OIRQ)) { ssi->err++; /* clear error status */ - rsnd_mod_write(&ssi->mod, SSISR, 0); + rsnd_mod_write(mod, SSISR, 0); } } 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); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - rsnd_src_ssiu_start(mod, rsnd_ssi_use_busif(mod)); + rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io)); rsnd_ssi_hw_start(ssi, io); @@ -363,6 +405,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, } 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); @@ -371,24 +414,29 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - rsnd_ssi_hw_stop(ssi); + rsnd_ssi_hw_stop(io, ssi); - rsnd_src_ssiu_stop(mod); + rsnd_src_ssiu_stop(mod, io); return 0; } -static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) +static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = data; - struct rsnd_mod *mod = &ssi->mod; + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_dma = rsnd_ssi_is_dma_mode(mod); - u32 status = rsnd_mod_read(mod, SSISR); + u32 status; + bool elapsed = false; + + spin_lock(&priv->lock); + + /* ignore all cases if not working */ + if (!rsnd_io_is_working(io)) + goto rsnd_ssi_interrupt_out; - if (!io) - return IRQ_NONE; + status = rsnd_mod_read(mod, SSISR); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -406,11 +454,11 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) else *buf = rsnd_mod_read(mod, SSIRDR); - rsnd_dai_pointer_update(io, sizeof(*buf)); + elapsed = rsnd_dai_pointer_update(io, sizeof(*buf)); } - /* PIO / DMA */ - if (status & (UIRQ | OIRQ)) { + /* DMA only */ + if (is_dma && (status & (UIRQ | OIRQ))) { struct device *dev = rsnd_priv_to_dev(priv); /* @@ -419,15 +467,28 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - rsnd_ssi_stop(mod, priv); + rsnd_ssi_stop(mod, io, priv); if (ssi->err < 1024) - rsnd_ssi_start(mod, priv); + rsnd_ssi_start(mod, io, priv); else dev_warn(dev, "no more SSI restart\n"); } rsnd_ssi_record_error(ssi, status); +rsnd_ssi_interrupt_out: + spin_unlock(&priv->lock); + + if (elapsed) + rsnd_dai_period_elapsed(io); +} + +static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt); + return IRQ_HANDLED; } @@ -435,6 +496,7 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) * SSI PIO */ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -444,7 +506,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, - dev_name(dev), ssi); + dev_name(dev), mod); return ret; } @@ -456,9 +518,11 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .hw_params = rsnd_ssi_hw_params, }; static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -469,33 +533,35 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, - dev_name(dev), ssi); + dev_name(dev), mod); if (ret) return ret; ret = rsnd_dma_init( - priv, rsnd_mod_to_dma(mod), + io, rsnd_mod_to_dma(mod), dma_id); return ret; } static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); int irq = ssi->info->irq; - rsnd_dma_quit(rsnd_mod_to_dma(mod)); + rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ - devm_free_irq(dev, irq, ssi); + devm_free_irq(dev, irq, mod); return 0; } static int rsnd_ssi_fallback(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -516,37 +582,39 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, } static int rsnd_ssi_dma_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - rsnd_dma_start(dma); + rsnd_dma_start(io, dma); - rsnd_ssi_start(mod, priv); + rsnd_ssi_start(mod, io, priv); return 0; } static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - rsnd_ssi_stop(mod, priv); + rsnd_ssi_stop(mod, io, priv); - rsnd_dma_stop(dma); + rsnd_dma_stop(io, dma); return 0; } -static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); char *name; - if (rsnd_ssi_use_busif(mod)) + if (rsnd_ssi_use_busif(io)) name = is_play ? "rxu" : "txu"; else name = is_play ? "rx" : "tx"; @@ -565,6 +633,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .start = rsnd_ssi_dma_start, .stop = rsnd_ssi_dma_stop, .fallback = rsnd_ssi_fallback, + .hw_params = rsnd_ssi_hw_params, }; int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) @@ -588,22 +657,24 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) id = 0; - return &((struct rsnd_ssi *)(priv->ssi) + id)->mod; + return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); } -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } -static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) +static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { - if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) + struct rsnd_mod *mod = rsnd_mod_get(ssi); + + if (!__rsnd_ssi_is_pin_sharing(mod)) return; - switch (rsnd_mod_id(&ssi->mod)) { + switch (rsnd_mod_id(mod)) { case 1: case 2: ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); @@ -629,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, struct device *dev = &pdev->dev; int nr, i; - if (!of_data) - return; - node = rsnd_ssi_of_node(priv); if (!node) return; @@ -702,10 +770,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, */ nr = info->ssi_info_nr; ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); - if (!ssi) { - dev_err(dev, "SSI allocate failed\n"); + if (!ssi) return -ENOMEM; - } priv->ssi = ssi; priv->ssi_nr = nr; @@ -728,11 +794,12 @@ int rsnd_ssi_probe(struct platform_device *pdev, else if (rsnd_ssi_pio_available(ssi)) ops = &rsnd_ssi_pio_ops; - ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i); + ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, + RSND_MOD_SSI, i); if (ret) return ret; - rsnd_ssi_parent_clk_setup(priv, ssi); + rsnd_ssi_parent_setup(priv, ssi); } return 0; @@ -745,6 +812,6 @@ void rsnd_ssi_remove(struct platform_device *pdev, int i; for_each_rsnd_ssi(ssi, priv, i) { - rsnd_mod_quit(&ssi->mod); + rsnd_mod_quit(rsnd_mod_get(ssi)); } } |