summaryrefslogtreecommitdiffstats
path: root/sound/soc/sh/rcar
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sh/rcar')
-rw-r--r--sound/soc/sh/rcar/adg.c218
-rw-r--r--sound/soc/sh/rcar/cmd.c18
-rw-r--r--sound/soc/sh/rcar/core.c160
-rw-r--r--sound/soc/sh/rcar/ctu.c281
-rw-r--r--sound/soc/sh/rcar/dma.c56
-rw-r--r--sound/soc/sh/rcar/dvc.c33
-rw-r--r--sound/soc/sh/rcar/gen.c56
-rw-r--r--sound/soc/sh/rcar/mix.c4
-rw-r--r--sound/soc/sh/rcar/rsnd.h115
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c27
-rw-r--r--sound/soc/sh/rcar/src.c171
-rw-r--r--sound/soc/sh/rcar/ssi.c334
-rw-r--r--sound/soc/sh/rcar/ssiu.c20
13 files changed, 977 insertions, 516 deletions
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 6d3ef366d536..606399de684d 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
return (0x6 + ws) << 8;
}
+static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ unsigned int target_rate,
+ unsigned int *target_val,
+ unsigned int *target_en)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int idx, sel, div, step;
+ unsigned int val, en;
+ unsigned int min, diff;
+ unsigned int sel_rate[] = {
+ clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
+ clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
+ clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
+ adg->rbga_rate_for_441khz, /* 0011: RBGA */
+ adg->rbgb_rate_for_48khz, /* 0100: RBGB */
+ };
+
+ min = ~0;
+ val = 0;
+ en = 0;
+ for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+ idx = 0;
+ step = 2;
+
+ if (!sel_rate[sel])
+ continue;
+
+ for (div = 2; div <= 98304; div += step) {
+ diff = abs(target_rate - sel_rate[sel] / div);
+ if (min > diff) {
+ val = (sel << 8) | idx;
+ min = diff;
+ en = 1 << (sel + 1); /* fixme */
+ }
+
+ /*
+ * step of 0_0000 / 0_0001 / 0_1101
+ * are out of order
+ */
+ if ((idx > 2) && (idx % 2))
+ step *= 2;
+ if (idx == 0x1c) {
+ div += step;
+ step *= 2;
+ }
+ idx++;
+ }
+ }
+
+ if (min == ~0) {
+ dev_err(dev, "no Input clock\n");
+ return;
+ }
+
+ *target_val = val;
+ if (target_en)
+ *target_en = en;
+}
+
+static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ unsigned int in_rate,
+ unsigned int out_rate,
+ u32 *in, u32 *out, u32 *en)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ unsigned int target_rate;
+ u32 *target_val;
+ u32 _in;
+ u32 _out;
+ u32 _en;
+
+ /* default = SSI WS */
+ _in =
+ _out = rsnd_adg_ssi_ws_timing_gen2(io);
+
+ target_rate = 0;
+ target_val = NULL;
+ _en = 0;
+ if (runtime->rate != in_rate) {
+ target_rate = out_rate;
+ target_val = &_out;
+ } else if (runtime->rate != out_rate) {
+ target_rate = in_rate;
+ target_val = &_in;
+ }
+
+ if (target_rate)
+ __rsnd_adg_get_timesel_ratio(priv, io,
+ target_rate,
+ target_val, &_en);
+
+ if (in)
+ *in = _in;
+ if (out)
+ *out = _out;
+ if (en)
+ *en = _en;
+}
+
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
struct rsnd_dai_stream *io)
{
@@ -100,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
int shift = (id % 2) ? 16 : 0;
u32 mask, val;
- val = rsnd_adg_ssi_ws_timing_gen2(io);
+ rsnd_adg_get_timesel_ratio(priv, io,
+ rsnd_src_get_in_rate(priv, io),
+ rsnd_src_get_out_rate(priv, io),
+ NULL, &val, NULL);
val = val << shift;
mask = 0xffff << shift;
@@ -110,25 +215,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
return 0;
}
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io,
- u32 timsel)
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
+ struct rsnd_dai_stream *io,
+ unsigned int in_rate,
+ unsigned int out_rate)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- int is_play = rsnd_io_is_play(io);
+ u32 in, out;
+ u32 mask, en;
int id = rsnd_mod_id(src_mod);
int shift = (id % 2) ? 16 : 0;
- u32 mask, ws;
- u32 in, out;
rsnd_mod_confirm_src(src_mod);
- ws = rsnd_adg_ssi_ws_timing_gen2(io);
-
- in = (is_play) ? timsel : ws;
- out = (is_play) ? ws : timsel;
+ rsnd_adg_get_timesel_ratio(priv, io,
+ in_rate, out_rate,
+ &in, &out, &en);
in = in << shift;
out = out << shift;
@@ -157,91 +261,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
break;
}
- return 0;
-}
-
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io,
- unsigned int src_rate,
- unsigned int dst_rate)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- struct device *dev = rsnd_priv_to_dev(priv);
- int idx, sel, div, step, ret;
- u32 val, en;
- unsigned int min, diff;
- unsigned int sel_rate [] = {
- clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
- clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
- clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
- adg->rbga_rate_for_441khz, /* 0011: RBGA */
- adg->rbgb_rate_for_48khz, /* 0100: RBGB */
- };
-
- rsnd_mod_confirm_src(src_mod);
-
- min = ~0;
- val = 0;
- en = 0;
- for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
- idx = 0;
- step = 2;
-
- if (!sel_rate[sel])
- continue;
-
- for (div = 2; div <= 98304; div += step) {
- diff = abs(src_rate - sel_rate[sel] / div);
- if (min > diff) {
- val = (sel << 8) | idx;
- min = diff;
- en = 1 << (sel + 1); /* fixme */
- }
-
- /*
- * step of 0_0000 / 0_0001 / 0_1101
- * are out of order
- */
- if ((idx > 2) && (idx % 2))
- step *= 2;
- if (idx == 0x1c) {
- div += step;
- step *= 2;
- }
- idx++;
- }
- }
-
- if (min == ~0) {
- dev_err(dev, "no Input clock\n");
- return -EIO;
- }
-
- ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
- if (ret < 0) {
- dev_err(dev, "timsel error\n");
- return ret;
- }
-
- rsnd_mod_bset(adg_mod, DIV_EN, en, en);
-
- dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
+ if (en)
+ rsnd_mod_bset(adg_mod, DIV_EN, en, en);
return 0;
}
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io)
-{
- u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
-
- rsnd_mod_confirm_src(src_mod);
-
- return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
-}
-
static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
@@ -518,13 +543,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
return -ENOMEM;
}
- /*
- * ADG is special module.
- * Use ADG mod without rsnd_mod_init() to make debug easy
- * for rsnd_write/rsnd_read
- */
- adg->mod.ops = &adg_ops;
- adg->mod.priv = priv;
+ rsnd_mod_init(priv, &adg->mod, &adg_ops,
+ NULL, NULL, 0, 0);
rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg);
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c
index cd1f064e63c4..abb5eaac854a 100644
--- a/sound/soc/sh/rcar/cmd.c
+++ b/sound/soc/sh/rcar/cmd.c
@@ -29,7 +29,6 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
{
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
- struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
@@ -38,6 +37,8 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
if (mix) {
struct rsnd_dai *rdai;
+ struct rsnd_mod *src;
+ struct rsnd_dai_stream *tio;
int i;
u32 path[] = {
[0] = 0,
@@ -55,16 +56,20 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
*/
data = 0;
for_each_rsnd_dai(rdai, priv, i) {
- io = &rdai->playback;
- if (mix == rsnd_io_to_mod_mix(io))
+ tio = &rdai->playback;
+ src = rsnd_io_to_mod_src(tio);
+ if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
- io = &rdai->capture;
- if (mix == rsnd_io_to_mod_mix(io))
+ tio = &rdai->capture;
+ src = rsnd_io_to_mod_src(tio);
+ if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
}
} else {
+ struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
u32 path[] = {
[0] = 0x30000,
[1] = 0x30001,
@@ -152,7 +157,8 @@ int rsnd_cmd_probe(struct rsnd_priv *priv)
for_each_rsnd_cmd(cmd, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
- &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
+ &rsnd_cmd_ops, NULL,
+ rsnd_mod_get_status, RSND_MOD_CMD, i);
if (ret)
return ret;
}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 02b4b085b8d7..3351a701c60e 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
return mod->ops->dma_req(io, mod);
}
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type)
+{
+ return &mod->status;
+}
+
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
- struct rsnd_mod_ops *ops,
- struct clk *clk,
- enum rsnd_mod_type type,
- int id)
+ struct rsnd_mod_ops *ops,
+ struct clk *clk,
+ u32* (*get_status)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type),
+ enum rsnd_mod_type type,
+ int id)
{
int ret = clk_prepare(clk);
@@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv,
mod->type = type;
mod->clk = clk;
mod->priv = priv;
+ mod->get_status = get_status;
return ret;
}
@@ -163,6 +174,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
{
if (mod->clk)
clk_unprepare(mod->clk);
+ mod->clk = NULL;
}
void rsnd_mod_interrupt(struct rsnd_mod *mod,
@@ -212,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io)
return rdai->slots_num;
}
-int rsnd_get_slot_width(struct rsnd_dai_stream *io)
+int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- int chan = runtime->channels;
- /* Multi channel Mode */
- if (rsnd_ssi_multi_slaves(io))
+ return runtime->channels;
+}
+
+int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io)
+{
+ int chan = rsnd_runtime_channel_original(io);
+ struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io);
+
+ if (ctu_mod) {
+ u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod);
+
+ if (converted_chan)
+ return converted_chan;
+ }
+
+ return chan;
+}
+
+int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
+{
+ int chan = rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io);
+
+ /* Use Multi SSI */
+ if (rsnd_runtime_is_ssi_multi(io))
chan /= rsnd_get_slot_num(io);
/* TDM Extend Mode needs 8ch */
@@ -228,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io)
return chan;
}
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
+{
+ int slots = rsnd_get_slot_num(io);
+ int chan = rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io);
+
+ return (chan >= 6) && (slots > 1);
+}
+
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
+{
+ return rsnd_runtime_channel_for_ssi(io) >= 6;
+}
+
/*
* ADINR function
*/
@@ -249,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
return 0;
}
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct device *dev = rsnd_priv_to_dev(priv);
- u32 chan = runtime->channels;
-
- switch (chan) {
- case 1:
- case 2:
- case 4:
- case 6:
- case 8:
- break;
- default:
- dev_warn(dev, "not supported channel\n");
- chan = 0;
- break;
- }
-
- return chan;
-}
-
/*
* DALIGN function
*/
@@ -324,31 +351,73 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct rsnd_mod *mod = (io)->mod[idx]; \
struct device *dev = rsnd_priv_to_dev(priv); \
- u32 *status = (io)->mod_status + idx; \
+ u32 *status = mod->get_status(io, mod, idx); \
u32 mask = 0xF << __rsnd_mod_shift_##func; \
u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \
int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
- *status = (*status & ~mask) + \
- (add << __rsnd_mod_shift_##func); \
+ if (add == 0xF) \
+ call = 0; \
+ else \
+ *status = (*status & ~mask) + \
+ (add << __rsnd_mod_shift_##func); \
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), \
*status, call ? #func : ""); \
if (call) \
ret = (mod)->ops->func(mod, io, param); \
+ if (ret) \
+ dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \
ret; \
})
+static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
+ {
+ /* CAPTURE */
+ RSND_MOD_AUDMAPP,
+ RSND_MOD_AUDMA,
+ RSND_MOD_DVC,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ RSND_MOD_SSIU,
+ RSND_MOD_SSIM3,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIP,
+ RSND_MOD_SSI,
+ }, {
+ /* PLAYBACK */
+ RSND_MOD_AUDMAPP,
+ RSND_MOD_AUDMA,
+ RSND_MOD_SSIM3,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIP,
+ RSND_MOD_SSI,
+ RSND_MOD_SSIU,
+ RSND_MOD_DVC,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ },
+};
+
#define rsnd_dai_call(fn, io, param...) \
({ \
struct rsnd_mod *mod; \
+ int type, is_play = rsnd_io_is_play(io); \
int ret = 0, i; \
for (i = 0; i < RSND_MOD_MAX; i++) { \
- mod = (io)->mod[i]; \
+ type = rsnd_mod_sequence[is_play][i]; \
+ mod = (io)->mod[type]; \
if (!mod) \
continue; \
- ret |= rsnd_mod_call(i, io, fn, param); \
+ ret |= rsnd_mod_call(type, io, fn, param); \
} \
ret; \
})
@@ -363,6 +432,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod,
if (!mod)
return -EIO;
+ if (io->mod[type] == mod)
+ return 0;
+
if (io->mod[type])
return -EINVAL;
@@ -511,9 +583,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = rsnd_dai_call(start, io, priv);
if (ret < 0)
goto dai_trigger_end;
+
+ ret = rsnd_dai_call(irq, io, priv, 1);
+ if (ret < 0)
+ goto dai_trigger_end;
+
break;
case SNDRV_PCM_TRIGGER_STOP:
- ret = rsnd_dai_call(stop, io, priv);
+ ret = rsnd_dai_call(irq, io, priv, 0);
+
+ ret |= rsnd_dai_call(stop, io, priv);
ret |= rsnd_dai_call(quit, io, priv);
@@ -863,7 +942,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
}
}
- if (change)
+ if (change && cfg->update)
cfg->update(cfg->io, mod);
return change;
@@ -923,7 +1002,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
int ch_size,
u32 max)
{
- if (ch_size > RSND_DVC_CHANNELS)
+ if (ch_size > RSND_MAX_CHANNELS)
return -EINVAL;
_cfg->cfg.max = max;
@@ -1055,7 +1134,6 @@ static int rsnd_probe(struct platform_device *pdev)
struct rsnd_priv *priv;
struct device *dev = &pdev->dev;
struct rsnd_dai *rdai;
- const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
int (*probe_func[])(struct rsnd_priv *priv) = {
rsnd_gen_probe,
rsnd_dma_probe,
@@ -1081,7 +1159,7 @@ static int rsnd_probe(struct platform_device *pdev)
}
priv->pdev = pdev;
- priv->flags = (unsigned long)of_id->data;
+ priv->flags = (unsigned long)of_device_get_match_data(dev);
spin_lock_init(&priv->lock);
/*
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c
index d53a225d19e9..9dcc1f9db026 100644
--- a/sound/soc/sh/rcar/ctu.c
+++ b/sound/soc/sh/rcar/ctu.c
@@ -12,8 +12,75 @@
#define CTU_NAME_SIZE 16
#define CTU_NAME "ctu"
+/*
+ * User needs to setup CTU by amixer, and its settings are
+ * based on below registers
+ *
+ * CTUn_CPMDR : amixser set "CTU Pass"
+ * CTUn_SV0xR : amixser set "CTU SV0"
+ * CTUn_SV1xR : amixser set "CTU SV1"
+ * CTUn_SV2xR : amixser set "CTU SV2"
+ * CTUn_SV3xR : amixser set "CTU SV3"
+ *
+ * [CTU Pass]
+ * 0000: default
+ * 0001: Connect input data of channel 0
+ * 0010: Connect input data of channel 1
+ * 0011: Connect input data of channel 2
+ * 0100: Connect input data of channel 3
+ * 0101: Connect input data of channel 4
+ * 0110: Connect input data of channel 5
+ * 0111: Connect input data of channel 6
+ * 1000: Connect input data of channel 7
+ * 1001: Connect calculated data by scale values of matrix row 0
+ * 1010: Connect calculated data by scale values of matrix row 1
+ * 1011: Connect calculated data by scale values of matrix row 2
+ * 1100: Connect calculated data by scale values of matrix row 3
+ *
+ * [CTU SVx]
+ * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07]
+ * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17]
+ * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27]
+ * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37]
+ * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ *
+ * [SVxx]
+ * Plus Minus
+ * value time dB value time dB
+ * -----------------------------------------------------------------------
+ * H'7F_FFFF 2 6 H'80_0000 2 6
+ * ...
+ * H'40_0000 1 0 H'C0_0000 1 0
+ * ...
+ * H'00_0001 2.38 x 10^-7 -132
+ * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132
+ *
+ *
+ * Ex) Input ch -> Output ch
+ * 1ch -> 0ch
+ * 0ch -> 1ch
+ *
+ * amixer set "CTU Reset" on
+ * amixer set "CTU Pass" 9,10
+ * amixer set "CTU SV0" 0,4194304
+ * amixer set "CTU SV1" 4194304,0
+ * or
+ * amixer set "CTU Reset" on
+ * amixer set "CTU Pass" 2,1
+ */
+
struct rsnd_ctu {
struct rsnd_mod mod;
+ struct rsnd_kctrl_cfg_m pass;
+ struct rsnd_kctrl_cfg_m sv0;
+ struct rsnd_kctrl_cfg_m sv1;
+ struct rsnd_kctrl_cfg_m sv2;
+ struct rsnd_kctrl_cfg_m sv3;
+ struct rsnd_kctrl_cfg_s reset;
+ int channels;
};
#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
@@ -23,12 +90,28 @@ struct rsnd_ctu {
((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
i++)
+#define rsnd_mod_to_ctu(_mod) \
+ container_of((_mod), struct rsnd_ctu, mod)
+
#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
-#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)
+
+static void rsnd_ctu_activation(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, CTU_SWRSR, 0);
+ rsnd_mod_write(mod, CTU_SWRSR, 1);
+}
+
+static void rsnd_ctu_halt(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, CTU_CTUIR, 1);
+ rsnd_mod_write(mod, CTU_SWRSR, 0);
+}
+
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod)
{
- rsnd_mod_write(mod, CTU_CTUIR, enable);
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+
+ return ctu->channels;
}
static int rsnd_ctu_probe_(struct rsnd_mod *mod,
@@ -38,17 +121,103 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod,
return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
}
+static void rsnd_ctu_value_init(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ u32 cpmdr = 0;
+ u32 scmdr = 0;
+ int i;
+
+ for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+ u32 val = ctu->pass.val[i];
+
+ cpmdr |= val << (28 - (i * 4));
+
+ if ((val > 0x8) && (scmdr < (val - 0x8)))
+ scmdr = val - 0x8;
+ }
+
+ rsnd_mod_write(mod, CTU_CTUIR, 1);
+
+ rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io));
+
+ rsnd_mod_write(mod, CTU_CPMDR, cpmdr);
+
+ rsnd_mod_write(mod, CTU_SCMDR, scmdr);
+
+ if (scmdr > 0) {
+ rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]);
+ rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]);
+ rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]);
+ rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]);
+ rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]);
+ rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]);
+ rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]);
+ rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]);
+ }
+ if (scmdr > 1) {
+ rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]);
+ rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]);
+ rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]);
+ rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]);
+ rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]);
+ rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]);
+ rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]);
+ rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]);
+ }
+ if (scmdr > 2) {
+ rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]);
+ rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]);
+ rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]);
+ rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]);
+ rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]);
+ rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]);
+ rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]);
+ rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]);
+ }
+ if (scmdr > 3) {
+ rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]);
+ rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]);
+ rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]);
+ rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]);
+ rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]);
+ rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]);
+ rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]);
+ rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]);
+ }
+
+ rsnd_mod_write(mod, CTU_CTUIR, 0);
+}
+
+static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ int i;
+
+ if (!ctu->reset.val)
+ return;
+
+ for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+ ctu->pass.val[i] = 0;
+ ctu->sv0.val[i] = 0;
+ ctu->sv1.val[i] = 0;
+ ctu->sv2.val[i] = 0;
+ ctu->sv3.val[i] = 0;
+ }
+ ctu->reset.val = 0;
+}
+
static int rsnd_ctu_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_power_on(mod);
- rsnd_ctu_initialize_lock(mod);
-
- rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io));
+ rsnd_ctu_activation(mod);
- rsnd_ctu_initialize_unlock(mod);
+ rsnd_ctu_value_init(io, mod);
return 0;
}
@@ -57,16 +226,110 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
+ rsnd_ctu_halt(mod);
+
rsnd_mod_power_off(mod);
return 0;
}
+static int rsnd_ctu_hw_params(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *fe_params)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+ /*
+ * CTU assumes that it is used under DPCM if user want to use
+ * channel transfer. Then, CTU should be FE.
+ * And then, this function will be called *after* BE settings.
+ * this means, each BE already has fixuped hw_params.
+ * see
+ * dpcm_fe_dai_hw_params()
+ * dpcm_be_dai_hw_params()
+ */
+ ctu->channels = 0;
+ if (fe->dai_link->dynamic) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct snd_soc_dpcm *dpcm;
+ struct snd_pcm_hw_params *be_params;
+ int stream = substream->stream;
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ be_params = &dpcm->hw_params;
+ if (params_channels(fe_params) != params_channels(be_params))
+ ctu->channels = params_channels(be_params);
+ }
+
+ dev_dbg(dev, "CTU convert channels %d\n", ctu->channels);
+ }
+
+ return 0;
+}
+
+static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ int ret;
+
+ /* CTU Pass */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
+ NULL,
+ &ctu->pass, RSND_MAX_CHANNELS,
+ 0xC);
+
+ /* ROW0 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
+ NULL,
+ &ctu->sv0, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW1 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
+ NULL,
+ &ctu->sv1, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW2 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
+ NULL,
+ &ctu->sv2, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW3 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
+ NULL,
+ &ctu->sv3, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* Reset */
+ ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
+ rsnd_ctu_value_reset,
+ &ctu->reset, 1);
+
+ return ret;
+}
+
static struct rsnd_mod_ops rsnd_ctu_ops = {
.name = CTU_NAME,
.probe = rsnd_ctu_probe_,
.init = rsnd_ctu_init,
.quit = rsnd_ctu_quit,
+ .hw_params = rsnd_ctu_hw_params,
+ .pcm_new = rsnd_ctu_pcm_new,
};
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
@@ -129,7 +392,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
- clk, RSND_MOD_CTU, i);
+ clk, rsnd_mod_get_status, RSND_MOD_CTU, i);
if (ret)
goto rsnd_ctu_probe_done;
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 418e6fdd06a3..7658e8fd7bdc 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -622,15 +622,13 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
}
}
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod, int id)
+int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+ struct rsnd_mod **dma_mod, int id)
{
- struct rsnd_mod *dma_mod;
struct rsnd_mod *mod_from = NULL;
struct rsnd_mod *mod_to = NULL;
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
- struct rsnd_dma *dma;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod_ops *ops;
enum rsnd_mod_type type;
@@ -646,17 +644,10 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
* rsnd_rdai_continuance_probe()
*/
if (!dmac)
- return ERR_PTR(-EAGAIN);
-
- dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
- if (!dma)
- return ERR_PTR(-ENOMEM);
+ return -EAGAIN;
rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
- dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
- dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
-
/* for Gen2 */
if (mod_from && mod_to) {
ops = &rsnd_dmapp_ops;
@@ -678,27 +669,38 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
type = RSND_MOD_AUDMA;
}
- dma_mod = rsnd_mod_get(dma);
+ if (!(*dma_mod)) {
+ struct rsnd_dma *dma;
- ret = rsnd_mod_init(priv, dma_mod,
- ops, NULL, type, dma_id);
- if (ret < 0)
- return ERR_PTR(ret);
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
- dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
- rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
- rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
- rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
+ *dma_mod = rsnd_mod_get(dma);
- ret = attach(io, dma, id, mod_from, mod_to);
- if (ret < 0)
- return ERR_PTR(ret);
+ dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+ dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
+
+ ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
+ rsnd_mod_get_status, type, dma_id);
+ if (ret < 0)
+ return ret;
- ret = rsnd_dai_connect(dma_mod, io, type);
+ dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
+ rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod),
+ rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
+ rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
+
+ ret = attach(io, dma, id, mod_from, mod_to);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = rsnd_dai_connect(*dma_mod, io, type);
if (ret < 0)
- return ERR_PTR(ret);
+ return ret;
- return rsnd_mod_get(dma);
+ return 0;
}
int rsnd_dma_probe(struct rsnd_priv *priv)
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index d45ffe496397..02d971f69eff 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -8,6 +8,29 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+/*
+ * Playback Volume
+ * amixer set "DVC Out" 100%
+ *
+ * Capture Volume
+ * amixer set "DVC In" 100%
+ *
+ * Playback Mute
+ * amixer set "DVC Out Mute" on
+ *
+ * Capture Mute
+ * amixer set "DVC In Mute" on
+ *
+ * Volume Ramp
+ * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps"
+ * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
+ * amixer set "DVC Out Ramp" on
+ * aplay xxx.wav &
+ * amixer set "DVC Out" 80% // Volume Down
+ * amixer set "DVC Out" 100% // Volume Up
+ */
+
#include "rsnd.h"
#define RSND_DVC_NAME_SIZE 16
@@ -83,15 +106,15 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
- u32 val[RSND_DVC_CHANNELS];
+ u32 val[RSND_MAX_CHANNELS];
int i;
/* Enable Ramp */
if (dvc->ren.val)
- for (i = 0; i < RSND_DVC_CHANNELS; i++)
+ for (i = 0; i < RSND_MAX_CHANNELS; i++)
val[i] = dvc->volume.cfg.max;
else
- for (i = 0; i < RSND_DVC_CHANNELS; i++)
+ for (i = 0; i < RSND_MAX_CHANNELS; i++)
val[i] = dvc->volume.val[i];
/* Enable Digital Volume */
@@ -116,7 +139,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
u32 vrdbr = 0;
adinr = rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io);
+ rsnd_runtime_channel_after_ctu(io);
/* Enable Digital Volume, Zero Cross Mute Mode */
dvucr |= 0x101;
@@ -373,7 +396,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
- clk, RSND_MOD_DVC, i);
+ clk, rsnd_mod_get_status, RSND_MOD_DVC, i);
if (ret)
goto rsnd_dvc_probe_done;
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index ea24247eba73..46c0ba7b6414 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -104,23 +104,6 @@ void rsnd_write(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
- regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
-
- dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- rsnd_reg_name(gen, reg), reg, data);
-}
-
-void rsnd_force_write(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- enum rsnd_reg reg, u32 data)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- if (!rsnd_is_accessible_reg(priv, gen, reg))
- return;
-
regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
@@ -137,8 +120,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
- regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
- mask, data);
+ regmap_fields_force_update_bits(gen->regs[reg],
+ rsnd_mod_id(mod), mask, data);
dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
@@ -260,8 +243,43 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
+ RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100),
RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100),
RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100),
+ RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100),
+ RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100),
+ RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100),
+ RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100),
+ RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100),
+ RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100),
+ RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100),
+ RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100),
+ RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100),
+ RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100),
+ RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100),
+ RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100),
+ RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100),
+ RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100),
+ RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100),
+ RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100),
+ RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100),
+ RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100),
+ RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100),
+ RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100),
+ RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100),
+ RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100),
+ RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100),
+ RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100),
+ RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100),
+ RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100),
+ RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100),
+ RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100),
+ RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100),
+ RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100),
+ RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100),
+ RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100),
+ RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100),
+ RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100),
RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40),
RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40),
RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40),
diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c
index 65542b6a89e9..195fc7bb22af 100644
--- a/sound/soc/sh/rcar/mix.c
+++ b/sound/soc/sh/rcar/mix.c
@@ -51,7 +51,7 @@ static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, MIX_MIXIR, 1);
/* General Information */
- rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
+ rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io));
/* volume step */
rsnd_mod_write(mod, MIX_MIXMR, 0);
@@ -172,7 +172,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
- clk, RSND_MOD_MIX, i);
+ clk, rsnd_mod_get_status, RSND_MOD_MIX, i);
if (ret)
goto rsnd_mix_probe_done;
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 317dd793149a..fc89a67258ca 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -86,8 +86,43 @@ enum rsnd_reg {
RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
RSND_REG_CMD_ROUTE_SLCT,
RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
+ RSND_REG_CTU_SWRSR,
RSND_REG_CTU_CTUIR,
RSND_REG_CTU_ADINR,
+ RSND_REG_CTU_CPMDR,
+ RSND_REG_CTU_SCMDR,
+ RSND_REG_CTU_SV00R,
+ RSND_REG_CTU_SV01R,
+ RSND_REG_CTU_SV02R,
+ RSND_REG_CTU_SV03R,
+ RSND_REG_CTU_SV04R,
+ RSND_REG_CTU_SV05R,
+ RSND_REG_CTU_SV06R,
+ RSND_REG_CTU_SV07R,
+ RSND_REG_CTU_SV10R,
+ RSND_REG_CTU_SV11R,
+ RSND_REG_CTU_SV12R,
+ RSND_REG_CTU_SV13R,
+ RSND_REG_CTU_SV14R,
+ RSND_REG_CTU_SV15R,
+ RSND_REG_CTU_SV16R,
+ RSND_REG_CTU_SV17R,
+ RSND_REG_CTU_SV20R,
+ RSND_REG_CTU_SV21R,
+ RSND_REG_CTU_SV22R,
+ RSND_REG_CTU_SV23R,
+ RSND_REG_CTU_SV24R,
+ RSND_REG_CTU_SV25R,
+ RSND_REG_CTU_SV26R,
+ RSND_REG_CTU_SV27R,
+ RSND_REG_CTU_SV30R,
+ RSND_REG_CTU_SV31R,
+ RSND_REG_CTU_SV32R,
+ RSND_REG_CTU_SV33R,
+ RSND_REG_CTU_SV34R,
+ RSND_REG_CTU_SV35R,
+ RSND_REG_CTU_SV36R,
+ RSND_REG_CTU_SV37R,
RSND_REG_MIX_SWRSR,
RSND_REG_MIX_MIXIR,
RSND_REG_MIX_ADINR,
@@ -147,8 +182,6 @@ struct rsnd_dai_stream;
rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
#define rsnd_mod_write(m, r, d) \
rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
-#define rsnd_mod_force_write(m, r, d) \
- rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
#define rsnd_mod_bset(m, r, s, d) \
rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
@@ -160,14 +193,13 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
u32 mask, u32 data);
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
/*
* R-Car DMA
*/
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod, int id);
+int rsnd_dma_attach(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id);
int rsnd_dma_probe(struct rsnd_priv *priv);
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name);
@@ -214,6 +246,9 @@ struct rsnd_mod_ops {
int (*stop)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
+ int (*irq)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv, int enable);
int (*pcm_new)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd);
@@ -233,47 +268,54 @@ struct rsnd_mod {
struct rsnd_mod_ops *ops;
struct rsnd_priv *priv;
struct clk *clk;
+ u32 *(*get_status)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type);
+ u32 status;
};
/*
* status
*
- * 0xH0000CBA
+ * 0xH0000CB0
*
- * A 0: probe 1: remove
* B 0: init 1: quit
* C 0: start 1: stop
*
* H is always called (see __rsnd_mod_call)
+ * H 0: probe 1: remove
* H 0: pcm_new
* H 0: fallback
* H 0: hw_params
*/
-#define __rsnd_mod_shift_probe 0
-#define __rsnd_mod_shift_remove 0
#define __rsnd_mod_shift_init 4
#define __rsnd_mod_shift_quit 4
#define __rsnd_mod_shift_start 8
#define __rsnd_mod_shift_stop 8
+#define __rsnd_mod_shift_probe 28 /* always called */
+#define __rsnd_mod_shift_remove 28 /* always called */
+#define __rsnd_mod_shift_irq 28 /* always called */
#define __rsnd_mod_shift_pcm_new 28 /* always called */
#define __rsnd_mod_shift_fallback 28 /* always called */
#define __rsnd_mod_shift_hw_params 28 /* always called */
-#define __rsnd_mod_add_probe 1
-#define __rsnd_mod_add_remove -1
+#define __rsnd_mod_add_probe 0
+#define __rsnd_mod_add_remove 0
#define __rsnd_mod_add_init 1
#define __rsnd_mod_add_quit -1
#define __rsnd_mod_add_start 1
#define __rsnd_mod_add_stop -1
+#define __rsnd_mod_add_irq 0
#define __rsnd_mod_add_pcm_new 0
#define __rsnd_mod_add_fallback 0
#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_call_probe 0
-#define __rsnd_mod_call_remove 1
+#define __rsnd_mod_call_remove 0
#define __rsnd_mod_call_init 0
#define __rsnd_mod_call_quit 1
#define __rsnd_mod_call_start 0
#define __rsnd_mod_call_stop 1
+#define __rsnd_mod_call_irq 0
#define __rsnd_mod_call_pcm_new 0
#define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 0
@@ -286,10 +328,13 @@ struct rsnd_mod {
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
- struct rsnd_mod_ops *ops,
- struct clk *clk,
- enum rsnd_mod_type type,
- int id);
+ struct rsnd_mod_ops *ops,
+ struct clk *clk,
+ u32* (*get_status)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type),
+ enum rsnd_mod_type type,
+ int id);
void rsnd_mod_quit(struct rsnd_mod *mod);
char *rsnd_mod_name(struct rsnd_mod *mod);
struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
@@ -297,6 +342,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
void rsnd_mod_interrupt(struct rsnd_mod *mod,
void (*callback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io));
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type);
+
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
struct device_node *node,
@@ -306,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
void rsnd_set_slot(struct rsnd_dai *rdai,
int slots, int slots_total);
int rsnd_get_slot(struct rsnd_dai_stream *io);
-int rsnd_get_slot_width(struct rsnd_dai_stream *io);
int rsnd_get_slot_num(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_original(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
+
/*
* R-Car sound DAI
*/
@@ -319,7 +373,7 @@ struct rsnd_dai_stream {
struct rsnd_mod *mod[RSND_MOD_MAX];
struct rsnd_dai_path_info *info; /* rcar_snd.h */
struct rsnd_dai *rdai;
- u32 mod_status[RSND_MOD_MAX];
+ u32 parent_ssi_status;
int byte_pos;
int period_pos;
int byte_per_period;
@@ -392,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
int rsnd_adg_probe(struct rsnd_priv *priv);
void rsnd_adg_remove(struct rsnd_priv *priv);
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
- unsigned int src_rate,
- unsigned int dst_rate);
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io);
+ unsigned int in_rate,
+ unsigned int out_rate);
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io);
@@ -498,10 +550,10 @@ struct rsnd_kctrl_cfg {
struct snd_kcontrol *kctrl;
};
-#define RSND_DVC_CHANNELS 8
+#define RSND_MAX_CHANNELS 8
struct rsnd_kctrl_cfg_m {
struct rsnd_kctrl_cfg cfg;
- u32 val[RSND_DVC_CHANNELS];
+ u32 val[RSND_MAX_CHANNELS];
};
struct rsnd_kctrl_cfg_s {
@@ -547,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
#define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
@@ -573,9 +625,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv);
int rsnd_src_probe(struct rsnd_priv *priv);
void rsnd_src_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
- struct rsnd_dai_stream *io,
- struct snd_pcm_runtime *runtime);
+
+#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1)
+#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ int is_in);
+
#define rsnd_src_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
#define rsnd_parse_connect_src(rdai, playback, capture) \
@@ -588,6 +644,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
*/
int rsnd_ctu_probe(struct rsnd_priv *priv);
void rsnd_ctu_remove(struct rsnd_priv *priv);
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod);
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_ctu_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
index 8a357fdf1077..1bc7ecfc42a9 100644
--- a/sound/soc/sh/rcar/rsrc-card.c
+++ b/sound/soc/sh/rcar/rsrc-card.c
@@ -66,12 +66,12 @@ struct rsrc_card_priv {
struct snd_soc_dai_link *dai_link;
int dai_num;
u32 convert_rate;
+ u32 convert_channels;
};
#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
#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)
{
@@ -145,11 +145,16 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
- if (!priv->convert_rate)
- return 0;
+ if (priv->convert_rate)
+ rate->min =
+ rate->max = priv->convert_rate;
- rate->min = rate->max = priv->convert_rate;
+ if (priv->convert_channels)
+ channels->min =
+ channels->max = priv->convert_channels;
return 0;
}
@@ -246,7 +251,7 @@ static int rsrc_card_parse_links(struct device_node *np,
struct device *dev = rsrc_priv_to_dev(priv);
const struct rsrc_card_of_data *of_data;
- of_data = rsrc_dev_to_of_data(dev);
+ of_data = of_device_get_match_data(dev);
/* FE is dummy */
dai_link->cpu_of_node = NULL;
@@ -396,7 +401,7 @@ static int rsrc_card_parse_of(struct device_node *node,
struct rsrc_card_priv *priv,
struct device *dev)
{
- const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+ const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev);
struct rsrc_card_dai *props;
struct snd_soc_dai_link *links;
int ret;
@@ -437,9 +442,13 @@ static int rsrc_card_parse_of(struct device_node *node,
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
- dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n",
- priv->snd_card.name ? priv->snd_card.name : "",
- priv->convert_rate);
+ /* channels transfer */
+ of_property_read_u32(node, "convert-channels", &priv->convert_channels);
+
+ dev_dbg(dev, "New rsrc-audio-card: %s\n",
+ priv->snd_card.name ? priv->snd_card.name : "");
+ dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate);
+ dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels);
ret = rsrc_card_dai_link_of(node, priv);
if (ret < 0)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 5eda056d9f20..15d6ffe8be74 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -25,7 +25,6 @@ struct rsnd_src {
struct rsnd_kctrl_cfg_s sen; /* sync convert enable */
struct rsnd_kctrl_cfg_s sync; /* sync convert */
u32 convert_rate; /* sampling rate convert */
- int err;
int irq;
};
@@ -34,7 +33,7 @@ struct rsnd_src {
#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
#define rsnd_src_to_dma(src) ((src)->dma)
#define rsnd_src_nr(priv) ((priv)->src_nr)
-#define rsnd_enable_sync_convert(src) ((src)->sen.val)
+#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val)
#define rsnd_mod_to_src(_mod) \
container_of((_mod), struct rsnd_src, mod)
@@ -94,15 +93,16 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
}
static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
- struct rsnd_src *src)
+ struct rsnd_mod *mod)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate;
if (!runtime)
return 0;
- if (!rsnd_enable_sync_convert(src))
+ if (!rsnd_src_sync_is_enabled(mod))
return src->convert_rate;
convert_rate = src->sync.val;
@@ -116,23 +116,33 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
return convert_rate;
}
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
- struct rsnd_dai_stream *io,
- struct snd_pcm_runtime *runtime)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ int is_in)
{
struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
- struct rsnd_src *src;
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
unsigned int rate = 0;
+ int is_play = rsnd_io_is_play(io);
- if (src_mod) {
- src = rsnd_mod_to_src(src_mod);
+ /*
+ *
+ * Playback
+ * runtime_rate -> [SRC] -> convert_rate
+ *
+ * Capture
+ * convert_rate -> [SRC] -> runtime_rate
+ */
- /*
- * return convert rate if SRC is used,
- * otherwise, return runtime->rate as usual
- */
- rate = rsnd_src_convert_rate(io, src);
- }
+ if (is_play == is_in)
+ return runtime->rate;
+
+ /*
+ * return convert rate if SRC is used,
+ * otherwise, return runtime->rate as usual
+ */
+ if (src_mod)
+ rate = rsnd_src_convert_rate(io, src_mod);
if (!rate)
rate = runtime->rate;
@@ -179,8 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 convert_rate = rsnd_src_convert_rate(io, src);
+ u32 fin, fout;
u32 ifscr, fsrate, adinr;
u32 cr, route;
u32 bsdsr, bsisr;
@@ -189,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
if (!runtime)
return;
+ fin = rsnd_src_get_in_rate(priv, io);
+ fout = rsnd_src_get_out_rate(priv, io);
+
/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
- if (!convert_rate)
+ if (fin == fout)
ratio = 0;
- else if (convert_rate > runtime->rate)
- ratio = 100 * convert_rate / runtime->rate;
+ else if (fin > fout)
+ ratio = 100 * fin / fout;
else
- ratio = 100 * runtime->rate / convert_rate;
+ ratio = 100 * fout / fin;
if (ratio > 600) {
dev_err(dev, "FSO/FSI ratio error\n");
@@ -206,16 +218,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
* SRC_ADINR
*/
adinr = rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io);
+ rsnd_runtime_channel_original(io);
/*
* SRC_IFSCR / SRC_IFSVR
*/
ifscr = 0;
fsrate = 0;
- if (convert_rate) {
+ if (fin != fout) {
ifscr = 1;
- fsrate = 0x0400000 / convert_rate * runtime->rate;
+ fsrate = 0x0400000 / fout * fin;
}
/*
@@ -223,10 +235,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
*/
cr = 0x00011110;
route = 0x0;
- if (convert_rate) {
+ if (fin != fout) {
route = 0x1;
- if (rsnd_enable_sync_convert(src)) {
+ if (rsnd_src_sync_is_enabled(mod)) {
cr |= 0x1;
route |= rsnd_io_is_play(io) ?
(0x1 << 24) : (0x1 << 25);
@@ -250,6 +262,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
break;
}
+ rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+
rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */
rsnd_mod_write(mod, SRC_ADINR, adinr);
rsnd_mod_write(mod, SRC_IFSCR, ifscr);
@@ -259,22 +273,17 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, SRC_BSISR, bsisr);
rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */
- rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
- if (convert_rate)
- rsnd_adg_set_convert_clk_gen2(mod, io,
- runtime->rate,
- convert_rate);
- else
- rsnd_adg_set_convert_timing_gen2(mod, io);
+ rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
}
-#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1)
-#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0)
-static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
+static int rsnd_src_irq(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv,
+ int enable)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 sys_int_val, int_val, sys_int_mask;
@@ -298,14 +307,16 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
/*
* WORKAROUND
*
- * ignore over flow error when rsnd_enable_sync_convert()
+ * ignore over flow error when rsnd_src_sync_is_enabled()
*/
- if (rsnd_enable_sync_convert(src))
+ if (rsnd_src_sync_is_enabled(mod))
sys_int_val = sys_int_val & 0xffff;
rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
+
+ return 0;
}
static void rsnd_src_status_clear(struct rsnd_mod *mod)
@@ -316,9 +327,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod)
rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
}
-static bool rsnd_src_record_error(struct rsnd_mod *mod)
+static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val0, val1;
bool ret = false;
@@ -327,18 +337,14 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod)
/*
* WORKAROUND
*
- * ignore over flow error when rsnd_enable_sync_convert()
+ * ignore over flow error when rsnd_src_sync_is_enabled()
*/
- if (rsnd_enable_sync_convert(src))
+ if (rsnd_src_sync_is_enabled(mod))
val0 = val0 & 0xffff;
if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) ||
- (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) {
- struct rsnd_src *src = rsnd_mod_to_src(mod);
-
- src->err++;
+ (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1))
ret = true;
- }
return ret;
}
@@ -347,7 +353,6 @@ static int rsnd_src_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val;
/*
@@ -355,7 +360,7 @@ static int rsnd_src_start(struct rsnd_mod *mod,
*
* Enable SRC output if you want to use sync convert together with DVC
*/
- val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ?
+ val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ?
0x01 : 0x11;
rsnd_mod_write(mod, SRC_CTRL, val);
@@ -367,11 +372,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- /*
- * stop SRC output only
- * see rsnd_src_quit
- */
- rsnd_mod_write(mod, SRC_CTRL, 0x01);
+ rsnd_mod_write(mod, SRC_CTRL, 0);
return 0;
}
@@ -390,10 +391,6 @@ static int rsnd_src_init(struct rsnd_mod *mod,
rsnd_src_status_clear(mod);
- rsnd_src_irq_enable(mod);
-
- src->err = 0;
-
/* reset sync convert_rate */
src->sync.val = 0;
@@ -405,21 +402,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- rsnd_src_irq_disable(mod);
-
- /* stop both out/in */
- rsnd_mod_write(mod, SRC_CTRL, 0);
rsnd_src_halt(mod);
rsnd_mod_power_off(mod);
- if (src->err)
- dev_warn(dev, "%s[%d] under/over flow err = %d\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
-
src->convert_rate = 0;
/* reset sync convert_rate */
@@ -432,8 +419,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
+ bool stop = false;
spin_lock(&priv->lock);
@@ -441,26 +427,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
if (!rsnd_io_is_working(io))
goto rsnd_src_interrupt_out;
- if (rsnd_src_record_error(mod)) {
-
- dev_dbg(dev, "%s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
-
- rsnd_src_stop(mod, io, priv);
- rsnd_src_start(mod, io, priv);
- }
-
- if (src->err > 1024) {
- rsnd_src_irq_disable(mod);
-
- dev_warn(dev, "no more %s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- }
+ if (rsnd_src_error_occurred(mod))
+ stop = true;
rsnd_src_status_clear(mod);
rsnd_src_interrupt_out:
spin_unlock(&priv->lock);
+
+ if (stop)
+ snd_pcm_stop_xrun(io->substream);
}
static irqreturn_t rsnd_src_interrupt(int irq, void *data)
@@ -485,7 +461,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
/*
* IRQ is not supported on non-DT
* see
- * rsnd_src_irq_enable()
+ * rsnd_src_irq()
*/
ret = devm_request_irq(dev, irq,
rsnd_src_interrupt,
@@ -495,9 +471,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
return ret;
}
- src->dma = rsnd_dma_attach(io, mod, 0);
- if (IS_ERR(src->dma))
- return PTR_ERR(src->dma);
+ ret = rsnd_dma_attach(io, mod, &src->dma, 0);
return ret;
}
@@ -506,8 +480,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -516,15 +488,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
*/
/*
- * SRC sync convert needs clock master
- */
- if (!rsnd_rdai_is_clk_master(rdai))
- return 0;
-
- /*
- * SRC In doesn't work if DVC was enabled
+ * It can't use SRC Synchronous convert
+ * when Capture if it uses CMD
*/
- if (dvc && !rsnd_io_is_play(io))
+ if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io))
return 0;
/*
@@ -557,6 +524,7 @@ static struct rsnd_mod_ops rsnd_src_ops = {
.quit = rsnd_src_quit,
.start = rsnd_src_start,
.stop = rsnd_src_stop,
+ .irq = rsnd_src_irq,
.hw_params = rsnd_src_hw_params,
.pcm_new = rsnd_src_pcm_new,
};
@@ -622,7 +590,8 @@ int rsnd_src_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(src),
- &rsnd_src_ops, clk, RSND_MOD_SRC, i);
+ &rsnd_src_ops, clk, rsnd_mod_get_status,
+ RSND_MOD_SRC, i);
if (ret)
goto rsnd_src_probe_done;
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 7ee89da4dd5f..5f848f054745 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -64,7 +64,6 @@
#define SSI_NAME "ssi"
struct rsnd_ssi {
- struct rsnd_ssi *parent;
struct rsnd_mod mod;
struct rsnd_mod *dma;
@@ -75,7 +74,6 @@ struct rsnd_ssi {
u32 wsr;
int chan;
int rate;
- int err;
int irq;
unsigned int usrcnt;
};
@@ -96,7 +94,10 @@ struct rsnd_ssi {
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_ssi_mode_flags(p) ((p)->flags)
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
-#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
+#define rsnd_ssi_is_multi_slave(mod, io) \
+ (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
+#define rsnd_ssi_is_run_mods(mod, io) \
+ (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
@@ -141,43 +142,13 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
udelay(50);
}
- dev_warn(dev, "status check failed\n");
-}
-
-static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
- if (rsnd_is_gen1(priv))
- return 0;
-
- /* enable SSI interrupt if Gen2 */
- rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
- rsnd_ssi_is_dma_mode(ssi_mod) ?
- 0x0e000000 : 0x0f000000);
-
- return 0;
-}
-
-static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
- if (rsnd_is_gen1(priv))
- return 0;
-
- /* disable SSI interrupt if Gen2 */
- rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
-
- return 0;
+ dev_warn(dev, "%s[%d] status check failed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
+static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod;
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct device *dev = rsnd_priv_to_dev(priv);
enum rsnd_mod_type types[] = {
RSND_MOD_SSIM1,
RSND_MOD_SSIM2,
@@ -185,16 +156,6 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
};
int i, mask;
- switch (runtime->channels) {
- case 2: /* Multi channel is not needed for Stereo */
- return 0;
- case 6:
- break;
- default:
- dev_err(dev, "unsupported channel\n");
- return 0;
- }
-
mask = 0;
for (i = 0; i < ARRAY_SIZE(types); i++) {
mod = rsnd_io_to_mod(io, types[i]);
@@ -207,22 +168,41 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
return mask;
}
-static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
+static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
+
+ return rsnd_ssi_multi_slaves_runtime(io) |
+ 1 << rsnd_mod_id(ssi_mod) |
+ 1 << rsnd_mod_id(ssi_parent_mod);
+}
+
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io)
+{
+ if (rsnd_runtime_is_ssi_multi(io))
+ return rsnd_ssi_multi_slaves(io);
+
+ return 0;
+}
+
+static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
- int slots = rsnd_get_slot_width(io);
+ int chan = rsnd_runtime_channel_for_ssi(io);
int j, ret;
int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12,
};
unsigned int main_rate;
- unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
+ unsigned int rate = rsnd_io_is_play(io) ?
+ rsnd_src_get_out_rate(priv, io) :
+ rsnd_src_get_in_rate(priv, io);
if (!rsnd_rdai_is_clk_master(rdai))
return 0;
@@ -249,10 +229,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/*
* this driver is assuming that
- * system word is 32bit x slots
+ * system word is 32bit x chan
* see rsnd_ssi_init()
*/
- main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
+ main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
if (0 == ret) {
@@ -274,11 +254,11 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
return -EIO;
}
-static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
+static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
if (!rsnd_rdai_is_clk_master(rdai))
@@ -296,17 +276,18 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
rsnd_adg_ssi_clk_stop(mod);
}
-static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
+static void rsnd_ssi_config_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 cr_own;
u32 cr_mode;
u32 wsr;
int is_tdm;
- is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
+ is_tdm = rsnd_runtime_is_ssi_tdm(io);
/*
* always use 32bit system word.
@@ -332,11 +313,9 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
case 32:
cr_own |= DWL_24;
break;
- default:
- return -EINVAL;
}
- if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) {
+ if (rsnd_ssi_is_dma_mode(mod)) {
cr_mode = UIEN | OIEN | /* over/under run */
DMEN; /* DMA : enable DMA */
} else {
@@ -357,8 +336,16 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
ssi->cr_own = cr_own;
ssi->cr_mode = cr_mode;
ssi->wsr = wsr;
+}
- return 0;
+static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ rsnd_mod_write(mod, SSIWSR, ssi->wsr);
+ rsnd_mod_write(mod, SSICR, ssi->cr_own |
+ ssi->cr_clk |
+ ssi->cr_mode); /* without EN */
}
/*
@@ -371,28 +358,25 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int ret;
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
ssi->usrcnt++;
rsnd_mod_power_on(mod);
- ret = rsnd_ssi_master_clk_start(ssi, io);
+ ret = rsnd_ssi_master_clk_start(mod, io);
if (ret < 0)
return ret;
- if (rsnd_ssi_is_parent(mod, io))
- return 0;
-
- ret = rsnd_ssi_config_init(ssi, io);
- if (ret < 0)
- return ret;
+ if (!rsnd_ssi_is_parent(mod, io))
+ rsnd_ssi_config_init(mod, io);
- ssi->err = -1; /* ignore 1st error */
+ rsnd_ssi_register_setup(mod);
/* clear error status */
rsnd_ssi_status_clear(mod);
- rsnd_ssi_irq_enable(mod);
-
return 0;
}
@@ -403,25 +387,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv);
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
if (!ssi->usrcnt) {
dev_err(dev, "%s[%d] usrcnt error\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
return -EIO;
}
- if (!rsnd_ssi_is_parent(mod, io)) {
- if (ssi->err > 0)
- dev_warn(dev, "%s[%d] under/over flow err = %d\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- ssi->err);
-
+ if (!rsnd_ssi_is_parent(mod, io))
ssi->cr_own = 0;
- ssi->err = 0;
- rsnd_ssi_irq_disable(mod);
- }
-
- rsnd_ssi_master_clk_stop(ssi, io);
+ rsnd_ssi_master_clk_stop(mod, io);
rsnd_mod_power_off(mod);
@@ -456,61 +434,43 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
return 0;
}
-static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
-{
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
- u32 status = rsnd_ssi_status_get(mod);
-
- /* under/over flow error */
- if (status & (UIRQ | OIRQ))
- ssi->err++;
-
- return status;
-}
-
-static int __rsnd_ssi_start(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- u32 cr;
-
- cr = ssi->cr_own |
- ssi->cr_clk |
- ssi->cr_mode;
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
/*
* EN will be set via SSIU :: SSI_CONTROL
* if Multi channel mode
*/
- if (!rsnd_ssi_multi_slaves(io))
- cr |= EN;
+ if (rsnd_ssi_multi_slaves_runtime(io))
+ return 0;
- rsnd_mod_write(mod, SSICR, cr);
- rsnd_mod_write(mod, SSIWSR, ssi->wsr);
+ rsnd_mod_bset(mod, SSICR, EN, EN);
return 0;
}
-static int rsnd_ssi_start(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 cr;
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
/*
- * no limit to start
+ * don't stop if not last user
* see also
- * rsnd_ssi_stop
+ * rsnd_ssi_start
* rsnd_ssi_interrupt
*/
- return __rsnd_ssi_start(mod, io, priv);
-}
-
-static int __rsnd_ssi_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- u32 cr;
+ if (ssi->usrcnt > 1)
+ return 0;
/*
* disable all IRQ,
@@ -532,33 +492,38 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod,
return 0;
}
-static int rsnd_ssi_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+static int rsnd_ssi_irq(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv,
+ int enable)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 val = 0;
- /*
- * don't stop if not last user
- * see also
- * rsnd_ssi_start
- * rsnd_ssi_interrupt
- */
- if (ssi->usrcnt > 1)
+ if (rsnd_is_gen1(priv))
return 0;
- return __rsnd_ssi_stop(mod, io, priv);
+ if (rsnd_ssi_is_parent(mod, io))
+ return 0;
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
+ if (enable)
+ val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000;
+
+ rsnd_mod_write(mod, SSI_INT_ENABLE, val);
+
+ return 0;
}
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
int is_dma = rsnd_ssi_is_dma_mode(mod);
u32 status;
bool elapsed = false;
+ bool stop = false;
spin_lock(&priv->lock);
@@ -566,7 +531,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
if (!rsnd_io_is_working(io))
goto rsnd_ssi_interrupt_out;
- status = rsnd_ssi_record_error(ssi);
+ status = rsnd_ssi_status_get(mod);
/* PIO only */
if (!is_dma && (status & DIRQ)) {
@@ -588,23 +553,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
}
/* DMA only */
- if (is_dma && (status & (UIRQ | OIRQ))) {
- /*
- * restart SSI
- */
- dev_dbg(dev, "%s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
-
- __rsnd_ssi_stop(mod, io, priv);
- __rsnd_ssi_start(mod, io, priv);
- }
-
- if (ssi->err > 1024) {
- rsnd_ssi_irq_disable(mod);
-
- dev_warn(dev, "no more %s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- }
+ if (is_dma && (status & (UIRQ | OIRQ)))
+ stop = true;
rsnd_ssi_status_clear(mod);
rsnd_ssi_interrupt_out:
@@ -612,6 +562,10 @@ rsnd_ssi_interrupt_out:
if (elapsed)
rsnd_dai_period_elapsed(io);
+
+ if (stop)
+ snd_pcm_stop_xrun(io->substream);
+
}
static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
@@ -627,12 +581,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
* SSI PIO
*/
static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+ struct rsnd_dai_stream *io)
{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
if (!__rsnd_ssi_is_pin_sharing(mod))
return;
+ if (!rsnd_rdai_is_clk_master(rdai))
+ return;
+
switch (rsnd_mod_id(mod)) {
case 1:
case 2:
@@ -647,6 +606,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
}
}
+static int rsnd_ssi_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ /*
+ * rsnd_rdai_is_clk_master() will be enabled after set_fmt,
+ * and, pcm_new will be called after it.
+ * This function reuse pcm_new at this point.
+ */
+ rsnd_ssi_parent_attach(mod, io);
+
+ return 0;
+}
+
static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
@@ -662,7 +635,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
if (rsnd_ssi_is_multi_slave(mod, io))
return 0;
- rsnd_ssi_parent_attach(mod, io, priv);
+ /*
+ * It can't judge ssi parent at this point
+ * see rsnd_ssi_pcm_new()
+ */
ret = rsnd_ssiu_attach(io, mod);
if (ret < 0)
@@ -683,6 +659,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
+ .irq = rsnd_ssi_irq,
+ .pcm_new = rsnd_ssi_pcm_new,
.hw_params = rsnd_ssi_hw_params,
};
@@ -705,9 +683,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
if (ret)
return ret;
- ssi->dma = rsnd_dma_attach(io, mod, dma_id);
- if (IS_ERR(ssi->dma))
- return PTR_ERR(ssi->dma);
+ /* SSI probe might be called many times in MUX multi path */
+ ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id);
return ret;
}
@@ -772,6 +749,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
+ .irq = rsnd_ssi_irq,
+ .pcm_new = rsnd_ssi_pcm_new,
.fallback = rsnd_ssi_fallback,
.hw_params = rsnd_ssi_hw_params,
};
@@ -858,6 +837,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
}
+static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type)
+{
+ /*
+ * SSIP (= SSI parent) needs to be special, otherwise,
+ * 2nd SSI might doesn't start. see also rsnd_mod_call()
+ *
+ * We can't include parent SSI status on SSI, because we don't know
+ * how many SSI requests parent SSI. Thus, it is localed on "io" now.
+ * ex) trouble case
+ * Playback: SSI0
+ * Capture : SSI1 (needs SSI0)
+ *
+ * 1) start Capture -> SSI0/SSI1 are started.
+ * 2) start Playback -> SSI0 doesn't work, because it is already
+ * marked as "started" on 1)
+ *
+ * OTOH, using each mod's status is good for MUX case.
+ * It doesn't need to start in 2nd start
+ * ex)
+ * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0
+ * |
+ * IO-1: SRC1 -> CTU2 -+
+ *
+ * 1) start IO-0 -> start SSI0
+ * 2) start IO-1 -> SSI0 doesn't need to start, because it is
+ * already started on 1)
+ */
+ if (type == RSND_MOD_SSIP)
+ return &io->parent_ssi_status;
+
+ return rsnd_mod_get_status(io, mod, type);
+}
+
int rsnd_ssi_probe(struct rsnd_priv *priv)
{
struct device_node *node;
@@ -920,7 +934,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
ops = &rsnd_ssi_dma_ops;
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
- RSND_MOD_SSI, i);
+ rsnd_ssi_get_status, RSND_MOD_SSI, i);
if (ret)
goto rsnd_ssi_probe_done;
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 06d72828e5bc..6f9b388ec5a8 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -27,7 +27,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
+ u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io);
int use_busif = rsnd_ssi_use_busif(io);
int id = rsnd_mod_id(mod);
u32 mask1, val1;
@@ -105,7 +105,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- if (rsnd_get_slot_width(io) >= 6) {
+ if (rsnd_runtime_is_ssi_tdm(io)) {
/*
* TDM Extend Mode
* see
@@ -115,13 +115,14 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
}
if (rsnd_ssi_use_busif(io)) {
- u32 val = rsnd_get_dalign(mod, io);
-
rsnd_mod_write(mod, SSI_BUSIF_ADINR,
rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io));
+ (rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io)));
rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
- rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
+ rsnd_mod_write(mod, SSI_BUSIF_DALIGN,
+ rsnd_get_dalign(mod, io));
}
return 0;
@@ -136,7 +137,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SSI_CTRL, 0x1);
- if (rsnd_ssi_multi_slaves(io))
+ if (rsnd_ssi_multi_slaves_runtime(io))
rsnd_mod_write(mod, SSI_CONTROL, 0x1);
return 0;
@@ -151,7 +152,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SSI_CTRL, 0);
- if (rsnd_ssi_multi_slaves(io))
+ if (rsnd_ssi_multi_slaves_runtime(io))
rsnd_mod_write(mod, SSI_CONTROL, 0);
return 0;
@@ -206,7 +207,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
for_each_rsnd_ssiu(ssiu, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
- ops, NULL, RSND_MOD_SSIU, i);
+ ops, NULL, rsnd_mod_get_status,
+ RSND_MOD_SSIU, i);
if (ret)
return ret;
}