Message ID | 20210828140205.21973-2-frattaroli.nicolas@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Rockchip I2S/TDM controller | expand |
Hi Nicolas, On 2021/8/28 22:02, Nicolas Frattaroli wrote: > This commit adds support for the rockchip i2s-tdm controller, > which enables audio output on the following rockchip SoCs: > - px30 > - rk1808 > - rk3308 > - rk3566 > - rk3568 > - rv1126 > > This is a cleaned up version of the downstream vendor kernel's > driver. It can be enabled through the SND_SOC_ROCKCHIP_I2S_TDM > configuration option. > > Signed-off-by: Nicolas Frattaroli <frattaroli.nicolas@gmail.com> > --- > MAINTAINERS | 6 + > sound/soc/rockchip/Kconfig | 11 + > sound/soc/rockchip/Makefile | 2 + > sound/soc/rockchip/rockchip_i2s_tdm.c | 1845 +++++++++++++++++++++++++ > sound/soc/rockchip/rockchip_i2s_tdm.h | 398 ++++++ > 5 files changed, 2262 insertions(+) > create mode 100644 sound/soc/rockchip/rockchip_i2s_tdm.c > create mode 100644 sound/soc/rockchip/rockchip_i2s_tdm.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 3f1843204567..5d459d42672c 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -16049,6 +16049,12 @@ F: Documentation/ABI/*/sysfs-driver-hid-roccat* > F: drivers/hid/hid-roccat* > F: include/linux/hid-roccat* > > +ROCKCHIP I2S TDM DRIVER > +M: Nicolas Frattaroli <frattaroli.nicolas@gmail.com> > +L: linux-rockchip@lists.infradead.org > +S: Maintained > +F: sound/soc/rockchip/rockchip_i2s_tdm.* > + > ROCKCHIP ISP V1 DRIVER > M: Helen Koike <helen.koike@collabora.com> > M: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> > diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig > index 053097b73e28..42f76bc0fb02 100644 > --- a/sound/soc/rockchip/Kconfig > +++ b/sound/soc/rockchip/Kconfig > @@ -16,6 +16,17 @@ config SND_SOC_ROCKCHIP_I2S > Rockchip I2S device. The device supports upto maximum of > 8 channels each for play and record. > > +config SND_SOC_ROCKCHIP_I2S_TDM > + tristate "Rockchip I2S/TDM Device Driver" > + depends on HAVE_CLK && SND_SOC_ROCKCHIP > + select SND_SOC_GENERIC_DMAENGINE_PCM > + help > + Say Y or M if you want to add support for the I2S/TDM driver for > + Rockchip I2S/TDM devices, found in Rockchip SoCs. These devices > + interface between the AHB bus and the I2S bus, and support up to a > + maximum of 8 channels each for playback and recording. > + > + > config SND_SOC_ROCKCHIP_PDM > tristate "Rockchip PDM Controller Driver" > depends on HAVE_CLK && SND_SOC_ROCKCHIP > diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile > index 65e814d46006..b10f5e7b136d 100644 > --- a/sound/soc/rockchip/Makefile > +++ b/sound/soc/rockchip/Makefile > @@ -1,11 +1,13 @@ > # SPDX-License-Identifier: GPL-2.0 > # ROCKCHIP Platform Support > snd-soc-rockchip-i2s-objs := rockchip_i2s.o > +snd-soc-rockchip-i2s-tdm-objs := rockchip_i2s_tdm.o > snd-soc-rockchip-pcm-objs := rockchip_pcm.o > snd-soc-rockchip-pdm-objs := rockchip_pdm.o > snd-soc-rockchip-spdif-objs := rockchip_spdif.o > > obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o > +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o > obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o > obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o > > diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c > new file mode 100644 > index 000000000000..f31a987feecf > --- /dev/null > +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c > @@ -0,0 +1,1845 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver > + * > + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. > + * Author: Sugar Zhang <sugar.zhang@rock-chips.com> > + * Author: Nicolas Frattaroli <frattaroli.nicolas@gmail.com> > + * > + */ > + > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/delay.h> > +#include <linux/mfd/syscon.h> > +#include <linux/module.h> > +#include <linux/of_address.h> > +#include <linux/of_device.h> > +#include <linux/of_gpio.h> > +#include <linux/pm_runtime.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > +#include <linux/spinlock.h> > +#include <sound/dmaengine_pcm.h> > +#include <sound/pcm_params.h> > + > +#include "rockchip_i2s_tdm.h" > + > +#define DRV_NAME "rockchip-i2s-tdm" > + > +#define DEFAULT_MCLK_FS 256 > +#define CH_GRP_MAX 4 /* The max channel 8 / 2 */ > +#define MULTIPLEX_CH_MAX 10 > +#define CLK_PPM_MIN (-1000) > +#define CLK_PPM_MAX (1000) > + > +#define TRCM_TXRX 0 > +#define TRCM_TX 1 > +#define TRCM_RX 2 > + > +struct txrx_config { > + u32 addr; > + u32 reg; > + u32 txonly; > + u32 rxonly; > +}; > + > +struct rk_i2s_soc_data { > + u32 softrst_offset; > + u32 grf_reg_offset; > + u32 grf_shift; > + int config_count; > + const struct txrx_config *configs; > + int (*init)(struct device *dev, u32 addr); > +}; > + > +struct rk_i2s_tdm_dev { > + struct device *dev; > + struct clk *hclk; > + struct clk *mclk_tx; > + struct clk *mclk_rx; > + /* The mclk_tx_src is parent of mclk_tx */ > + struct clk *mclk_tx_src; > + /* The mclk_rx_src is parent of mclk_rx */ > + struct clk *mclk_rx_src; > + /* > + * The mclk_root0 and mclk_root1 are root parent and supplies for > + * the different FS. > + * > + * e.g: > + * mclk_root0 is VPLL0, used for FS=48000Hz > + * mclk_root1 is VPLL1, used for FS=44100Hz > + */ > + struct clk *mclk_root0; > + struct clk *mclk_root1; > + struct regmap *regmap; > + struct regmap *grf; > + struct snd_dmaengine_dai_dma_data capture_dma_data; > + struct snd_dmaengine_dai_dma_data playback_dma_data; > + struct reset_control *tx_reset; > + struct reset_control *rx_reset; > + struct rk_i2s_soc_data *soc_data; > + void __iomem *cru_base; > + bool is_master_mode; > + bool io_multiplex; > + bool mclk_calibrate; > + bool tdm_mode; > + bool tdm_fsync_half_frame; > + unsigned int mclk_rx_freq; > + unsigned int mclk_tx_freq; > + unsigned int mclk_root0_freq; > + unsigned int mclk_root1_freq; > + unsigned int mclk_root0_initial_freq; > + unsigned int mclk_root1_initial_freq; > + unsigned int frame_width; > + unsigned int clk_trcm; > + unsigned int i2s_sdis[CH_GRP_MAX]; > + unsigned int i2s_sdos[CH_GRP_MAX]; > + int clk_ppm; > + int tx_reset_id; > + int rx_reset_id; > + atomic_t refcount; > + spinlock_t lock; /* xfer lock */ > + bool has_playback; > + bool has_capture; > +}; > + > +static int to_ch_num(unsigned int val) > +{ > + switch (val) { > + case I2S_CHN_4: > + return 4; > + case I2S_CHN_6: > + return 6; > + case I2S_CHN_8: > + return 8; > + default: > + return 2; > + } > +} > + > +static void i2s_tdm_disable_unprepare_mclk(struct rk_i2s_tdm_dev *i2s_tdm) > +{ > + if (!IS_ERR(i2s_tdm->mclk_tx)) > + clk_disable_unprepare(i2s_tdm->mclk_tx); > + if (!IS_ERR(i2s_tdm->mclk_rx)) > + clk_disable_unprepare(i2s_tdm->mclk_rx); > + if (i2s_tdm->mclk_calibrate) { > + if (!IS_ERR(i2s_tdm->mclk_tx_src)) > + clk_disable_unprepare(i2s_tdm->mclk_tx_src); > + if (!IS_ERR(i2s_tdm->mclk_rx_src)) > + clk_disable_unprepare(i2s_tdm->mclk_rx_src); > + if (!IS_ERR(i2s_tdm->mclk_root0)) > + clk_disable_unprepare(i2s_tdm->mclk_root0); > + if (!IS_ERR(i2s_tdm->mclk_root1)) > + clk_disable_unprepare(i2s_tdm->mclk_root1); > + } > +} > + Maybe we can drop 'IS_ERR(i2s_tdm->mclk_xx)', because it has been done by clk_disable_unprepare. > +/** > + * i2s_tdm_prepare_enable_mclk - prepare to enable all mclks, disable them on > + * failure. > + * @i2s_tdm: rk_i2s_tdm_dev struct > + * > + * This function attempts to enable all mclk clocks, but cleans up after > + * itself on failure. Guarantees to balance its calls. > + * > + * Returns success (0) or negative errno. > + */ > +static int i2s_tdm_prepare_enable_mclk(struct rk_i2s_tdm_dev *i2s_tdm) > +{ > + int ret = 0; > + > + if (!IS_ERR(i2s_tdm->mclk_tx)) { > + ret = clk_prepare_enable(i2s_tdm->mclk_tx); > + if (ret) > + goto err_mclk_tx; > + } > + if (!IS_ERR(i2s_tdm->mclk_rx)) { > + ret = clk_prepare_enable(i2s_tdm->mclk_rx); > + if (ret) > + goto err_mclk_rx; > + } > + if (i2s_tdm->mclk_calibrate) { > + if (!IS_ERR(i2s_tdm->mclk_tx_src)) { > + ret = clk_prepare_enable(i2s_tdm->mclk_tx_src); > + if (ret) > + goto err_mclk_rx; > + } > + if (!IS_ERR(i2s_tdm->mclk_rx_src)) { > + ret = clk_prepare_enable(i2s_tdm->mclk_rx_src); > + if (ret) > + goto err_mclk_rx_src; > + } > + if (!IS_ERR(i2s_tdm->mclk_root0)) { > + ret = clk_prepare_enable(i2s_tdm->mclk_root0); > + if (ret) > + goto err_mclk_root0; > + } > + if (!IS_ERR(i2s_tdm->mclk_root1)) { > + ret = clk_prepare_enable(i2s_tdm->mclk_root1); > + if (ret) > + goto err_mclk_root1; > + } > + } > + > + return 0; > + Maybe we can drop 'IS_ERR(i2s_tdm->mclk_xx)', because it has been done in function probe. > +err_mclk_root1: > + clk_disable_unprepare(i2s_tdm->mclk_root0); > +err_mclk_root0: > + clk_disable_unprepare(i2s_tdm->mclk_rx_src); > +err_mclk_rx_src: > + clk_disable_unprepare(i2s_tdm->mclk_tx_src); > +err_mclk_rx: > + clk_disable_unprepare(i2s_tdm->mclk_tx); > +err_mclk_tx: > + return ret; > +} > + > +static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); > + > + regcache_cache_only(i2s_tdm->regmap, true); > + i2s_tdm_disable_unprepare_mclk(i2s_tdm); > + > + if (!IS_ERR(i2s_tdm->hclk)) > + clk_disable_unprepare(i2s_tdm->hclk); > + > + return 0; > +} > + > +static int __maybe_unused i2s_tdm_runtime_resume(struct device *dev) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); > + int ret; > + > + if (!IS_ERR(i2s_tdm->hclk)) { > + ret = clk_prepare_enable(i2s_tdm->hclk); > + if (ret) > + goto err_hclk; > + } > + > + ret = i2s_tdm_prepare_enable_mclk(i2s_tdm); > + if (ret) > + goto err_mclk; > + > + regcache_cache_only(i2s_tdm->regmap, false); > + regcache_mark_dirty(i2s_tdm->regmap); > + > + ret = regcache_sync(i2s_tdm->regmap); > + if (ret) > + goto err_regcache; > + > + return 0; > + > +err_regcache: > + i2s_tdm_disable_unprepare_mclk(i2s_tdm); > +err_mclk: > + if (!IS_ERR(i2s_tdm->hclk)) > + clk_disable_unprepare(i2s_tdm->hclk); > +err_hclk: > + return ret; > +} > + > +static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai) > +{ > + return snd_soc_dai_get_drvdata(dai); > +} > + > +static void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm, > + int tx_bank, int tx_offset, > + int rx_bank, int rx_offset) > +{ > + void __iomem *cru_reset; > + unsigned long flags; > + > + cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; > + > + if (tx_bank == rx_bank) { > + writel(BIT(tx_offset) | BIT(rx_offset) | > + (BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), > + cru_reset + (tx_bank * 4)); > + } else { > + local_irq_save(flags); > + writel(BIT(tx_offset) | (BIT(tx_offset) << 16), > + cru_reset + (tx_bank * 4)); > + writel(BIT(rx_offset) | (BIT(rx_offset) << 16), > + cru_reset + (rx_bank * 4)); > + local_irq_restore(flags); > + } > +} > + > +static void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm, > + int tx_bank, int tx_offset, > + int rx_bank, int rx_offset) > +{ > + void __iomem *cru_reset; > + unsigned long flags; > + > + cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; > + > + if (tx_bank == rx_bank) { > + writel((BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), > + cru_reset + (tx_bank * 4)); > + } else { > + local_irq_save(flags); > + writel((BIT(tx_offset) << 16), > + cru_reset + (tx_bank * 4)); > + writel((BIT(rx_offset) << 16), > + cru_reset + (rx_bank * 4)); > + local_irq_restore(flags); > + } > +} > + > +/* > + * Makes sure that both tx and rx are reset at the same time to sync lrck > + * when clk_trcm > 0. > + */ > +static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm) > +{ > + int tx_id, rx_id; > + int tx_bank, rx_bank, tx_offset, rx_offset; > + > + if (!i2s_tdm->cru_base || !i2s_tdm->soc_data) > + return; > + > + tx_id = i2s_tdm->tx_reset_id; > + rx_id = i2s_tdm->rx_reset_id; > + if (tx_id < 0 || rx_id < 0) > + return; > + > + tx_bank = tx_id / 16; > + tx_offset = tx_id % 16; > + rx_bank = rx_id / 16; > + rx_offset = rx_id % 16; > + dev_dbg(i2s_tdm->dev, > + "tx_bank: %d, rx_bank: %d, tx_offset: %d, rx_offset: %d\n", > + tx_bank, rx_bank, tx_offset, rx_offset); > + > + rockchip_snd_xfer_reset_assert(i2s_tdm, tx_bank, tx_offset, > + rx_bank, rx_offset); > + > + udelay(10); > + > + rockchip_snd_xfer_reset_deassert(i2s_tdm, tx_bank, tx_offset, > + rx_bank, rx_offset); > + udelay(10); > +} > + > +static void rockchip_snd_reset(struct reset_control *rc) > +{ > + reset_control_assert(rc); > + udelay(10); > + reset_control_deassert(rc); > + udelay(10); > +} > + > +static void rockchip_snd_xfer_clear(struct rk_i2s_tdm_dev *i2s_tdm, > + unsigned int clr) > +{ > + unsigned int xfer_mask; > + unsigned int xfer_val; > + unsigned int val; > + int retry = 10; > + bool tx = clr & I2S_CLR_TXC; > + bool rx = clr & I2S_CLR_RXC; > + > + if (!(rx || tx)) > + return; > + > + xfer_mask = (tx ? I2S_XFER_TXS_START : 0) | > + (rx ? I2S_XFER_RXS_START : 0); > + xfer_val = (tx ? I2S_XFER_TXS_STOP : 0) | > + (rx ? I2S_XFER_RXS_STOP : 0); > + > + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, xfer_mask, xfer_val); > + udelay(150); > + regmap_update_bits(i2s_tdm->regmap, I2S_CLR, clr, clr); > + > + regmap_read(i2s_tdm->regmap, I2S_CLR, &val); > + /* Wait on the clear operation to finish */ > + while (val) { > + udelay(15); > + regmap_read(i2s_tdm->regmap, I2S_CLR, &val); > + retry--; > + if (!retry) { > + dev_warn(i2s_tdm->dev, "clear failed, reset %s%s\n", > + tx ? "tx" : "", rx ? "rx" : ""); > + if (rx && tx) > + rockchip_snd_xfer_sync_reset(i2s_tdm); > + else if (tx) > + rockchip_snd_reset(i2s_tdm->tx_reset); > + else if (rx) > + rockchip_snd_reset(i2s_tdm->rx_reset); > + break; > + } > + } > +} > + > +/* only used when clk_trcm > 0 */ > +static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream, > + struct snd_soc_dai *dai, int on) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); > + > + spin_lock(&i2s_tdm->lock); should be spin_lock_irqsave,or maybe a potential deadlock as follows: CPU0 CPU1 ---- ---- lock(&(&i2s_tdm->lock)->rlock); local_irq_disable(); lock(&(&substream->self_group.lock)->rlock); lock(&(&i2s_tdm->lock)->rlock); <Interrupt> lock(&(&substream->self_group.lock)->rlock); > + if (on) { > + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_TDE_ENABLE, > + I2S_DMACR_TDE_ENABLE); > + else > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_RDE_ENABLE, > + I2S_DMACR_RDE_ENABLE); > + > + if (atomic_inc_return(&i2s_tdm->refcount) == 1) { > + rockchip_snd_xfer_sync_reset(i2s_tdm); > + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, > + I2S_XFER_TXS_START | > + I2S_XFER_RXS_START, > + I2S_XFER_TXS_START | > + I2S_XFER_RXS_START); > + } > + } else { > + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_TDE_ENABLE, > + I2S_DMACR_TDE_DISABLE); > + else > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_RDE_ENABLE, > + I2S_DMACR_RDE_DISABLE); > + > + if (atomic_dec_and_test(&i2s_tdm->refcount)) { > + rockchip_snd_xfer_clear(i2s_tdm, > + I2S_CLR_TXC | I2S_CLR_RXC); > + } > + } > + spin_unlock(&i2s_tdm->lock); spin_unlock_irqrestore > +} > + > +static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) > +{ > + if (on) { > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); > + > + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, > + I2S_XFER_TXS_START, > + I2S_XFER_TXS_START); > + } else { > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); > + > + rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC); > + } > +} > + > +static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) > +{ > + if (on) { > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); > + > + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, > + I2S_XFER_RXS_START, > + I2S_XFER_RXS_START); > + } else { > + rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_RXC); > + } > +} > + > +static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, > + unsigned int fmt) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); > + unsigned int mask, val, tdm_val, txcr_val, rxcr_val; > + int ret; > + bool is_tdm = i2s_tdm->tdm_mode; > + > + ret = pm_runtime_get_sync(cpu_dai->dev); > + if (ret < 0 && ret != -EACCES) { > + pm_runtime_put_noidle(cpu_dai->dev); > + return ret; > + } > + > + mask = I2S_CKR_MSS_MASK; > + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { > + case SND_SOC_DAIFMT_CBS_CFS: > + val = I2S_CKR_MSS_MASTER; > + i2s_tdm->is_master_mode = true; > + break; > + case SND_SOC_DAIFMT_CBM_CFM: > + val = I2S_CKR_MSS_SLAVE; > + i2s_tdm->is_master_mode = false; > + break; > + default: > + ret = -EINVAL; > + goto err_pm_put; > + } > + > + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val); > + > + mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK; > + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { > + case SND_SOC_DAIFMT_NB_NF: > + val = I2S_CKR_CKP_NORMAL | > + I2S_CKR_TLP_NORMAL | > + I2S_CKR_RLP_NORMAL; > + break; > + case SND_SOC_DAIFMT_NB_IF: > + val = I2S_CKR_CKP_NORMAL | > + I2S_CKR_TLP_INVERTED | > + I2S_CKR_RLP_INVERTED; > + break; > + case SND_SOC_DAIFMT_IB_NF: > + val = I2S_CKR_CKP_INVERTED | > + I2S_CKR_TLP_NORMAL | > + I2S_CKR_RLP_NORMAL; > + break; > + case SND_SOC_DAIFMT_IB_IF: > + val = I2S_CKR_CKP_INVERTED | > + I2S_CKR_TLP_INVERTED | > + I2S_CKR_RLP_INVERTED; > + break; > + default: > + ret = -EINVAL; > + goto err_pm_put; > + } > + > + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val); > + > + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { > + case SND_SOC_DAIFMT_RIGHT_J: > + txcr_val = I2S_TXCR_IBM_RSJM; > + rxcr_val = I2S_RXCR_IBM_RSJM; > + break; > + case SND_SOC_DAIFMT_LEFT_J: > + txcr_val = I2S_TXCR_IBM_LSJM; > + rxcr_val = I2S_RXCR_IBM_LSJM; > + break; > + case SND_SOC_DAIFMT_I2S: > + txcr_val = I2S_TXCR_IBM_NORMAL; > + rxcr_val = I2S_RXCR_IBM_NORMAL; > + break; > + case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ > + txcr_val = I2S_TXCR_TFS_PCM; > + rxcr_val = I2S_RXCR_TFS_PCM; > + break; > + case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ > + txcr_val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1); > + rxcr_val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1); > + break; > + default: > + ret = -EINVAL; > + goto err_pm_put; > + } > + > + mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK; > + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, txcr_val); > + > + mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK; > + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, rxcr_val); > + > + if (is_tdm) { > + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { > + case SND_SOC_DAIFMT_RIGHT_J: > + val = I2S_TXCR_TFS_TDM_I2S; > + tdm_val = TDM_SHIFT_CTRL(2); > + break; > + case SND_SOC_DAIFMT_LEFT_J: > + val = I2S_TXCR_TFS_TDM_I2S; > + tdm_val = TDM_SHIFT_CTRL(1); > + break; > + case SND_SOC_DAIFMT_I2S: > + val = I2S_TXCR_TFS_TDM_I2S; > + tdm_val = TDM_SHIFT_CTRL(0); > + break; > + case SND_SOC_DAIFMT_DSP_A: > + val = I2S_TXCR_TFS_TDM_PCM; > + tdm_val = TDM_SHIFT_CTRL(0); > + break; > + case SND_SOC_DAIFMT_DSP_B: > + val = I2S_TXCR_TFS_TDM_PCM; > + tdm_val = TDM_SHIFT_CTRL(2); > + break; > + default: > + ret = -EINVAL; > + goto err_pm_put; > + } > + > + tdm_val |= TDM_FSYNC_WIDTH_SEL1(1); > + if (i2s_tdm->tdm_fsync_half_frame) > + tdm_val |= TDM_FSYNC_WIDTH_HALF_FRAME; > + else > + tdm_val |= TDM_FSYNC_WIDTH_ONE_FRAME; > + > + mask = I2S_TXCR_TFS_MASK; > + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, val); > + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val); > + > + mask = TDM_FSYNC_WIDTH_SEL1_MSK | TDM_FSYNC_WIDTH_SEL0_MSK | > + TDM_SHIFT_CTRL_MSK; > + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR, > + mask, tdm_val); > + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR, > + mask, tdm_val); > + } > + > +err_pm_put: > + pm_runtime_put(cpu_dai->dev); > + > + return ret; > +} > + > +static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream, > + struct rk_i2s_tdm_dev *i2s_tdm) > +{ > + int stream; > + > + stream = SNDRV_PCM_STREAM_LAST - substream->stream; > + if (stream == SNDRV_PCM_STREAM_PLAYBACK) > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_TDE_ENABLE, > + I2S_DMACR_TDE_DISABLE); > + else > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_RDE_ENABLE, > + I2S_DMACR_RDE_DISABLE); > + > + rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC | I2S_CLR_RXC); > +} > + > +static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, > + struct rk_i2s_tdm_dev *i2s_tdm) > +{ > + int stream; > + > + stream = SNDRV_PCM_STREAM_LAST - substream->stream; > + if (stream == SNDRV_PCM_STREAM_PLAYBACK) > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_TDE_ENABLE, > + I2S_DMACR_TDE_ENABLE); > + else > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, > + I2S_DMACR_RDE_ENABLE, > + I2S_DMACR_RDE_ENABLE); > + > + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, > + I2S_XFER_TXS_START | > + I2S_XFER_RXS_START, > + I2S_XFER_TXS_START | > + I2S_XFER_RXS_START); > +} > + > +static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm, > + struct clk *clk, unsigned long rate, > + int ppm) > +{ > + unsigned long rate_target; > + int delta, ret; > + > + if (ppm == i2s_tdm->clk_ppm) > + return 0; > + > + delta = (ppm < 0) ? -1 : 1; > + delta *= (int)div64_u64((u64)rate * (u64)abs(ppm) + 500000, > + 1000000); > + > + rate_target = rate + delta; > + > + if (!rate_target) > + return -EINVAL; > + > + ret = clk_set_rate(clk, rate_target); > + if (ret) > + return ret; > + > + i2s_tdm->clk_ppm = ppm; > + > + return ret; > +} > + > +static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, > + struct snd_pcm_substream *substream, > + unsigned int lrck_freq) > +{ > + struct clk *mclk_root; > + struct clk *mclk_parent; > + unsigned int mclk_root_freq; > + unsigned int mclk_root_initial_freq; > + unsigned int mclk_parent_freq; > + unsigned int div, delta; > + u64 ppm; > + int ret; > + > + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) > + mclk_parent = i2s_tdm->mclk_tx_src; > + else > + mclk_parent = i2s_tdm->mclk_rx_src; > + > + switch (lrck_freq) { > + case 8000: > + case 16000: > + case 24000: > + case 32000: > + case 48000: > + case 64000: > + case 96000: > + case 192000: > + mclk_root = i2s_tdm->mclk_root0; > + mclk_root_freq = i2s_tdm->mclk_root0_freq; > + mclk_root_initial_freq = i2s_tdm->mclk_root0_initial_freq; > + mclk_parent_freq = DEFAULT_MCLK_FS * 192000; > + break; > + case 11025: > + case 22050: > + case 44100: > + case 88200: > + case 176400: > + mclk_root = i2s_tdm->mclk_root1; > + mclk_root_freq = i2s_tdm->mclk_root1_freq; > + mclk_root_initial_freq = i2s_tdm->mclk_root1_initial_freq; > + mclk_parent_freq = DEFAULT_MCLK_FS * 176400; > + break; > + default: > + dev_err(i2s_tdm->dev, "Invalid LRCK freq: %u Hz\n", > + lrck_freq); > + return -EINVAL; > + } > + > + ret = clk_set_parent(mclk_parent, mclk_root); > + if (ret) > + return ret; > + > + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, mclk_root, > + mclk_root_freq, 0); > + if (ret) > + return ret; > + > + delta = abs(mclk_root_freq % mclk_parent_freq - mclk_parent_freq); > + ppm = div64_u64((uint64_t)delta * 1000000, (uint64_t)mclk_root_freq); > + > + if (ppm) { > + div = DIV_ROUND_CLOSEST(mclk_root_initial_freq, mclk_parent_freq); > + if (!div) > + return -EINVAL; > + > + mclk_root_freq = mclk_parent_freq * round_up(div, 2); > + > + ret = clk_set_rate(mclk_root, mclk_root_freq); > + if (ret) > + return ret; > + > + i2s_tdm->mclk_root0_freq = clk_get_rate(i2s_tdm->mclk_root0); > + i2s_tdm->mclk_root1_freq = clk_get_rate(i2s_tdm->mclk_root1); > + } > + > + return clk_set_rate(mclk_parent, mclk_parent_freq); > +} > + > +static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm, > + struct snd_pcm_substream *substream, > + struct clk **mclk) > +{ > + unsigned int mclk_freq; > + int ret; > + > + if (i2s_tdm->clk_trcm) { > + if (i2s_tdm->mclk_tx_freq != i2s_tdm->mclk_rx_freq) { > + dev_err(i2s_tdm->dev, > + "clk_trcm, tx: %d and rx: %d should be same\n", > + i2s_tdm->mclk_tx_freq, > + i2s_tdm->mclk_rx_freq); > + return -EINVAL; > + } > + > + ret = clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq); > + if (ret) > + return ret; > + > + ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq); > + if (ret) > + return ret; > + > + /* mclk_rx is also ok. */ > + *mclk = i2s_tdm->mclk_tx; > + } else { > + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { > + *mclk = i2s_tdm->mclk_tx; > + mclk_freq = i2s_tdm->mclk_tx_freq; > + } else { > + *mclk = i2s_tdm->mclk_rx; > + mclk_freq = i2s_tdm->mclk_rx_freq; > + } > + > + ret = clk_set_rate(*mclk, mclk_freq); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream, > + struct snd_soc_dai *dai) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); > + int usable_chs = MULTIPLEX_CH_MAX; > + unsigned int val = 0; > + > + if (!i2s_tdm->io_multiplex) > + return 0; > + > + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { > + struct snd_pcm_str *playback_str = > + &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; > + > + if (playback_str->substream_opened) { > + regmap_read(i2s_tdm->regmap, I2S_TXCR, &val); > + val &= I2S_TXCR_CSR_MASK; > + usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); > + } > + > + regmap_read(i2s_tdm->regmap, I2S_RXCR, &val); > + val &= I2S_RXCR_CSR_MASK; > + > + if (to_ch_num(val) > usable_chs) { > + dev_err(i2s_tdm->dev, > + "Capture chs(%d) > usable chs(%d)\n", > + to_ch_num(val), usable_chs); > + return -EINVAL; > + } > + > + switch (val) { > + case I2S_CHN_4: > + val = I2S_IO_6CH_OUT_4CH_IN; > + break; > + case I2S_CHN_6: > + val = I2S_IO_4CH_OUT_6CH_IN; > + break; > + case I2S_CHN_8: > + val = I2S_IO_2CH_OUT_8CH_IN; > + break; > + default: > + val = I2S_IO_8CH_OUT_2CH_IN; > + break; > + } > + } else { > + struct snd_pcm_str *capture_str = > + &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; > + > + if (capture_str->substream_opened) { > + regmap_read(i2s_tdm->regmap, I2S_RXCR, &val); > + val &= I2S_RXCR_CSR_MASK; > + usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); > + } > + > + regmap_read(i2s_tdm->regmap, I2S_TXCR, &val); > + val &= I2S_TXCR_CSR_MASK; > + > + if (to_ch_num(val) > usable_chs) { > + dev_err(i2s_tdm->dev, > + "Playback chs(%d) > usable chs(%d)\n", > + to_ch_num(val), usable_chs); > + return -EINVAL; > + } > + > + switch (val) { > + case I2S_CHN_4: > + val = I2S_IO_4CH_OUT_6CH_IN; > + break; > + case I2S_CHN_6: > + val = I2S_IO_6CH_OUT_4CH_IN; > + break; > + case I2S_CHN_8: > + val = I2S_IO_8CH_OUT_2CH_IN; > + break; > + default: > + val = I2S_IO_2CH_OUT_8CH_IN; > + break; > + } > + } > + > + val <<= i2s_tdm->soc_data->grf_shift; > + val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16; > + regmap_write(i2s_tdm->grf, i2s_tdm->soc_data->grf_reg_offset, val); > + > + return 0; > +} > + > +static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream, > + struct snd_soc_dai *dai, > + unsigned int div_bclk, > + unsigned int div_lrck, > + unsigned int fmt) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); > + > + if (!i2s_tdm->clk_trcm) > + return 0; > + > + spin_lock(&i2s_tdm->lock); spin_lock_irqsave > + if (atomic_read(&i2s_tdm->refcount)) > + rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm); > + > + regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, > + I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK, > + I2S_CLKDIV_TXM(div_bclk) | I2S_CLKDIV_RXM(div_bclk)); > + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, > + I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK, > + I2S_CKR_TSD(div_lrck) | I2S_CKR_RSD(div_lrck)); > + > + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) > + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, > + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, > + fmt); > + else > + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, > + I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, > + fmt); > + > + if (atomic_read(&i2s_tdm->refcount)) > + rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm); > + spin_unlock(&i2s_tdm->lock); spin_unlock_irqrestore > + > + return 0; > +} > + > +static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, > + struct snd_pcm_hw_params *params, > + struct snd_soc_dai *dai) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); > + struct clk *mclk; > + int ret = 0; > + unsigned int val = 0; > + unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64; > + > + if (i2s_tdm->is_master_mode) { > + if (i2s_tdm->mclk_calibrate) > + rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream, > + params_rate(params)); > + > + ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk); > + if (ret) > + return ret; > + > + mclk_rate = clk_get_rate(mclk); > + bclk_rate = i2s_tdm->frame_width * params_rate(params); > + if (!bclk_rate) > + return -EINVAL; > + > + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); > + div_lrck = bclk_rate / params_rate(params); > + } > + > + switch (params_format(params)) { > + case SNDRV_PCM_FORMAT_S8: > + val |= I2S_TXCR_VDW(8); > + break; > + case SNDRV_PCM_FORMAT_S16_LE: > + val |= I2S_TXCR_VDW(16); > + break; > + case SNDRV_PCM_FORMAT_S20_3LE: > + val |= I2S_TXCR_VDW(20); > + break; > + case SNDRV_PCM_FORMAT_S24_LE: > + val |= I2S_TXCR_VDW(24); > + break; > + case SNDRV_PCM_FORMAT_S32_LE: > + val |= I2S_TXCR_VDW(32); > + break; > + default: > + return -EINVAL; > + } > + > + switch (params_channels(params)) { > + case 8: > + val |= I2S_CHN_8; > + break; > + case 6: > + val |= I2S_CHN_6; > + break; > + case 4: > + val |= I2S_CHN_4; > + break; > + case 2: > + val |= I2S_CHN_2; > + break; > + default: > + return -EINVAL; > + } > + > + if (i2s_tdm->clk_trcm) { > + rockchip_i2s_trcm_mode(substream, dai, div_bclk, div_lrck, val); > + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { > + regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, > + I2S_CLKDIV_TXM_MASK, > + I2S_CLKDIV_TXM(div_bclk)); > + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, > + I2S_CKR_TSD_MASK, > + I2S_CKR_TSD(div_lrck)); > + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, > + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, > + val); > + } else { > + regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, > + I2S_CLKDIV_RXM_MASK, > + I2S_CLKDIV_RXM(div_bclk)); > + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, > + I2S_CKR_RSD_MASK, > + I2S_CKR_RSD(div_lrck)); > + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, > + I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, > + val); > + } > + > + return rockchip_i2s_io_multiplex(substream, dai); > +} > + > +static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, > + int cmd, struct snd_soc_dai *dai) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); > + > + switch (cmd) { > + case SNDRV_PCM_TRIGGER_START: > + case SNDRV_PCM_TRIGGER_RESUME: > + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: > + if (i2s_tdm->clk_trcm) > + rockchip_snd_txrxctrl(substream, dai, 1); > + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) > + rockchip_snd_rxctrl(i2s_tdm, 1); > + else > + rockchip_snd_txctrl(i2s_tdm, 1); > + break; > + case SNDRV_PCM_TRIGGER_SUSPEND: > + case SNDRV_PCM_TRIGGER_STOP: > + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: > + if (i2s_tdm->clk_trcm) > + rockchip_snd_txrxctrl(substream, dai, 0); > + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) > + rockchip_snd_rxctrl(i2s_tdm, 0); > + else > + rockchip_snd_txctrl(i2s_tdm, 0); > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, > + unsigned int freq, int dir) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); > + > + /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */ > + if (i2s_tdm->clk_trcm) { > + i2s_tdm->mclk_tx_freq = freq; > + i2s_tdm->mclk_rx_freq = freq; > + } else { > + if (stream == SNDRV_PCM_STREAM_PLAYBACK) > + i2s_tdm->mclk_tx_freq = freq; > + else > + i2s_tdm->mclk_rx_freq = freq; > + } > + > + dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n", > + stream ? "rx" : "tx", freq); > + > + return 0; > +} > + > +static int rockchip_i2s_tdm_clk_compensation_info(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_info *uinfo) > +{ > + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; > + uinfo->count = 1; > + uinfo->value.integer.min = CLK_PPM_MIN; > + uinfo->value.integer.max = CLK_PPM_MAX; > + uinfo->value.integer.step = 1; > + > + return 0; > +} > + > +static int rockchip_i2s_tdm_clk_compensation_get(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); > + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); > + > + ucontrol->value.integer.value[0] = i2s_tdm->clk_ppm; > + > + return 0; > +} > + > +static int rockchip_i2s_tdm_clk_compensation_put(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); > + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); > + int ret = 0, ppm = 0; > + > + if (ucontrol->value.integer.value[0] < CLK_PPM_MIN || > + ucontrol->value.integer.value[0] > CLK_PPM_MAX) > + return -EINVAL; > + > + ppm = ucontrol->value.integer.value[0]; > + > + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root0, > + i2s_tdm->mclk_root0_freq, ppm); > + if (ret) > + return ret; > + > + if (clk_is_match(i2s_tdm->mclk_root0, i2s_tdm->mclk_root1)) > + return 0; > + > + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root1, > + i2s_tdm->mclk_root1_freq, ppm); > + > + return ret; > +} > + > +static struct snd_kcontrol_new rockchip_i2s_tdm_compensation_control = { > + .iface = SNDRV_CTL_ELEM_IFACE_PCM, > + .name = "PCM Clk Compensation In PPM", > + .info = rockchip_i2s_tdm_clk_compensation_info, > + .get = rockchip_i2s_tdm_clk_compensation_get, > + .put = rockchip_i2s_tdm_clk_compensation_put, > +}; > + > +static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); > + > + if (i2s_tdm->has_capture) > + dai->capture_dma_data = &i2s_tdm->capture_dma_data; > + if (i2s_tdm->has_playback) > + dai->playback_dma_data = &i2s_tdm->playback_dma_data; > + > + if (i2s_tdm->mclk_calibrate) > + snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1); > + > + return 0; > +} > + > +static int rockchip_dai_tdm_slot(struct snd_soc_dai *dai, > + unsigned int tx_mask, unsigned int rx_mask, > + int slots, int slot_width) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); > + unsigned int mask, val; > + > + i2s_tdm->tdm_mode = true; > + i2s_tdm->frame_width = slots * slot_width; > + mask = TDM_SLOT_BIT_WIDTH_MSK | TDM_FRAME_WIDTH_MSK; > + val = TDM_SLOT_BIT_WIDTH(slot_width) | > + TDM_FRAME_WIDTH(slots * slot_width); > + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR, > + mask, val); > + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR, > + mask, val); > + > + return 0; > +} > + > +static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { > + .hw_params = rockchip_i2s_tdm_hw_params, > + .set_sysclk = rockchip_i2s_tdm_set_sysclk, > + .set_fmt = rockchip_i2s_tdm_set_fmt, > + .set_tdm_slot = rockchip_dai_tdm_slot, > + .trigger = rockchip_i2s_tdm_trigger, > +}; > + > +static const struct snd_soc_component_driver rockchip_i2s_tdm_component = { > + .name = DRV_NAME, > +}; > + > +static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case I2S_TXCR: > + case I2S_RXCR: > + case I2S_CKR: > + case I2S_DMACR: > + case I2S_INTCR: > + case I2S_XFER: > + case I2S_CLR: > + case I2S_TXDR: > + case I2S_TDM_TXCR: > + case I2S_TDM_RXCR: > + case I2S_CLKDIV: > + return true; > + default: > + return false; > + } > +} > + > +static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case I2S_TXCR: > + case I2S_RXCR: > + case I2S_CKR: > + case I2S_DMACR: > + case I2S_INTCR: > + case I2S_XFER: > + case I2S_CLR: > + case I2S_TXDR: > + case I2S_RXDR: > + case I2S_TXFIFOLR: > + case I2S_INTSR: > + case I2S_RXFIFOLR: > + case I2S_TDM_TXCR: > + case I2S_TDM_RXCR: > + case I2S_CLKDIV: > + return true; > + default: > + return false; > + } > +} > + > +static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case I2S_TXFIFOLR: > + case I2S_INTSR: > + case I2S_CLR: > + case I2S_TXDR: > + case I2S_RXDR: > + case I2S_RXFIFOLR: > + return true; > + default: > + return false; > + } > +} > + > +static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case I2S_RXDR: > + return true; > + default: > + return false; > + } > +} > + > +static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = { > + {0x00, 0x7200000f}, > + {0x04, 0x01c8000f}, > + {0x08, 0x00001f1f}, > + {0x10, 0x001f0000}, > + {0x14, 0x01f00000}, > + {0x30, 0x00003eff}, > + {0x34, 0x00003eff}, > + {0x38, 0x00000707}, > +}; > + > +static const struct regmap_config rockchip_i2s_tdm_regmap_config = { > + .reg_bits = 32, > + .reg_stride = 4, > + .val_bits = 32, > + .max_register = I2S_CLKDIV, > + .reg_defaults = rockchip_i2s_tdm_reg_defaults, > + .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults), > + .writeable_reg = rockchip_i2s_tdm_wr_reg, > + .readable_reg = rockchip_i2s_tdm_rd_reg, > + .volatile_reg = rockchip_i2s_tdm_volatile_reg, > + .precious_reg = rockchip_i2s_tdm_precious_reg, > + .cache_type = REGCACHE_FLAT, > +}; > + > +static int common_soc_init(struct device *dev, u32 addr) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); > + const struct txrx_config *configs = i2s_tdm->soc_data->configs; > + u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm; > + int i; > + > + if (trcm == TRCM_TXRX) > + return 0; > + > + for (i = 0; i < i2s_tdm->soc_data->config_count; i++) { > + if (addr != configs[i].addr) > + continue; > + reg = configs[i].reg; > + if (trcm == TRCM_TX) > + val = configs[i].txonly; > + else > + val = configs[i].rxonly; > + > + if (reg) > + regmap_write(i2s_tdm->grf, reg, val); > + } > + > + return 0; > +} > + > +static const struct txrx_config px30_txrx_config[] = { > + { 0xff060000, 0x184, PX30_I2S0_CLK_TXONLY, PX30_I2S0_CLK_RXONLY }, > +}; > + > +static const struct txrx_config rk1808_txrx_config[] = { > + { 0xff7e0000, 0x190, RK1808_I2S0_CLK_TXONLY, RK1808_I2S0_CLK_RXONLY }, > +}; > + > +static const struct txrx_config rk3308_txrx_config[] = { > + { 0xff300000, 0x308, RK3308_I2S0_CLK_TXONLY, RK3308_I2S0_CLK_RXONLY }, > + { 0xff310000, 0x308, RK3308_I2S1_CLK_TXONLY, RK3308_I2S1_CLK_RXONLY }, > +}; > + > +static const struct txrx_config rk3568_txrx_config[] = { > + { 0xfe410000, 0x504, RK3568_I2S1_CLK_TXONLY, RK3568_I2S1_CLK_RXONLY }, > + { 0xfe410000, 0x508, RK3568_I2S1_MCLK_TX_OE, RK3568_I2S1_MCLK_RX_OE }, > + { 0xfe420000, 0x508, RK3568_I2S2_MCLK_OE, RK3568_I2S2_MCLK_OE }, > + { 0xfe430000, 0x504, RK3568_I2S3_CLK_TXONLY, RK3568_I2S3_CLK_RXONLY }, > + { 0xfe430000, 0x508, RK3568_I2S3_MCLK_TXONLY, RK3568_I2S3_MCLK_RXONLY }, > + { 0xfe430000, 0x508, RK3568_I2S3_MCLK_OE, RK3568_I2S3_MCLK_OE }, > +}; > + > +static const struct txrx_config rv1126_txrx_config[] = { > + { 0xff800000, 0x10260, RV1126_I2S0_CLK_TXONLY, RV1126_I2S0_CLK_RXONLY }, > +}; > + > +static struct rk_i2s_soc_data px30_i2s_soc_data = { > + .softrst_offset = 0x0300, > + .configs = px30_txrx_config, > + .config_count = ARRAY_SIZE(px30_txrx_config), > + .init = common_soc_init, > +}; > + > +static struct rk_i2s_soc_data rk1808_i2s_soc_data = { > + .softrst_offset = 0x0300, > + .configs = rk1808_txrx_config, > + .config_count = ARRAY_SIZE(rk1808_txrx_config), > + .init = common_soc_init, > +}; > + > +static struct rk_i2s_soc_data rk3308_i2s_soc_data = { > + .softrst_offset = 0x0400, > + .grf_reg_offset = 0x0308, > + .grf_shift = 5, > + .configs = rk3308_txrx_config, > + .config_count = ARRAY_SIZE(rk3308_txrx_config), > + .init = common_soc_init, > +}; > + > +static struct rk_i2s_soc_data rk3568_i2s_soc_data = { > + .softrst_offset = 0x0400, > + .configs = rk3568_txrx_config, > + .config_count = ARRAY_SIZE(rk3568_txrx_config), > + .init = common_soc_init, > +}; > + > +static struct rk_i2s_soc_data rv1126_i2s_soc_data = { > + .softrst_offset = 0x0300, > + .configs = rv1126_txrx_config, > + .config_count = ARRAY_SIZE(rv1126_txrx_config), > + .init = common_soc_init, > +}; > + > +static const struct of_device_id rockchip_i2s_tdm_match[] = { > + { .compatible = "rockchip,px30-i2s-tdm", .data = &px30_i2s_soc_data }, > + { .compatible = "rockchip,rk1808-i2s-tdm", .data = &rk1808_i2s_soc_data }, > + { .compatible = "rockchip,rk3308-i2s-tdm", .data = &rk3308_i2s_soc_data }, > + { .compatible = "rockchip,rk3568-i2s-tdm", .data = &rk3568_i2s_soc_data }, > + { .compatible = "rockchip,rv1126-i2s-tdm", .data = &rv1126_i2s_soc_data }, > + {}, > +}; > + > +static int of_i2s_resetid_get(struct device_node *node, > + const char *id) > +{ > + struct of_phandle_args args; > + int index = 0; > + int ret; > + > + if (id) > + index = of_property_match_string(node, > + "reset-names", id); > + ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", > + index, &args); > + if (ret) > + return ret; > + > + return args.args[0]; > +} > + > +static struct snd_soc_dai_driver i2s_tdm_dai = { > + .probe = rockchip_i2s_tdm_dai_probe, > + .playback = { > + .stream_name = "Playback", > + }, > + .capture = { > + .stream_name = "Capture", > + }, > + .ops = &rockchip_i2s_tdm_dai_ops, > +}; > + > +static void rockchip_i2s_tdm_init_dai(struct rk_i2s_tdm_dev *i2s_tdm) > +{ > + struct property *dma_names; > + const char *dma_name; > + u64 formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | > + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | > + SNDRV_PCM_FMTBIT_S32_LE); > + struct device_node *node = i2s_tdm->dev->of_node; > + > + of_property_for_each_string(node, "dma-names", dma_names, dma_name) { > + if (!strcmp(dma_name, "tx")) > + i2s_tdm->has_playback = true; > + if (!strcmp(dma_name, "rx")) > + i2s_tdm->has_capture = true; > + } > + > + if (i2s_tdm->has_playback) { > + i2s_tdm_dai.playback.channels_min = 2; > + i2s_tdm_dai.playback.channels_max = 8; > + i2s_tdm_dai.playback.rates = SNDRV_PCM_RATE_8000_192000; > + i2s_tdm_dai.playback.formats = formats; > + } > + > + if (i2s_tdm->has_capture) { > + i2s_tdm_dai.capture.channels_min = 2; > + i2s_tdm_dai.capture.channels_max = 8; > + i2s_tdm_dai.capture.rates = SNDRV_PCM_RATE_8000_192000; > + i2s_tdm_dai.capture.formats = formats; > + } > +} > + > +static int rockchip_i2s_tdm_path_check(struct rk_i2s_tdm_dev *i2s_tdm, > + int num, > + bool is_rx_path) > +{ > + unsigned int *i2s_data; > + int i, j; > + > + if (is_rx_path) > + i2s_data = i2s_tdm->i2s_sdis; > + else > + i2s_data = i2s_tdm->i2s_sdos; > + > + for (i = 0; i < num; i++) { > + if (i2s_data[i] > CH_GRP_MAX - 1) { > + dev_err(i2s_tdm->dev, > + "%s path i2s_data[%d]: %d is overflow, max is: %d\n", > + is_rx_path ? "RX" : "TX", > + i, i2s_data[i], CH_GRP_MAX); > + return -EINVAL; > + } > + > + for (j = 0; j < num; j++) { > + if (i == j) > + continue; > + > + if (i2s_data[i] == i2s_data[j]) { > + dev_err(i2s_tdm->dev, > + "%s path invalid routed i2s_data: [%d]%d == [%d]%d\n", > + is_rx_path ? "RX" : "TX", > + i, i2s_data[i], > + j, i2s_data[j]); > + return -EINVAL; > + } > + } > + } > + > + return 0; > +} > + > +static void rockchip_i2s_tdm_tx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, > + int num) > +{ > + int idx; > + > + for (idx = 0; idx < num; idx++) { > + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, > + I2S_TXCR_PATH_MASK(idx), > + I2S_TXCR_PATH(idx, i2s_tdm->i2s_sdos[idx])); > + } > +} > + > +static void rockchip_i2s_tdm_rx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, > + int num) > +{ > + int idx; > + > + for (idx = 0; idx < num; idx++) { > + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, > + I2S_RXCR_PATH_MASK(idx), > + I2S_RXCR_PATH(idx, i2s_tdm->i2s_sdis[idx])); > + } > +} > + > +static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm, > + int num, bool is_rx_path) > +{ > + if (is_rx_path) > + rockchip_i2s_tdm_rx_path_config(i2s_tdm, num); > + else > + rockchip_i2s_tdm_tx_path_config(i2s_tdm, num); > +} > + > +static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, > + struct device_node *np, > + bool is_rx_path) > +{ > + char *i2s_tx_path_prop = "rockchip,i2s-tx-route"; > + char *i2s_rx_path_prop = "rockchip,i2s-rx-route"; > + char *i2s_path_prop; > + unsigned int *i2s_data; > + int num, ret = 0; > + > + if (is_rx_path) { > + i2s_path_prop = i2s_rx_path_prop; > + i2s_data = i2s_tdm->i2s_sdis; > + } else { > + i2s_path_prop = i2s_tx_path_prop; > + i2s_data = i2s_tdm->i2s_sdos; > + } > + > + num = of_count_phandle_with_args(np, i2s_path_prop, NULL); > + if (num < 0) { > + if (num != -ENOENT) { > + dev_err(i2s_tdm->dev, > + "Failed to read '%s' num: %d\n", > + i2s_path_prop, num); > + ret = num; > + } > + return ret; > + } else if (num != CH_GRP_MAX) { > + dev_err(i2s_tdm->dev, > + "The num: %d should be: %d\n", num, CH_GRP_MAX); > + return -EINVAL; > + } > + > + ret = of_property_read_u32_array(np, i2s_path_prop, > + i2s_data, num); > + if (ret < 0) { > + dev_err(i2s_tdm->dev, > + "Failed to read '%s': %d\n", > + i2s_path_prop, ret); > + return ret; > + } > + > + ret = rockchip_i2s_tdm_path_check(i2s_tdm, num, is_rx_path); > + if (ret < 0) { > + dev_err(i2s_tdm->dev, > + "Failed to check i2s data bus: %d\n", ret); > + return ret; > + } > + > + rockchip_i2s_tdm_path_config(i2s_tdm, num, is_rx_path); > + > + return 0; > +} > + > +static int rockchip_i2s_tdm_tx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, > + struct device_node *np) > +{ > + return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 0); > +} > + > +static int rockchip_i2s_tdm_rx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, > + struct device_node *np) > +{ > + return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 1); > +} > + > +static int rockchip_i2s_tdm_probe(struct platform_device *pdev) > +{ > + struct device_node *node = pdev->dev.of_node; > + struct device_node *cru_node; > + const struct of_device_id *of_id; > + struct rk_i2s_tdm_dev *i2s_tdm; > + struct resource *res; > + void __iomem *regs; > + int ret; > + int val; > + > + i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL); > + if (!i2s_tdm) > + return -ENOMEM; > + > + i2s_tdm->dev = &pdev->dev; > + > + of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev); > + if (!of_id || !of_id->data) > + return -EINVAL; > + > + spin_lock_init(&i2s_tdm->lock); > + i2s_tdm->soc_data = (struct rk_i2s_soc_data *)of_id->data; > + > + rockchip_i2s_tdm_init_dai(i2s_tdm); > + > + i2s_tdm->frame_width = 64; > + if (!of_property_read_u32(node, "rockchip,frame-width", &val)) { > + if (val >= 32 && (val % 2 == 0) && val <= 512) { > + i2s_tdm->frame_width = val; > + } else { > + dev_err(i2s_tdm->dev, "unsupported frame width: '%d'\n", > + val); > + return -EINVAL; > + } > + } > + > + i2s_tdm->clk_trcm = TRCM_TXRX; > + if (of_property_read_bool(node, "rockchip,trcm-sync-tx-only")) > + i2s_tdm->clk_trcm = TRCM_TX; > + if (of_property_read_bool(node, "rockchip,trcm-sync-rx-only")) { > + if (i2s_tdm->clk_trcm) { > + dev_err(i2s_tdm->dev, "invalid trcm-sync configuration\n"); > + return -EINVAL; > + } > + i2s_tdm->clk_trcm = TRCM_RX; > + } > + if (i2s_tdm->clk_trcm != TRCM_TXRX) > + i2s_tdm_dai.symmetric_rate = 1; > + > + i2s_tdm->tdm_fsync_half_frame = > + of_property_read_bool(node, "rockchip,tdm-fsync-half-frame"); > + > + i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf"); > + if (IS_ERR(i2s_tdm->grf)) > + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->grf), > + "Error in rockchip,grf\n"); > + > + if (i2s_tdm->clk_trcm != TRCM_TXRX) { > + cru_node = of_parse_phandle(node, "rockchip,cru", 0); > + i2s_tdm->cru_base = of_iomap(cru_node, 0); > + of_node_put(cru_node); > + if (!i2s_tdm->cru_base) { > + dev_err(i2s_tdm->dev, > + "Missing or unsupported rockchip,cru node\n"); > + return -ENOENT; > + } > + > + i2s_tdm->tx_reset_id = of_i2s_resetid_get(node, "tx-m"); > + i2s_tdm->rx_reset_id = of_i2s_resetid_get(node, "rx-m"); > + } > + > + i2s_tdm->tx_reset = devm_reset_control_get_exclusive(&pdev->dev, > + "tx-m"); > + if (IS_ERR(i2s_tdm->tx_reset)) { > + ret = PTR_ERR(i2s_tdm->tx_reset); > + return dev_err_probe(i2s_tdm->dev, ret, > + "Error in tx-m reset control\n"); > + } > + > + i2s_tdm->rx_reset = devm_reset_control_get_exclusive(&pdev->dev, > + "rx-m"); > + if (IS_ERR(i2s_tdm->rx_reset)) { > + ret = PTR_ERR(i2s_tdm->rx_reset); > + return dev_err_probe(i2s_tdm->dev, ret, > + "Error in rx-m reset control\n"); > + } > + Maybe we should use _get_optional,because there are some controllers which has capture only or playback only. and move it into 'rockchip_i2s_tdm_init_dai'? > + i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk"); > + if (IS_ERR(i2s_tdm->hclk)) { > + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->hclk), > + "Failed to get clock hclk\n"); > + } > + > + i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx"); > + if (IS_ERR(i2s_tdm->mclk_tx)) { > + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_tx), > + "Failed to get clock mclk_tx\n"); > + } > + > + i2s_tdm->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx"); > + if (IS_ERR(i2s_tdm->mclk_rx)) { > + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_rx), > + "Failed to get clock mclk_rx\n"); > + } > + > + i2s_tdm->io_multiplex = > + of_property_read_bool(node, "rockchip,io-multiplex"); > + > + i2s_tdm->mclk_calibrate = > + of_property_read_bool(node, "rockchip,mclk-calibrate"); > + if (i2s_tdm->mclk_calibrate) { > + i2s_tdm->mclk_tx_src = devm_clk_get(&pdev->dev, "mclk_tx_src"); > + if (IS_ERR(i2s_tdm->mclk_tx_src)) { > + return dev_err_probe(i2s_tdm->dev, > + PTR_ERR(i2s_tdm->mclk_tx_src), > + "Failed to get clock mclk_tx_src\n"); > + } > + i2s_tdm->mclk_rx_src = devm_clk_get(&pdev->dev, "mclk_rx_src"); > + if (IS_ERR(i2s_tdm->mclk_rx_src)) { > + return dev_err_probe(i2s_tdm->dev, > + PTR_ERR(i2s_tdm->mclk_rx_src), > + "Failed to get clock mclk_rx_src\n"); > + } > + i2s_tdm->mclk_root0 = devm_clk_get(&pdev->dev, "mclk_root0"); > + if (IS_ERR(i2s_tdm->mclk_root0)) { > + return dev_err_probe(i2s_tdm->dev, > + PTR_ERR(i2s_tdm->mclk_root0), > + "Failed to get clock mclk_root0\n"); > + } > + i2s_tdm->mclk_root1 = devm_clk_get(&pdev->dev, "mclk_root1"); > + if (IS_ERR(i2s_tdm->mclk_root1)) { > + return dev_err_probe(i2s_tdm->dev, > + PTR_ERR(i2s_tdm->mclk_root1), > + "Failed to get clock mclk_root1\n"); > + } > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + regs = devm_ioremap_resource(&pdev->dev, res); Replace: res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); by devm_platform_get_and_ioremap_resource > + if (IS_ERR(regs)) { > + return dev_err_probe(i2s_tdm->dev, PTR_ERR(regs), > + "Failed to get resource IORESOURCE_MEM\n"); > + } > + > + i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs, > + &rockchip_i2s_tdm_regmap_config); > + if (IS_ERR(i2s_tdm->regmap)) { > + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->regmap), > + "Failed to initialise regmap\n"); > + } > + > + i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR; > + i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; > + i2s_tdm->playback_dma_data.maxburst = 8; > + > + i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR; > + i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; > + i2s_tdm->capture_dma_data.maxburst = 8; move into 'rockchip_i2s_tdm_init_dai'? because playback or capture is optional on some controllers. > + > + ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node); > + if (ret < 0) { > + dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n", ret); > + return ret; > + } > + > + ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node); > + if (ret < 0) { > + dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n", ret); > + return ret; > + } > + > + atomic_set(&i2s_tdm->refcount, 0); > + dev_set_drvdata(&pdev->dev, i2s_tdm); > + > + ret = clk_prepare_enable(i2s_tdm->hclk); > + if (ret) { > + return dev_err_probe(i2s_tdm->dev, ret, > + "Failed to enable clock hclk\n"); > + } > + > + ret = i2s_tdm_prepare_enable_mclk(i2s_tdm); > + if (ret) { > + return dev_err_probe(i2s_tdm->dev, ret, > + "Failed to enable one or more mclks\n"); > + goto err_disable_hclk; > + } > + > + if (i2s_tdm->mclk_calibrate) { > + i2s_tdm->mclk_root0_initial_freq = clk_get_rate(i2s_tdm->mclk_root0); > + i2s_tdm->mclk_root1_initial_freq = clk_get_rate(i2s_tdm->mclk_root1); > + i2s_tdm->mclk_root0_freq = i2s_tdm->mclk_root0_initial_freq; > + i2s_tdm->mclk_root1_freq = i2s_tdm->mclk_root1_initial_freq; > + } > + > + pm_runtime_enable(&pdev->dev); > + > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, > + I2S_DMACR_TDL(16)); > + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, > + I2S_DMACR_RDL(16)); > + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, I2S_CKR_TRCM_MASK, > + i2s_tdm->clk_trcm << I2S_CKR_TRCM_SHIFT); > + > + if (i2s_tdm->soc_data && i2s_tdm->soc_data->init) > + i2s_tdm->soc_data->init(&pdev->dev, res->start); > + > + ret = devm_snd_soc_register_component(&pdev->dev, > + &rockchip_i2s_tdm_component, > + &i2s_tdm_dai, 1); > + > + if (ret) { > + dev_err(&pdev->dev, "Could not register DAI\n"); > + goto err_suspend; > + } > + > + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); > + if (ret) { > + dev_err(&pdev->dev, "Could not register PCM\n"); > + goto err_suspend; > + } > + > + return 0; > + > +err_suspend: > + if (!pm_runtime_status_suspended(&pdev->dev)) > + i2s_tdm_runtime_suspend(&pdev->dev); > + pm_runtime_disable(&pdev->dev); > + > +err_disable_hclk: > + if (!IS_ERR(i2s_tdm->hclk)) > + clk_disable_unprepare(i2s_tdm->hclk); > + > + return ret; > +} > + > +static int rockchip_i2s_tdm_remove(struct platform_device *pdev) > +{ > + if (!pm_runtime_status_suspended(&pdev->dev)) > + i2s_tdm_runtime_suspend(&pdev->dev); > + > + pm_runtime_disable(&pdev->dev); > + > + return 0; > +} > + > +static int __maybe_unused rockchip_i2s_tdm_suspend(struct device *dev) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); > + > + regcache_mark_dirty(i2s_tdm->regmap); > + > + return 0; > +} > + > +static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev) > +{ > + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); > + int ret; > + > + ret = pm_runtime_get_sync(dev); > + if (ret < 0) > + return ret; > + ret = regcache_sync(i2s_tdm->regmap); > + pm_runtime_put(dev); > + > + return ret; > +} > + > +static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = { > + SET_RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume, > + NULL) > + SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend, > + rockchip_i2s_tdm_resume) > +}; > + > +static struct platform_driver rockchip_i2s_tdm_driver = { > + .probe = rockchip_i2s_tdm_probe, > + .remove = rockchip_i2s_tdm_remove, > + .driver = { > + .name = DRV_NAME, > + .of_match_table = of_match_ptr(rockchip_i2s_tdm_match), > + .pm = &rockchip_i2s_tdm_pm_ops, > + }, > +}; > +module_platform_driver(rockchip_i2s_tdm_driver); > + > +MODULE_DESCRIPTION("ROCKCHIP I2S/TDM ASoC Interface"); > +MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("platform:" DRV_NAME); > +MODULE_DEVICE_TABLE(of, rockchip_i2s_tdm_match); > diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.h b/sound/soc/rockchip/rockchip_i2s_tdm.h > new file mode 100644 > index 000000000000..0aa1c6da1e2c > --- /dev/null > +++ b/sound/soc/rockchip/rockchip_i2s_tdm.h > @@ -0,0 +1,398 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver > + * > + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. > + * Author: Sugar Zhang <sugar.zhang@rock-chips.com> > + * > + */ > + > +#ifndef _ROCKCHIP_I2S_TDM_H > +#define _ROCKCHIP_I2S_TDM_H > + > +/* > + * TXCR > + * transmit operation control register > + */ > +#define I2S_TXCR_PATH_SHIFT(x) (23 + (x) * 2) > +#define I2S_TXCR_PATH_MASK(x) (0x3 << I2S_TXCR_PATH_SHIFT(x)) > +#define I2S_TXCR_PATH(x, v) ((v) << I2S_TXCR_PATH_SHIFT(x)) > +#define I2S_TXCR_RCNT_SHIFT 17 > +#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT) > +#define I2S_TXCR_CSR_SHIFT 15 > +#define I2S_TXCR_CSR(x) ((x) << I2S_TXCR_CSR_SHIFT) > +#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT) > +#define I2S_TXCR_HWT BIT(14) > +#define I2S_TXCR_SJM_SHIFT 12 > +#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT) > +#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT) > +#define I2S_TXCR_FBM_SHIFT 11 > +#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT) > +#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT) > +#define I2S_TXCR_IBM_SHIFT 9 > +#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT) > +#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT) > +#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT) > +#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT) > +#define I2S_TXCR_PBM_SHIFT 7 > +#define I2S_TXCR_PBM_MODE(x) ((x) << I2S_TXCR_PBM_SHIFT) > +#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT) > +#define I2S_TXCR_TFS_SHIFT 5 > +#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT) > +#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT) > +#define I2S_TXCR_TFS_TDM_PCM (2 << I2S_TXCR_TFS_SHIFT) > +#define I2S_TXCR_TFS_TDM_I2S (3 << I2S_TXCR_TFS_SHIFT) > +#define I2S_TXCR_TFS_MASK (3 << I2S_TXCR_TFS_SHIFT) > +#define I2S_TXCR_VDW_SHIFT 0 > +#define I2S_TXCR_VDW(x) (((x) - 1) << I2S_TXCR_VDW_SHIFT) > +#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT) > + > +/* > + * RXCR > + * receive operation control register > + */ > +#define I2S_RXCR_PATH_SHIFT(x) (17 + (x) * 2) > +#define I2S_RXCR_PATH_MASK(x) (0x3 << I2S_RXCR_PATH_SHIFT(x)) > +#define I2S_RXCR_PATH(x, v) ((v) << I2S_RXCR_PATH_SHIFT(x)) > +#define I2S_RXCR_CSR_SHIFT 15 > +#define I2S_RXCR_CSR(x) ((x) << I2S_RXCR_CSR_SHIFT) > +#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT) > +#define I2S_RXCR_HWT BIT(14) > +#define I2S_RXCR_SJM_SHIFT 12 > +#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT) > +#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT) > +#define I2S_RXCR_FBM_SHIFT 11 > +#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT) > +#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT) > +#define I2S_RXCR_IBM_SHIFT 9 > +#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT) > +#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT) > +#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT) > +#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT) > +#define I2S_RXCR_PBM_SHIFT 7 > +#define I2S_RXCR_PBM_MODE(x) ((x) << I2S_RXCR_PBM_SHIFT) > +#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT) > +#define I2S_RXCR_TFS_SHIFT 5 > +#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT) > +#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT) > +#define I2S_RXCR_TFS_TDM_PCM (2 << I2S_RXCR_TFS_SHIFT) > +#define I2S_RXCR_TFS_TDM_I2S (3 << I2S_RXCR_TFS_SHIFT) > +#define I2S_RXCR_TFS_MASK (3 << I2S_RXCR_TFS_SHIFT) > +#define I2S_RXCR_VDW_SHIFT 0 > +#define I2S_RXCR_VDW(x) (((x) - 1) << I2S_RXCR_VDW_SHIFT) > +#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT) > + > +/* > + * CKR > + * clock generation register > + */ > +#define I2S_CKR_TRCM_SHIFT 28 > +#define I2S_CKR_TRCM(x) ((x) << I2S_CKR_TRCM_SHIFT) > +#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT) > +#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT) > +#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT) > +#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT) > +#define I2S_CKR_MSS_SHIFT 27 > +#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT) > +#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT) > +#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT) > +#define I2S_CKR_CKP_SHIFT 26 > +#define I2S_CKR_CKP_NORMAL (0 << I2S_CKR_CKP_SHIFT) > +#define I2S_CKR_CKP_INVERTED (1 << I2S_CKR_CKP_SHIFT) > +#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT) > +#define I2S_CKR_RLP_SHIFT 25 > +#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT) > +#define I2S_CKR_RLP_INVERTED (1 << I2S_CKR_RLP_SHIFT) > +#define I2S_CKR_RLP_MASK (1 << I2S_CKR_RLP_SHIFT) > +#define I2S_CKR_TLP_SHIFT 24 > +#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT) > +#define I2S_CKR_TLP_INVERTED (1 << I2S_CKR_TLP_SHIFT) > +#define I2S_CKR_TLP_MASK (1 << I2S_CKR_TLP_SHIFT) > +#define I2S_CKR_MDIV_SHIFT 16 > +#define I2S_CKR_MDIV(x) (((x) - 1) << I2S_CKR_MDIV_SHIFT) > +#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT) > +#define I2S_CKR_RSD_SHIFT 8 > +#define I2S_CKR_RSD(x) (((x) - 1) << I2S_CKR_RSD_SHIFT) > +#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT) > +#define I2S_CKR_TSD_SHIFT 0 > +#define I2S_CKR_TSD(x) (((x) - 1) << I2S_CKR_TSD_SHIFT) > +#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT) > + > +/* > + * FIFOLR > + * FIFO level register > + */ > +#define I2S_FIFOLR_RFL_SHIFT 24 > +#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT) > +#define I2S_FIFOLR_TFL3_SHIFT 18 > +#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT) > +#define I2S_FIFOLR_TFL2_SHIFT 12 > +#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT) > +#define I2S_FIFOLR_TFL1_SHIFT 6 > +#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT) > +#define I2S_FIFOLR_TFL0_SHIFT 0 > +#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT) > + > +/* > + * DMACR > + * DMA control register > + */ > +#define I2S_DMACR_RDE_SHIFT 24 > +#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT) > +#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT) > +#define I2S_DMACR_RDL_SHIFT 16 > +#define I2S_DMACR_RDL(x) (((x) - 1) << I2S_DMACR_RDL_SHIFT) > +#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT) > +#define I2S_DMACR_TDE_SHIFT 8 > +#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT) > +#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT) > +#define I2S_DMACR_TDL_SHIFT 0 > +#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT) > +#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT) > + > +/* > + * INTCR > + * interrupt control register > + */ > +#define I2S_INTCR_RFT_SHIFT 20 > +#define I2S_INTCR_RFT(x) (((x) - 1) << I2S_INTCR_RFT_SHIFT) > +#define I2S_INTCR_RXOIC BIT(18) > +#define I2S_INTCR_RXOIE_SHIFT 17 > +#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT) > +#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT) > +#define I2S_INTCR_RXFIE_SHIFT 16 > +#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT) > +#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT) > +#define I2S_INTCR_TFT_SHIFT 4 > +#define I2S_INTCR_TFT(x) (((x) - 1) << I2S_INTCR_TFT_SHIFT) > +#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT) > +#define I2S_INTCR_TXUIC BIT(2) > +#define I2S_INTCR_TXUIE_SHIFT 1 > +#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT) > +#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT) > + > +/* > + * INTSR > + * interrupt status register > + */ > +#define I2S_INTSR_TXEIE_SHIFT 0 > +#define I2S_INTSR_TXEIE_DISABLE (0 << I2S_INTSR_TXEIE_SHIFT) > +#define I2S_INTSR_TXEIE_ENABLE (1 << I2S_INTSR_TXEIE_SHIFT) > +#define I2S_INTSR_RXOI_SHIFT 17 > +#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT) > +#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT) > +#define I2S_INTSR_RXFI_SHIFT 16 > +#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT) > +#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT) > +#define I2S_INTSR_TXUI_SHIFT 1 > +#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT) > +#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT) > +#define I2S_INTSR_TXEI_SHIFT 0 > +#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT) > +#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT) > + > +/* > + * XFER > + * Transfer start register > + */ > +#define I2S_XFER_RXS_SHIFT 1 > +#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT) > +#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT) > +#define I2S_XFER_TXS_SHIFT 0 > +#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT) > +#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT) > + > +/* > + * CLR > + * clear SCLK domain logic register > + */ > +#define I2S_CLR_RXC BIT(1) > +#define I2S_CLR_TXC BIT(0) > + > +/* > + * TXDR > + * Transimt FIFO data register, write only. > + */ > +#define I2S_TXDR_MASK (0xff) > + > +/* > + * RXDR > + * Receive FIFO data register, write only. > + */ > +#define I2S_RXDR_MASK (0xff) > + > +/* > + * TDM_CTRL > + * TDM ctrl register > + */ > +#define TDM_FSYNC_WIDTH_SEL1_MSK GENMASK(20, 18) > +#define TDM_FSYNC_WIDTH_SEL1(x) (((x) - 1) << 18) > +#define TDM_FSYNC_WIDTH_SEL0_MSK BIT(17) > +#define TDM_FSYNC_WIDTH_HALF_FRAME 0 > +#define TDM_FSYNC_WIDTH_ONE_FRAME BIT(17) > +#define TDM_SHIFT_CTRL_MSK GENMASK(16, 14) > +#define TDM_SHIFT_CTRL(x) ((x) << 14) > +#define TDM_SLOT_BIT_WIDTH_MSK GENMASK(13, 9) > +#define TDM_SLOT_BIT_WIDTH(x) (((x) - 1) << 9) > +#define TDM_FRAME_WIDTH_MSK GENMASK(8, 0) > +#define TDM_FRAME_WIDTH(x) (((x) - 1) << 0) > + > +/* > + * CLKDIV > + * Mclk div register > + */ > +#define I2S_CLKDIV_TXM_SHIFT 0 > +#define I2S_CLKDIV_TXM(x) (((x) - 1) << I2S_CLKDIV_TXM_SHIFT) > +#define I2S_CLKDIV_TXM_MASK (0xff << I2S_CLKDIV_TXM_SHIFT) > +#define I2S_CLKDIV_RXM_SHIFT 8 > +#define I2S_CLKDIV_RXM(x) (((x) - 1) << I2S_CLKDIV_RXM_SHIFT) > +#define I2S_CLKDIV_RXM_MASK (0xff << I2S_CLKDIV_RXM_SHIFT) > + > +/* Clock divider id */ > +enum { > + ROCKCHIP_DIV_MCLK = 0, > + ROCKCHIP_DIV_BCLK, > +}; > + > +/* channel select */ > +#define I2S_CSR_SHIFT 15 > +#define I2S_CHN_2 (0 << I2S_CSR_SHIFT) > +#define I2S_CHN_4 (1 << I2S_CSR_SHIFT) > +#define I2S_CHN_6 (2 << I2S_CSR_SHIFT) > +#define I2S_CHN_8 (3 << I2S_CSR_SHIFT) > + > +/* io direction cfg register */ > +#define I2S_IO_DIRECTION_MASK (7) > +#define I2S_IO_8CH_OUT_2CH_IN (7) > +#define I2S_IO_6CH_OUT_4CH_IN (3) > +#define I2S_IO_4CH_OUT_6CH_IN (1) > +#define I2S_IO_2CH_OUT_8CH_IN (0) > + > +/* I2S REGS */ > +#define I2S_TXCR (0x0000) > +#define I2S_RXCR (0x0004) > +#define I2S_CKR (0x0008) > +#define I2S_TXFIFOLR (0x000c) > +#define I2S_DMACR (0x0010) > +#define I2S_INTCR (0x0014) > +#define I2S_INTSR (0x0018) > +#define I2S_XFER (0x001c) > +#define I2S_CLR (0x0020) > +#define I2S_TXDR (0x0024) > +#define I2S_RXDR (0x0028) > +#define I2S_RXFIFOLR (0x002c) > +#define I2S_TDM_TXCR (0x0030) > +#define I2S_TDM_RXCR (0x0034) > +#define I2S_CLKDIV (0x0038) > + > +#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16)) > + > +/* PX30 GRF CONFIGS */ > +#define PX30_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 13, 12) > +#define PX30_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 13, 12) > +#define PX30_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5) > +#define PX30_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5) > + > +#define PX30_I2S0_CLK_TXONLY \ > + (PX30_I2S0_MCLK_OUT_SRC_FROM_TX | PX30_I2S0_CLK_IN_SRC_FROM_TX) > + > +#define PX30_I2S0_CLK_RXONLY \ > + (PX30_I2S0_MCLK_OUT_SRC_FROM_RX | PX30_I2S0_CLK_IN_SRC_FROM_RX) > + > +/* RK1808 GRF CONFIGS */ > +#define RK1808_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2) > +#define RK1808_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2) > +#define RK1808_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 1, 0) > +#define RK1808_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 1, 0) > + > +#define RK1808_I2S0_CLK_TXONLY \ > + (RK1808_I2S0_MCLK_OUT_SRC_FROM_TX | RK1808_I2S0_CLK_IN_SRC_FROM_TX) > + > +#define RK1808_I2S0_CLK_RXONLY \ > + (RK1808_I2S0_MCLK_OUT_SRC_FROM_RX | RK1808_I2S0_CLK_IN_SRC_FROM_RX) > + > +/* RK3308 GRF CONFIGS */ > +#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 10, 10) > +#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 10, 10) > +#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 9, 9) > +#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 9, 9) > +#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 8, 8) > +#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 8, 8) > +#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2) > +#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2) > +#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 1, 1) > +#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 1, 1) > +#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 0, 0) > +#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 0, 0) > + > +#define RK3308_I2S0_CLK_TXONLY \ > + (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX | \ > + RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX | \ > + RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX) > + > +#define RK3308_I2S0_CLK_RXONLY \ > + (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX | \ > + RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX | \ > + RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX) > + > +#define RK3308_I2S1_CLK_TXONLY \ > + (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX | \ > + RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX | \ > + RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX) > + > +#define RK3308_I2S1_CLK_RXONLY \ > + (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX | \ > + RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX | \ > + RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX) > + > +/* RK3568 GRF CONFIGS */ > +#define RK3568_I2S1_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5) > +#define RK3568_I2S1_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5) > + > +#define RK3568_I2S1_CLK_TXONLY \ > + RK3568_I2S1_MCLK_OUT_SRC_FROM_TX > + > +#define RK3568_I2S1_CLK_RXONLY \ > + RK3568_I2S1_MCLK_OUT_SRC_FROM_RX > + > +#define RK3568_I2S3_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 15, 15) > +#define RK3568_I2S3_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 15, 15) > +#define RK3568_I2S3_SCLK_SRC_FROM_TX HIWORD_UPDATE(1, 7, 7) > +#define RK3568_I2S3_SCLK_SRC_FROM_RX HIWORD_UPDATE(0, 7, 7) > +#define RK3568_I2S3_LRCK_SRC_FROM_TX HIWORD_UPDATE(1, 6, 6) > +#define RK3568_I2S3_LRCK_SRC_FROM_RX HIWORD_UPDATE(0, 6, 6) > + > +#define RK3568_I2S3_MCLK_TXONLY \ > + RK3568_I2S3_MCLK_OUT_SRC_FROM_TX > + > +#define RK3568_I2S3_CLK_TXONLY \ > + (RK3568_I2S3_SCLK_SRC_FROM_TX | \ > + RK3568_I2S3_LRCK_SRC_FROM_TX) > + > +#define RK3568_I2S3_MCLK_RXONLY \ > + RK3568_I2S3_MCLK_OUT_SRC_FROM_RX > + > +#define RK3568_I2S3_CLK_RXONLY \ > + (RK3568_I2S3_SCLK_SRC_FROM_RX | \ > + RK3568_I2S3_LRCK_SRC_FROM_RX) > + > +#define RK3568_I2S3_MCLK_IE HIWORD_UPDATE(0, 3, 3) > +#define RK3568_I2S3_MCLK_OE HIWORD_UPDATE(1, 3, 3) > +#define RK3568_I2S2_MCLK_IE HIWORD_UPDATE(0, 2, 2) > +#define RK3568_I2S2_MCLK_OE HIWORD_UPDATE(1, 2, 2) > +#define RK3568_I2S1_MCLK_TX_IE HIWORD_UPDATE(0, 1, 1) > +#define RK3568_I2S1_MCLK_TX_OE HIWORD_UPDATE(1, 1, 1) > +#define RK3568_I2S1_MCLK_RX_IE HIWORD_UPDATE(0, 0, 0) > +#define RK3568_I2S1_MCLK_RX_OE HIWORD_UPDATE(1, 0, 0) > + > +/* RV1126 GRF CONFIGS */ > +#define RV1126_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 9, 9) > +#define RV1126_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 9, 9) > + > +#define RV1126_I2S0_CLK_TXONLY \ > + RV1126_I2S0_MCLK_OUT_SRC_FROM_TX > + > +#define RV1126_I2S0_CLK_RXONLY \ > + RV1126_I2S0_MCLK_OUT_SRC_FROM_RX > + > +#endif /* _ROCKCHIP_I2S_TDM_H */
diff --git a/MAINTAINERS b/MAINTAINERS index 3f1843204567..5d459d42672c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16049,6 +16049,12 @@ F: Documentation/ABI/*/sysfs-driver-hid-roccat* F: drivers/hid/hid-roccat* F: include/linux/hid-roccat* +ROCKCHIP I2S TDM DRIVER +M: Nicolas Frattaroli <frattaroli.nicolas@gmail.com> +L: linux-rockchip@lists.infradead.org +S: Maintained +F: sound/soc/rockchip/rockchip_i2s_tdm.* + ROCKCHIP ISP V1 DRIVER M: Helen Koike <helen.koike@collabora.com> M: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 053097b73e28..42f76bc0fb02 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -16,6 +16,17 @@ config SND_SOC_ROCKCHIP_I2S Rockchip I2S device. The device supports upto maximum of 8 channels each for play and record. +config SND_SOC_ROCKCHIP_I2S_TDM + tristate "Rockchip I2S/TDM Device Driver" + depends on HAVE_CLK && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for the I2S/TDM driver for + Rockchip I2S/TDM devices, found in Rockchip SoCs. These devices + interface between the AHB bus and the I2S bus, and support up to a + maximum of 8 channels each for playback and recording. + + config SND_SOC_ROCKCHIP_PDM tristate "Rockchip PDM Controller Driver" depends on HAVE_CLK && SND_SOC_ROCKCHIP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 65e814d46006..b10f5e7b136d 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,11 +1,13 @@ # SPDX-License-Identifier: GPL-2.0 # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-i2s-tdm-objs := rockchip_i2s_tdm.o snd-soc-rockchip-pcm-objs := rockchip_pcm.o snd-soc-rockchip-pdm-objs := rockchip_pdm.o snd-soc-rockchip-spdif-objs := rockchip_spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c new file mode 100644 index 000000000000..f31a987feecf --- /dev/null +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -0,0 +1,1845 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver + * + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * Author: Sugar Zhang <sugar.zhang@rock-chips.com> + * Author: Nicolas Frattaroli <frattaroli.nicolas@gmail.com> + * + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/spinlock.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "rockchip_i2s_tdm.h" + +#define DRV_NAME "rockchip-i2s-tdm" + +#define DEFAULT_MCLK_FS 256 +#define CH_GRP_MAX 4 /* The max channel 8 / 2 */ +#define MULTIPLEX_CH_MAX 10 +#define CLK_PPM_MIN (-1000) +#define CLK_PPM_MAX (1000) + +#define TRCM_TXRX 0 +#define TRCM_TX 1 +#define TRCM_RX 2 + +struct txrx_config { + u32 addr; + u32 reg; + u32 txonly; + u32 rxonly; +}; + +struct rk_i2s_soc_data { + u32 softrst_offset; + u32 grf_reg_offset; + u32 grf_shift; + int config_count; + const struct txrx_config *configs; + int (*init)(struct device *dev, u32 addr); +}; + +struct rk_i2s_tdm_dev { + struct device *dev; + struct clk *hclk; + struct clk *mclk_tx; + struct clk *mclk_rx; + /* The mclk_tx_src is parent of mclk_tx */ + struct clk *mclk_tx_src; + /* The mclk_rx_src is parent of mclk_rx */ + struct clk *mclk_rx_src; + /* + * The mclk_root0 and mclk_root1 are root parent and supplies for + * the different FS. + * + * e.g: + * mclk_root0 is VPLL0, used for FS=48000Hz + * mclk_root1 is VPLL1, used for FS=44100Hz + */ + struct clk *mclk_root0; + struct clk *mclk_root1; + struct regmap *regmap; + struct regmap *grf; + struct snd_dmaengine_dai_dma_data capture_dma_data; + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct reset_control *tx_reset; + struct reset_control *rx_reset; + struct rk_i2s_soc_data *soc_data; + void __iomem *cru_base; + bool is_master_mode; + bool io_multiplex; + bool mclk_calibrate; + bool tdm_mode; + bool tdm_fsync_half_frame; + unsigned int mclk_rx_freq; + unsigned int mclk_tx_freq; + unsigned int mclk_root0_freq; + unsigned int mclk_root1_freq; + unsigned int mclk_root0_initial_freq; + unsigned int mclk_root1_initial_freq; + unsigned int frame_width; + unsigned int clk_trcm; + unsigned int i2s_sdis[CH_GRP_MAX]; + unsigned int i2s_sdos[CH_GRP_MAX]; + int clk_ppm; + int tx_reset_id; + int rx_reset_id; + atomic_t refcount; + spinlock_t lock; /* xfer lock */ + bool has_playback; + bool has_capture; +}; + +static int to_ch_num(unsigned int val) +{ + switch (val) { + case I2S_CHN_4: + return 4; + case I2S_CHN_6: + return 6; + case I2S_CHN_8: + return 8; + default: + return 2; + } +} + +static void i2s_tdm_disable_unprepare_mclk(struct rk_i2s_tdm_dev *i2s_tdm) +{ + if (!IS_ERR(i2s_tdm->mclk_tx)) + clk_disable_unprepare(i2s_tdm->mclk_tx); + if (!IS_ERR(i2s_tdm->mclk_rx)) + clk_disable_unprepare(i2s_tdm->mclk_rx); + if (i2s_tdm->mclk_calibrate) { + if (!IS_ERR(i2s_tdm->mclk_tx_src)) + clk_disable_unprepare(i2s_tdm->mclk_tx_src); + if (!IS_ERR(i2s_tdm->mclk_rx_src)) + clk_disable_unprepare(i2s_tdm->mclk_rx_src); + if (!IS_ERR(i2s_tdm->mclk_root0)) + clk_disable_unprepare(i2s_tdm->mclk_root0); + if (!IS_ERR(i2s_tdm->mclk_root1)) + clk_disable_unprepare(i2s_tdm->mclk_root1); + } +} + +/** + * i2s_tdm_prepare_enable_mclk - prepare to enable all mclks, disable them on + * failure. + * @i2s_tdm: rk_i2s_tdm_dev struct + * + * This function attempts to enable all mclk clocks, but cleans up after + * itself on failure. Guarantees to balance its calls. + * + * Returns success (0) or negative errno. + */ +static int i2s_tdm_prepare_enable_mclk(struct rk_i2s_tdm_dev *i2s_tdm) +{ + int ret = 0; + + if (!IS_ERR(i2s_tdm->mclk_tx)) { + ret = clk_prepare_enable(i2s_tdm->mclk_tx); + if (ret) + goto err_mclk_tx; + } + if (!IS_ERR(i2s_tdm->mclk_rx)) { + ret = clk_prepare_enable(i2s_tdm->mclk_rx); + if (ret) + goto err_mclk_rx; + } + if (i2s_tdm->mclk_calibrate) { + if (!IS_ERR(i2s_tdm->mclk_tx_src)) { + ret = clk_prepare_enable(i2s_tdm->mclk_tx_src); + if (ret) + goto err_mclk_rx; + } + if (!IS_ERR(i2s_tdm->mclk_rx_src)) { + ret = clk_prepare_enable(i2s_tdm->mclk_rx_src); + if (ret) + goto err_mclk_rx_src; + } + if (!IS_ERR(i2s_tdm->mclk_root0)) { + ret = clk_prepare_enable(i2s_tdm->mclk_root0); + if (ret) + goto err_mclk_root0; + } + if (!IS_ERR(i2s_tdm->mclk_root1)) { + ret = clk_prepare_enable(i2s_tdm->mclk_root1); + if (ret) + goto err_mclk_root1; + } + } + + return 0; + +err_mclk_root1: + clk_disable_unprepare(i2s_tdm->mclk_root0); +err_mclk_root0: + clk_disable_unprepare(i2s_tdm->mclk_rx_src); +err_mclk_rx_src: + clk_disable_unprepare(i2s_tdm->mclk_tx_src); +err_mclk_rx: + clk_disable_unprepare(i2s_tdm->mclk_tx); +err_mclk_tx: + return ret; +} + +static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + + regcache_cache_only(i2s_tdm->regmap, true); + i2s_tdm_disable_unprepare_mclk(i2s_tdm); + + if (!IS_ERR(i2s_tdm->hclk)) + clk_disable_unprepare(i2s_tdm->hclk); + + return 0; +} + +static int __maybe_unused i2s_tdm_runtime_resume(struct device *dev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + int ret; + + if (!IS_ERR(i2s_tdm->hclk)) { + ret = clk_prepare_enable(i2s_tdm->hclk); + if (ret) + goto err_hclk; + } + + ret = i2s_tdm_prepare_enable_mclk(i2s_tdm); + if (ret) + goto err_mclk; + + regcache_cache_only(i2s_tdm->regmap, false); + regcache_mark_dirty(i2s_tdm->regmap); + + ret = regcache_sync(i2s_tdm->regmap); + if (ret) + goto err_regcache; + + return 0; + +err_regcache: + i2s_tdm_disable_unprepare_mclk(i2s_tdm); +err_mclk: + if (!IS_ERR(i2s_tdm->hclk)) + clk_disable_unprepare(i2s_tdm->hclk); +err_hclk: + return ret; +} + +static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai) +{ + return snd_soc_dai_get_drvdata(dai); +} + +static void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm, + int tx_bank, int tx_offset, + int rx_bank, int rx_offset) +{ + void __iomem *cru_reset; + unsigned long flags; + + cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; + + if (tx_bank == rx_bank) { + writel(BIT(tx_offset) | BIT(rx_offset) | + (BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), + cru_reset + (tx_bank * 4)); + } else { + local_irq_save(flags); + writel(BIT(tx_offset) | (BIT(tx_offset) << 16), + cru_reset + (tx_bank * 4)); + writel(BIT(rx_offset) | (BIT(rx_offset) << 16), + cru_reset + (rx_bank * 4)); + local_irq_restore(flags); + } +} + +static void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm, + int tx_bank, int tx_offset, + int rx_bank, int rx_offset) +{ + void __iomem *cru_reset; + unsigned long flags; + + cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; + + if (tx_bank == rx_bank) { + writel((BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), + cru_reset + (tx_bank * 4)); + } else { + local_irq_save(flags); + writel((BIT(tx_offset) << 16), + cru_reset + (tx_bank * 4)); + writel((BIT(rx_offset) << 16), + cru_reset + (rx_bank * 4)); + local_irq_restore(flags); + } +} + +/* + * Makes sure that both tx and rx are reset at the same time to sync lrck + * when clk_trcm > 0. + */ +static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm) +{ + int tx_id, rx_id; + int tx_bank, rx_bank, tx_offset, rx_offset; + + if (!i2s_tdm->cru_base || !i2s_tdm->soc_data) + return; + + tx_id = i2s_tdm->tx_reset_id; + rx_id = i2s_tdm->rx_reset_id; + if (tx_id < 0 || rx_id < 0) + return; + + tx_bank = tx_id / 16; + tx_offset = tx_id % 16; + rx_bank = rx_id / 16; + rx_offset = rx_id % 16; + dev_dbg(i2s_tdm->dev, + "tx_bank: %d, rx_bank: %d, tx_offset: %d, rx_offset: %d\n", + tx_bank, rx_bank, tx_offset, rx_offset); + + rockchip_snd_xfer_reset_assert(i2s_tdm, tx_bank, tx_offset, + rx_bank, rx_offset); + + udelay(10); + + rockchip_snd_xfer_reset_deassert(i2s_tdm, tx_bank, tx_offset, + rx_bank, rx_offset); + udelay(10); +} + +static void rockchip_snd_reset(struct reset_control *rc) +{ + reset_control_assert(rc); + udelay(10); + reset_control_deassert(rc); + udelay(10); +} + +static void rockchip_snd_xfer_clear(struct rk_i2s_tdm_dev *i2s_tdm, + unsigned int clr) +{ + unsigned int xfer_mask; + unsigned int xfer_val; + unsigned int val; + int retry = 10; + bool tx = clr & I2S_CLR_TXC; + bool rx = clr & I2S_CLR_RXC; + + if (!(rx || tx)) + return; + + xfer_mask = (tx ? I2S_XFER_TXS_START : 0) | + (rx ? I2S_XFER_RXS_START : 0); + xfer_val = (tx ? I2S_XFER_TXS_STOP : 0) | + (rx ? I2S_XFER_RXS_STOP : 0); + + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, xfer_mask, xfer_val); + udelay(150); + regmap_update_bits(i2s_tdm->regmap, I2S_CLR, clr, clr); + + regmap_read(i2s_tdm->regmap, I2S_CLR, &val); + /* Wait on the clear operation to finish */ + while (val) { + udelay(15); + regmap_read(i2s_tdm->regmap, I2S_CLR, &val); + retry--; + if (!retry) { + dev_warn(i2s_tdm->dev, "clear failed, reset %s%s\n", + tx ? "tx" : "", rx ? "rx" : ""); + if (rx && tx) + rockchip_snd_xfer_sync_reset(i2s_tdm); + else if (tx) + rockchip_snd_reset(i2s_tdm->tx_reset); + else if (rx) + rockchip_snd_reset(i2s_tdm->rx_reset); + break; + } + } +} + +/* only used when clk_trcm > 0 */ +static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int on) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); + + spin_lock(&i2s_tdm->lock); + if (on) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, + I2S_DMACR_TDE_ENABLE); + else + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_RDE_ENABLE, + I2S_DMACR_RDE_ENABLE); + + if (atomic_inc_return(&i2s_tdm->refcount) == 1) { + rockchip_snd_xfer_sync_reset(i2s_tdm); + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START); + } + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, + I2S_DMACR_TDE_DISABLE); + else + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_RDE_ENABLE, + I2S_DMACR_RDE_DISABLE); + + if (atomic_dec_and_test(&i2s_tdm->refcount)) { + rockchip_snd_xfer_clear(i2s_tdm, + I2S_CLR_TXC | I2S_CLR_RXC); + } + } + spin_unlock(&i2s_tdm->lock); +} + +static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) +{ + if (on) { + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); + + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, + I2S_XFER_TXS_START, + I2S_XFER_TXS_START); + } else { + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); + + rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC); + } +} + +static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) +{ + if (on) { + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); + + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, + I2S_XFER_RXS_START, + I2S_XFER_RXS_START); + } else { + rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_RXC); + } +} + +static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); + unsigned int mask, val, tdm_val, txcr_val, rxcr_val; + int ret; + bool is_tdm = i2s_tdm->tdm_mode; + + ret = pm_runtime_get_sync(cpu_dai->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(cpu_dai->dev); + return ret; + } + + mask = I2S_CKR_MSS_MASK; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val = I2S_CKR_MSS_MASTER; + i2s_tdm->is_master_mode = true; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val = I2S_CKR_MSS_SLAVE; + i2s_tdm->is_master_mode = false; + break; + default: + ret = -EINVAL; + goto err_pm_put; + } + + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val); + + mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = I2S_CKR_CKP_NORMAL | + I2S_CKR_TLP_NORMAL | + I2S_CKR_RLP_NORMAL; + break; + case SND_SOC_DAIFMT_NB_IF: + val = I2S_CKR_CKP_NORMAL | + I2S_CKR_TLP_INVERTED | + I2S_CKR_RLP_INVERTED; + break; + case SND_SOC_DAIFMT_IB_NF: + val = I2S_CKR_CKP_INVERTED | + I2S_CKR_TLP_NORMAL | + I2S_CKR_RLP_NORMAL; + break; + case SND_SOC_DAIFMT_IB_IF: + val = I2S_CKR_CKP_INVERTED | + I2S_CKR_TLP_INVERTED | + I2S_CKR_RLP_INVERTED; + break; + default: + ret = -EINVAL; + goto err_pm_put; + } + + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, mask, val); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + txcr_val = I2S_TXCR_IBM_RSJM; + rxcr_val = I2S_RXCR_IBM_RSJM; + break; + case SND_SOC_DAIFMT_LEFT_J: + txcr_val = I2S_TXCR_IBM_LSJM; + rxcr_val = I2S_RXCR_IBM_LSJM; + break; + case SND_SOC_DAIFMT_I2S: + txcr_val = I2S_TXCR_IBM_NORMAL; + rxcr_val = I2S_RXCR_IBM_NORMAL; + break; + case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ + txcr_val = I2S_TXCR_TFS_PCM; + rxcr_val = I2S_RXCR_TFS_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ + txcr_val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1); + rxcr_val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1); + break; + default: + ret = -EINVAL; + goto err_pm_put; + } + + mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK; + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, txcr_val); + + mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK; + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, rxcr_val); + + if (is_tdm) { + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = I2S_TXCR_TFS_TDM_I2S; + tdm_val = TDM_SHIFT_CTRL(2); + break; + case SND_SOC_DAIFMT_LEFT_J: + val = I2S_TXCR_TFS_TDM_I2S; + tdm_val = TDM_SHIFT_CTRL(1); + break; + case SND_SOC_DAIFMT_I2S: + val = I2S_TXCR_TFS_TDM_I2S; + tdm_val = TDM_SHIFT_CTRL(0); + break; + case SND_SOC_DAIFMT_DSP_A: + val = I2S_TXCR_TFS_TDM_PCM; + tdm_val = TDM_SHIFT_CTRL(0); + break; + case SND_SOC_DAIFMT_DSP_B: + val = I2S_TXCR_TFS_TDM_PCM; + tdm_val = TDM_SHIFT_CTRL(2); + break; + default: + ret = -EINVAL; + goto err_pm_put; + } + + tdm_val |= TDM_FSYNC_WIDTH_SEL1(1); + if (i2s_tdm->tdm_fsync_half_frame) + tdm_val |= TDM_FSYNC_WIDTH_HALF_FRAME; + else + tdm_val |= TDM_FSYNC_WIDTH_ONE_FRAME; + + mask = I2S_TXCR_TFS_MASK; + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, mask, val); + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, mask, val); + + mask = TDM_FSYNC_WIDTH_SEL1_MSK | TDM_FSYNC_WIDTH_SEL0_MSK | + TDM_SHIFT_CTRL_MSK; + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR, + mask, tdm_val); + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR, + mask, tdm_val); + } + +err_pm_put: + pm_runtime_put(cpu_dai->dev); + + return ret; +} + +static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream, + struct rk_i2s_tdm_dev *i2s_tdm) +{ + int stream; + + stream = SNDRV_PCM_STREAM_LAST - substream->stream; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, + I2S_DMACR_TDE_DISABLE); + else + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_RDE_ENABLE, + I2S_DMACR_RDE_DISABLE); + + rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC | I2S_CLR_RXC); +} + +static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, + struct rk_i2s_tdm_dev *i2s_tdm) +{ + int stream; + + stream = SNDRV_PCM_STREAM_LAST - substream->stream; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, + I2S_DMACR_TDE_ENABLE); + else + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_RDE_ENABLE, + I2S_DMACR_RDE_ENABLE); + + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START); +} + +static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm, + struct clk *clk, unsigned long rate, + int ppm) +{ + unsigned long rate_target; + int delta, ret; + + if (ppm == i2s_tdm->clk_ppm) + return 0; + + delta = (ppm < 0) ? -1 : 1; + delta *= (int)div64_u64((u64)rate * (u64)abs(ppm) + 500000, + 1000000); + + rate_target = rate + delta; + + if (!rate_target) + return -EINVAL; + + ret = clk_set_rate(clk, rate_target); + if (ret) + return ret; + + i2s_tdm->clk_ppm = ppm; + + return ret; +} + +static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, + struct snd_pcm_substream *substream, + unsigned int lrck_freq) +{ + struct clk *mclk_root; + struct clk *mclk_parent; + unsigned int mclk_root_freq; + unsigned int mclk_root_initial_freq; + unsigned int mclk_parent_freq; + unsigned int div, delta; + u64 ppm; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mclk_parent = i2s_tdm->mclk_tx_src; + else + mclk_parent = i2s_tdm->mclk_rx_src; + + switch (lrck_freq) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 64000: + case 96000: + case 192000: + mclk_root = i2s_tdm->mclk_root0; + mclk_root_freq = i2s_tdm->mclk_root0_freq; + mclk_root_initial_freq = i2s_tdm->mclk_root0_initial_freq; + mclk_parent_freq = DEFAULT_MCLK_FS * 192000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + case 176400: + mclk_root = i2s_tdm->mclk_root1; + mclk_root_freq = i2s_tdm->mclk_root1_freq; + mclk_root_initial_freq = i2s_tdm->mclk_root1_initial_freq; + mclk_parent_freq = DEFAULT_MCLK_FS * 176400; + break; + default: + dev_err(i2s_tdm->dev, "Invalid LRCK freq: %u Hz\n", + lrck_freq); + return -EINVAL; + } + + ret = clk_set_parent(mclk_parent, mclk_root); + if (ret) + return ret; + + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, mclk_root, + mclk_root_freq, 0); + if (ret) + return ret; + + delta = abs(mclk_root_freq % mclk_parent_freq - mclk_parent_freq); + ppm = div64_u64((uint64_t)delta * 1000000, (uint64_t)mclk_root_freq); + + if (ppm) { + div = DIV_ROUND_CLOSEST(mclk_root_initial_freq, mclk_parent_freq); + if (!div) + return -EINVAL; + + mclk_root_freq = mclk_parent_freq * round_up(div, 2); + + ret = clk_set_rate(mclk_root, mclk_root_freq); + if (ret) + return ret; + + i2s_tdm->mclk_root0_freq = clk_get_rate(i2s_tdm->mclk_root0); + i2s_tdm->mclk_root1_freq = clk_get_rate(i2s_tdm->mclk_root1); + } + + return clk_set_rate(mclk_parent, mclk_parent_freq); +} + +static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm, + struct snd_pcm_substream *substream, + struct clk **mclk) +{ + unsigned int mclk_freq; + int ret; + + if (i2s_tdm->clk_trcm) { + if (i2s_tdm->mclk_tx_freq != i2s_tdm->mclk_rx_freq) { + dev_err(i2s_tdm->dev, + "clk_trcm, tx: %d and rx: %d should be same\n", + i2s_tdm->mclk_tx_freq, + i2s_tdm->mclk_rx_freq); + return -EINVAL; + } + + ret = clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq); + if (ret) + return ret; + + ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq); + if (ret) + return ret; + + /* mclk_rx is also ok. */ + *mclk = i2s_tdm->mclk_tx; + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + *mclk = i2s_tdm->mclk_tx; + mclk_freq = i2s_tdm->mclk_tx_freq; + } else { + *mclk = i2s_tdm->mclk_rx; + mclk_freq = i2s_tdm->mclk_rx_freq; + } + + ret = clk_set_rate(*mclk, mclk_freq); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); + int usable_chs = MULTIPLEX_CH_MAX; + unsigned int val = 0; + + if (!i2s_tdm->io_multiplex) + return 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + struct snd_pcm_str *playback_str = + &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; + + if (playback_str->substream_opened) { + regmap_read(i2s_tdm->regmap, I2S_TXCR, &val); + val &= I2S_TXCR_CSR_MASK; + usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); + } + + regmap_read(i2s_tdm->regmap, I2S_RXCR, &val); + val &= I2S_RXCR_CSR_MASK; + + if (to_ch_num(val) > usable_chs) { + dev_err(i2s_tdm->dev, + "Capture chs(%d) > usable chs(%d)\n", + to_ch_num(val), usable_chs); + return -EINVAL; + } + + switch (val) { + case I2S_CHN_4: + val = I2S_IO_6CH_OUT_4CH_IN; + break; + case I2S_CHN_6: + val = I2S_IO_4CH_OUT_6CH_IN; + break; + case I2S_CHN_8: + val = I2S_IO_2CH_OUT_8CH_IN; + break; + default: + val = I2S_IO_8CH_OUT_2CH_IN; + break; + } + } else { + struct snd_pcm_str *capture_str = + &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + + if (capture_str->substream_opened) { + regmap_read(i2s_tdm->regmap, I2S_RXCR, &val); + val &= I2S_RXCR_CSR_MASK; + usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); + } + + regmap_read(i2s_tdm->regmap, I2S_TXCR, &val); + val &= I2S_TXCR_CSR_MASK; + + if (to_ch_num(val) > usable_chs) { + dev_err(i2s_tdm->dev, + "Playback chs(%d) > usable chs(%d)\n", + to_ch_num(val), usable_chs); + return -EINVAL; + } + + switch (val) { + case I2S_CHN_4: + val = I2S_IO_4CH_OUT_6CH_IN; + break; + case I2S_CHN_6: + val = I2S_IO_6CH_OUT_4CH_IN; + break; + case I2S_CHN_8: + val = I2S_IO_8CH_OUT_2CH_IN; + break; + default: + val = I2S_IO_2CH_OUT_8CH_IN; + break; + } + } + + val <<= i2s_tdm->soc_data->grf_shift; + val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16; + regmap_write(i2s_tdm->grf, i2s_tdm->soc_data->grf_reg_offset, val); + + return 0; +} + +static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + unsigned int div_bclk, + unsigned int div_lrck, + unsigned int fmt) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); + + if (!i2s_tdm->clk_trcm) + return 0; + + spin_lock(&i2s_tdm->lock); + if (atomic_read(&i2s_tdm->refcount)) + rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm); + + regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, + I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK, + I2S_CLKDIV_TXM(div_bclk) | I2S_CLKDIV_RXM(div_bclk)); + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, + I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK, + I2S_CKR_TSD(div_lrck) | I2S_CKR_RSD(div_lrck)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, + fmt); + else + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, + I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, + fmt); + + if (atomic_read(&i2s_tdm->refcount)) + rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm); + spin_unlock(&i2s_tdm->lock); + + return 0; +} + +static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); + struct clk *mclk; + int ret = 0; + unsigned int val = 0; + unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64; + + if (i2s_tdm->is_master_mode) { + if (i2s_tdm->mclk_calibrate) + rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream, + params_rate(params)); + + ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk); + if (ret) + return ret; + + mclk_rate = clk_get_rate(mclk); + bclk_rate = i2s_tdm->frame_width * params_rate(params); + if (!bclk_rate) + return -EINVAL; + + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + div_lrck = bclk_rate / params_rate(params); + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + val |= I2S_TXCR_VDW(8); + break; + case SNDRV_PCM_FORMAT_S16_LE: + val |= I2S_TXCR_VDW(16); + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val |= I2S_TXCR_VDW(20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + val |= I2S_TXCR_VDW(24); + break; + case SNDRV_PCM_FORMAT_S32_LE: + val |= I2S_TXCR_VDW(32); + break; + default: + return -EINVAL; + } + + switch (params_channels(params)) { + case 8: + val |= I2S_CHN_8; + break; + case 6: + val |= I2S_CHN_6; + break; + case 4: + val |= I2S_CHN_4; + break; + case 2: + val |= I2S_CHN_2; + break; + default: + return -EINVAL; + } + + if (i2s_tdm->clk_trcm) { + rockchip_i2s_trcm_mode(substream, dai, div_bclk, div_lrck, val); + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, + I2S_CLKDIV_TXM_MASK, + I2S_CLKDIV_TXM(div_bclk)); + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, + I2S_CKR_TSD_MASK, + I2S_CKR_TSD(div_lrck)); + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, + val); + } else { + regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV, + I2S_CLKDIV_RXM_MASK, + I2S_CLKDIV_RXM(div_bclk)); + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, + I2S_CKR_RSD_MASK, + I2S_CKR_RSD(div_lrck)); + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, + I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, + val); + } + + return rockchip_i2s_io_multiplex(substream, dai); +} + +static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (i2s_tdm->clk_trcm) + rockchip_snd_txrxctrl(substream, dai, 1); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rockchip_snd_rxctrl(i2s_tdm, 1); + else + rockchip_snd_txctrl(i2s_tdm, 1); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (i2s_tdm->clk_trcm) + rockchip_snd_txrxctrl(substream, dai, 0); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rockchip_snd_rxctrl(i2s_tdm, 0); + else + rockchip_snd_txctrl(i2s_tdm, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, + unsigned int freq, int dir) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); + + /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */ + if (i2s_tdm->clk_trcm) { + i2s_tdm->mclk_tx_freq = freq; + i2s_tdm->mclk_rx_freq = freq; + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_tdm->mclk_tx_freq = freq; + else + i2s_tdm->mclk_rx_freq = freq; + } + + dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n", + stream ? "rx" : "tx", freq); + + return 0; +} + +static int rockchip_i2s_tdm_clk_compensation_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = CLK_PPM_MIN; + uinfo->value.integer.max = CLK_PPM_MAX; + uinfo->value.integer.step = 1; + + return 0; +} + +static int rockchip_i2s_tdm_clk_compensation_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); + + ucontrol->value.integer.value[0] = i2s_tdm->clk_ppm; + + return 0; +} + +static int rockchip_i2s_tdm_clk_compensation_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); + int ret = 0, ppm = 0; + + if (ucontrol->value.integer.value[0] < CLK_PPM_MIN || + ucontrol->value.integer.value[0] > CLK_PPM_MAX) + return -EINVAL; + + ppm = ucontrol->value.integer.value[0]; + + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root0, + i2s_tdm->mclk_root0_freq, ppm); + if (ret) + return ret; + + if (clk_is_match(i2s_tdm->mclk_root0, i2s_tdm->mclk_root1)) + return 0; + + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root1, + i2s_tdm->mclk_root1_freq, ppm); + + return ret; +} + +static struct snd_kcontrol_new rockchip_i2s_tdm_compensation_control = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Clk Compensation In PPM", + .info = rockchip_i2s_tdm_clk_compensation_info, + .get = rockchip_i2s_tdm_clk_compensation_get, + .put = rockchip_i2s_tdm_clk_compensation_put, +}; + +static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) +{ + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); + + if (i2s_tdm->has_capture) + dai->capture_dma_data = &i2s_tdm->capture_dma_data; + if (i2s_tdm->has_playback) + dai->playback_dma_data = &i2s_tdm->playback_dma_data; + + if (i2s_tdm->mclk_calibrate) + snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1); + + return 0; +} + +static int rockchip_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); + unsigned int mask, val; + + i2s_tdm->tdm_mode = true; + i2s_tdm->frame_width = slots * slot_width; + mask = TDM_SLOT_BIT_WIDTH_MSK | TDM_FRAME_WIDTH_MSK; + val = TDM_SLOT_BIT_WIDTH(slot_width) | + TDM_FRAME_WIDTH(slots * slot_width); + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_TXCR, + mask, val); + regmap_update_bits(i2s_tdm->regmap, I2S_TDM_RXCR, + mask, val); + + return 0; +} + +static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { + .hw_params = rockchip_i2s_tdm_hw_params, + .set_sysclk = rockchip_i2s_tdm_set_sysclk, + .set_fmt = rockchip_i2s_tdm_set_fmt, + .set_tdm_slot = rockchip_dai_tdm_slot, + .trigger = rockchip_i2s_tdm_trigger, +}; + +static const struct snd_soc_component_driver rockchip_i2s_tdm_component = { + .name = DRV_NAME, +}; + +static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_TXCR: + case I2S_RXCR: + case I2S_CKR: + case I2S_DMACR: + case I2S_INTCR: + case I2S_XFER: + case I2S_CLR: + case I2S_TXDR: + case I2S_TDM_TXCR: + case I2S_TDM_RXCR: + case I2S_CLKDIV: + return true; + default: + return false; + } +} + +static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_TXCR: + case I2S_RXCR: + case I2S_CKR: + case I2S_DMACR: + case I2S_INTCR: + case I2S_XFER: + case I2S_CLR: + case I2S_TXDR: + case I2S_RXDR: + case I2S_TXFIFOLR: + case I2S_INTSR: + case I2S_RXFIFOLR: + case I2S_TDM_TXCR: + case I2S_TDM_RXCR: + case I2S_CLKDIV: + return true; + default: + return false; + } +} + +static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_TXFIFOLR: + case I2S_INTSR: + case I2S_CLR: + case I2S_TXDR: + case I2S_RXDR: + case I2S_RXFIFOLR: + return true; + default: + return false; + } +} + +static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_RXDR: + return true; + default: + return false; + } +} + +static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = { + {0x00, 0x7200000f}, + {0x04, 0x01c8000f}, + {0x08, 0x00001f1f}, + {0x10, 0x001f0000}, + {0x14, 0x01f00000}, + {0x30, 0x00003eff}, + {0x34, 0x00003eff}, + {0x38, 0x00000707}, +}; + +static const struct regmap_config rockchip_i2s_tdm_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = I2S_CLKDIV, + .reg_defaults = rockchip_i2s_tdm_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults), + .writeable_reg = rockchip_i2s_tdm_wr_reg, + .readable_reg = rockchip_i2s_tdm_rd_reg, + .volatile_reg = rockchip_i2s_tdm_volatile_reg, + .precious_reg = rockchip_i2s_tdm_precious_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int common_soc_init(struct device *dev, u32 addr) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + const struct txrx_config *configs = i2s_tdm->soc_data->configs; + u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm; + int i; + + if (trcm == TRCM_TXRX) + return 0; + + for (i = 0; i < i2s_tdm->soc_data->config_count; i++) { + if (addr != configs[i].addr) + continue; + reg = configs[i].reg; + if (trcm == TRCM_TX) + val = configs[i].txonly; + else + val = configs[i].rxonly; + + if (reg) + regmap_write(i2s_tdm->grf, reg, val); + } + + return 0; +} + +static const struct txrx_config px30_txrx_config[] = { + { 0xff060000, 0x184, PX30_I2S0_CLK_TXONLY, PX30_I2S0_CLK_RXONLY }, +}; + +static const struct txrx_config rk1808_txrx_config[] = { + { 0xff7e0000, 0x190, RK1808_I2S0_CLK_TXONLY, RK1808_I2S0_CLK_RXONLY }, +}; + +static const struct txrx_config rk3308_txrx_config[] = { + { 0xff300000, 0x308, RK3308_I2S0_CLK_TXONLY, RK3308_I2S0_CLK_RXONLY }, + { 0xff310000, 0x308, RK3308_I2S1_CLK_TXONLY, RK3308_I2S1_CLK_RXONLY }, +}; + +static const struct txrx_config rk3568_txrx_config[] = { + { 0xfe410000, 0x504, RK3568_I2S1_CLK_TXONLY, RK3568_I2S1_CLK_RXONLY }, + { 0xfe410000, 0x508, RK3568_I2S1_MCLK_TX_OE, RK3568_I2S1_MCLK_RX_OE }, + { 0xfe420000, 0x508, RK3568_I2S2_MCLK_OE, RK3568_I2S2_MCLK_OE }, + { 0xfe430000, 0x504, RK3568_I2S3_CLK_TXONLY, RK3568_I2S3_CLK_RXONLY }, + { 0xfe430000, 0x508, RK3568_I2S3_MCLK_TXONLY, RK3568_I2S3_MCLK_RXONLY }, + { 0xfe430000, 0x508, RK3568_I2S3_MCLK_OE, RK3568_I2S3_MCLK_OE }, +}; + +static const struct txrx_config rv1126_txrx_config[] = { + { 0xff800000, 0x10260, RV1126_I2S0_CLK_TXONLY, RV1126_I2S0_CLK_RXONLY }, +}; + +static struct rk_i2s_soc_data px30_i2s_soc_data = { + .softrst_offset = 0x0300, + .configs = px30_txrx_config, + .config_count = ARRAY_SIZE(px30_txrx_config), + .init = common_soc_init, +}; + +static struct rk_i2s_soc_data rk1808_i2s_soc_data = { + .softrst_offset = 0x0300, + .configs = rk1808_txrx_config, + .config_count = ARRAY_SIZE(rk1808_txrx_config), + .init = common_soc_init, +}; + +static struct rk_i2s_soc_data rk3308_i2s_soc_data = { + .softrst_offset = 0x0400, + .grf_reg_offset = 0x0308, + .grf_shift = 5, + .configs = rk3308_txrx_config, + .config_count = ARRAY_SIZE(rk3308_txrx_config), + .init = common_soc_init, +}; + +static struct rk_i2s_soc_data rk3568_i2s_soc_data = { + .softrst_offset = 0x0400, + .configs = rk3568_txrx_config, + .config_count = ARRAY_SIZE(rk3568_txrx_config), + .init = common_soc_init, +}; + +static struct rk_i2s_soc_data rv1126_i2s_soc_data = { + .softrst_offset = 0x0300, + .configs = rv1126_txrx_config, + .config_count = ARRAY_SIZE(rv1126_txrx_config), + .init = common_soc_init, +}; + +static const struct of_device_id rockchip_i2s_tdm_match[] = { + { .compatible = "rockchip,px30-i2s-tdm", .data = &px30_i2s_soc_data }, + { .compatible = "rockchip,rk1808-i2s-tdm", .data = &rk1808_i2s_soc_data }, + { .compatible = "rockchip,rk3308-i2s-tdm", .data = &rk3308_i2s_soc_data }, + { .compatible = "rockchip,rk3568-i2s-tdm", .data = &rk3568_i2s_soc_data }, + { .compatible = "rockchip,rv1126-i2s-tdm", .data = &rv1126_i2s_soc_data }, + {}, +}; + +static int of_i2s_resetid_get(struct device_node *node, + const char *id) +{ + struct of_phandle_args args; + int index = 0; + int ret; + + if (id) + index = of_property_match_string(node, + "reset-names", id); + ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", + index, &args); + if (ret) + return ret; + + return args.args[0]; +} + +static struct snd_soc_dai_driver i2s_tdm_dai = { + .probe = rockchip_i2s_tdm_dai_probe, + .playback = { + .stream_name = "Playback", + }, + .capture = { + .stream_name = "Capture", + }, + .ops = &rockchip_i2s_tdm_dai_ops, +}; + +static void rockchip_i2s_tdm_init_dai(struct rk_i2s_tdm_dev *i2s_tdm) +{ + struct property *dma_names; + const char *dma_name; + u64 formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE); + struct device_node *node = i2s_tdm->dev->of_node; + + of_property_for_each_string(node, "dma-names", dma_names, dma_name) { + if (!strcmp(dma_name, "tx")) + i2s_tdm->has_playback = true; + if (!strcmp(dma_name, "rx")) + i2s_tdm->has_capture = true; + } + + if (i2s_tdm->has_playback) { + i2s_tdm_dai.playback.channels_min = 2; + i2s_tdm_dai.playback.channels_max = 8; + i2s_tdm_dai.playback.rates = SNDRV_PCM_RATE_8000_192000; + i2s_tdm_dai.playback.formats = formats; + } + + if (i2s_tdm->has_capture) { + i2s_tdm_dai.capture.channels_min = 2; + i2s_tdm_dai.capture.channels_max = 8; + i2s_tdm_dai.capture.rates = SNDRV_PCM_RATE_8000_192000; + i2s_tdm_dai.capture.formats = formats; + } +} + +static int rockchip_i2s_tdm_path_check(struct rk_i2s_tdm_dev *i2s_tdm, + int num, + bool is_rx_path) +{ + unsigned int *i2s_data; + int i, j; + + if (is_rx_path) + i2s_data = i2s_tdm->i2s_sdis; + else + i2s_data = i2s_tdm->i2s_sdos; + + for (i = 0; i < num; i++) { + if (i2s_data[i] > CH_GRP_MAX - 1) { + dev_err(i2s_tdm->dev, + "%s path i2s_data[%d]: %d is overflow, max is: %d\n", + is_rx_path ? "RX" : "TX", + i, i2s_data[i], CH_GRP_MAX); + return -EINVAL; + } + + for (j = 0; j < num; j++) { + if (i == j) + continue; + + if (i2s_data[i] == i2s_data[j]) { + dev_err(i2s_tdm->dev, + "%s path invalid routed i2s_data: [%d]%d == [%d]%d\n", + is_rx_path ? "RX" : "TX", + i, i2s_data[i], + j, i2s_data[j]); + return -EINVAL; + } + } + } + + return 0; +} + +static void rockchip_i2s_tdm_tx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, + int num) +{ + int idx; + + for (idx = 0; idx < num; idx++) { + regmap_update_bits(i2s_tdm->regmap, I2S_TXCR, + I2S_TXCR_PATH_MASK(idx), + I2S_TXCR_PATH(idx, i2s_tdm->i2s_sdos[idx])); + } +} + +static void rockchip_i2s_tdm_rx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, + int num) +{ + int idx; + + for (idx = 0; idx < num; idx++) { + regmap_update_bits(i2s_tdm->regmap, I2S_RXCR, + I2S_RXCR_PATH_MASK(idx), + I2S_RXCR_PATH(idx, i2s_tdm->i2s_sdis[idx])); + } +} + +static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm, + int num, bool is_rx_path) +{ + if (is_rx_path) + rockchip_i2s_tdm_rx_path_config(i2s_tdm, num); + else + rockchip_i2s_tdm_tx_path_config(i2s_tdm, num); +} + +static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, + struct device_node *np, + bool is_rx_path) +{ + char *i2s_tx_path_prop = "rockchip,i2s-tx-route"; + char *i2s_rx_path_prop = "rockchip,i2s-rx-route"; + char *i2s_path_prop; + unsigned int *i2s_data; + int num, ret = 0; + + if (is_rx_path) { + i2s_path_prop = i2s_rx_path_prop; + i2s_data = i2s_tdm->i2s_sdis; + } else { + i2s_path_prop = i2s_tx_path_prop; + i2s_data = i2s_tdm->i2s_sdos; + } + + num = of_count_phandle_with_args(np, i2s_path_prop, NULL); + if (num < 0) { + if (num != -ENOENT) { + dev_err(i2s_tdm->dev, + "Failed to read '%s' num: %d\n", + i2s_path_prop, num); + ret = num; + } + return ret; + } else if (num != CH_GRP_MAX) { + dev_err(i2s_tdm->dev, + "The num: %d should be: %d\n", num, CH_GRP_MAX); + return -EINVAL; + } + + ret = of_property_read_u32_array(np, i2s_path_prop, + i2s_data, num); + if (ret < 0) { + dev_err(i2s_tdm->dev, + "Failed to read '%s': %d\n", + i2s_path_prop, ret); + return ret; + } + + ret = rockchip_i2s_tdm_path_check(i2s_tdm, num, is_rx_path); + if (ret < 0) { + dev_err(i2s_tdm->dev, + "Failed to check i2s data bus: %d\n", ret); + return ret; + } + + rockchip_i2s_tdm_path_config(i2s_tdm, num, is_rx_path); + + return 0; +} + +static int rockchip_i2s_tdm_tx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, + struct device_node *np) +{ + return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 0); +} + +static int rockchip_i2s_tdm_rx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, + struct device_node *np) +{ + return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 1); +} + +static int rockchip_i2s_tdm_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *cru_node; + const struct of_device_id *of_id; + struct rk_i2s_tdm_dev *i2s_tdm; + struct resource *res; + void __iomem *regs; + int ret; + int val; + + i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL); + if (!i2s_tdm) + return -ENOMEM; + + i2s_tdm->dev = &pdev->dev; + + of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev); + if (!of_id || !of_id->data) + return -EINVAL; + + spin_lock_init(&i2s_tdm->lock); + i2s_tdm->soc_data = (struct rk_i2s_soc_data *)of_id->data; + + rockchip_i2s_tdm_init_dai(i2s_tdm); + + i2s_tdm->frame_width = 64; + if (!of_property_read_u32(node, "rockchip,frame-width", &val)) { + if (val >= 32 && (val % 2 == 0) && val <= 512) { + i2s_tdm->frame_width = val; + } else { + dev_err(i2s_tdm->dev, "unsupported frame width: '%d'\n", + val); + return -EINVAL; + } + } + + i2s_tdm->clk_trcm = TRCM_TXRX; + if (of_property_read_bool(node, "rockchip,trcm-sync-tx-only")) + i2s_tdm->clk_trcm = TRCM_TX; + if (of_property_read_bool(node, "rockchip,trcm-sync-rx-only")) { + if (i2s_tdm->clk_trcm) { + dev_err(i2s_tdm->dev, "invalid trcm-sync configuration\n"); + return -EINVAL; + } + i2s_tdm->clk_trcm = TRCM_RX; + } + if (i2s_tdm->clk_trcm != TRCM_TXRX) + i2s_tdm_dai.symmetric_rate = 1; + + i2s_tdm->tdm_fsync_half_frame = + of_property_read_bool(node, "rockchip,tdm-fsync-half-frame"); + + i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf"); + if (IS_ERR(i2s_tdm->grf)) + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->grf), + "Error in rockchip,grf\n"); + + if (i2s_tdm->clk_trcm != TRCM_TXRX) { + cru_node = of_parse_phandle(node, "rockchip,cru", 0); + i2s_tdm->cru_base = of_iomap(cru_node, 0); + of_node_put(cru_node); + if (!i2s_tdm->cru_base) { + dev_err(i2s_tdm->dev, + "Missing or unsupported rockchip,cru node\n"); + return -ENOENT; + } + + i2s_tdm->tx_reset_id = of_i2s_resetid_get(node, "tx-m"); + i2s_tdm->rx_reset_id = of_i2s_resetid_get(node, "rx-m"); + } + + i2s_tdm->tx_reset = devm_reset_control_get_exclusive(&pdev->dev, + "tx-m"); + if (IS_ERR(i2s_tdm->tx_reset)) { + ret = PTR_ERR(i2s_tdm->tx_reset); + return dev_err_probe(i2s_tdm->dev, ret, + "Error in tx-m reset control\n"); + } + + i2s_tdm->rx_reset = devm_reset_control_get_exclusive(&pdev->dev, + "rx-m"); + if (IS_ERR(i2s_tdm->rx_reset)) { + ret = PTR_ERR(i2s_tdm->rx_reset); + return dev_err_probe(i2s_tdm->dev, ret, + "Error in rx-m reset control\n"); + } + + i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(i2s_tdm->hclk)) { + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->hclk), + "Failed to get clock hclk\n"); + } + + i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx"); + if (IS_ERR(i2s_tdm->mclk_tx)) { + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_tx), + "Failed to get clock mclk_tx\n"); + } + + i2s_tdm->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx"); + if (IS_ERR(i2s_tdm->mclk_rx)) { + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->mclk_rx), + "Failed to get clock mclk_rx\n"); + } + + i2s_tdm->io_multiplex = + of_property_read_bool(node, "rockchip,io-multiplex"); + + i2s_tdm->mclk_calibrate = + of_property_read_bool(node, "rockchip,mclk-calibrate"); + if (i2s_tdm->mclk_calibrate) { + i2s_tdm->mclk_tx_src = devm_clk_get(&pdev->dev, "mclk_tx_src"); + if (IS_ERR(i2s_tdm->mclk_tx_src)) { + return dev_err_probe(i2s_tdm->dev, + PTR_ERR(i2s_tdm->mclk_tx_src), + "Failed to get clock mclk_tx_src\n"); + } + i2s_tdm->mclk_rx_src = devm_clk_get(&pdev->dev, "mclk_rx_src"); + if (IS_ERR(i2s_tdm->mclk_rx_src)) { + return dev_err_probe(i2s_tdm->dev, + PTR_ERR(i2s_tdm->mclk_rx_src), + "Failed to get clock mclk_rx_src\n"); + } + i2s_tdm->mclk_root0 = devm_clk_get(&pdev->dev, "mclk_root0"); + if (IS_ERR(i2s_tdm->mclk_root0)) { + return dev_err_probe(i2s_tdm->dev, + PTR_ERR(i2s_tdm->mclk_root0), + "Failed to get clock mclk_root0\n"); + } + i2s_tdm->mclk_root1 = devm_clk_get(&pdev->dev, "mclk_root1"); + if (IS_ERR(i2s_tdm->mclk_root1)) { + return dev_err_probe(i2s_tdm->dev, + PTR_ERR(i2s_tdm->mclk_root1), + "Failed to get clock mclk_root1\n"); + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) { + return dev_err_probe(i2s_tdm->dev, PTR_ERR(regs), + "Failed to get resource IORESOURCE_MEM\n"); + } + + i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &rockchip_i2s_tdm_regmap_config); + if (IS_ERR(i2s_tdm->regmap)) { + return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->regmap), + "Failed to initialise regmap\n"); + } + + i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR; + i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + i2s_tdm->playback_dma_data.maxburst = 8; + + i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR; + i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + i2s_tdm->capture_dma_data.maxburst = 8; + + ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node); + if (ret < 0) { + dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n", ret); + return ret; + } + + ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node); + if (ret < 0) { + dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n", ret); + return ret; + } + + atomic_set(&i2s_tdm->refcount, 0); + dev_set_drvdata(&pdev->dev, i2s_tdm); + + ret = clk_prepare_enable(i2s_tdm->hclk); + if (ret) { + return dev_err_probe(i2s_tdm->dev, ret, + "Failed to enable clock hclk\n"); + } + + ret = i2s_tdm_prepare_enable_mclk(i2s_tdm); + if (ret) { + return dev_err_probe(i2s_tdm->dev, ret, + "Failed to enable one or more mclks\n"); + goto err_disable_hclk; + } + + if (i2s_tdm->mclk_calibrate) { + i2s_tdm->mclk_root0_initial_freq = clk_get_rate(i2s_tdm->mclk_root0); + i2s_tdm->mclk_root1_initial_freq = clk_get_rate(i2s_tdm->mclk_root1); + i2s_tdm->mclk_root0_freq = i2s_tdm->mclk_root0_initial_freq; + i2s_tdm->mclk_root1_freq = i2s_tdm->mclk_root1_initial_freq; + } + + pm_runtime_enable(&pdev->dev); + + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, + I2S_DMACR_TDL(16)); + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, + I2S_DMACR_RDL(16)); + regmap_update_bits(i2s_tdm->regmap, I2S_CKR, I2S_CKR_TRCM_MASK, + i2s_tdm->clk_trcm << I2S_CKR_TRCM_SHIFT); + + if (i2s_tdm->soc_data && i2s_tdm->soc_data->init) + i2s_tdm->soc_data->init(&pdev->dev, res->start); + + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_i2s_tdm_component, + &i2s_tdm_dai, 1); + + if (ret) { + dev_err(&pdev->dev, "Could not register DAI\n"); + goto err_suspend; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM\n"); + goto err_suspend; + } + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + i2s_tdm_runtime_suspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + +err_disable_hclk: + if (!IS_ERR(i2s_tdm->hclk)) + clk_disable_unprepare(i2s_tdm->hclk); + + return ret; +} + +static int rockchip_i2s_tdm_remove(struct platform_device *pdev) +{ + if (!pm_runtime_status_suspended(&pdev->dev)) + i2s_tdm_runtime_suspend(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int __maybe_unused rockchip_i2s_tdm_suspend(struct device *dev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + + regcache_mark_dirty(i2s_tdm->regmap); + + return 0; +} + +static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + return ret; + ret = regcache_sync(i2s_tdm->regmap); + pm_runtime_put(dev); + + return ret; +} + +static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = { + SET_RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend, + rockchip_i2s_tdm_resume) +}; + +static struct platform_driver rockchip_i2s_tdm_driver = { + .probe = rockchip_i2s_tdm_probe, + .remove = rockchip_i2s_tdm_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(rockchip_i2s_tdm_match), + .pm = &rockchip_i2s_tdm_pm_ops, + }, +}; +module_platform_driver(rockchip_i2s_tdm_driver); + +MODULE_DESCRIPTION("ROCKCHIP I2S/TDM ASoC Interface"); +MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, rockchip_i2s_tdm_match); diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.h b/sound/soc/rockchip/rockchip_i2s_tdm.h new file mode 100644 index 000000000000..0aa1c6da1e2c --- /dev/null +++ b/sound/soc/rockchip/rockchip_i2s_tdm.h @@ -0,0 +1,398 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver + * + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * Author: Sugar Zhang <sugar.zhang@rock-chips.com> + * + */ + +#ifndef _ROCKCHIP_I2S_TDM_H +#define _ROCKCHIP_I2S_TDM_H + +/* + * TXCR + * transmit operation control register + */ +#define I2S_TXCR_PATH_SHIFT(x) (23 + (x) * 2) +#define I2S_TXCR_PATH_MASK(x) (0x3 << I2S_TXCR_PATH_SHIFT(x)) +#define I2S_TXCR_PATH(x, v) ((v) << I2S_TXCR_PATH_SHIFT(x)) +#define I2S_TXCR_RCNT_SHIFT 17 +#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT) +#define I2S_TXCR_CSR_SHIFT 15 +#define I2S_TXCR_CSR(x) ((x) << I2S_TXCR_CSR_SHIFT) +#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT) +#define I2S_TXCR_HWT BIT(14) +#define I2S_TXCR_SJM_SHIFT 12 +#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT) +#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT) +#define I2S_TXCR_FBM_SHIFT 11 +#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT) +#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT) +#define I2S_TXCR_IBM_SHIFT 9 +#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT) +#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT) +#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT) +#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT) +#define I2S_TXCR_PBM_SHIFT 7 +#define I2S_TXCR_PBM_MODE(x) ((x) << I2S_TXCR_PBM_SHIFT) +#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT) +#define I2S_TXCR_TFS_SHIFT 5 +#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT) +#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT) +#define I2S_TXCR_TFS_TDM_PCM (2 << I2S_TXCR_TFS_SHIFT) +#define I2S_TXCR_TFS_TDM_I2S (3 << I2S_TXCR_TFS_SHIFT) +#define I2S_TXCR_TFS_MASK (3 << I2S_TXCR_TFS_SHIFT) +#define I2S_TXCR_VDW_SHIFT 0 +#define I2S_TXCR_VDW(x) (((x) - 1) << I2S_TXCR_VDW_SHIFT) +#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT) + +/* + * RXCR + * receive operation control register + */ +#define I2S_RXCR_PATH_SHIFT(x) (17 + (x) * 2) +#define I2S_RXCR_PATH_MASK(x) (0x3 << I2S_RXCR_PATH_SHIFT(x)) +#define I2S_RXCR_PATH(x, v) ((v) << I2S_RXCR_PATH_SHIFT(x)) +#define I2S_RXCR_CSR_SHIFT 15 +#define I2S_RXCR_CSR(x) ((x) << I2S_RXCR_CSR_SHIFT) +#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT) +#define I2S_RXCR_HWT BIT(14) +#define I2S_RXCR_SJM_SHIFT 12 +#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT) +#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT) +#define I2S_RXCR_FBM_SHIFT 11 +#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT) +#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT) +#define I2S_RXCR_IBM_SHIFT 9 +#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT) +#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT) +#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT) +#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT) +#define I2S_RXCR_PBM_SHIFT 7 +#define I2S_RXCR_PBM_MODE(x) ((x) << I2S_RXCR_PBM_SHIFT) +#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT) +#define I2S_RXCR_TFS_SHIFT 5 +#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT) +#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT) +#define I2S_RXCR_TFS_TDM_PCM (2 << I2S_RXCR_TFS_SHIFT) +#define I2S_RXCR_TFS_TDM_I2S (3 << I2S_RXCR_TFS_SHIFT) +#define I2S_RXCR_TFS_MASK (3 << I2S_RXCR_TFS_SHIFT) +#define I2S_RXCR_VDW_SHIFT 0 +#define I2S_RXCR_VDW(x) (((x) - 1) << I2S_RXCR_VDW_SHIFT) +#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT) + +/* + * CKR + * clock generation register + */ +#define I2S_CKR_TRCM_SHIFT 28 +#define I2S_CKR_TRCM(x) ((x) << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_MSS_SHIFT 27 +#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT) +#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT) +#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT) +#define I2S_CKR_CKP_SHIFT 26 +#define I2S_CKR_CKP_NORMAL (0 << I2S_CKR_CKP_SHIFT) +#define I2S_CKR_CKP_INVERTED (1 << I2S_CKR_CKP_SHIFT) +#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT) +#define I2S_CKR_RLP_SHIFT 25 +#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT) +#define I2S_CKR_RLP_INVERTED (1 << I2S_CKR_RLP_SHIFT) +#define I2S_CKR_RLP_MASK (1 << I2S_CKR_RLP_SHIFT) +#define I2S_CKR_TLP_SHIFT 24 +#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT) +#define I2S_CKR_TLP_INVERTED (1 << I2S_CKR_TLP_SHIFT) +#define I2S_CKR_TLP_MASK (1 << I2S_CKR_TLP_SHIFT) +#define I2S_CKR_MDIV_SHIFT 16 +#define I2S_CKR_MDIV(x) (((x) - 1) << I2S_CKR_MDIV_SHIFT) +#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT) +#define I2S_CKR_RSD_SHIFT 8 +#define I2S_CKR_RSD(x) (((x) - 1) << I2S_CKR_RSD_SHIFT) +#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT) +#define I2S_CKR_TSD_SHIFT 0 +#define I2S_CKR_TSD(x) (((x) - 1) << I2S_CKR_TSD_SHIFT) +#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT) + +/* + * FIFOLR + * FIFO level register + */ +#define I2S_FIFOLR_RFL_SHIFT 24 +#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT) +#define I2S_FIFOLR_TFL3_SHIFT 18 +#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT) +#define I2S_FIFOLR_TFL2_SHIFT 12 +#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT) +#define I2S_FIFOLR_TFL1_SHIFT 6 +#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT) +#define I2S_FIFOLR_TFL0_SHIFT 0 +#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT) + +/* + * DMACR + * DMA control register + */ +#define I2S_DMACR_RDE_SHIFT 24 +#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT) +#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT) +#define I2S_DMACR_RDL_SHIFT 16 +#define I2S_DMACR_RDL(x) (((x) - 1) << I2S_DMACR_RDL_SHIFT) +#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT) +#define I2S_DMACR_TDE_SHIFT 8 +#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT) +#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT) +#define I2S_DMACR_TDL_SHIFT 0 +#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT) +#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT) + +/* + * INTCR + * interrupt control register + */ +#define I2S_INTCR_RFT_SHIFT 20 +#define I2S_INTCR_RFT(x) (((x) - 1) << I2S_INTCR_RFT_SHIFT) +#define I2S_INTCR_RXOIC BIT(18) +#define I2S_INTCR_RXOIE_SHIFT 17 +#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT) +#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT) +#define I2S_INTCR_RXFIE_SHIFT 16 +#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT) +#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT) +#define I2S_INTCR_TFT_SHIFT 4 +#define I2S_INTCR_TFT(x) (((x) - 1) << I2S_INTCR_TFT_SHIFT) +#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT) +#define I2S_INTCR_TXUIC BIT(2) +#define I2S_INTCR_TXUIE_SHIFT 1 +#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT) +#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT) + +/* + * INTSR + * interrupt status register + */ +#define I2S_INTSR_TXEIE_SHIFT 0 +#define I2S_INTSR_TXEIE_DISABLE (0 << I2S_INTSR_TXEIE_SHIFT) +#define I2S_INTSR_TXEIE_ENABLE (1 << I2S_INTSR_TXEIE_SHIFT) +#define I2S_INTSR_RXOI_SHIFT 17 +#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT) +#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT) +#define I2S_INTSR_RXFI_SHIFT 16 +#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT) +#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT) +#define I2S_INTSR_TXUI_SHIFT 1 +#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT) +#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT) +#define I2S_INTSR_TXEI_SHIFT 0 +#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT) +#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT) + +/* + * XFER + * Transfer start register + */ +#define I2S_XFER_RXS_SHIFT 1 +#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT) +#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT) +#define I2S_XFER_TXS_SHIFT 0 +#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT) +#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT) + +/* + * CLR + * clear SCLK domain logic register + */ +#define I2S_CLR_RXC BIT(1) +#define I2S_CLR_TXC BIT(0) + +/* + * TXDR + * Transimt FIFO data register, write only. + */ +#define I2S_TXDR_MASK (0xff) + +/* + * RXDR + * Receive FIFO data register, write only. + */ +#define I2S_RXDR_MASK (0xff) + +/* + * TDM_CTRL + * TDM ctrl register + */ +#define TDM_FSYNC_WIDTH_SEL1_MSK GENMASK(20, 18) +#define TDM_FSYNC_WIDTH_SEL1(x) (((x) - 1) << 18) +#define TDM_FSYNC_WIDTH_SEL0_MSK BIT(17) +#define TDM_FSYNC_WIDTH_HALF_FRAME 0 +#define TDM_FSYNC_WIDTH_ONE_FRAME BIT(17) +#define TDM_SHIFT_CTRL_MSK GENMASK(16, 14) +#define TDM_SHIFT_CTRL(x) ((x) << 14) +#define TDM_SLOT_BIT_WIDTH_MSK GENMASK(13, 9) +#define TDM_SLOT_BIT_WIDTH(x) (((x) - 1) << 9) +#define TDM_FRAME_WIDTH_MSK GENMASK(8, 0) +#define TDM_FRAME_WIDTH(x) (((x) - 1) << 0) + +/* + * CLKDIV + * Mclk div register + */ +#define I2S_CLKDIV_TXM_SHIFT 0 +#define I2S_CLKDIV_TXM(x) (((x) - 1) << I2S_CLKDIV_TXM_SHIFT) +#define I2S_CLKDIV_TXM_MASK (0xff << I2S_CLKDIV_TXM_SHIFT) +#define I2S_CLKDIV_RXM_SHIFT 8 +#define I2S_CLKDIV_RXM(x) (((x) - 1) << I2S_CLKDIV_RXM_SHIFT) +#define I2S_CLKDIV_RXM_MASK (0xff << I2S_CLKDIV_RXM_SHIFT) + +/* Clock divider id */ +enum { + ROCKCHIP_DIV_MCLK = 0, + ROCKCHIP_DIV_BCLK, +}; + +/* channel select */ +#define I2S_CSR_SHIFT 15 +#define I2S_CHN_2 (0 << I2S_CSR_SHIFT) +#define I2S_CHN_4 (1 << I2S_CSR_SHIFT) +#define I2S_CHN_6 (2 << I2S_CSR_SHIFT) +#define I2S_CHN_8 (3 << I2S_CSR_SHIFT) + +/* io direction cfg register */ +#define I2S_IO_DIRECTION_MASK (7) +#define I2S_IO_8CH_OUT_2CH_IN (7) +#define I2S_IO_6CH_OUT_4CH_IN (3) +#define I2S_IO_4CH_OUT_6CH_IN (1) +#define I2S_IO_2CH_OUT_8CH_IN (0) + +/* I2S REGS */ +#define I2S_TXCR (0x0000) +#define I2S_RXCR (0x0004) +#define I2S_CKR (0x0008) +#define I2S_TXFIFOLR (0x000c) +#define I2S_DMACR (0x0010) +#define I2S_INTCR (0x0014) +#define I2S_INTSR (0x0018) +#define I2S_XFER (0x001c) +#define I2S_CLR (0x0020) +#define I2S_TXDR (0x0024) +#define I2S_RXDR (0x0028) +#define I2S_RXFIFOLR (0x002c) +#define I2S_TDM_TXCR (0x0030) +#define I2S_TDM_RXCR (0x0034) +#define I2S_CLKDIV (0x0038) + +#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16)) + +/* PX30 GRF CONFIGS */ +#define PX30_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 13, 12) +#define PX30_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 13, 12) +#define PX30_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5) +#define PX30_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5) + +#define PX30_I2S0_CLK_TXONLY \ + (PX30_I2S0_MCLK_OUT_SRC_FROM_TX | PX30_I2S0_CLK_IN_SRC_FROM_TX) + +#define PX30_I2S0_CLK_RXONLY \ + (PX30_I2S0_MCLK_OUT_SRC_FROM_RX | PX30_I2S0_CLK_IN_SRC_FROM_RX) + +/* RK1808 GRF CONFIGS */ +#define RK1808_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2) +#define RK1808_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2) +#define RK1808_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 1, 0) +#define RK1808_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 1, 0) + +#define RK1808_I2S0_CLK_TXONLY \ + (RK1808_I2S0_MCLK_OUT_SRC_FROM_TX | RK1808_I2S0_CLK_IN_SRC_FROM_TX) + +#define RK1808_I2S0_CLK_RXONLY \ + (RK1808_I2S0_MCLK_OUT_SRC_FROM_RX | RK1808_I2S0_CLK_IN_SRC_FROM_RX) + +/* RK3308 GRF CONFIGS */ +#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 10, 10) +#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 10, 10) +#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 9, 9) +#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 9, 9) +#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 8, 8) +#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 8, 8) +#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2) +#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2) +#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 1, 1) +#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 1, 1) +#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 0, 0) +#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 0, 0) + +#define RK3308_I2S0_CLK_TXONLY \ + (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX | \ + RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX | \ + RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX) + +#define RK3308_I2S0_CLK_RXONLY \ + (RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX | \ + RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX | \ + RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX) + +#define RK3308_I2S1_CLK_TXONLY \ + (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX | \ + RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX | \ + RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX) + +#define RK3308_I2S1_CLK_RXONLY \ + (RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX | \ + RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX | \ + RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX) + +/* RK3568 GRF CONFIGS */ +#define RK3568_I2S1_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5) +#define RK3568_I2S1_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5) + +#define RK3568_I2S1_CLK_TXONLY \ + RK3568_I2S1_MCLK_OUT_SRC_FROM_TX + +#define RK3568_I2S1_CLK_RXONLY \ + RK3568_I2S1_MCLK_OUT_SRC_FROM_RX + +#define RK3568_I2S3_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 15, 15) +#define RK3568_I2S3_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 15, 15) +#define RK3568_I2S3_SCLK_SRC_FROM_TX HIWORD_UPDATE(1, 7, 7) +#define RK3568_I2S3_SCLK_SRC_FROM_RX HIWORD_UPDATE(0, 7, 7) +#define RK3568_I2S3_LRCK_SRC_FROM_TX HIWORD_UPDATE(1, 6, 6) +#define RK3568_I2S3_LRCK_SRC_FROM_RX HIWORD_UPDATE(0, 6, 6) + +#define RK3568_I2S3_MCLK_TXONLY \ + RK3568_I2S3_MCLK_OUT_SRC_FROM_TX + +#define RK3568_I2S3_CLK_TXONLY \ + (RK3568_I2S3_SCLK_SRC_FROM_TX | \ + RK3568_I2S3_LRCK_SRC_FROM_TX) + +#define RK3568_I2S3_MCLK_RXONLY \ + RK3568_I2S3_MCLK_OUT_SRC_FROM_RX + +#define RK3568_I2S3_CLK_RXONLY \ + (RK3568_I2S3_SCLK_SRC_FROM_RX | \ + RK3568_I2S3_LRCK_SRC_FROM_RX) + +#define RK3568_I2S3_MCLK_IE HIWORD_UPDATE(0, 3, 3) +#define RK3568_I2S3_MCLK_OE HIWORD_UPDATE(1, 3, 3) +#define RK3568_I2S2_MCLK_IE HIWORD_UPDATE(0, 2, 2) +#define RK3568_I2S2_MCLK_OE HIWORD_UPDATE(1, 2, 2) +#define RK3568_I2S1_MCLK_TX_IE HIWORD_UPDATE(0, 1, 1) +#define RK3568_I2S1_MCLK_TX_OE HIWORD_UPDATE(1, 1, 1) +#define RK3568_I2S1_MCLK_RX_IE HIWORD_UPDATE(0, 0, 0) +#define RK3568_I2S1_MCLK_RX_OE HIWORD_UPDATE(1, 0, 0) + +/* RV1126 GRF CONFIGS */ +#define RV1126_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 9, 9) +#define RV1126_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 9, 9) + +#define RV1126_I2S0_CLK_TXONLY \ + RV1126_I2S0_MCLK_OUT_SRC_FROM_TX + +#define RV1126_I2S0_CLK_RXONLY \ + RV1126_I2S0_MCLK_OUT_SRC_FROM_RX + +#endif /* _ROCKCHIP_I2S_TDM_H */
This commit adds support for the rockchip i2s-tdm controller, which enables audio output on the following rockchip SoCs: - px30 - rk1808 - rk3308 - rk3566 - rk3568 - rv1126 This is a cleaned up version of the downstream vendor kernel's driver. It can be enabled through the SND_SOC_ROCKCHIP_I2S_TDM configuration option. Signed-off-by: Nicolas Frattaroli <frattaroli.nicolas@gmail.com> --- MAINTAINERS | 6 + sound/soc/rockchip/Kconfig | 11 + sound/soc/rockchip/Makefile | 2 + sound/soc/rockchip/rockchip_i2s_tdm.c | 1845 +++++++++++++++++++++++++ sound/soc/rockchip/rockchip_i2s_tdm.h | 398 ++++++ 5 files changed, 2262 insertions(+) create mode 100644 sound/soc/rockchip/rockchip_i2s_tdm.c create mode 100644 sound/soc/rockchip/rockchip_i2s_tdm.h