diff options
Diffstat (limited to 'sound/soc/renesas')
-rw-r--r-- | sound/soc/renesas/Kconfig | 7 | ||||
-rw-r--r-- | sound/soc/renesas/migor.c | 2 | ||||
-rw-r--r-- | sound/soc/renesas/rcar/Makefile | 3 | ||||
-rw-r--r-- | sound/soc/renesas/rcar/adg.c | 32 | ||||
-rw-r--r-- | sound/soc/renesas/rcar/core.c | 31 | ||||
-rw-r--r-- | sound/soc/renesas/rcar/msiof.c | 566 | ||||
-rw-r--r-- | sound/soc/renesas/rcar/rsnd.h | 1 | ||||
-rw-r--r-- | sound/soc/renesas/rcar/src.c | 116 | ||||
-rw-r--r-- | sound/soc/renesas/rcar/ssi.c | 3 | ||||
-rw-r--r-- | sound/soc/renesas/rz-ssi.c | 2 |
10 files changed, 713 insertions, 50 deletions
diff --git a/sound/soc/renesas/Kconfig b/sound/soc/renesas/Kconfig index cb01fb36355f..dabf02a955ca 100644 --- a/sound/soc/renesas/Kconfig +++ b/sound/soc/renesas/Kconfig @@ -46,6 +46,13 @@ config SND_SOC_RCAR help This option enables R-Car SRU/SCU/SSIU/SSI sound support +config SND_SOC_MSIOF + tristate "R-Car series MSIOF support" + depends on OF + select SND_DMAENGINE_PCM + help + This option enables R-Car MSIOF sound support + config SND_SOC_RZ tristate "RZ/G2L series SSIF-2 support" depends on ARCH_RZG2L || COMPILE_TEST diff --git a/sound/soc/renesas/migor.c b/sound/soc/renesas/migor.c index 5a0bc6edac0a..45fc594d331b 100644 --- a/sound/soc/renesas/migor.c +++ b/sound/soc/renesas/migor.c @@ -132,7 +132,7 @@ static struct snd_soc_dai_link migor_dai = { .name = "wm8978", .stream_name = "WM8978", .dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &migor_dai_ops, SND_SOC_DAILINK_REG(wm8978), }; diff --git a/sound/soc/renesas/rcar/Makefile b/sound/soc/renesas/rcar/Makefile index 45eb875a912a..3a2c875595bd 100644 --- a/sound/soc/renesas/rcar/Makefile +++ b/sound/soc/renesas/rcar/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 snd-soc-rcar-y := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o debugfs.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o + +snd-soc-msiof-y := msiof.o +obj-$(CONFIG_SND_SOC_MSIOF) += snd-soc-msiof.o diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 191f212d338c..8641b73d1f77 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -19,6 +19,7 @@ #define CLKOUT3 3 #define CLKOUTMAX 4 +#define BRGCKR_31 (1 << 31) #define BRRx_MASK(x) (0x3FF & x) static struct rsnd_mod_ops adg_ops = { @@ -30,6 +31,7 @@ static struct rsnd_mod_ops adg_ops = { #define ADG_HZ_SIZE 2 struct rsnd_adg { + struct clk *adg; struct clk *clkin[CLKINMAX]; struct clk *clkout[CLKOUTMAX]; struct clk *null_clk; @@ -361,10 +363,13 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) rsnd_adg_set_ssi_clk(ssi_mod, data); + ckr = adg->ckr & ~BRGCKR_31; if (0 == (rate % 8000)) - ckr = 0x80000000; /* BRGB output = 48kHz */ - - rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); + ckr |= BRGCKR_31; /* use BRGB output = 48kHz */ + if (ckr != adg->ckr) { + rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr); + adg->ckr = ckr; + } dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", (ckr) ? 'B' : 'A', @@ -382,6 +387,10 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) int ret = 0, i; if (enable) { + ret = clk_prepare_enable(adg->adg); + if (ret < 0) + return ret; + rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr); rsnd_mod_write(adg_mod, BRRA, adg->brga); rsnd_mod_write(adg_mod, BRRB, adg->brgb); @@ -415,6 +424,10 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) if (ret < 0) rsnd_adg_clk_disable(priv); + /* disable adg */ + if (!enable) + clk_disable_unprepare(adg->adg); + return ret; } @@ -471,6 +484,16 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) clkin_size = ARRAY_SIZE(clkin_name_gen4); } + /* + * get adg + * No "adg" is not error + */ + clk = devm_clk_get(dev, "adg"); + if (IS_ERR_OR_NULL(clk)) + clk = rsnd_adg_null_clk_get(priv); + adg->adg = clk; + + /* get clkin */ for (i = 0; i < clkin_size; i++) { clk = devm_clk_get(dev, clkin_name[i]); @@ -683,6 +706,9 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) } rsnd_adg_get_clkout_end: + if (0 == (req_rate[0] % 8000)) + ckr |= BRGCKR_31; /* use BRGB output = 48kHz */ + adg->ckr = ckr; adg->brga = brga; adg->brgb = brgb; diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index d3709fd0409e..a72f36d3ca2c 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -597,7 +597,7 @@ int rsnd_dai_connect(struct rsnd_mod *mod, dev_dbg(dev, "%s is connected to io (%s)\n", rsnd_mod_name(mod), - rsnd_io_is_play(io) ? "Playback" : "Capture"); + snd_pcm_direction_name(io->substream->stream)); return 0; } @@ -1482,8 +1482,13 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) int dai_i; nr = rsnd_dai_of_node(priv, &is_graph); + + /* + * There is a case that it is used only for ADG (Sound Clock). + * No DAI is not error + */ if (!nr) - return -EINVAL; + return 0; rdrv = devm_kcalloc(dev, nr, sizeof(*rdrv), GFP_KERNEL); rdai = devm_kcalloc(dev, nr, sizeof(*rdai), GFP_KERNEL); @@ -1770,20 +1775,6 @@ 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); - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); - - if (!runtime) { - dev_warn(dev, "Can't update kctrl when idle\n"); - return 0; - } - - return 1; -} - struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg) { cfg->cfg.val = cfg->val; @@ -2073,7 +2064,7 @@ static void rsnd_remove(struct platform_device *pdev) remove_func[i](priv); } -static int __maybe_unused rsnd_suspend(struct device *dev) +static int rsnd_suspend(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); @@ -2082,7 +2073,7 @@ static int __maybe_unused rsnd_suspend(struct device *dev) return 0; } -static int __maybe_unused rsnd_resume(struct device *dev) +static int rsnd_resume(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); @@ -2090,13 +2081,13 @@ static int __maybe_unused rsnd_resume(struct device *dev) } static const struct dev_pm_ops rsnd_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume) + SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume) }; static struct platform_driver rsnd_driver = { .driver = { .name = "rcar_sound", - .pm = &rsnd_pm_ops, + .pm = pm_ptr(&rsnd_pm_ops), .of_match_table = rsnd_of_match, }, .probe = rsnd_probe, diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c new file mode 100644 index 000000000000..75c9e91bada1 --- /dev/null +++ b/sound/soc/renesas/rcar/msiof.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car MSIOF (Clock-Synchronized Serial Interface with FIFO) I2S driver +// +// Copyright (C) 2025 Renesas Solutions Corp. +// Author: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// + +/* + * [NOTE] + * + * This driver doesn't support Clock/Frame Provider Mode + * + * Basically MSIOF is created for SPI, but we can use it as I2S (Sound), etc. Because of it, when + * we use it as I2S (Sound) with Provider Mode, we need to send dummy TX data even though it was + * used for RX. Because SPI HW needs TX Clock/Frame output for RX purpose. + * But it makes driver code complex in I2S (Sound). + * + * And when we use it as I2S (Sound) as Provider Mode, the clock source is [MSO clock] (= 133.33MHz) + * SoC internal clock. It is not for 48kHz/44.1kHz base clock. Thus the output/input will not be + * accurate sound. + * + * Because of these reasons, this driver doesn't support Clock/Frame Provider Mode. Use it as + * Clock/Frame Consumer Mode. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <sound/dmaengine_pcm.h> +#include <sound/soc.h> + +/* register */ +#define SITMDR1 0x00 +#define SITMDR2 0x04 +#define SITMDR3 0x08 +#define SIRMDR1 0x10 +#define SIRMDR2 0x14 +#define SIRMDR3 0x18 +#define SICTR 0x28 +#define SISTR 0x40 +#define SIIER 0x44 +#define SITFDR 0x50 +#define SIRFDR 0x60 + +/* SITMDR1/ SIRMDR1 */ +#define PCON (1 << 30) /* Transfer Signal Connection */ +#define SYNCMD_LR (3 << 28) /* L/R mode */ +#define SYNCAC (1 << 25) /* Sync Polarity (Active-low) */ +#define DTDL_1 (1 << 20) /* 1-clock-cycle delay */ +#define TXSTP (1 << 0) /* Transmission/Reception Stop on FIFO */ + +/* SITMDR2 and SIRMDR2 */ +#define BITLEN1(x) (((x) - 1) << 24) /* Data Size (8-32 bits) */ +#define GRP (1 << 30) /* Group count */ + +/* SICTR */ +#define TEDG (1 << 27) /* Transmit Timing (1 = falling edge) */ +#define REDG (1 << 26) /* Receive Timing (1 = rising edge) */ +#define TXE (1 << 9) /* Transmit Enable */ +#define RXE (1 << 8) /* Receive Enable */ + +/* SISTR */ +#define TFSERR (1 << 21) /* Transmit Frame Synchronization Error */ +#define TFOVF (1 << 20) /* Transmit FIFO Overflow */ +#define TFUDF (1 << 19) /* Transmit FIFO Underflow */ +#define RFSERR (1 << 5) /* Receive Frame Synchronization Error */ +#define RFUDF (1 << 4) /* Receive FIFO Underflow */ +#define RFOVF (1 << 3) /* Receive FIFO Overflow */ +#define SISTR_ERR_TX (TFSERR | TFOVF | TFUDF) +#define SISTR_ERR_RX (RFSERR | RFOVF | RFUDF) +#define SISTR_ERR (SISTR_ERR_TX | SISTR_ERR_RX) + +/* SIIER */ +#define TDMAE (1 << 31) /* Transmit Data DMA Transfer Req. Enable */ +#define TDREQE (1 << 28) /* Transmit Data Transfer Request Enable */ +#define RDMAE (1 << 15) /* Receive Data DMA Transfer Req. Enable */ +#define RDREQE (1 << 12) /* Receive Data Transfer Request Enable */ + +/* + * The data on memory in 24bit case is located at <right> side + * [ xxxxxx] + * [ xxxxxx] + * [ xxxxxx] + * + * HW assuming signal in 24bit case is located at <left> side + * ---+ +---------+ + * +---------+ +---------+... + * [xxxxxx ][xxxxxx ][xxxxxx ] + * + * When we use 24bit data, it will be transferred via 32bit width via DMA, + * and MSIOF/DMA doesn't support data shift, we can't use 24bit data correctly. + * There is no such issue on 16/32bit data case. + */ +#define MSIOF_RATES SNDRV_PCM_RATE_8000_192000 +#define MSIOF_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +struct msiof_priv { + struct device *dev; + struct snd_pcm_substream *substream[SNDRV_PCM_STREAM_LAST + 1]; + spinlock_t lock; + void __iomem *base; + resource_size_t phy_addr; + + /* for error */ + int err_syc[SNDRV_PCM_STREAM_LAST + 1]; + int err_ovf[SNDRV_PCM_STREAM_LAST + 1]; + int err_udf[SNDRV_PCM_STREAM_LAST + 1]; + + /* bit field */ + u32 flags; +#define MSIOF_FLAGS_NEED_DELAY (1 << 0) +}; +#define msiof_flag_has(priv, flag) (priv->flags & flag) +#define msiof_flag_set(priv, flag) (priv->flags |= flag) + +#define msiof_is_play(substream) ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK) +#define msiof_read(priv, reg) ioread32((priv)->base + reg) +#define msiof_write(priv, reg, val) iowrite32(val, (priv)->base + reg) +#define msiof_status_clear(priv) msiof_write(priv, SISTR, SISTR_ERR) + +static void msiof_update(struct msiof_priv *priv, u32 reg, u32 mask, u32 val) +{ + u32 old = msiof_read(priv, reg); + u32 new = (old & ~mask) | (val & mask); + + if (old != new) + msiof_write(priv, reg, new); +} + +static void msiof_update_and_wait(struct msiof_priv *priv, u32 reg, u32 mask, u32 val, u32 expect) +{ + u32 data; + int ret; + + msiof_update(priv, reg, mask, val); + + ret = readl_poll_timeout_atomic(priv->base + reg, data, + (data & mask) == expect, 1, 128); + if (ret) + dev_warn(priv->dev, "write timeout [0x%02x] 0x%08x / 0x%08x\n", + reg, data, expect); +} + +static int msiof_hw_start(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct msiof_priv *priv = snd_soc_component_get_drvdata(component); + struct snd_pcm_runtime *runtime = substream->runtime; + int is_play = msiof_is_play(substream); + int width = snd_pcm_format_width(runtime->format); + u32 val; + + /* + * see + * [NOTE] on top of this driver + */ + /* + * see + * Datasheet 109.3.6 [Transmit and Receive Procedures] + * + * TX: Fig 109.14 - Fig 109.23 + * RX: Fig 109.15 + */ + + /* reset errors */ + priv->err_syc[substream->stream] = + priv->err_ovf[substream->stream] = + priv->err_udf[substream->stream] = 0; + + /* SITMDRx */ + if (is_play) { + val = PCON | SYNCMD_LR | SYNCAC | TXSTP; + if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) + val |= DTDL_1; + + msiof_write(priv, SITMDR1, val); + + val = BITLEN1(width); + msiof_write(priv, SITMDR2, val | GRP); + msiof_write(priv, SITMDR3, val); + + } + /* SIRMDRx */ + else { + val = SYNCMD_LR | SYNCAC; + if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) + val |= DTDL_1; + + msiof_write(priv, SIRMDR1, val); + + val = BITLEN1(width); + msiof_write(priv, SIRMDR2, val | GRP); + msiof_write(priv, SIRMDR3, val); + } + + /* SIIER */ + if (is_play) + val = TDREQE | TDMAE | SISTR_ERR_TX; + else + val = RDREQE | RDMAE | SISTR_ERR_RX; + msiof_update(priv, SIIER, val, val); + + /* SICTR */ + if (is_play) + val = TXE | TEDG; + else + val = RXE | REDG; + msiof_update_and_wait(priv, SICTR, val, val, val); + + msiof_status_clear(priv); + + /* Start DMAC */ + snd_dmaengine_pcm_trigger(substream, cmd); + + return 0; +} + +static int msiof_hw_stop(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct msiof_priv *priv = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + int is_play = msiof_is_play(substream); + u32 val; + + /* SIIER */ + if (is_play) + val = TDREQE | TDMAE | SISTR_ERR_TX; + else + val = RDREQE | RDMAE | SISTR_ERR_RX; + msiof_update(priv, SIIER, val, 0); + + /* Stop DMAC */ + snd_dmaengine_pcm_trigger(substream, cmd); + + /* SICTR */ + if (is_play) + val = TXE; + else + val = RXE; + msiof_update_and_wait(priv, SICTR, val, 0, 0); + + /* indicate error status if exist */ + if (priv->err_syc[substream->stream] || + priv->err_ovf[substream->stream] || + priv->err_udf[substream->stream]) + dev_warn(dev, "FSERR(%s) = %d, FOVF = %d, FUDF = %d\n", + snd_pcm_direction_name(substream->stream), + priv->err_syc[substream->stream], + priv->err_ovf[substream->stream], + priv->err_udf[substream->stream]); + + return 0; +} + +static int msiof_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct msiof_priv *priv = snd_soc_dai_get_drvdata(dai); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + /* + * It supports Clock/Frame Consumer Mode only + * see + * [NOTE] on top of this driver + */ + case SND_SOC_DAIFMT_BC_FC: + break; + /* others are error */ + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + /* it supports NB_NF only */ + case SND_SOC_DAIFMT_NB_NF: + default: + break; + /* others are error */ + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + msiof_flag_set(priv, MSIOF_FLAGS_NEED_DELAY); + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Select below from Sound Card, not auto + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ +static const u64 msiof_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF; + +static const struct snd_soc_dai_ops msiof_dai_ops = { + .set_fmt = msiof_dai_set_fmt, + .auto_selectable_formats = &msiof_dai_formats, + .num_auto_selectable_formats = 1, +}; + +static struct snd_soc_dai_driver msiof_dai_driver = { + .name = "msiof-dai", + .playback = { + .rates = MSIOF_RATES, + .formats = MSIOF_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = MSIOF_RATES, + .formats = MSIOF_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &msiof_dai_ops, +}; + +static struct snd_pcm_hardware msiof_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 64, +}; + +static int msiof_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct device *dev = component->dev; + struct dma_chan *chan; + static const char * const dma_names[] = {"rx", "tx"}; + int is_play = msiof_is_play(substream); + int ret; + + chan = of_dma_request_slave_channel(dev->of_node, dma_names[is_play]); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + ret = snd_dmaengine_pcm_open(substream, chan); + if (ret < 0) + goto open_err_dma; + + snd_soc_set_runtime_hwparams(substream, &msiof_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS); + +open_err_dma: + if (ret < 0) + dma_release_channel(chan); + + return ret; +} + +static int msiof_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_close_release_chan(substream); +} + +static snd_pcm_uframes_t msiof_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_pointer(substream); +} + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) +static int msiof_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + return 0; +} + +static int msiof_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct device *dev = component->dev; + struct msiof_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + priv->substream[substream->stream] = substream; + fallthrough; + case SNDRV_PCM_TRIGGER_RESUME: + ret = msiof_hw_start(component, substream, cmd); + break; + case SNDRV_PCM_TRIGGER_STOP: + priv->substream[substream->stream] = NULL; + fallthrough; + case SNDRV_PCM_TRIGGER_SUSPEND: + ret = msiof_hw_stop(component, substream, cmd); + break; + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int msiof_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct msiof_priv *priv = dev_get_drvdata(component->dev); + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct dma_slave_config cfg = {}; + unsigned long flags; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + + ret = snd_hwparams_to_dma_slave_config(substream, params, &cfg); + if (ret < 0) + goto hw_params_out; + + cfg.dst_addr = priv->phy_addr + SITFDR; + cfg.src_addr = priv->phy_addr + SIRFDR; + + ret = dmaengine_slave_config(chan, &cfg); +hw_params_out: + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static const struct snd_soc_component_driver msiof_component_driver = { + .name = "msiof", + .open = msiof_open, + .close = msiof_close, + .pointer = msiof_pointer, + .pcm_construct = msiof_new, + .trigger = msiof_trigger, + .hw_params = msiof_hw_params, +}; + +static irqreturn_t msiof_interrupt(int irq, void *data) +{ + struct msiof_priv *priv = data; + struct snd_pcm_substream *substream; + u32 sistr; + + spin_lock(&priv->lock); + + sistr = msiof_read(priv, SISTR); + msiof_status_clear(priv); + + spin_unlock(&priv->lock); + + /* overflow/underflow error */ + substream = priv->substream[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream && (sistr & SISTR_ERR_TX)) { + // snd_pcm_stop_xrun(substream); + if (sistr & TFSERR) + priv->err_syc[SNDRV_PCM_STREAM_PLAYBACK]++; + if (sistr & TFOVF) + priv->err_ovf[SNDRV_PCM_STREAM_PLAYBACK]++; + if (sistr & TFUDF) + priv->err_udf[SNDRV_PCM_STREAM_PLAYBACK]++; + } + + substream = priv->substream[SNDRV_PCM_STREAM_CAPTURE]; + if (substream && (sistr & SISTR_ERR_RX)) { + // snd_pcm_stop_xrun(substream); + if (sistr & RFSERR) + priv->err_syc[SNDRV_PCM_STREAM_CAPTURE]++; + if (sistr & RFOVF) + priv->err_ovf[SNDRV_PCM_STREAM_CAPTURE]++; + if (sistr & RFUDF) + priv->err_udf[SNDRV_PCM_STREAM_CAPTURE]++; + } + + return IRQ_HANDLED; +} + +static int msiof_probe(struct platform_device *pdev) +{ + struct msiof_priv *priv; + struct device *dev = &pdev->dev; + struct resource *res; + int irq, ret; + + /* Check MSIOF as Sound mode or SPI mode */ + struct device_node *port __free(device_node) = of_graph_get_next_port(dev->of_node, NULL); + if (!port) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_request_irq(dev, irq, msiof_interrupt, 0, dev_name(dev), priv); + if (ret) + return ret; + + priv->dev = dev; + priv->phy_addr = res->start; + + spin_lock_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + devm_pm_runtime_enable(dev); + + ret = devm_snd_soc_register_component(dev, &msiof_component_driver, + &msiof_dai_driver, 1); + + return ret; +} + +static const struct of_device_id msiof_of_match[] = { + { .compatible = "renesas,rcar-gen4-msiof", }, + {}, +}; +MODULE_DEVICE_TABLE(of, msiof_of_match); + +static struct platform_driver msiof_driver = { + .driver = { + .name = "msiof-pcm-audio", + .of_match_table = msiof_of_match, + }, + .probe = msiof_probe, +}; +module_platform_driver(msiof_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car MSIOF I2S audio driver"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index a5f54b65313c..04c70690f7a2 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -742,7 +742,6 @@ struct rsnd_kctrl_cfg_s { #define rsnd_kctrl_vals(x) ((x).val) /* = (x).cfg.val[0] */ 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, diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c index e7f86db0d94c..7d73b183bda6 100644 --- a/sound/soc/renesas/rcar/src.c +++ b/sound/soc/renesas/rcar/src.c @@ -35,6 +35,7 @@ struct rsnd_src { struct rsnd_mod *dma; struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ + u32 current_sync_rate; int irq; }; @@ -100,7 +101,7 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, if (!rsnd_src_sync_is_enabled(mod)) return rsnd_io_converted_rate(io); - convert_rate = src->sync.val; + convert_rate = src->current_sync_rate; if (!convert_rate) convert_rate = rsnd_io_converted_rate(io); @@ -201,13 +202,73 @@ static const u32 chan222222[] = { static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 fin, fout, new_rate; + int inc, cnt, rate; + u64 base, val; + + if (!runtime) + return; + + if (!rsnd_src_sync_is_enabled(mod)) + return; + + fin = rsnd_src_get_in_rate(priv, io); + fout = rsnd_src_get_out_rate(priv, io); + + new_rate = src->sync.val; + + if (!new_rate) + new_rate = fout; + + /* Do nothing if no diff */ + if (new_rate == src->current_sync_rate) + return; + + /* + * SRCm_IFSVR::INTIFS can change within 1% + * see + * SRCm_IFSVR::INTIFS Note + */ + inc = fout / 100; + cnt = abs(new_rate - fout) / inc; + if (fout > new_rate) + inc *= -1; + + /* + * After start running SRC, we can update only SRC_IFSVR + * for Synchronous Mode + */ + base = (u64)0x0400000 * fin; + rate = fout; + for (int i = 0; i < cnt; i++) { + val = base; + rate += inc; + do_div(val, rate); + + rsnd_mod_write(mod, SRC_IFSVR, val); + } + val = base; + do_div(val, new_rate); + + rsnd_mod_write(mod, SRC_IFSVR, val); + + /* update current_sync_rate */ + src->current_sync_rate = new_rate; +} + +static void rsnd_src_init_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); int is_play = rsnd_io_is_play(io); int use_src = 0; u32 fin, fout; - u32 ifscr, fsrate, adinr; + u32 ifscr, adinr; u32 cr, route; u32 i_busif, o_busif, tmp; const u32 *bsdsr_table; @@ -245,26 +306,15 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, adinr = rsnd_get_adinr_bit(mod, io) | chan; /* - * SRC_IFSCR / SRC_IFSVR - */ - ifscr = 0; - fsrate = 0; - if (use_src) { - u64 n; - - ifscr = 1; - n = (u64)0x0400000 * fin; - do_div(n, fout); - fsrate = n; - } - - /* + * SRC_IFSCR * SRC_SRCCR / SRC_ROUTE_MODE0 */ + ifscr = 0; cr = 0x00011110; route = 0x0; if (use_src) { route = 0x1; + ifscr = 0x1; if (rsnd_src_sync_is_enabled(mod)) { cr |= 0x1; @@ -335,7 +385,6 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ rsnd_mod_write(mod, SRC_ADINR, adinr); rsnd_mod_write(mod, SRC_IFSCR, ifscr); - rsnd_mod_write(mod, SRC_IFSVR, fsrate); rsnd_mod_write(mod, SRC_SRCCR, cr); rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); @@ -348,6 +397,9 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); + /* update SRC_IFSVR */ + rsnd_src_set_convert_rate(io, mod); + return; convert_rate_err: @@ -467,7 +519,8 @@ static int rsnd_src_init(struct rsnd_mod *mod, int ret; /* reset sync convert_rate */ - src->sync.val = 0; + src->sync.val = + src->current_sync_rate = 0; ret = rsnd_mod_power_on(mod); if (ret < 0) @@ -475,7 +528,7 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_activation(mod); - rsnd_src_set_convert_rate(io, mod); + rsnd_src_init_convert_rate(io, mod); rsnd_src_status_clear(mod); @@ -493,7 +546,8 @@ static int rsnd_src_quit(struct rsnd_mod *mod, rsnd_mod_power_off(mod); /* reset sync convert_rate */ - src->sync.val = 0; + src->sync.val = + src->current_sync_rate = 0; return 0; } @@ -531,6 +585,22 @@ static irqreturn_t rsnd_src_interrupt(int irq, void *data) return IRQ_HANDLED; } +static int rsnd_src_kctrl_accept_runtime(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + if (!runtime) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "\"SRC Out Rate\" can use during running\n"); + + return 0; + } + + return 1; +} + static int rsnd_src_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -585,7 +655,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, "SRC Out Rate Switch" : "SRC In Rate Switch", rsnd_kctrl_accept_anytime, - rsnd_src_set_convert_rate, + rsnd_src_init_convert_rate, &src->sen, 1); if (ret < 0) return ret; @@ -594,7 +664,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_kctrl_accept_runtime, rsnd_src_set_convert_rate, &src->sync, 192000); diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c index b3d4e8ae07ef..0c6424a1fcac 100644 --- a/sound/soc/renesas/rcar/ssi.c +++ b/sound/soc/renesas/rcar/ssi.c @@ -336,7 +336,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, return 0; rate_err: - dev_err(dev, "unsupported clock rate\n"); + dev_err(dev, "unsupported clock rate (%d)\n", rate); + return ret; } diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 3a0af4ca7ab6..0f7458a43901 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -1244,7 +1244,7 @@ static int rz_ssi_runtime_resume(struct device *dev) static const struct dev_pm_ops rz_ssi_pm_ops = { RUNTIME_PM_OPS(rz_ssi_runtime_suspend, rz_ssi_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver rz_ssi_driver = { |