From 2852d2419ffa43c65fdacfea8e725f0d2071c956 Mon Sep 17 00:00:00 2001 From: pmedawala Date: Wed, 28 Sep 2022 06:58:55 +0000 Subject: [PATCH] nvidia: oot: add tegra-safety-audio build support Copied sound/tegra-safety-audio source files to nvidia-oot folder Bug 3735757 Change-Id: I8a18ee25a6c33a00e6b28a2e09b6576197a35652 Signed-off-by: pmedawala Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2783432 Reviewed-by: Bitan Biswas Reviewed-by: Uday Gupta Reviewed-by: Laxman Dewangan Tested-by: Uday Gupta GVS: Gerrit_Virtual_Submit --- Makefile | 3 + sound/tegra-safety-audio/Makefile | 5 + sound/tegra-safety-audio/i2s.c | 476 ++++++++++++++ sound/tegra-safety-audio/sound-card.c | 731 ++++++++++++++++++++++ sound/tegra-safety-audio/tegra_i2s.h | 129 ++++ sound/tegra-safety-audio/tegra_i2s_regs.h | 238 +++++++ 6 files changed, 1582 insertions(+) create mode 100644 sound/tegra-safety-audio/Makefile create mode 100644 sound/tegra-safety-audio/i2s.c create mode 100644 sound/tegra-safety-audio/sound-card.c create mode 100644 sound/tegra-safety-audio/tegra_i2s.h create mode 100644 sound/tegra-safety-audio/tegra_i2s_regs.h diff --git a/Makefile b/Makefile index c8af276a..7d9fd627 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,6 @@ obj-m += drivers/ obj-m += sound/soc/tegra/ +ifeq ($(shell test $$VERSION -lt 6; echo $$?),0) +obj-m += sound/tegra-safety-audio/ +endif diff --git a/sound/tegra-safety-audio/Makefile b/sound/tegra-safety-audio/Makefile new file mode 100644 index 00000000..abe8f4c7 --- /dev/null +++ b/sound/tegra-safety-audio/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +safety-i2s-objs := i2s.o sound-card.o +obj-m += safety-i2s.o diff --git a/sound/tegra-safety-audio/i2s.c b/sound/tegra-safety-audio/i2s.c new file mode 100644 index 00000000..abeeb9e1 --- /dev/null +++ b/sound/tegra-safety-audio/i2s.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#include +#include +#include +#include "tegra_i2s_regs.h" +#include "tegra_i2s.h" +#include + +static inline struct i2s_dev *i2s_inst(unsigned int id) +{ + static struct i2s_dev *i2s; + + if (!i2s) + i2s = safety_i2s_get_priv(); + + WARN_ON(id >= NUM_SAFETY_I2S_INST); + + return &i2s[id]; +} +#define I2S_BASE(id) ((i2s_inst(id))->base) + +#ifdef SAFETY_I2S_DEBUG +void i2s_dump_all_regs(unsigned int id) +{ + int i; + + pr_alert("RX Registers:\n"); + for (i = 0; i <= 0x2c; i += 4) + pr_alert("0x%08x = 0x%08x\n", 0x02450000 + id * 0x10000 + i, + readl(I2S_BASE(id) + i)); + + pr_alert("TX Registers:\n"); + for (i = 0x80; i <= 0xb0; i += 4) + pr_alert("0x%08x = 0x%08x\n", 0x02450000 + id * 0x10000 + i, + readl(I2S_BASE(id) + i)); + + pr_alert("Common Registers:\n"); + for (i = 0x100; i <= 0x120; i += 4) + pr_alert("0x%08x = 0x%08x\n", 0x02450000 + id * 0x10000 + i, + readl(I2S_BASE(id) + i)); + +} +#endif + +static unsigned int is_i2s_enabled(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_ENABLE); + + return (val & T234_I2S_EN_MASK); +} + +unsigned int i2s_enable(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_ENABLE); + val |= T234_I2S_EN; + writel(val, I2S_BASE(id) + T234_I2S_ENABLE); + + return 0; +} + +unsigned int i2s_disable(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_ENABLE); + val &= ~T234_I2S_EN; + writel(val, I2S_BASE(id) + T234_I2S_ENABLE); + + return 0; +} + +static unsigned int is_i2s_tx_enabled(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE); + + return (val & T234_I2S_EN_MASK); +} + +unsigned int i2s_enable_tx(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE); + val |= T234_I2S_TX_EN; + writel(val, I2S_BASE(id) + T234_I2S_TX_ENABLE); + + return 0; +} + +static unsigned int is_i2s_rx_enabled(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE); + + return (val & T234_I2S_EN_MASK); +} + +unsigned int i2s_enable_rx(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE); + val |= T234_I2S_RX_EN; + writel(val, I2S_BASE(id) + T234_I2S_RX_ENABLE); + + return 0; +} + +static unsigned int is_i2s_loopback_enabled(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_CTRL); + + return !!(val & T234_I2S_CTRL_LPBK_MASK); +} + +unsigned int i2s_set_loopback(unsigned int id, unsigned int loopback_enable) +{ + unsigned int val; + unsigned int enable; + unsigned int rx_enable; + unsigned int tx_enable; + + if (loopback_enable == is_i2s_loopback_enabled(id)) { + pr_info("I2S%d already has loopback in %s state\n", + (id + 1), loopback_enable ? "enabled" : "disabled"); + return 0; + } + + /* I2S needs to be disabled before enabling Loopback */ + enable = is_i2s_enabled(id); + + if (enable) { + tx_enable = is_i2s_tx_enabled(id); + if (tx_enable) + i2s_disable_tx(id); + + rx_enable = is_i2s_rx_enabled(id); + if (rx_enable) + i2s_disable_rx(id); + + i2s_disable(id); + } + + val = readl(I2S_BASE(id) + T234_I2S_CTRL); + val = loopback_enable ? (val | T234_I2S_CTRL_LPBK_EN) : + (val & ~T234_I2S_CTRL_LPBK_MASK); + writel(val, I2S_BASE(id) + T234_I2S_CTRL); + + if (enable) { + i2s_enable(id); + + if (rx_enable) + i2s_enable_rx(id); + + if (tx_enable) + i2s_enable_tx(id); + } + + pr_info("I2S%d loopback set to %s state\n", + (id + 1), loopback_enable ? "enabled" : "disabled"); + return 0; +} + +static int i2s_sw_reset(unsigned int id, int direction, int timeout) +{ + unsigned int sw_reset_reg, sw_reset_mask; + unsigned int sw_reset_en, sw_reset_default; + unsigned int tx_fifo_ctrl, rx_fifo_ctrl, tx_ctrl, rx_ctrl, ctrl, val; + int wait = timeout; + + tx_fifo_ctrl = readl(I2S_BASE(id) + T234_I2S_TX_FIFO_CTRL); + rx_fifo_ctrl = readl(I2S_BASE(id) + T234_I2S_RX_FIFO_CTRL); + tx_ctrl = readl(I2S_BASE(id) + T234_I2S_TX_CTRL); + rx_ctrl = readl(I2S_BASE(id) + T234_I2S_RX_CTRL); + ctrl = readl(I2S_BASE(id) + T234_I2S_CTRL); + + if (direction == PCM_STREAM_CAPTURE) { + sw_reset_reg = T234_I2S_RX_SOFT_RESET; + sw_reset_mask = T234_I2S_RX_SOFT_RESET_MASK; + sw_reset_en = T234_I2S_RX_SOFT_RESET_EN; + sw_reset_default = T234_I2S_RX_SOFT_RESET_DEFAULT; + } else { + sw_reset_reg = T234_I2S_TX_SOFT_RESET; + sw_reset_mask = T234_I2S_TX_SOFT_RESET_MASK; + sw_reset_en = T234_I2S_TX_SOFT_RESET_EN; + sw_reset_default = T234_I2S_TX_SOFT_RESET_DEFAULT; + } + + updatel(I2S_BASE(id) + sw_reset_reg, sw_reset_mask, sw_reset_en); + + do { + val = readl(I2S_BASE(id) + sw_reset_reg); + wait--; + udelay(10); + + if (!wait) { + pr_err("RESET bit not cleared yet\n"); + return -1; + } + } while (val & sw_reset_mask); + + updatel(I2S_BASE(id) + sw_reset_reg, sw_reset_mask, sw_reset_default); + + writel(tx_fifo_ctrl, I2S_BASE(id) + T234_I2S_TX_FIFO_CTRL); + writel(rx_fifo_ctrl, I2S_BASE(id) + T234_I2S_RX_FIFO_CTRL); + writel(tx_ctrl, I2S_BASE(id) + T234_I2S_TX_CTRL); + writel(rx_ctrl, I2S_BASE(id) + T234_I2S_RX_CTRL); + writel(ctrl, I2S_BASE(id) + T234_I2S_CTRL); + + return 0; +} + +static int i2s_get_status(unsigned int id, int direction) +{ + unsigned int status_reg; + + status_reg = (direction == PCM_STREAM_CAPTURE) ? + T234_I2S_RX_STATUS : + T234_I2S_TX_STATUS; + + return readl(I2S_BASE(id) + status_reg); +} + +/* Should be called after disabling RX */ +static int i2s_rx_stop(unsigned int id) +{ + int dcnt = 10, ret; + int status; + + /* wait until I2S RX ENABLE bit is cleared + * and FIFO becomes empty as the DMA is still on + */ + while (((status = i2s_get_status(id, PCM_STREAM_CAPTURE)) & + T234_I2S_RX_STATUS_ENABLED) && dcnt--) + udelay(10); + + if (dcnt < 0 || !(status & T234_I2S_RX_STATUS_FIFO_EMPTY)) { + /* HW needs sw reset to make sure previous trans be clean */ + ret = i2s_sw_reset(id, PCM_STREAM_CAPTURE, 0xffff); + if (ret) { + pr_err("Failed at I2S%d_RX sw reset\n", id + 7); + return ret; + } + } + return 0; +} + +unsigned int i2s_disable_rx(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_RX_ENABLE); + val &= ~T234_I2S_RX_EN; + writel(val, I2S_BASE(id) + T234_I2S_RX_ENABLE); + + i2s_rx_stop(id); + + return 0; +} + +/* Should be called after disabling TX */ +static int i2s_tx_stop(unsigned int id) +{ + int dcnt = 10, ret; + int status; + + /* wait until I2S TX ENABLE bit is cleared */ + while ((status = i2s_get_status(id, PCM_STREAM_PLAYBACK) & + T234_I2S_TX_STATUS_ENABLED) && dcnt--) + udelay(10); + + if (dcnt < 0 || !(status & T234_I2S_TX_STATUS_FIFO_EMPTY)) { + /* HW needs sw reset to make sure previous trans be clean */ + ret = i2s_sw_reset(id, PCM_STREAM_PLAYBACK, 0xffff); + if (ret) { + pr_err("Failed at I2S%d_TX sw reset\n", id + 7); + return ret; + } + } + return 0; +} + +unsigned int i2s_disable_tx(unsigned int id) +{ + unsigned int val; + + val = readl(I2S_BASE(id) + T234_I2S_TX_ENABLE); + val &= ~T234_I2S_TX_EN; + writel(val, I2S_BASE(id) + T234_I2S_TX_ENABLE); + + i2s_tx_stop(id); + + return 0; +} + +int i2s_configure(unsigned int id, struct i2s_config *config) +{ + unsigned int ctrl = 0, timing = 0, rxctrl = 0, txctrl = 0; + unsigned int slotctrl = 0, tx_slotctrl = 0, rx_slotctrl = 0; + unsigned int i2sclock, bitcnt; + unsigned int clock_trim = config->clock_trim; + unsigned int fifo_ctrl = 0, threshold = 0; + volatile void __iomem *i2s_base = I2S_BASE(id); + + switch (config->mode) { + case I2S_FRAME_FORMAT_I2S: + if (config->clock_mode == I2S_MASTER) + ctrl |= T234_I2S_CTRL_MASTER_EN; + + ctrl |= (config->fsync_width << + T234_I2S_CTRL_FSYNC_WIDTH_SHIFT); + ctrl &= ~T234_I2S_CTRL_EDGE_CTRL_MASK; + if (config->edge_ctrl == I2S_CLK_NEG_EDGE) + ctrl |= T234_I2S_CTRL_EDGE_CTRL_NEG_EDGE; + + /* LRCK_MODE */ + ctrl &= ~T234_I2S_CTRL_FRAME_FORMAT_MASK; + + ctrl &= ~T234_I2S_CTRL_LRCK_POLARITY_MASK; + if (config->clock_polarity == LRCK_HIGH) + ctrl |= T234_I2S_CTRL_LRCK_POLARITY_HIGH; + + i2sclock = config->srate * config->bit_size * config->channels; + + if (config->bclk_ratio != 0) + i2sclock *= config->bclk_ratio; + + bitcnt = (i2sclock / config->srate) - 1; + + if (i2sclock % (2 * config->srate)) + timing |= T234_I2S_TIMING_NON_SYM_EN; + + timing |= ((bitcnt >> 1) << + T234_I2S_TIMING_CHANNEL_BIT_CNT_SHIFT); + + ctrl |= (((config->bit_size >> 2) - 1) << + T234_I2S_CTRL_BIT_SIZE_SHIFT); + rxctrl |= (config->offset << + T234_I2S_RX_CTRL_DATA_OFFSET_SHIFT) & + T234_I2S_RX_CTRL_DATA_OFFSET_MASK; + txctrl |= (config->offset << + T234_I2S_TX_CTRL_DATA_OFFSET_SHIFT) & + T234_I2S_TX_CTRL_DATA_OFFSET_MASK; + + if (config->pcm_mask_bits) { + rxctrl |= ((config->pcm_mask_bits) << + T234_I2S_RX_CTRL_MASK_BITS_SHIFT); + txctrl |= ((config->pcm_mask_bits) << + T234_I2S_TX_CTRL_MASK_BITS_SHIFT); + } + + txctrl &= ~T234_I2S_TX_CTRL_HIGHZ_CTRL_MASK; + + if (config->highz_ctrl == 1) + txctrl |= T234_I2S_TX_CTRL_HIGHZ_CTRL_HIGHZ; + else if (config->highz_ctrl == 2) + txctrl |= + T234_I2S_TX_CTRL_HIGHZ_CTRL_HIGHZ_ON_HALF_BIT_CLK; + + writel(timing, i2s_base + T234_I2S_TIMING); + + break; + + case I2S_FRAME_FORMAT_TDM: + if (config->clock_mode == I2S_MASTER) + ctrl |= T234_I2S_CTRL_MASTER_EN; + ctrl |= (config->fsync_width << + T234_I2S_CTRL_FSYNC_WIDTH_SHIFT); + + ctrl &= ~T234_I2S_CTRL_EDGE_CTRL_MASK; + if (config->edge_ctrl == I2S_CLK_NEG_EDGE) + ctrl |= T234_I2S_CTRL_EDGE_CTRL_NEG_EDGE; + + ctrl &= ~T234_I2S_CTRL_FRAME_FORMAT_MASK; + ctrl |= T234_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE; + + ctrl &= ~T234_I2S_CTRL_LRCK_POLARITY_MASK; + if (config->clock_polarity == LRCK_HIGH) + ctrl |= T234_I2S_CTRL_LRCK_POLARITY_HIGH; + + ctrl |= (((config->bit_size >> 2) - 1) << + T234_I2S_CTRL_BIT_SIZE_SHIFT); + + i2sclock = config->srate * config->bit_size * config->channels; + + if (config->bclk_ratio != 0) + i2sclock *= config->bclk_ratio; + + bitcnt = (i2sclock / config->srate) - 1; + + if (i2sclock % (2 * config->srate)) + timing |= T234_I2S_TIMING_NON_SYM_EN; + + timing |= (bitcnt << T234_I2S_TIMING_CHANNEL_BIT_CNT_SHIFT); + + rxctrl |= (config->offset << + T234_I2S_RX_CTRL_DATA_OFFSET_SHIFT) & + T234_I2S_RX_CTRL_DATA_OFFSET_MASK; + txctrl |= (config->offset << + T234_I2S_TX_CTRL_DATA_OFFSET_SHIFT) & + T234_I2S_TX_CTRL_DATA_OFFSET_MASK; + + if (config->pcm_mask_bits) { + rxctrl |= ((config->pcm_mask_bits) << + T234_I2S_RX_CTRL_MASK_BITS_SHIFT); + txctrl |= ((config->pcm_mask_bits) << + T234_I2S_TX_CTRL_MASK_BITS_SHIFT); + } + + txctrl &= ~T234_I2S_TX_CTRL_HIGHZ_CTRL_MASK; + + if (config->highz_ctrl == 1) + txctrl |= T234_I2S_TX_CTRL_HIGHZ_CTRL_HIGHZ; + else if (config->highz_ctrl == 2) + txctrl |= + T234_I2S_TX_CTRL_HIGHZ_CTRL_HIGHZ_ON_HALF_BIT_CLK; + + slotctrl |= ((config->total_slots - 1) << + T234_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT); + + tx_slotctrl |= (config->tx_mask << + T234_I2S_TX_SLOT_CTRL_SLOT_ENABLES_SHIFT); + rx_slotctrl |= (config->rx_mask << + T234_I2S_RX_SLOT_CTRL_SLOT_ENABLES_SHIFT); + + writel(timing, i2s_base + T234_I2S_TIMING); + break; + + default: + return -1; + } + + if (clock_trim > T234_I2S_SCLK_TRIM_SEL_MASK) { + pr_alert("Clock trim invalid\n"); + return -1; + } + clock_trim = ((clock_trim & T234_I2S_SCLK_TRIM_SEL_MASK) << + T234_I2S_SCLK_TRIM_SEL_SHIFT); + + //TODO: Add proper register offset macros and masks etc. + fifo_ctrl = (((config->channels - 1) << 4) & (0xf << 4)) | + (((config->channels) << 16) & (0x7f << 16)) | + (1 << 24); + + threshold = 0; + + /* Overwrite the timing register only in the master mode. + * Setting it to 0x0 causes noise during slave mode playback. + * Setting the I2S control registers + */ + writel(ctrl, i2s_base + T234_I2S_CTRL); + writel(rxctrl, i2s_base + T234_I2S_RX_CTRL); + writel(txctrl, i2s_base + T234_I2S_TX_CTRL); + writel(slotctrl, i2s_base + T234_I2S_SLOT_CTRL); + writel(tx_slotctrl, i2s_base + T234_I2S_TX_SLOT_CTRL); + writel(rx_slotctrl, i2s_base + T234_I2S_RX_SLOT_CTRL); + writel(clock_trim, i2s_base + T234_I2S_CLK_TRIM); + writel(fifo_ctrl, i2s_base + T234_I2S_RX_FIFO_CTRL); + writel(fifo_ctrl, i2s_base + T234_I2S_TX_FIFO_CTRL); + writel(threshold, i2s_base + T234_I2S_TX_START_THRESHOLD); + + return 0; +} \ No newline at end of file diff --git a/sound/tegra-safety-audio/sound-card.c b/sound/tegra-safety-audio/sound-card.c new file mode 100644 index 00000000..1fb038dd --- /dev/null +++ b/sound/tegra-safety-audio/sound-card.c @@ -0,0 +1,731 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#define pr_fmt(msg) "Safety I2S: " msg + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra_i2s.h" + +struct safety_audio_priv { + struct snd_card *card; +}; + +static int safety_i2s_trigger(struct snd_pcm_substream *substream, int cmd); + +static const char * const clk_names[] = { + "pll_a_out0", + "i2s7", "i2s7_clk_parent", "i2s7_ext_audio_sync", + "i2s7_audio_sync", "i2s7_sync_input", + "i2s8", "i2s8_clk_parent", "i2s8_ext_audio_sync", + "i2s8_audio_sync", "i2s8_sync_input", +}; + +static const char * const reset_names[] = { + "i2s7_reset", "i2s8_reset" +}; + +//TODO: Either encapsulate it or allocate dynamically +static struct i2s_dev i2s[NUM_SAFETY_I2S_INST]; +static unsigned int enabled_i2s_mask[NUM_SAFETY_I2S_INST]; +static struct safety_audio_priv *priv; + +static const struct snd_pcm_hardware t234_pcm_hardware = { + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 16, + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = PAGE_SIZE * 16, + .period_bytes_max = PAGE_SIZE * 32, + .periods_min = 1, + .periods_max = 4, + .fifo_size = 256, + .buffer_bytes_max = PAGE_SIZE * 128, +}; + +static const struct i2s_config i2s_defaults = { + .srate = 48000, + .channels = 8, + .fsync_width = 255, + .bclk_ratio = 1, + .pcm_mask_bits = 0, + .highz_ctrl = 0, + .bit_size = 32, + .total_slots = 8 +}; + +struct i2s_dev *safety_i2s_get_priv(void) +{ + return i2s; +} + +static int loopback_control_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uc) +{ + int id = (int)kctl->private_value; + int enable = uc->value.integer.value[0]; + + return i2s_set_loopback(id, enable); +} + +static int loopback_control_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uc) +{ + return 0; +} + +static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "I2S7 Loopback", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .put = loopback_control_put, + .get = loopback_control_get, + .info = snd_myctl_mono_info, + .private_value = 0 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "I2S8 Loopback", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .put = loopback_control_put, + .get = loopback_control_get, + .info = snd_myctl_mono_info, + .private_value = 1 + } +}; + +static int safety_i2s_add_kcontrols(struct snd_card *card, int id) +{ + return snd_ctl_add(card, snd_ctl_new1(&controls[id], i2s)); +} + +static int prealloc_dma_buff(struct snd_pcm *pcm, unsigned int stream, size_t size) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buff = &substream->dma_buffer; + + buff->area = dma_alloc_coherent(pcm->card->dev, size, + &buff->addr, GFP_KERNEL); + if (!buff->area) + return -ENOMEM; + + buff->private_data = NULL; + buff->dev.type = SNDRV_DMA_TYPE_DEV; + buff->dev.dev = pcm->card->dev; + buff->bytes = size; + + return 0; +} + +static int setup_plls(struct device *dev) +{ + /* Do nothing for now */ + + return 0; +} + +static int i2s_reset_init_and_deassert(struct device *dev, unsigned int id) +{ + struct reset_control *reset; + int ret; + + reset = of_reset_control_get(dev->of_node, reset_names[id]); + if (IS_ERR(reset)) { + pr_alert("No reset information found in DT, skipping..."); + return PTR_ERR(reset); + } + + i2s[id].reset = reset; + pr_alert("Clearing reset for i2s%d... ", id + 7); + ret = reset_control_deassert(reset); + if (ret) + pr_alert("Failed!\n"); + else + pr_alert("Success!\n"); + + return ret; +} + +static int i2s_clock_init(struct device *dev, unsigned int id) +{ + i2s[id].audio_sync_input = devm_clk_get(dev, + clk_names[id * CLK_NUM_ENTRIES + CLK_AUDIO_INPUT_SYNC]); + if (IS_ERR(i2s[id].audio_sync_input)) { + pr_alert("Could not get audio_sync_input clock from DT\n"); + return -EINVAL; + } + + i2s[id].audio_sync = devm_clk_get(dev, + clk_names[id * CLK_NUM_ENTRIES + CLK_AUDIO_SYNC]); + if (IS_ERR(i2s[id].audio_sync)) { + pr_alert("Could not get audio_sync clock from DT\n"); + return -EINVAL; + } + + i2s[id].i2s_sync = devm_clk_get(dev, + clk_names[id * CLK_NUM_ENTRIES + CLK_I2S_SYNC]); + if (IS_ERR(i2s[id].i2s_sync)) { + pr_alert("Could not get i2s_sync clock from DT\n"); + return -EINVAL; + } + + i2s[id].clk_i2s = devm_clk_get(dev, + clk_names[id * CLK_NUM_ENTRIES + CLK_I2S]); + if (IS_ERR(i2s[id].clk_i2s)) { + pr_alert("Could not get clk_i2s clock from DT\n"); + return -EINVAL; + } + + i2s[id].clk_i2s_src = devm_clk_get(dev, + clk_names[id * CLK_NUM_ENTRIES + CLK_I2S_SOURCE]); + if (IS_ERR(i2s[id].clk_i2s_src)) { + pr_alert("Could not get clk_i2s_src clock from DT\n"); + return -EINVAL; + } + + return 0; +} + +static int32_t +i2s_get_mode(const void *mode, uint32_t *tx_mode, uint32_t *data_offset) +{ + uint32_t i; + struct { + char *name; + uint32_t mode; + uint32_t data_offset; + } i2s_mode[] = { + {"dsp_a", 1, 1}, + {"dsp_b", 1, 0}, + {"i2s", 0, 1}, + }; + + for (i = 0; i < ARRAY_SIZE(i2s_mode); i++) { + if (strcmp(mode, i2s_mode[i].name) == 0) { + *tx_mode = i2s_mode[i].mode; + *data_offset = i2s_mode[i].data_offset; + return 0; + } + } + return -1; +} + +#ifdef SAFETY_I2S_DEBUG +static void dump_config(struct i2s_config *config) +{ +#define dump(x) pr_alert("%s = %u\n", #x, x) + + dump(config->mode); + dump(config->clock_mode); + dump(config->clock_polarity); + dump(config->edge_ctrl); + dump(config->total_slots); + dump(config->bclk); + dump(config->bit_size); + dump(config->channels); + dump(config->offset); + dump(config->tx_mask); + dump(config->rx_mask); + dump(config->srate); + dump(config->bclk_ratio); + dump(config->fsync_width); + dump(config->pcm_mask_bits); + dump(config->highz_ctrl); + dump(config->clock_trim); +} +#endif + +static int i2s_parse_dt(struct device *dev, unsigned int id) +{ + char name[5]; + struct device_node *i2s_node; + + struct i2s_config *i2s_config; + + const void *prop; + int ret = 0; + + ret = sprintf(name, I2S_DT_NODE, I2S_NODE_START_INDEX + id); + if (ret < 0) + return -EINVAL; + + i2s_config = &i2s[id].config; + memcpy(i2s_config, &i2s_defaults, sizeof(i2s_config[0])); + + i2s_config->clock_polarity = 1; + + i2s_node = of_get_child_by_name(dev->of_node, name); + if (i2s_node == NULL) { + pr_alert("Invalid device tree node\n"); + return -EINVAL; + } + + if (of_find_property(i2s_node, "frame-slave", NULL)) + i2s_config->clock_mode = 1; + + prop = of_get_property(i2s_node, "format", NULL); + if (prop != NULL) { + i2s_get_mode(prop, &i2s_config->mode, &i2s_config->offset); + if (strcmp("i2s", prop) == 0) + i2s_config->clock_polarity = 0; + } + + if (of_find_property(i2s_node, "bitclock-inversion", NULL)) + i2s_config->edge_ctrl = 1; + + if (of_find_property(i2s_node, "frame-inversion", NULL)) + i2s_config->clock_polarity = !i2s_config->clock_polarity; + + prop = of_get_property(i2s_node, "tx-mask", NULL); + if (prop != NULL) + i2s_config->tx_mask = be32_to_cpup(prop); + + prop = of_get_property(i2s_node, "rx-mask", NULL); + if (prop != NULL) + i2s_config->rx_mask = be32_to_cpup(prop); + + prop = of_get_property(i2s_node, "clk-trim", NULL); + if (prop != NULL) + i2s_config->clock_trim = be32_to_cpup(prop); + + prop = of_get_property(i2s_node, "fsync-width", NULL); + if (prop != NULL) + i2s_config->fsync_width = be32_to_cpup(prop); + + prop = of_get_property(i2s_node, "srate", NULL); + if (prop != NULL) + i2s_config->srate = be32_to_cpup(prop); + + prop = of_get_property(i2s_node, "num-channel", NULL); + if (prop != NULL) + i2s_config->channels = be32_to_cpup(prop); + + prop = of_get_property(i2s_node, "bit-format", NULL); + if (prop != NULL) + i2s_config->bit_size = be32_to_cpup(prop); + +#ifdef SAFETY_I2S_DEBUG + dump_config(i2s_config); +#endif + return 0; +} + +static int is_supported_rate(int rate) +{ + return 1; +} + +static int i2s_set_rate(unsigned int id, int rate) +{ + unsigned long i2s_clk_freq; + int err; + + if (!is_supported_rate(rate)) + return -EINVAL; + + i2s_clk_freq = i2s[id].config.channels * i2s[id].config.srate * + i2s[id].config.bit_size * i2s[id].config.bclk_ratio; + + if (i2s[id].config.clock_mode == I2S_MASTER) { + err = clk_set_parent(i2s[id].audio_sync, i2s[id].i2s_sync); + if (err != 0) + return -EINVAL; + + err = clk_set_parent(i2s[id].clk_i2s, i2s[id].clk_i2s_src); + if (err != 0) + return -EINVAL; + + err = clk_set_rate(i2s[id].clk_i2s, i2s_clk_freq); + if (err != 0) + return -EINVAL; + + err = clk_prepare_enable(i2s[id].clk_i2s); + if (err != 0) + return -EINVAL; + } + + err = clk_set_rate(i2s[id].audio_sync_input, i2s_clk_freq); + if (err != 0) + return -EINVAL; + + return 0; +} + +static void i2s_setup(unsigned int id) +{ + i2s_set_rate(id, i2s[id].config.srate); + + i2s_configure(id, &i2s[id].config); + +} + +static void safety_i2s_start(struct snd_pcm_substream *substream) +{ + struct dma_data *data = substream->private_data; + unsigned int id = data->req_sel - 1; + int rx = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ? 1 : 0; + + if (rx) + i2s_enable_rx(id); + else + i2s_enable_tx(id); + + i2s_enable(id); + data->triggered = 1; +} + +static void safety_i2s_stop(struct snd_pcm_substream *substream) +{ + struct dma_data *data = substream->private_data; + unsigned int id = data->req_sel - 1; + int rx = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ? 1 : 0; + + if (rx) + i2s_disable_rx(id); + else + i2s_disable_tx(id); + + data->triggered = 0; +} + +static int gpcdma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct dma_slave_config slave_config; + struct dma_data *dma_data = substream->private_data; + struct dma_chan *chan; + int ret; + + chan = snd_dmaengine_pcm_get_chan(substream); + ret = snd_hwparams_to_dma_slave_config(substream, params, + &slave_config); + if (ret) { + pr_alert("gpcdma hw params failed, err = %d\n", ret); + return ret; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config.dst_addr_width = (dma_data->width == 16) ? + DMA_SLAVE_BUSWIDTH_2_BYTES : + DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config.dst_addr = dma_data->addr; + /* MC burst should be multiple of this for proper + * stopping of GPCDMA during CYCLIC transfer. + * Currently, GPCDMA configures the MC burst + * to 2 words unless MMIO supports 64 so we just match it. + */ + slave_config.dst_maxburst = 2; + } else { + slave_config.src_addr_width = (dma_data->width == 16) ? + DMA_SLAVE_BUSWIDTH_2_BYTES : + DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config.src_addr = dma_data->addr; + slave_config.src_maxburst = 2; + } + + slave_config.slave_id = dma_data->req_sel; + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret < 0) { + pr_alert("dma slave config failed, err = %d\n", ret); + return ret; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return 0; +} + +static int safety_i2s_probe(struct device *dev, unsigned int id) +{ + i2s_parse_dt(dev, id); + setup_plls(dev); + + i2s_reset_init_and_deassert(dev, id); + + i2s_clock_init(dev, id); + i2s_setup(id); + + return 0; +} + +static int safety_i2s_open(struct snd_pcm_substream *substream) +{ + struct i2s_dev *i2s = snd_pcm_substream_chip(substream); + struct dma_data *dma_data = + (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ? + &i2s->capture_data : &i2s->playback_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dma_chan *chan; + int ret; + + /* overwrite pcm->private_data and + * maintain only substram specific data + */ + substream->private_data = dma_data; + + runtime->hw.info = t234_pcm_hardware.info; + runtime->hw.rates = t234_pcm_hardware.rates; + runtime->hw.rate_min = t234_pcm_hardware.rate_min; + runtime->hw.rate_max = t234_pcm_hardware.rate_max; + runtime->hw.formats = t234_pcm_hardware.formats; + runtime->hw.period_bytes_min = t234_pcm_hardware.period_bytes_min; + runtime->hw.period_bytes_max = t234_pcm_hardware.period_bytes_max; + runtime->hw.periods_min = t234_pcm_hardware.periods_min; + runtime->hw.periods_max = t234_pcm_hardware.periods_max; + runtime->hw.channels_min = t234_pcm_hardware.channels_min; + runtime->hw.channels_max = t234_pcm_hardware.channels_max; + runtime->hw.buffer_bytes_max = t234_pcm_hardware.buffer_bytes_max; + runtime->hw.fifo_size = t234_pcm_hardware.fifo_size; + + //TODO: Support buffer size update from device tree + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8); + if (ret) { + pr_alert("Failed to set constraint %d\n", ret); + return ret; + } + chan = dma_request_slave_channel(substream->pcm->card->dev, + dma_data->dma_chan_name); + if (!chan) { + pr_alert("failed to allocate dma channel\n"); + return -ENODEV; + } + + ret = snd_dmaengine_pcm_open(substream, chan); + if (ret) { + pr_alert("failed to open dmaengine\n"); + return ret; + } + + return 0; +} + +static int safety_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int ret; + /* configure hardware according to the following params + * channels, bits per sample, samples per second, period size + * and num periods + */ + ret = gpcdma_hw_params(substream, params); + if (!ret) + ret = i2s_hw_params(substream, params); + + return ret; +} + +static int safety_i2s_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int safety_i2s_close(struct snd_pcm_substream *substream) +{ + struct dma_data *data = substream->private_data; + + if (data->triggered) { + safety_i2s_trigger(substream, SNDRV_PCM_TRIGGER_STOP); + data->triggered = 0; + } + + snd_dmaengine_pcm_close_release_chan(substream); + + return 0; +} + +static int safety_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_dmaengine_pcm_trigger(substream, cmd); + safety_i2s_start(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + safety_i2s_stop(substream); + snd_dmaengine_pcm_trigger(substream, cmd); + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct snd_pcm_ops playback_ops = { + .open = safety_i2s_open, + .close = safety_i2s_close, + .hw_params = safety_i2s_hw_params, + .prepare = safety_i2s_prepare, + .pointer = snd_dmaengine_pcm_pointer, + .trigger = safety_i2s_trigger +}; + +static const struct of_device_id match_table[] = { + { .compatible = "nvidia,tegra234-safety-audio" }, + {} +}; + +static unsigned int parse_enabled_i2s_mask(struct device *dev) +{ + unsigned int num_enabled = 0; + int i; + + i = of_property_read_variable_u32_array(dev->of_node, + "enabled-i2s-mask", enabled_i2s_mask, + NUM_SAFETY_I2S_INST, 0); + WARN_ON(i != NUM_SAFETY_I2S_INST); + for (i = 0; i < NUM_SAFETY_I2S_INST; i++) + num_enabled += !!(enabled_i2s_mask[i]); + + return num_enabled; +} + +static int t234_safety_audio_probe(struct platform_device *pdev) +{ + struct snd_card *card; + int ret, pcm_instance = 0; + unsigned int i = 0; + struct snd_pcm *pcm; + char name[5] = {0}; + + if (parse_enabled_i2s_mask(&pdev->dev) == 0) { + pr_err("No safety-i2s interfaces are available on this board\n"); + return -ENODEV; + } + + ret = snd_card_new(&pdev->dev, -1, "Safety I2S sound card", + THIS_MODULE, sizeof(*priv), &card); + if (ret < 0) + return ret; + + priv = card->private_data; + priv->card = card; + + for (i = 0; i < NUM_SAFETY_I2S_INST; i++) { + struct resource *r; + size_t buffer_size = t234_pcm_hardware.buffer_bytes_max; + void __iomem *base; + + if (!enabled_i2s_mask[i]) + continue; + + if ((sprintf(name, I2S_DT_NODE, I2S_NODE_START_INDEX + i)) < 0) + return -EINVAL; + ret = snd_pcm_new(card, name, pcm_instance++, 1, 1, &pcm); + if (ret < 0) { + pr_alert("Could not register i2s pcm, ret: %d\n", ret); + return ret; + } + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &playback_ops); + + base = devm_platform_get_and_ioremap_resource(pdev, i, &r); + if (IS_ERR(base)) { + pr_alert("could not remap base\n"); + return -EINVAL; + } + + i2s[i].base = base; + + safety_i2s_probe(&pdev->dev, i); + + i2s[i].capture_data.addr = r->start + 0x20; + //TODO: Read from DT later on + i2s[i].capture_data.size = buffer_size; + i2s[i].capture_data.width = i2s[i].config.bit_size; + i2s[i].capture_data.req_sel = i + 1; + i2s[i].capture_data.dma_chan_name = + (i ? "i2s8-rx" : "i2s7-rx"); + + i2s[i].playback_data.addr = r->start + 0xa0; + //TODO: Read from DT later on + i2s[i].playback_data.size = buffer_size; + i2s[i].playback_data.width = i2s[i].config.bit_size; + i2s[i].playback_data.req_sel = i + 1; + i2s[i].playback_data.dma_chan_name = + (i ? "i2s8-tx" : "i2s7-tx"); + + pcm->private_data = &i2s[i]; + + //TODO: check the return value + prealloc_dma_buff(pcm, SNDRV_PCM_STREAM_PLAYBACK, buffer_size); + prealloc_dma_buff(pcm, SNDRV_PCM_STREAM_CAPTURE, buffer_size); + + safety_i2s_add_kcontrols(card, i); + } + + ret = snd_card_register(card); + if (ret < 0) + pr_alert("Error registering I2S card, ret = %d\n", ret); + else + pr_alert("Sound card registered successfully\n"); + + return ret; +} + +static int t234_safety_audio_remove(struct platform_device *pdev) +{ + snd_card_free(priv->card); + + return 0; +} + +static struct platform_driver t234_safety_audio_driver = { + .probe = t234_safety_audio_probe, + .remove = t234_safety_audio_remove, + .driver = { + .name = "tegra234-safety-audio", + .owner = THIS_MODULE, + .of_match_table = match_table, + }, +}; + +MODULE_DEVICE_TABLE(of, match_table); +MODULE_LICENSE("GPL"); + +module_platform_driver(t234_safety_audio_driver); diff --git a/sound/tegra-safety-audio/tegra_i2s.h b/sound/tegra-safety-audio/tegra_i2s.h new file mode 100644 index 00000000..73cde6df --- /dev/null +++ b/sound/tegra-safety-audio/tegra_i2s.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef _TEGRA_I2S_H_ +#define _TEGRA_I2S_H_ + +#define PCM_STREAM_PLAYBACK 0 +#define PCM_STREAM_CAPTURE 1 + +#define NUM_SAFETY_I2S_INST 2 +#define I2S_DT_NODE "i2s%u" +#define I2S_NODE_START_INDEX 7 + +enum clock_mode { + I2S_SLAVE = 0, + I2S_MASTER +}; + +enum edge_ctrl { + I2S_CLK_POS_EDGE = 0, + I2S_CLK_NEG_EDGE +}; + +enum clock_polarity { + LRCK_LOW = 0, + LRCK_HIGH +}; + +enum i2s_mode { + I2S_FRAME_FORMAT_I2S = 0, + I2S_FRAME_FORMAT_TDM +}; + +enum i2s_clocks { + CLK_PLLA_OUT0, + CLK_I2S, + CLK_I2S_SOURCE, + CLK_I2S_SYNC, + CLK_AUDIO_SYNC, + CLK_AUDIO_INPUT_SYNC, + CLK_NUM_ENTRIES = CLK_AUDIO_INPUT_SYNC +}; + +struct i2s_config { + unsigned int mode; + unsigned int clock_mode; + unsigned int clock_polarity; + unsigned int edge_ctrl; + unsigned int total_slots; + unsigned int bclk; + unsigned int bit_size; + unsigned int channels; + unsigned int offset; + unsigned int tx_mask; + unsigned int rx_mask; + unsigned int srate; + unsigned int bclk_ratio; + unsigned int fsync_width; + unsigned int pcm_mask_bits; + unsigned int highz_ctrl; + unsigned int clock_trim; +}; + +struct dma_data { + const char *dma_chan_name; + unsigned long addr; + unsigned int size; + unsigned int width; + unsigned int req_sel; + unsigned int triggered; +}; + +struct i2s_dev { + volatile void __iomem *base; + struct dma_data capture_data; + struct dma_data playback_data; + struct clk *clk_i2s; + struct clk *clk_i2s_src; + struct clk *audio_sync; + struct clk *i2s_sync; + struct clk *audio_sync_input; + struct reset_control *reset; + struct i2s_config config; + +}; + +struct i2s_dev *safety_i2s_get_priv(void); + +#ifdef SAFETY_I2S_DEBUG +void i2s_dump_all_regs(unsigned int id); +#endif + +int i2s_configure(unsigned int id, struct i2s_config *config); + +/* Enable I2S controller */ +unsigned int i2s_enable(unsigned int id); + +/* Disable I2S controller */ +unsigned int i2s_disable(unsigned int id); + +/* Enable the Tx engine on I2S controller IO */ +unsigned int i2s_enable_tx(unsigned int id); + +/* Disable the Tx engine on I2S controller IO */ +unsigned int i2s_disable_tx(unsigned int id); + +/* Enable the Rx engine on I2S controller IO */ +unsigned int i2s_enable_rx(unsigned int id); + +/* Disable the Rx engine on I2S controller IO */ +unsigned int i2s_disable_rx(unsigned int id); + +static inline void +updatel(volatile void __iomem *addr, unsigned int mask, unsigned int val) +{ + unsigned int prev_val, new_val; + + prev_val = readl(addr); + new_val = (prev_val & (~mask)) | (val & mask); + + writel(new_val, addr); +} + +/* Enable/Disable digital loopback with I2S controller */ +unsigned int i2s_set_loopback(unsigned int id, unsigned int enable); + +#endif /* _TEGRA_I2S_H_ */ diff --git a/sound/tegra-safety-audio/tegra_i2s_regs.h b/sound/tegra-safety-audio/tegra_i2s_regs.h new file mode 100644 index 00000000..13cc83d1 --- /dev/null +++ b/sound/tegra-safety-audio/tegra_i2s_regs.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef __TEGRA_I2S_REGS_H_ +#define __TEGRA_I2S_REGS_H_ + +#define T234_I2S_RX_ENABLE 0x000 +#define T234_I2S_RX_SOFT_RESET 0x004 +#define T234_I2S_RX_STATUS 0x008 +#define T234_I2S_RX_INT_STATUS 0x00c +#define T234_I2S_RX_INT_SET 0x010 +#define T234_I2S_RX_INT_MASK 0x014 +#define T234_I2S_RX_INT_CLEAR 0x018 +#define T234_I2S_RX_FIFO_CTRL 0x01c +#define T234_I2S_RX_FIFO_RD_DATA 0x020 +#define T234_I2S_RX_CTRL 0x024 +#define T234_I2S_RX_SLOT_CTRL 0x028 +#define T234_I2S_RX_CLK_TRIM 0x02c +#define T234_I2S_TX_ENABLE 0x080 +#define T234_I2S_TX_SOFT_RESET 0x084 +#define T234_I2S_TX_STATUS 0x088 +#define T234_I2S_TX_INT_STATUS 0x08c +#define T234_I2S_TX_INT_SET 0x090 +#define T234_I2S_TX_INT_MASK 0x094 +#define T234_I2S_TX_INT_CLEAR 0x098 +#define T234_I2S_TX_FIFO_CTRL 0x09c +#define T234_I2S_TX_FIFO_WR_DATA 0x0a0 +#define T234_I2S_TX_START_THRESHOLD 0x0a4 +#define T234_I2S_TX_CTRL 0x0a8 +#define T234_I2S_TX_SLOT_CTRL 0x0ac +#define T234_I2S_TX_CLK_TRIM 0x0b0 +#define T234_I2S_ENABLE 0x100 +#define T234_I2S_SOFT_RESET 0x104 +#define T234_I2S_CG 0x108 +#define T234_I2S_STATUS 0x10c +#define T234_I2S_INT_STATUS 0x110 +#define T234_I2S_CTRL 0x114 +#define T234_I2S_TIMING 0x118 +#define T234_I2S_SLOT_CTRL 0x11c +#define T234_I2S_CLK_TRIM 0x120 + +/* Fields in T234_I2S_RX_ENABLE */ +#define T234_I2S_RX_EN_SHIFT 0 +#define T234_I2S_RX_EN (1 << T234_I2S_RX_EN_SHIFT) + +/* Fields in T234_I2S_RX_CTRL */ +#define T234_I2S_RX_CTRL_DATA_OFFSET_SHIFT 8 +#define T234_I2S_RX_CTRL_DATA_OFFSET_MASK \ + (0x7ff << T234_I2S_RX_CTRL_DATA_OFFSET_SHIFT) + +#define T234_I2S_RX_CTRL_MASK_BITS_SHIFT 4 +#define T234_I2S_RX_CTRL_BIT_ORDER_SHIFT 0 +#define T234_I2S_RX_CTRL_BIT_ORDER_MASK \ + (1 << T234_I2S_RX_CTRL_BIT_ORDER_SHIFT) +#define T234_I2S_RX_CTRL_BIT_ORDER_MSB_FIRST \ + (0 << T234_I2S_RX_CTRL_BIT_ORDER_SHIFT) +#define T234_I2S_RX_CTRL_BIT_ORDER_LSB_FIRST \ + (1 << T234_I2S_RX_CTRL_BIT_ORDER_SHIFT) + +/* Fields in T234_I2S_TX_ENABLE */ +#define T234_I2S_TX_EN_SHIFT 0 +#define T234_I2S_TX_EN (1 << T234_I2S_TX_EN_SHIFT) + +/* Fields in T234_I2S_TX_CTRL */ +#define T234_I2S_TX_CTRL_DATA_OFFSET_SHIFT 8 +#define T234_I2S_TX_CTRL_DATA_OFFSET_MASK \ + (0x7ff << T234_I2S_TX_CTRL_DATA_OFFSET_SHIFT) + +#define T234_I2S_TX_CTRL_MASK_BITS_SHIFT 4 + +#define T234_I2S_TX_CTRL_HIGHZ_CTRL_SHIFT 1 +#define T234_I2S_TX_CTRL_HIGHZ_CTRL_MASK \ + (3 << T234_I2S_TX_CTRL_HIGHZ_CTRL_SHIFT) +#define T234_I2S_TX_CTRL_HIGHZ_CTRL_NOHIGHZ \ + (0 << T234_I2S_TX_CTRL_HIGHZ_CTRL_SHIFT) +#define T234_I2S_TX_CTRL_HIGHZ_CTRL_HIGHZ \ + (1 << T234_I2S_TX_CTRL_HIGHZ_CTRL_SHIFT) +#define T234_I2S_TX_CTRL_HIGHZ_CTRL_HIGHZ_ON_HALF_BIT_CLK \ + (2 << T234_I2S_TX_CTRL_HIGHZ_CTRL_SHIFT) + +#define T234_I2S_TX_CTRL_BIT_ORDER_SHIFT 0 +#define T234_I2S_TX_CTRL_BIT_ORDER_MASK \ + (1 << T234_I2S_TX_CTRL_BIT_ORDER_SHIFT) +#define T234_I2S_TX_CTRL_BIT_ORDER_MSB_FIRST \ + (0 << T234_I2S_TX_CTRL_BIT_ORDER_SHIFT) +#define T234_I2S_TX_CTRL_BIT_ORDER_LSB_FIRST \ + (1 << T234_I2S_TX_CTRL_BIT_ORDER_SHIFT) + +/* Fields in T234_I2S_ENABLE */ +#define T234_I2S_EN_SHIFT 0 +#define T234_I2S_EN_MASK (1 << T234_I2S_EN_SHIFT) +#define T234_I2S_EN (1 << T234_I2S_EN_SHIFT) + +/* Fields in T234_I2S_CTRL */ +#define T234_I2S_CTRL_FSYNC_WIDTH_SHIFT 16 +#define T234_I2S_CTRL_FSYNC_WIDTH_MASK \ + (0xff << T234_I2S_CTRL_FSYNC_WIDTH_SHIFT) + +#define T234_I2S_POS_EDGE 0 +#define T234_I2S_NEG_EDGE 1 +#define T234_I2S_CTRL_EDGE_CTRL_SHIFT 10 +#define T234_I2S_CTRL_EDGE_CTRL_MASK \ + (1 << T234_I2S_CTRL_EDGE_CTRL_SHIFT) +#define T234_I2S_CTRL_EDGE_CTRL_POS_EDGE \ + (T234_I2S_POS_EDGE << T234_I2S_CTRL_EDGE_CTRL_SHIFT) +#define T234_I2S_CTRL_EDGE_CTRL_NEG_EDGE \ + (T234_I2S_NEG_EDGE << T234_I2S_CTRL_EDGE_CTRL_SHIFT) + +#define T234_I2S_CTRL_PIPE_MACRO_EN_SHIFT 9 +#define T234_I2S_CTRL_PIPE_MACRO_EN \ + (1 << T234_I2S_CTRL_PIPE_MACRO_EN_SHIFT) + +#define T234_I2S_FRAME_FORMAT_LRCK 0 +#define T234_I2S_FRAME_FORMAT_FSYNC 1 +#define T234_I2S_CTRL_FRAME_FORMAT_SHIFT 6 +#define T234_I2S_CTRL_FRAME_FORMAT_MASK \ + (7 << T234_I2S_CTRL_FRAME_FORMAT_SHIFT) +#define T234_I2S_CTRL_FRAME_FORMAT_LRCK_MODE \ + (T234_I2S_FRAME_FORMAT_LRCK << T234_I2S_CTRL_FRAME_FORMAT_SHIFT) +#define T234_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE \ + (T234_I2S_FRAME_FORMAT_FSYNC << T234_I2S_CTRL_FRAME_FORMAT_SHIFT) + +#define T234_I2S_CTRL_MASTER_EN_SHIFT 5 +#define T234_I2S_CTRL_MASTER_EN_MASK \ + (1 << T234_I2S_CTRL_MASTER_EN_SHIFT) +#define T234_I2S_CTRL_MASTER_EN \ + (1 << T234_I2S_CTRL_MASTER_EN_SHIFT) +#define T234_I2S_CTRL_SLAVE_EN \ + (1 << T234_I2S_CTRL_MASTER_EN_SHIFT) + +#define T234_I2S_CTRL_LRCK_POLARITY_SHIFT 4 +#define T234_I2S_CTRL_LRCK_POLARITY_MASK \ + (1 << T234_I2S_CTRL_LRCK_POLARITY_SHIFT) +#define T234_I2S_CTRL_LRCK_POLARITY_LOW \ + (0 << T234_I2S_CTRL_LRCK_POLARITY_SHIFT) +#define T234_I2S_CTRL_LRCK_POLARITY_HIGH \ + (1 << T234_I2S_CTRL_LRCK_POLARITY_SHIFT) + +#define T234_I2S_CTRL_LPBK_SHIFT 3 +#define T234_I2S_CTRL_LPBK_MASK (1 << T234_I2S_CTRL_LPBK_SHIFT) +#define T234_I2S_CTRL_LPBK_EN (1 << T234_I2S_CTRL_LPBK_SHIFT) + +#define T234_I2S_BITS_8 1 +#define T234_I2S_BITS_12 2 +#define T234_I2S_BITS_16 3 +#define T234_I2S_BITS_20 4 +#define T234_I2S_BITS_24 5 +#define T234_I2S_BITS_28 6 +#define T234_I2S_BITS_32 7 +#define T234_I2S_CTRL_BIT_SIZE_SHIFT 0 +#define T234_I2S_CTRL_BIT_SIZE_MASK \ + (7 << T234_I2S_CTRL_BIT_SIZE_SHIFT) +#define T234_I2S_CTRL_BIT_SIZE_8 \ + (T234_I2S_BITS_8 << T234_I2S_CTRL_BIT_SIZE_SHIFT) +#define T234_I2S_CTRL_BIT_SIZE_12 \ + (T234_I2S_BITS_12 << T234_I2S_CTRL_BIT_SIZE_SHIFT) +#define T234_I2S_CTRL_BIT_SIZE_16 \ + (T234_I2S_BITS_16 << T234_I2S_CTRL_BIT_SIZE_SHIFT) +#define T234_I2S_CTRL_BIT_SIZE_20 \ + (T234_I2S_BITS_20 << T234_I2S_CTRL_BIT_SIZE_SHIFT) +#define T234_I2S_CTRL_BIT_SIZE_24 \ + (T234_I2S_BITS_24 << T234_I2S_CTRL_BIT_SIZE_SHIFT) +#define T234_I2S_CTRL_BIT_SIZE_28 \ + (T234_I2S_BITS_28 << T234_I2S_CTRL_BIT_SIZE_SHIFT) +#define T234_I2S_CTRL_BIT_SIZE_32 \ + (T234_I2S_BITS_32 << T234_I2S_CTRL_BIT_SIZE_SHIFT) + +/* Fields in T234_I2S_TIMING */ +#define T234_I2S_TIMING_NON_SYM_EN_SHIFT 12 +#define T234_I2S_TIMING_NON_SYM_EN \ + (1 << T234_I2S_TIMING_NON_SYM_EN_SHIFT) +#define T234_I2S_TIMING_CHANNEL_BIT_CNT_MASK 0x3ff +#define T234_I2S_TIMING_CHANNEL_BIT_CNT_SHIFT 0 + +/* Fields in T234_I2S_RX_SOFT_RESET */ +#define T234_I2S_RX_SOFT_RESET_SHIFT 0 +#define T234_I2S_RX_SOFT_RESET_MASK \ + (1 << T234_I2S_RX_SOFT_RESET_SHIFT) +#define T234_I2S_RX_SOFT_RESET_EN \ + (1 << T234_I2S_RX_SOFT_RESET_SHIFT) +#define T234_I2S_RX_SOFT_RESET_DEFAULT \ + (0 << T234_I2S_RX_SOFT_RESET_SHIFT) + +#define T234_I2S_SCLK_TRIM_SEL_SHIFT 8 +#define T234_I2S_SCLK_TRIM_SEL_MASK 0x1F + +/* Fields in T234_I2S_RX_STATUS */ +#define T234_I2S_RX_STATUS_ENABLED_SHIFT 0 +#define T234_I2S_RX_STATUS_ENABLED \ + (1 << T234_I2S_RX_STATUS_ENABLED_SHIFT) +#define T234_I2S_RX_STATUS_FIFO_EMPTY_SHIFT 1 +#define T234_I2S_RX_STATUS_FIFO_EMPTY \ + (1 << T234_I2S_RX_STATUS_FIFO_EMPTY_SHIFT) +#define T234_I2S_RX_STATUS_FIFO_FULL_SHIFT 2 +#define T234_I2S_RX_STATUS_FIFO_FULL \ + (1 << T234_I2S_RX_STATUS_FIFO_FULL_SHIFT) +#define T234_I2S_RX_STATUS_FIFO_COUNT_SHIFT 8 +#define T234_I2S_RX_STATUS_FIFO_COUNT_MASK \ + (0x7f << T234_I2S_RX_STATUS_FIFO_COUNT_SHIFT) + +/* Fields in T234_I2S_TX_STATUS */ +#define T234_I2S_TX_STATUS_ENABLED_SHIFT 0 +#define T234_I2S_TX_STATUS_ENABLED \ + (1 << T234_I2S_TX_STATUS_ENABLED_SHIFT) +#define T234_I2S_TX_STATUS_FIFO_EMPTY_SHIFT 1 +#define T234_I2S_TX_STATUS_FIFO_EMPTY \ + (1 << T234_I2S_TX_STATUS_FIFO_EMPTY_SHIFT) +#define T234_I2S_TX_STATUS_FIFO_FULL_SHIFT 2 +#define T234_I2S_TX_STATUS_FIFO_FULL \ + (1 << T234_I2S_TX_STATUS_FIFO_FULL_SHIFT) +#define T234_I2S_TX_STATUS_FIFO_COUNT_SHIFT 8 +#define T234_I2S_TX_STATUS_FIFO_COUNT_MASK \ + (0x7f << T234_I2S_TX_STATUS_FIFO_COUNT_SHIFT) + +/* Fields in T234_I2S_TX_SOFT_RESET */ +#define T234_I2S_TX_SOFT_RESET_SHIFT 0 +#define T234_I2S_TX_SOFT_RESET_MASK \ + (1 << T234_I2S_TX_SOFT_RESET_SHIFT) +#define T234_I2S_TX_SOFT_RESET_EN \ + (1 << T234_I2S_TX_SOFT_RESET_SHIFT) +#define T234_I2S_TX_SOFT_RESET_DEFAULT \ + (0 << T234_I2S_TX_SOFT_RESET_SHIFT) + +/* Fields in T234_I2S_SLOT_CTRL */ +#define T234_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 0 +#define T234_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK 0xf + +/* Fields in T234_I2S_TX_SLOT_CTRL */ +#define T234_I2S_TX_SLOT_CTRL_SLOT_ENABLES_SHIFT 0 +#define T234_I2S_TX_SLOT_CTRL_SLOT_ENABLES_MASK 0xffff + +/* Fields in T234_I2S_RX_SLOT_CTRL */ +#define T234_I2S_RX_SLOT_CTRL_SLOT_ENABLES_SHIFT 0 +#define T234_I2S_RX_SLOT_CTRL_SLOT_ENABLES_MASK 0xffff + +#endif /* _TEGRA_I2S_REGS_H_ */