diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-07-03 20:51:42 +0300 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-07-03 20:51:42 +0300 |
commit | 818a23e3882b1bf65d1719e407be04716e69a4d5 (patch) | |
tree | 6e4267b570541f779e7d85c11eb14312fa86fbb1 /sound/soc/sh | |
parent | a178232ddab55048e347b43b697bfa5013ef819e (diff) | |
parent | b821d298f8864dc3c3d42643812df43289159507 (diff) | |
download | linux-818a23e3882b1bf65d1719e407be04716e69a4d5.tar.xz |
Merge tag 'asoc-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v4.13
The big news with this release is the of-graph card, this provides a
replacement for simple-card that is much more flexibile and scalable,
allowing many more systems to use a generic sound card than was possible
before:
- The of-graph card, finally merged after a long and dedicated effort
by Morimoto-san.
- New widget types intended mainly for use with DSPs.
- New drivers for Allwinner V3s SoCs, Ensonic ES8316, several classes
of x86 machine, Rockchip PDM controllers, STM32 I2S and S/PDIF
controllers and ZTE AUD96P22 CODECs.
Diffstat (limited to 'sound/soc/sh')
-rw-r--r-- | sound/soc/sh/Kconfig | 2 | ||||
-rw-r--r-- | sound/soc/sh/fsi.c | 13 | ||||
-rw-r--r-- | sound/soc/sh/rcar/adg.c | 61 | ||||
-rw-r--r-- | sound/soc/sh/rcar/cmd.c | 7 | ||||
-rw-r--r-- | sound/soc/sh/rcar/core.c | 494 | ||||
-rw-r--r-- | sound/soc/sh/rcar/ctu.c | 6 | ||||
-rw-r--r-- | sound/soc/sh/rcar/dma.c | 32 | ||||
-rw-r--r-- | sound/soc/sh/rcar/dvc.c | 12 | ||||
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 2 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 68 | ||||
-rw-r--r-- | sound/soc/sh/rcar/src.c | 20 | ||||
-rw-r--r-- | sound/soc/sh/rcar/ssi.c | 241 | ||||
-rw-r--r-- | sound/soc/sh/rcar/ssiu.c | 37 |
13 files changed, 710 insertions, 285 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 147ebecfed94..1aa5cd77ca24 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -38,7 +38,7 @@ config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" depends on COMMON_CLK depends on OF || COMPILE_TEST - select SND_SIMPLE_CARD + select SND_SIMPLE_CARD_UTILS select REGMAP_MMIO help This option enables R-Car SRU/SCU/SSIU/SSI sound support diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index ead520182e26..7c4bdd82bb95 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -301,7 +301,12 @@ struct fsi_master { spinlock_t lock; }; -static int fsi_stream_is_play(struct fsi_priv *fsi, struct fsi_stream *io); +static inline int fsi_stream_is_play(struct fsi_priv *fsi, + struct fsi_stream *io) +{ + return &fsi->playback == io; +} + /* * basic read write function @@ -489,12 +494,6 @@ static void fsi_count_fifo_err(struct fsi_priv *fsi) /* * fsi_stream_xx() function */ -static inline int fsi_stream_is_play(struct fsi_priv *fsi, - struct fsi_stream *io) -{ - return &fsi->playback == io; -} - static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi, struct snd_pcm_substream *substream) { diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d3b0dc145a56..197cb3ec075f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -308,23 +308,12 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) } } -int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) -{ - rsnd_adg_set_ssi_clk(ssi_mod, 0); - - return 0; -} - -int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) +int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) { - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct clk *clk; int i; - u32 data; - u32 ckr = 0; int sel_table[] = { [CLKA] = 0x1, [CLKB] = 0x2, @@ -338,30 +327,42 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) * find suitable clock from * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. */ - data = 0; for_each_rsnd_clk(clk, adg, i) { - if (rate == clk_get_rate(clk)) { - data = sel_table[i]; - goto found_clock; - } + if (rate == clk_get_rate(clk)) + return sel_table[i]; } /* * find divided clock from BRGA/BRGB */ - if (rate == adg->rbga_rate_for_441khz) { - data = 0x10; - goto found_clock; - } + if (rate == adg->rbga_rate_for_441khz) + return 0x10; - if (rate == adg->rbgb_rate_for_48khz) { - data = 0x20; - goto found_clock; - } + if (rate == adg->rbgb_rate_for_48khz) + return 0x20; return -EIO; +} -found_clock: +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) +{ + rsnd_adg_set_ssi_clk(ssi_mod, 0); + + return 0; +} + +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + int data; + u32 ckr = 0; + + data = rsnd_adg_clk_query(priv, rate); + if (data < 0) + return data; rsnd_adg_set_ssi_clk(ssi_mod, data); @@ -480,6 +481,9 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, if (req_rate[0] % 48000 == 0) adg->flags = AUDIO_OUT_48; + if (of_get_property(np, "clkout-lr-asynchronous", NULL)) + adg->flags = LRCLK_ASYNC; + /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC * have 44.1kHz or 48kHz base clocks for now. @@ -555,7 +559,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, clk = clk_register_fixed_rate(dev, clkout_name[i], parent_clk_name, 0, req_rate[0]); - adg->clkout[i] = ERR_PTR(-ENOENT); if (!IS_ERR(clk)) adg->clkout[i] = clk; } @@ -580,7 +583,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) { struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np = dev->of_node; int ret; adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); @@ -597,9 +599,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); - if (of_get_property(np, "clkout-lr-asynchronous", NULL)) - adg->flags = LRCLK_ASYNC; - priv->adg = adg; rsnd_adg_clk_enable(priv); diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index d879c010cf03..f1d4fb566892 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -31,7 +31,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); struct device *dev = rsnd_priv_to_dev(priv); u32 data; - u32 path[] = { + static const u32 path[] = { [1] = 1 << 0, [5] = 1 << 8, [6] = 1 << 12, @@ -71,7 +71,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, } else { struct rsnd_mod *src = rsnd_io_to_mod_src(io); - u8 cmd_case[] = { + static const u8 cmd_case[] = { [0] = 0x3, [1] = 0x3, [2] = 0x4, @@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, [9] = 0x2, }; + if (unlikely(!src)) + return -EIO; + data = path[rsnd_mod_id(src)] | cmd_case[rsnd_mod_id(src)] << 16; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 8c1f4e2e0c4f..3f2ced26ed37 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -203,27 +203,6 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) return !!io->substream; } -void rsnd_set_slot(struct rsnd_dai *rdai, - int slots, int num) -{ - rdai->slots = slots; - rdai->slots_num = num; -} - -int rsnd_get_slot(struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - - return rdai->slots; -} - -int rsnd_get_slot_num(struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - - return rdai->slots_num; -} - int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); @@ -248,13 +227,14 @@ int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) { + struct rsnd_dai *rdai = rsnd_io_to_rdai(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); + chan /= rsnd_rdai_ssi_lane_get(rdai); /* TDM Extend Mode needs 8ch */ if (chan == 6) @@ -265,12 +245,13 @@ int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) { - int slots = rsnd_get_slot_num(io); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + int lane = rsnd_rdai_ssi_lane_get(rdai); int chan = rsnd_io_is_play(io) ? rsnd_runtime_channel_after_ctu(io) : rsnd_runtime_channel_original(io); - return (chan >= 6) && (slots > 1); + return (chan > 2) && (lane > 1); } int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) @@ -310,6 +291,24 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u32 val = 0x76543210; u32 mask = ~0; + /* + * *Hardware* L/R and *Software* L/R are inverted. + * We need to care about inversion timing to control + * Playback/Capture correctly. + * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R + * + * sL/R : software L/R + * hL/R : hardware L/R + * (*) : conversion timing + * + * Playback + * sL/R (*) hL/R hL/R hL/R hL/R hL/R + * [MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec + * + * Capture + * hL/R hL/R hL/R hL/R hL/R (*) sL/R + * codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM] + */ if (rsnd_io_is_play(io)) { struct rsnd_mod *src = rsnd_io_to_mod_src(io); @@ -470,8 +469,7 @@ static int rsnd_status_update(u32 *status, #define rsnd_dai_call(fn, io, param...) \ ({ \ - struct rsnd_priv *priv = rsnd_io_to_priv(io); \ - struct device *dev = rsnd_priv_to_dev(priv); \ + struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); \ struct rsnd_mod *mod; \ int is_play = rsnd_io_is_play(io); \ int ret = 0, i; \ @@ -532,6 +530,24 @@ static void rsnd_dai_disconnect(struct rsnd_mod *mod, io->mod[type] = NULL; } +int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, + int max_channels) +{ + if (max_channels > 0) + rdai->max_channels = max_channels; + + return rdai->max_channels; +} + +int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, + int ssi_lane) +{ + if (ssi_lane > 0) + rdai->ssi_lane = ssi_lane; + + return rdai->ssi_lane; +} + struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) { if ((id < 0) || (id >= rsnd_rdai_nr(priv))) @@ -551,40 +567,6 @@ static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) /* * rsnd_soc_dai functions */ -int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) -{ - struct snd_pcm_substream *substream = io->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - int pos = io->byte_pos + additional; - - pos %= (runtime->periods * io->byte_per_period); - - return pos; -} - -bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) -{ - io->byte_pos += byte; - - if (io->byte_pos >= io->next_period_byte) { - struct snd_pcm_substream *substream = io->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - - io->period_pos++; - io->next_period_byte += io->byte_per_period; - - if (io->period_pos >= runtime->periods) { - io->byte_pos = 0; - io->period_pos = 0; - io->next_period_byte = io->byte_per_period; - } - - return true; - } - - return false; -} - void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) { struct snd_pcm_substream *substream = io->substream; @@ -602,15 +584,7 @@ void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - io->substream = substream; - io->byte_pos = 0; - io->period_pos = 0; - io->byte_per_period = runtime->period_size * - runtime->channels * - samples_to_bytes(runtime, 1); - io->next_period_byte = io->byte_per_period; } static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io) @@ -749,9 +723,13 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, struct device *dev = rsnd_priv_to_dev(priv); switch (slots) { + case 2: case 6: + case 8: + case 16: /* TDM Extend Mode */ - rsnd_set_slot(rdai, slots, 1); + rsnd_rdai_channels_set(rdai, slots); + rsnd_rdai_ssi_lane_set(rdai, 1); break; default: dev_err(dev, "unsupported TDM slots (%d)\n", slots); @@ -761,22 +739,177 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static unsigned int rsnd_soc_hw_channels_list[] = { + 2, 6, 8, 16, +}; + +static unsigned int rsnd_soc_hw_rate_list[] = { + 8000, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, +}; + +static int rsnd_soc_hw_rule(struct rsnd_priv *priv, + unsigned int *list, int list_num, + struct snd_interval *baseline, struct snd_interval *iv) +{ + struct snd_interval p; + unsigned int rate; + int i; + + snd_interval_any(&p); + p.min = UINT_MAX; + p.max = 0; + + for (i = 0; i < list_num; i++) { + + if (!snd_interval_test(iv, list[i])) + continue; + + rate = rsnd_ssi_clk_query(priv, + baseline->min, list[i], NULL); + if (rate > 0) { + p.min = min(p.min, list[i]); + p.max = max(p.max, list[i]); + } + + rate = rsnd_ssi_clk_query(priv, + baseline->max, list[i], NULL); + if (rate > 0) { + p.min = min(p.min, list[i]); + p.max = max(p.max, list[i]); + } + } + + return snd_interval_refine(iv, &p); +} + +static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval ic; + struct snd_soc_dai *dai = rule->private; + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + + /* + * possible sampling rate limitation is same as + * 2ch if it supports multi ssi + */ + ic = *ic_; + if (1 < rsnd_rdai_ssi_lane_get(rdai)) { + ic.min = 2; + ic.max = 2; + } + + return rsnd_soc_hw_rule(priv, rsnd_soc_hw_rate_list, + ARRAY_SIZE(rsnd_soc_hw_rate_list), + &ic, ir); +} + + +static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval ic; + struct snd_soc_dai *dai = rule->private; + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + + /* + * possible sampling rate limitation is same as + * 2ch if it supports multi ssi + */ + ic = *ic_; + if (1 < rsnd_rdai_ssi_lane_get(rdai)) { + ic.min = 2; + ic.max = 2; + } + + return rsnd_soc_hw_rule(priv, rsnd_soc_hw_channels_list, + ARRAY_SIZE(rsnd_soc_hw_channels_list), + ir, &ic); +} + +static void rsnd_soc_hw_constraint(struct snd_pcm_runtime *runtime, + struct snd_soc_dai *dai) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint; + unsigned int max_channels = rsnd_rdai_channels_get(rdai); + int i; + + /* + * Channel Limitation + * It depends on Platform design + */ + constraint->list = rsnd_soc_hw_channels_list; + constraint->count = 0; + constraint->mask = 0; + + for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) { + if (rsnd_soc_hw_channels_list[i] > max_channels) + break; + constraint->count = i + 1; + } + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, constraint); + + /* + * Sampling Rate / Channel Limitation + * It depends on Clock Master Mode + */ + if (!rsnd_rdai_is_clk_master(rdai)) + return; + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + rsnd_soc_hw_rule_rate, dai, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + rsnd_soc_hw_rule_channels, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + int ret; + + /* rsnd_io_to_runtime() is not yet enabled here */ + rsnd_soc_hw_constraint(substream->runtime, dai); /* * call rsnd_dai_call without spinlock */ - return rsnd_dai_call(nolock_start, io, priv); + ret = rsnd_dai_call(nolock_start, io, priv); + if (ret < 0) + rsnd_dai_call(nolock_stop, io, priv); + + return ret; } static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); /* @@ -820,32 +953,132 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, of_node_put(node); } -static int rsnd_dai_probe(struct rsnd_priv *priv) +static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, + int *is_graph) { + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; struct device_node *dai_node; - struct device_node *dai_np; + struct device_node *ret; + + *is_graph = 0; + + /* + * parse both previous dai (= rcar_sound,dai), and + * graph dai (= ports/port) + */ + dai_node = of_get_child_by_name(np, RSND_NODE_DAI); + if (dai_node) { + ret = dai_node; + goto of_node_compatible; + } + + ret = np; + + dai_node = of_graph_get_next_endpoint(np, NULL); + if (dai_node) + goto of_node_graph; + + return NULL; + +of_node_graph: + *is_graph = 1; +of_node_compatible: + of_node_put(dai_node); + + return ret; +} + +static void __rsnd_dai_probe(struct rsnd_priv *priv, + struct device_node *dai_np, + int dai_i, int is_graph) +{ struct device_node *playback, *capture; struct rsnd_dai_stream *io_playback; struct rsnd_dai_stream *io_capture; - struct snd_soc_dai_driver *rdrv, *drv; + struct snd_soc_dai_driver *drv; struct rsnd_dai *rdai; struct device *dev = rsnd_priv_to_dev(priv); - int nr, dai_i, io_i; - int ret; + int io_i; + + rdai = rsnd_rdai_get(priv, dai_i); + drv = priv->daidrv + dai_i; + io_playback = &rdai->playback; + io_capture = &rdai->capture; + + snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); + + rdai->priv = priv; + drv->name = rdai->name; + drv->ops = &rsnd_soc_dai_ops; + + snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, + "DAI%d Playback", dai_i); + drv->playback.rates = RSND_RATES; + drv->playback.formats = RSND_FMTS; + drv->playback.channels_min = 2; + drv->playback.channels_max = 16; + drv->playback.stream_name = rdai->playback.name; + + snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, + "DAI%d Capture", dai_i); + drv->capture.rates = RSND_RATES; + drv->capture.formats = RSND_FMTS; + drv->capture.channels_min = 2; + drv->capture.channels_max = 16; + drv->capture.stream_name = rdai->capture.name; + + rdai->playback.rdai = rdai; + rdai->capture.rdai = rdai; + rsnd_rdai_channels_set(rdai, 2); /* default 2ch */ + rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */ + + for (io_i = 0;; io_i++) { + playback = of_parse_phandle(dai_np, "playback", io_i); + capture = of_parse_phandle(dai_np, "capture", io_i); + + if (!playback && !capture) + break; - dai_node = rsnd_dai_of_node(priv); - nr = of_get_child_count(dai_node); - if (!nr) { - ret = -EINVAL; - goto rsnd_dai_probe_done; + rsnd_parse_connect_ssi(rdai, playback, capture); + rsnd_parse_connect_src(rdai, playback, capture); + rsnd_parse_connect_ctu(rdai, playback, capture); + rsnd_parse_connect_mix(rdai, playback, capture); + rsnd_parse_connect_dvc(rdai, playback, capture); + + of_node_put(playback); + of_node_put(capture); } + dev_dbg(dev, "%s (%s/%s)\n", rdai->name, + rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", + rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); +} + +static int rsnd_dai_probe(struct rsnd_priv *priv) +{ + struct device_node *dai_node; + struct device_node *dai_np; + struct snd_soc_dai_driver *rdrv; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai *rdai; + int nr; + int is_graph; + int dai_i; + + dai_node = rsnd_dai_of_node(priv, &is_graph); + if (is_graph) + nr = of_graph_get_endpoint_count(dai_node); + else + nr = of_get_child_count(dai_node); + + if (!nr) + return -EINVAL; + rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL); rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL); - if (!rdrv || !rdai) { - ret = -ENOMEM; - goto rsnd_dai_probe_done; - } + if (!rdrv || !rdai) + return -ENOMEM; priv->rdai_nr = nr; priv->daidrv = rdrv; @@ -855,68 +1088,18 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) * parse all dai */ dai_i = 0; - for_each_child_of_node(dai_node, dai_np) { - rdai = rsnd_rdai_get(priv, dai_i); - drv = rdrv + dai_i; - io_playback = &rdai->playback; - io_capture = &rdai->capture; - - snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); - - rdai->priv = priv; - drv->name = rdai->name; - drv->ops = &rsnd_soc_dai_ops; - - snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, - "DAI%d Playback", dai_i); - drv->playback.rates = RSND_RATES; - drv->playback.formats = RSND_FMTS; - drv->playback.channels_min = 2; - drv->playback.channels_max = 6; - drv->playback.stream_name = rdai->playback.name; - - snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, - "DAI%d Capture", dai_i); - drv->capture.rates = RSND_RATES; - drv->capture.formats = RSND_FMTS; - drv->capture.channels_min = 2; - drv->capture.channels_max = 6; - drv->capture.stream_name = rdai->capture.name; - - rdai->playback.rdai = rdai; - rdai->capture.rdai = rdai; - rsnd_set_slot(rdai, 2, 1); /* default */ - - for (io_i = 0;; io_i++) { - playback = of_parse_phandle(dai_np, "playback", io_i); - capture = of_parse_phandle(dai_np, "capture", io_i); - - if (!playback && !capture) - break; - - rsnd_parse_connect_ssi(rdai, playback, capture); - rsnd_parse_connect_src(rdai, playback, capture); - rsnd_parse_connect_ctu(rdai, playback, capture); - rsnd_parse_connect_mix(rdai, playback, capture); - rsnd_parse_connect_dvc(rdai, playback, capture); - - of_node_put(playback); - of_node_put(capture); + if (is_graph) { + for_each_endpoint_of_node(dai_node, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_i, is_graph); + rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i); + dai_i++; } - - dai_i++; - - dev_dbg(dev, "%s (%s/%s)\n", rdai->name, - rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", - rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); + } else { + for_each_child_of_node(dai_node, dai_np) + __rsnd_dai_probe(priv, dai_np, dai_i++, is_graph); } - ret = 0; - -rsnd_dai_probe_done: - of_node_put(dai_node); - - return ret; + return 0; } /* @@ -965,12 +1148,14 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream, static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + snd_pcm_uframes_t pointer = 0; + + rsnd_dai_call(pointer, io, &pointer); - return bytes_to_frames(runtime, io->byte_pos); + return pointer; } static struct snd_pcm_ops rsnd_pcm_ops = { @@ -1033,6 +1218,9 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); int i, change = 0; + if (!cfg->accept(cfg->io)) + return 0; + for (i = 0; i < cfg->size; i++) { if (cfg->texts) { change |= (uc->value.enumerated.item[i] != cfg->val[i]); @@ -1049,6 +1237,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, return change; } +int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io) +{ + return 1; +} + +int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + return !!runtime; +} + struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg) { cfg->cfg.val = cfg->val; @@ -1067,6 +1267,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, + int (*accept)(struct rsnd_dai_stream *io), void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod), struct rsnd_kctrl_cfg *cfg, @@ -1101,6 +1302,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, cfg->texts = texts; cfg->max = max; cfg->size = size; + cfg->accept = accept; cfg->update = update; cfg->card = card; cfg->kctrl = kctrl; @@ -1332,7 +1534,7 @@ static int rsnd_resume(struct device *dev) return 0; } -static struct dev_pm_ops rsnd_pm_ops = { +static const struct dev_pm_ops rsnd_pm_ops = { .suspend = rsnd_suspend, .resume = rsnd_resume, }; diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 9dcc1f9db026..4ba8f2fe7a4c 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -279,12 +279,14 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* CTU Pass */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", + rsnd_kctrl_accept_anytime, NULL, &ctu->pass, RSND_MAX_CHANNELS, 0xC); /* ROW0 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv0, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -293,6 +295,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* ROW1 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv1, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -301,6 +304,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* ROW2 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv2, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -309,6 +313,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* ROW3 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv3, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -317,6 +322,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* Reset */ ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", + rsnd_kctrl_accept_anytime, rsnd_ctu_value_reset, &ctu->reset, 1); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 241cb3b08a07..60aa5e96a49f 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -25,6 +25,7 @@ struct rsnd_dmaen { struct dma_chan *chan; + dma_cookie_t cookie; dma_addr_t dma_buf; unsigned int dma_len; unsigned int dma_period; @@ -103,10 +104,6 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod, * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. * But, Audio-DMAC-peri-peri doesn't have interrupt, * and this driver is assuming that here. - * - * If Audio-DMAC-peri-peri has interrpt, - * rsnd_dai_pointer_update() will be called twice, - * ant it will breaks io->byte_pos */ spin_lock_irqsave(&priv->lock, flags); @@ -121,7 +118,7 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod, */ rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2); - elapsed = rsnd_dai_pointer_update(io, io->byte_per_period); + elapsed = true; dmaen->dma_cnt++; } @@ -292,7 +289,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, for (i = 0; i < 2; i++) rsnd_dmaen_sync(dmaen, io, i); - if (dmaengine_submit(desc) < 0) { + dmaen->cookie = dmaengine_submit(desc); + if (dmaen->cookie < 0) { dev_err(dev, "dmaengine_submit() fail\n"); return -EIO; } @@ -348,12 +346,34 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, return 0; } +static int rsnd_dmaen_pointer(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct dma_tx_state state; + enum dma_status status; + unsigned int pos = 0; + + status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + if (state.residue > 0 && state.residue <= dmaen->dma_len) + pos = dmaen->dma_len - state.residue; + } + *pointer = bytes_to_frames(runtime, pos); + + return 0; +} + static struct rsnd_mod_ops rsnd_dmaen_ops = { .name = "audmac", .nolock_start = rsnd_dmaen_nolock_start, .nolock_stop = rsnd_dmaen_nolock_stop, .start = rsnd_dmaen_start, .stop = rsnd_dmaen_stop, + .pointer= rsnd_dmaen_pointer, }; /* diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 463de8360985..99d2d9459e75 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -249,16 +249,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); int is_play = rsnd_io_is_play(io); - int slots = rsnd_get_slot(io); + int channels = rsnd_rdai_channels_get(rdai); int ret; /* Volume */ ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Playback Volume" : "DVC In Capture Volume", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, - &dvc->volume, slots, + &dvc->volume, channels, 0x00800000 - 1); if (ret < 0) return ret; @@ -267,8 +269,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Mute Switch" : "DVC In Mute Switch", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, - &dvc->mute, slots, + &dvc->mute, channels, 1); if (ret < 0) return ret; @@ -277,6 +280,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_s(mod, io, rtd, is_play ? "DVC Out Ramp Switch" : "DVC In Ramp Switch", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->ren, 1); if (ret < 0) @@ -285,6 +289,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rup, dvc_ramp_rate); @@ -294,6 +299,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rdown, dvc_ramp_rate); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 4b0980728e13..ee00e3516911 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -219,6 +219,8 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884), RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), + RSND_GEN_S_REG(HDMI0_SEL, 0x9e0), + RSND_GEN_S_REG(HDMI1_SEL, 0x9e4), /* FIXME: it needs SSI_MODE2/3 in the future */ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 323af41ecfcb..99c57611df88 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -18,6 +18,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/of_graph.h> #include <linux/of_irq.h> #include <linux/sh_dma.h> #include <linux/workqueue.h> @@ -170,6 +171,8 @@ enum rsnd_reg { RSND_REG_SSI_SYS_STATUS5, RSND_REG_SSI_SYS_STATUS6, RSND_REG_SSI_SYS_STATUS7, + RSND_REG_HDMI0_SEL, + RSND_REG_HDMI1_SEL, /* SSI */ RSND_REG_SSICR, @@ -268,6 +271,9 @@ struct rsnd_mod_ops { struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); + int (*pointer)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer); int (*fallback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); @@ -305,6 +311,7 @@ struct rsnd_mod { * H 0: pcm_new * H 0: fallback * H 0: hw_params + * H 0: pointer */ #define __rsnd_mod_shift_nolock_start 0 #define __rsnd_mod_shift_nolock_stop 0 @@ -318,6 +325,7 @@ struct rsnd_mod { #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_shift_pointer 28 /* always called */ #define __rsnd_mod_add_probe 0 #define __rsnd_mod_add_remove 0 @@ -331,6 +339,7 @@ struct rsnd_mod { #define __rsnd_mod_add_pcm_new 0 #define __rsnd_mod_add_fallback 0 #define __rsnd_mod_add_hw_params 0 +#define __rsnd_mod_add_pointer 0 #define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_remove 0 @@ -342,6 +351,7 @@ struct rsnd_mod { #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 +#define __rsnd_mod_call_pointer 0 #define __rsnd_mod_call_nolock_start 0 #define __rsnd_mod_call_nolock_stop 1 @@ -389,11 +399,6 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); -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_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); @@ -420,13 +425,8 @@ struct rsnd_dai_stream { char name[RSND_DAI_NAME_SIZE]; struct snd_pcm_substream *substream; struct rsnd_mod *mod[RSND_MOD_MAX]; - struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai *rdai; u32 parent_ssi_status; - int byte_pos; - int period_pos; - int byte_per_period; - int next_period_byte; }; #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) @@ -449,9 +449,10 @@ struct rsnd_dai { struct rsnd_dai_stream playback; struct rsnd_dai_stream capture; struct rsnd_priv *priv; + struct snd_pcm_hw_constraint_list constraint; - int slots; - int slots_num; + int max_channels; /* 2ch - 16ch */ + int ssi_lane; /* 1lane - 4lane */ unsigned int clk_master:1; unsigned int bit_clk_inv:1; @@ -471,13 +472,24 @@ struct rsnd_dai { struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); -bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +#define rsnd_rdai_channels_set(rdai, max_channels) \ + rsnd_rdai_channels_ctrl(rdai, max_channels) +#define rsnd_rdai_channels_get(rdai) \ + rsnd_rdai_channels_ctrl(rdai, 0) +int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, + int max_channels); + +#define rsnd_rdai_ssi_lane_set(rdai, ssi_lane) \ + rsnd_rdai_ssi_lane_ctrl(rdai, ssi_lane) +#define rsnd_rdai_ssi_lane_get(rdai) \ + rsnd_rdai_ssi_lane_ctrl(rdai, 0) +int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, + int ssi_lane); + void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io); -int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type); -#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI) /* * R-Car Gen1/Gen2 @@ -491,6 +503,7 @@ phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); /* * R-Car ADG */ +int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate); 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); @@ -596,6 +609,7 @@ struct rsnd_kctrl_cfg { unsigned int size; u32 *val; const char * const *texts; + int (*accept)(struct rsnd_dai_stream *io); void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); struct rsnd_dai_stream *io; struct snd_card *card; @@ -613,12 +627,15 @@ struct rsnd_kctrl_cfg_s { u32 val; }; +int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io); +int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io); struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg); struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg); int rsnd_kctrl_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, + int (*accept)(struct rsnd_dai_stream *io), void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod), struct rsnd_kctrl_cfg *cfg, @@ -626,16 +643,16 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, int size, u32 max); -#define rsnd_kctrl_new_m(mod, io, rtd, name, update, cfg, size, max) \ - rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_m(cfg), \ +#define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \ NULL, size, max) -#define rsnd_kctrl_new_s(mod, io, rtd, name, update, cfg, max) \ - rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \ +#define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ NULL, 1, max) -#define rsnd_kctrl_new_e(mod, io, rtd, name, update, cfg, texts) \ - rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \ +#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ texts, 1, ARRAY_SIZE(texts)) /* @@ -648,6 +665,13 @@ 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_runtime(struct rsnd_dai_stream *io); +#define RSND_SSI_HDMI_PORT0 0xf0 +#define RSND_SSI_HDMI_PORT1 0xf1 +int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io); +void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, + struct device_node *endpoint, + int dai_i); + #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); @@ -656,6 +680,8 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); +unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv, + int param1, int param2, int *idx); /* * R-Car SSIU diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 76a477a3ccb5..7aa239e28491 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -12,10 +12,6 @@ #define SRC_NAME "src" -/* SRCx_STATUS */ -#define OUF_SRCO ((1 << 12) | (1 << 13)) -#define OUF_SRCI ((1 << 9) | (1 << 8)) - /* SCU_SYSTEM_STATUS0/1 */ #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) @@ -55,20 +51,6 @@ struct rsnd_src { * */ -/* - * src.c is caring... - * - * Gen1 - * - * [mem] -> [SRU] -> [SSI] - * |--------| - * - * Gen2 - * - * [mem] -> [SRC] -> [SSIU] -> [SSI] - * |-----------------| - */ - static void rsnd_src_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, SRC_SWRSR, 0); @@ -515,6 +497,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, rsnd_io_is_play(io) ? "SRC Out Rate Switch" : "SRC In Rate Switch", + rsnd_kctrl_accept_anytime, rsnd_src_set_convert_rate, &src->sen, 1); if (ret < 0) @@ -524,6 +507,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", + rsnd_kctrl_accept_runtime, rsnd_src_set_convert_rate, &src->sync, 192000); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 91e5c07911b4..46feddd78ee2 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -11,6 +11,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <sound/simple_card_utils.h> #include <linux/delay.h> #include "rsnd.h" #define RSND_SSI_NAME_SIZE 16 @@ -76,11 +77,18 @@ struct rsnd_ssi { int rate; int irq; unsigned int usrcnt; + + int byte_pos; + int period_pos; + int byte_per_period; + int next_period_byte; }; /* flags */ #define RSND_SSI_CLK_PIN_SHARE (1 << 0) #define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ +#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */ +#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */ #define for_each_rsnd_ssi(pos, priv, i) \ for (i = 0; \ @@ -99,6 +107,20 @@ struct rsnd_ssi { #define rsnd_ssi_is_run_mods(mod, io) \ (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) +int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI0) + return RSND_SSI_HDMI_PORT0; + + if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI1) + return RSND_SSI_HDMI_PORT1; + + return 0; +} + int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); @@ -186,6 +208,46 @@ u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) return 0; } +unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv, + int param1, int param2, int *idx) +{ + int ssi_clk_mul_table[] = { + 1, 2, 4, 8, 16, 6, 12, + }; + int j, ret; + unsigned int main_rate; + + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * It will set SSIWSR.CONT here, but SSICR.CKDV = 000 + * with it is not allowed. (SSIWSR.WS_MODE with + * SSICR.CKDV = 000 is not allowed either). + * Skip it. See SSICR.CKDV + */ + if (j == 0) + continue; + + /* + * this driver is assuming that + * system word is 32bit x chan + * see rsnd_ssi_init() + */ + main_rate = 32 * param1 * param2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_clk_query(priv, main_rate); + if (ret < 0) + continue; + + if (idx) + *idx = j; + + return main_rate; + } + + return 0; +} + static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { @@ -195,10 +257,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); int chan = rsnd_runtime_channel_for_ssi(io); - int j, ret; - int ssi_clk_mul_table[] = { - 1, 2, 4, 8, 16, 6, 12, - }; + int idx, ret; unsigned int main_rate; unsigned int rate = rsnd_io_is_play(io) ? rsnd_src_get_out_rate(priv, io) : @@ -222,45 +281,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, return 0; } - /* - * Find best clock, and try to start ADG - */ - for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { - - /* - * It will set SSIWSR.CONT here, but SSICR.CKDV = 000 - * with it is not allowed. (SSIWSR.WS_MODE with - * SSICR.CKDV = 000 is not allowed either). - * Skip it. See SSICR.CKDV - */ - if (j == 0) - continue; - - /* - * this driver is assuming that - * system word is 32bit x chan - * see rsnd_ssi_init() - */ - main_rate = rate * 32 * chan * 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); - ssi->wsr = CONT; + main_rate = rsnd_ssi_clk_query(priv, rate, chan, &idx); + if (!main_rate) { + dev_err(dev, "unsupported clock rate\n"); + return -EIO; + } - ssi->rate = rate; + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); + if (ret < 0) + return ret; - dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod), rate); + ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(idx); + ssi->wsr = CONT; + ssi->rate = rate; - return 0; - } - } + dev_dbg(dev, "%s[%d] outputs %u Hz\n", + rsnd_mod_name(mod), + rsnd_mod_id(mod), rate); - dev_err(dev, "unsupported clock rate\n"); - return -EIO; + return 0; } static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, @@ -357,6 +396,59 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod) ssi->cr_mode); /* without EN */ } +static void rsnd_ssi_pointer_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + ssi->byte_pos = 0; + ssi->period_pos = 0; + ssi->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + ssi->next_period_byte = ssi->byte_per_period; +} + +static int rsnd_ssi_pointer_offset(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + int additional) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + int pos = ssi->byte_pos + additional; + + pos %= (runtime->periods * ssi->byte_per_period); + + return pos; +} + +static bool rsnd_ssi_pointer_update(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + int byte) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + ssi->byte_pos += byte; + + if (ssi->byte_pos >= ssi->next_period_byte) { + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + ssi->period_pos++; + ssi->next_period_byte += ssi->byte_per_period; + + if (ssi->period_pos >= runtime->periods) { + ssi->byte_pos = 0; + ssi->period_pos = 0; + ssi->next_period_byte = ssi->byte_per_period; + } + + return true; + } + + return false; +} + /* * SSI mod common functions */ @@ -370,6 +462,8 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (!rsnd_ssi_is_run_mods(mod, io)) return 0; + rsnd_ssi_pointer_init(mod, io); + ssi->usrcnt++; rsnd_mod_power_on(mod); @@ -549,7 +643,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!is_dma && (status & DIRQ)) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 *buf = (u32 *)(runtime->dma_area + - rsnd_dai_pointer_offset(io, 0)); + rsnd_ssi_pointer_offset(mod, io, 0)); int shift = 0; switch (runtime->sample_bits) { @@ -568,7 +662,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, else *buf = (rsnd_mod_read(mod, SSIRDR) >> shift); - elapsed = rsnd_dai_pointer_update(io, sizeof(*buf)); + elapsed = rsnd_ssi_pointer_update(mod, io, sizeof(*buf)); } /* DMA only */ @@ -675,6 +769,18 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, return ret; } +static int rsnd_ssi_pointer(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *pointer = bytes_to_frames(runtime, ssi->byte_pos); + + return 0; +} + static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, .probe = rsnd_ssi_common_probe, @@ -683,6 +789,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, .irq = rsnd_ssi_irq, + .pointer= rsnd_ssi_pointer, .pcm_new = rsnd_ssi_pcm_new, .hw_params = rsnd_ssi_hw_params, }; @@ -787,13 +894,6 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) /* - * Non SSI - */ -static struct rsnd_mod_ops rsnd_ssi_non_ops = { - .name = SSI_NAME, -}; - -/* * ssi mod function */ static void rsnd_ssi_connect(struct rsnd_mod *mod, @@ -814,7 +914,8 @@ static void rsnd_ssi_connect(struct rsnd_mod *mod, type = types[i]; if (!rsnd_io_to_mod(io, type)) { rsnd_dai_connect(mod, io, type); - rsnd_set_slot(rdai, 2 * (i + 1), (i + 1)); + rsnd_rdai_channels_set(rdai, (i + 1) * 2); + rsnd_rdai_ssi_lane_set(rdai, (i + 1)); return; } } @@ -847,6 +948,47 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, of_node_put(node); } +static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *remote_ep) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); + struct rsnd_ssi *ssi; + + if (!mod) + return; + + ssi = rsnd_mod_to_ssi(mod); + + if (strstr(remote_ep->full_name, "hdmi0")) { + ssi->flags |= RSND_SSI_HDMI0; + dev_dbg(dev, "%s[%d] connected to HDMI0\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + if (strstr(remote_ep->full_name, "hdmi1")) { + ssi->flags |= RSND_SSI_HDMI1; + dev_dbg(dev, "%s[%d] connected to HDMI1\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } +} + +void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, + struct device_node *endpoint, + int dai_i) +{ + struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); + struct device_node *remote_ep; + + remote_ep = of_graph_get_remote_endpoint(endpoint); + if (!remote_ep) + return; + + __rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep); + __rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture, remote_ep); +} + struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) @@ -952,7 +1094,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) goto rsnd_ssi_probe_done; } - ops = &rsnd_ssi_non_ops; if (of_property_read_bool(np, "pio-transfer")) ops = &rsnd_ssi_pio_ops; else diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 512d238b79e2..bed2c9c0004b 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -123,6 +123,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + int hdmi = rsnd_ssi_hdmi_port(io); int ret; ret = rsnd_ssiu_init(mod, io, priv); @@ -150,6 +151,42 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, rsnd_get_dalign(mod, io)); } + if (hdmi) { + enum rsnd_mod_type rsnd_ssi_array[] = { + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *pos; + u32 val; + int i, shift; + + i = rsnd_mod_id(ssi_mod); + + /* output all same SSI as default */ + val = i << 16 | + i << 20 | + i << 24 | + i << 28 | + i; + + for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) { + shift = (i * 4) + 16; + val = (val & ~(0xF << shift)) | + rsnd_mod_id(pos) << shift; + } + + switch (hdmi) { + case RSND_SSI_HDMI_PORT0: + rsnd_mod_write(mod, HDMI0_SEL, val); + break; + case RSND_SSI_HDMI_PORT1: + rsnd_mod_write(mod, HDMI1_SEL, val); + break; + } + } + return 0; } |