diff options
author | Takashi Iwai <tiwai@suse.de> | 2021-11-01 18:58:27 +0300 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2021-11-01 18:58:27 +0300 |
commit | a0292f3ebe63f8ed7ea28de57751f6bfb9416242 (patch) | |
tree | c1a9c859dbc4f9cd1c9dfcf255f58ade4d14177f /sound/soc/tegra | |
parent | 8f27b689066113a3e579d4df171c980c54368c4e (diff) | |
parent | 318a54c0ee4aaa3bfd69fdf505588510c7672c0c (diff) | |
download | linux-a0292f3ebe63f8ed7ea28de57751f6bfb9416242.tar.xz |
Merge tag 'asoc-v5.16' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v5.16
This is an unusually large set of updates, mostly a large crop of
unusually big drivers coupled with extensive overhauls of existing code.
There's a SH change here for the DAI format terminology, the change is
straightforward and the SH maintainers don't seem very active.
- A new version of the audio graph card which supports a wider range of
systems.
- Move of the Cirrus DSP framework into drivers/firmware to allow for
future use by non-audio DSPs.
- Several conversions to YAML DT bindings.
- Continuing cleanups to the SOF and Intel code.
- A very big overhaul of the cs42l42 driver, correcting many problems.
- Support for AMD Vangogh and Yelow Cap, Cirrus CS35L41, Maxim
MAX98520 and MAX98360A, Mediatek MT8195, Nuvoton NAU8821, nVidia
Tegra210, NXP i.MX8ULP, Qualcomm AudioReach, Realtek ALC5682I-VS,
RT5682S, and RT9120 and Rockchip RV1126 and RK3568
Diffstat (limited to 'sound/soc/tegra')
-rw-r--r-- | sound/soc/tegra/Kconfig | 48 | ||||
-rw-r--r-- | sound/soc/tegra/Makefile | 10 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_adx.c | 531 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_adx.h | 72 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_ahub.c | 511 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_amx.c | 600 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_amx.h | 93 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_mixer.c | 674 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_mixer.h | 100 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_mvc.c | 645 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_mvc.h | 117 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_sfc.c | 3549 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_sfc.h | 78 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_asoc_machine.c | 62 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_asoc_machine.h | 1 |
15 files changed, 7078 insertions, 13 deletions
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 83c87f35a7d3..cd454871d654 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -108,6 +108,54 @@ config SND_SOC_TEGRA210_ADMAIF channel. Buffer size is configurable for each ADMAIIF channel. Say Y or M if you want to add support for Tegra210 ADMAIF module. +config SND_SOC_TEGRA210_MVC + tristate "Tegra210 MVC module" + help + Config to enable the digital Master Volume Controller (MVC) which + provides gain or attenuation to a digital signal path. It can be + used in input or output signal path. It can be used either for + per-stream volume control or for master volume control. + Say Y or M if you want to add support for Tegra210 MVC module. + +config SND_SOC_TEGRA210_SFC + tristate "Tegra210 SFC module" + help + Config to enable the Sampling Frequency Converter (SFC) which + converts the sampling frequency of input signal to another + frequency. It supports sampling frequency conversion of streams + upto 2 channels (stereo). + Say Y or M if you want to add support for Tegra210 SFC module. + +config SND_SOC_TEGRA210_AMX + tristate "Tegra210 AMX module" + help + Config to enable the Audio Multiplexer (AMX) which can multiplex + four input streams (each of up to 16 channels) and generate + output stream (of up to 16 channels). A byte RAM helps to form an + output frame by any combination of bytes from the input frames. + Say Y or M if you want to add support for Tegra210 AMX module. + +config SND_SOC_TEGRA210_ADX + tristate "Tegra210 ADX module" + help + Config to enable the Audio Demultiplexer (ADX) which takes an + input stream (up to 16 channels) and demultiplexes it into four + output streams (each of up to 16 channels). A byte RAM helps to + form output frames by any combination of bytes from the input + frame. Its design is identical to that of byte RAM in the AMX + except that the data flow direction is reversed. + Say Y or M if you want to add support for Tegra210 ADX module. + +config SND_SOC_TEGRA210_MIXER + tristate "Tegra210 Mixer module" + help + Config to enable the Mixer module which can help to mix multiple + audio streams. It supports mixing of upto 10 input streams, + where each stream can contain maximum of 8 channels. It supports + 5 output each of which can be a mix of any combination of 10 + input streams. + Say Y or M if you want to add support for Tegra210 Mixer module. + config SND_SOC_TEGRA_AUDIO_GRAPH_CARD tristate "Audio Graph Card based Tegra driver" depends on SND_AUDIO_GRAPH_CARD diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index e2cec9ae31c9..f19d56690a0d 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -13,6 +13,11 @@ snd-soc-tegra210-dmic-objs := tegra210_dmic.o snd-soc-tegra210-i2s-objs := tegra210_i2s.o snd-soc-tegra186-dspk-objs := tegra186_dspk.o snd-soc-tegra210-admaif-objs := tegra210_admaif.o +snd-soc-tegra210-mvc-objs := tegra210_mvc.o +snd-soc-tegra210-sfc-objs := tegra210_sfc.o +snd-soc-tegra210-amx-objs := tegra210_amx.o +snd-soc-tegra210-adx-objs := tegra210_adx.o +snd-soc-tegra210-mixer-objs := tegra210_mixer.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o @@ -26,6 +31,11 @@ obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o +obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o +obj-$(CONFIG_SND_SOC_TEGRA210_SFC) += snd-soc-tegra210-sfc.o +obj-$(CONFIG_SND_SOC_TEGRA210_AMX) += snd-soc-tegra210-amx.o +obj-$(CONFIG_SND_SOC_TEGRA210_ADX) += snd-soc-tegra210-adx.o +obj-$(CONFIG_SND_SOC_TEGRA210_MIXER) += snd-soc-tegra210-mixer.o # Tegra machine Support snd-soc-tegra-wm8903-objs := tegra_wm8903.o diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c new file mode 100644 index 000000000000..d7c7849c2f92 --- /dev/null +++ b/sound/soc/tegra/tegra210_adx.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_adx.c - Tegra210 ADX driver +// +// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra210_adx.h" +#include "tegra_cif.h" + +static const struct reg_default tegra210_adx_reg_defaults[] = { + { TEGRA210_ADX_RX_INT_MASK, 0x00000001}, + { TEGRA210_ADX_RX_CIF_CTRL, 0x00007000}, + { TEGRA210_ADX_TX_INT_MASK, 0x0000000f }, + { TEGRA210_ADX_TX1_CIF_CTRL, 0x00007000}, + { TEGRA210_ADX_TX2_CIF_CTRL, 0x00007000}, + { TEGRA210_ADX_TX3_CIF_CTRL, 0x00007000}, + { TEGRA210_ADX_TX4_CIF_CTRL, 0x00007000}, + { TEGRA210_ADX_CG, 0x1}, + { TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000}, +}; + +static void tegra210_adx_write_map_ram(struct tegra210_adx *adx) +{ + int i; + + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, + TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN | + TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN | + TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE); + + for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++) + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA, + adx->map[i]); + + regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]); + regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]); +} + +static int tegra210_adx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); + unsigned int val; + int err; + + /* Ensure if ADX status is disabled */ + err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS, + val, !(val & 0x1), 10, 10000); + if (err < 0) { + dev_err(dai->dev, "failed to stop ADX, err = %d\n", err); + return err; + } + + /* + * Soft Reset: Below performs module soft reset which clears + * all FSM logic, flushes flow control of FIFO and resets the + * state register. It also brings module back to disabled + * state (without flushing the data in the pipe). + */ + regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET, + TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK, + TEGRA210_ADX_SOFT_RESET_SOFT_EN); + + err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET, + val, !(val & 0x1), 10, 10000); + if (err < 0) { + dev_err(dai->dev, "failed to reset ADX, err = %d\n", err); + return err; + } + + return 0; +} + +static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev) +{ + struct tegra210_adx *adx = dev_get_drvdata(dev); + + regcache_cache_only(adx->regmap, true); + regcache_mark_dirty(adx->regmap); + + return 0; +} + +static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev) +{ + struct tegra210_adx *adx = dev_get_drvdata(dev); + + regcache_cache_only(adx->regmap, false); + regcache_sync(adx->regmap); + + tegra210_adx_write_map_ram(adx); + + return 0; +} + +static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai, + unsigned int channels, + unsigned int format, + unsigned int reg) +{ + struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); + struct tegra_cif_conf cif_conf; + int audio_bits; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + if (channels < 1 || channels > 16) + return -EINVAL; + + switch (format) { + case SNDRV_PCM_FORMAT_S8: + audio_bits = TEGRA_ACIF_BITS_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EINVAL; + } + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = audio_bits; + + tegra_set_cif(adx->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return tegra210_adx_set_audio_cif(dai, params_channels(params), + params_format(params), + TEGRA210_ADX_TX1_CIF_CTRL + ((dai->id - 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE)); +} + +static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return tegra210_adx_set_audio_cif(dai, params_channels(params), + params_format(params), + TEGRA210_ADX_RX_CIF_CTRL); +} + +static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc; + unsigned char *bytes_map = (unsigned char *)&adx->map; + int enabled; + + mc = (struct soc_mixer_control *)kcontrol->private_value; + enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32)); + + if (enabled) + ucontrol->value.integer.value[0] = bytes_map[mc->reg]; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt); + unsigned char *bytes_map = (unsigned char *)&adx->map; + int value = ucontrol->value.integer.value[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value;; + + if (value >= 0 && value <= 255) { + /* update byte map and enable slot */ + bytes_map[mc->reg] = value; + adx->byte_mask[mc->reg / 32] |= (1 << (mc->reg % 32)); + } else { + /* reset byte map and disable slot */ + bytes_map[mc->reg] = 0; + adx->byte_mask[mc->reg / 32] &= ~(1 << (mc->reg % 32)); + } + + return 1; +} + +static const struct snd_soc_dai_ops tegra210_adx_in_dai_ops = { + .hw_params = tegra210_adx_in_hw_params, + .startup = tegra210_adx_startup, +}; + +static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops = { + .hw_params = tegra210_adx_out_hw_params, +}; + +#define IN_DAI \ + { \ + .name = "ADX-RX-CIF", \ + .playback = { \ + .stream_name = "RX-CIF-Playback", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "RX-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra210_adx_in_dai_ops, \ + } + +#define OUT_DAI(id) \ + { \ + .name = "ADX-TX" #id "-CIF", \ + .playback = { \ + .stream_name = "TX" #id "-CIF-Playback",\ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "TX" #id "-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra210_adx_out_dai_ops, \ + } + +static struct snd_soc_dai_driver tegra210_adx_dais[] = { + IN_DAI, + OUT_DAI(1), + OUT_DAI(2), + OUT_DAI(3), + OUT_DAI(4), +}; + +static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA210_ADX_ENABLE, + TEGRA210_ADX_ENABLE_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0), + SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0), + SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0), +}; + +#define STREAM_ROUTES(id, sname) \ + { "XBAR-" sname, NULL, "XBAR-TX" }, \ + { "RX-CIF-" sname, NULL, "XBAR-" sname }, \ + { "RX", NULL, "RX-CIF-" sname }, \ + { "TX" #id, NULL, "RX" }, \ + { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ + { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ + { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } + +#define ADX_ROUTES(id) \ + STREAM_ROUTES(id, "Playback"), \ + STREAM_ROUTES(id, "Capture") + +#define STREAM_ROUTES(id, sname) \ + { "XBAR-" sname, NULL, "XBAR-TX" }, \ + { "RX-CIF-" sname, NULL, "XBAR-" sname }, \ + { "RX", NULL, "RX-CIF-" sname }, \ + { "TX" #id, NULL, "RX" }, \ + { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ + { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ + { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } + +#define ADX_ROUTES(id) \ + STREAM_ROUTES(id, "Playback"), \ + STREAM_ROUTES(id, "Capture") + +static const struct snd_soc_dapm_route tegra210_adx_routes[] = { + ADX_ROUTES(1), + ADX_ROUTES(2), + ADX_ROUTES(3), + ADX_ROUTES(4), +}; + +#define TEGRA210_ADX_BYTE_MAP_CTRL(reg) \ + SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \ + tegra210_adx_get_byte_map, \ + tegra210_adx_put_byte_map) + +static struct snd_kcontrol_new tegra210_adx_controls[] = { + TEGRA210_ADX_BYTE_MAP_CTRL(0), + TEGRA210_ADX_BYTE_MAP_CTRL(1), + TEGRA210_ADX_BYTE_MAP_CTRL(2), + TEGRA210_ADX_BYTE_MAP_CTRL(3), + TEGRA210_ADX_BYTE_MAP_CTRL(4), + TEGRA210_ADX_BYTE_MAP_CTRL(5), + TEGRA210_ADX_BYTE_MAP_CTRL(6), + TEGRA210_ADX_BYTE_MAP_CTRL(7), + TEGRA210_ADX_BYTE_MAP_CTRL(8), + TEGRA210_ADX_BYTE_MAP_CTRL(9), + TEGRA210_ADX_BYTE_MAP_CTRL(10), + TEGRA210_ADX_BYTE_MAP_CTRL(11), + TEGRA210_ADX_BYTE_MAP_CTRL(12), + TEGRA210_ADX_BYTE_MAP_CTRL(13), + TEGRA210_ADX_BYTE_MAP_CTRL(14), + TEGRA210_ADX_BYTE_MAP_CTRL(15), + TEGRA210_ADX_BYTE_MAP_CTRL(16), + TEGRA210_ADX_BYTE_MAP_CTRL(17), + TEGRA210_ADX_BYTE_MAP_CTRL(18), + TEGRA210_ADX_BYTE_MAP_CTRL(19), + TEGRA210_ADX_BYTE_MAP_CTRL(20), + TEGRA210_ADX_BYTE_MAP_CTRL(21), + TEGRA210_ADX_BYTE_MAP_CTRL(22), + TEGRA210_ADX_BYTE_MAP_CTRL(23), + TEGRA210_ADX_BYTE_MAP_CTRL(24), + TEGRA210_ADX_BYTE_MAP_CTRL(25), + TEGRA210_ADX_BYTE_MAP_CTRL(26), + TEGRA210_ADX_BYTE_MAP_CTRL(27), + TEGRA210_ADX_BYTE_MAP_CTRL(28), + TEGRA210_ADX_BYTE_MAP_CTRL(29), + TEGRA210_ADX_BYTE_MAP_CTRL(30), + TEGRA210_ADX_BYTE_MAP_CTRL(31), + TEGRA210_ADX_BYTE_MAP_CTRL(32), + TEGRA210_ADX_BYTE_MAP_CTRL(33), + TEGRA210_ADX_BYTE_MAP_CTRL(34), + TEGRA210_ADX_BYTE_MAP_CTRL(35), + TEGRA210_ADX_BYTE_MAP_CTRL(36), + TEGRA210_ADX_BYTE_MAP_CTRL(37), + TEGRA210_ADX_BYTE_MAP_CTRL(38), + TEGRA210_ADX_BYTE_MAP_CTRL(39), + TEGRA210_ADX_BYTE_MAP_CTRL(40), + TEGRA210_ADX_BYTE_MAP_CTRL(41), + TEGRA210_ADX_BYTE_MAP_CTRL(42), + TEGRA210_ADX_BYTE_MAP_CTRL(43), + TEGRA210_ADX_BYTE_MAP_CTRL(44), + TEGRA210_ADX_BYTE_MAP_CTRL(45), + TEGRA210_ADX_BYTE_MAP_CTRL(46), + TEGRA210_ADX_BYTE_MAP_CTRL(47), + TEGRA210_ADX_BYTE_MAP_CTRL(48), + TEGRA210_ADX_BYTE_MAP_CTRL(49), + TEGRA210_ADX_BYTE_MAP_CTRL(50), + TEGRA210_ADX_BYTE_MAP_CTRL(51), + TEGRA210_ADX_BYTE_MAP_CTRL(52), + TEGRA210_ADX_BYTE_MAP_CTRL(53), + TEGRA210_ADX_BYTE_MAP_CTRL(54), + TEGRA210_ADX_BYTE_MAP_CTRL(55), + TEGRA210_ADX_BYTE_MAP_CTRL(56), + TEGRA210_ADX_BYTE_MAP_CTRL(57), + TEGRA210_ADX_BYTE_MAP_CTRL(58), + TEGRA210_ADX_BYTE_MAP_CTRL(59), + TEGRA210_ADX_BYTE_MAP_CTRL(60), + TEGRA210_ADX_BYTE_MAP_CTRL(61), + TEGRA210_ADX_BYTE_MAP_CTRL(62), + TEGRA210_ADX_BYTE_MAP_CTRL(63), +}; + +static const struct snd_soc_component_driver tegra210_adx_cmpnt = { + .dapm_widgets = tegra210_adx_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets), + .dapm_routes = tegra210_adx_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_adx_routes), + .controls = tegra210_adx_controls, + .num_controls = ARRAY_SIZE(tegra210_adx_controls), +}; + +static bool tegra210_adx_wr_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL: + case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL: + case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG: + case TEGRA210_ADX_CTRL ... TEGRA210_ADX_IN_BYTE_EN1: + case TEGRA210_ADX_CFG_RAM_CTRL ... TEGRA210_ADX_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_adx_rd_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_adx_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case TEGRA210_ADX_RX_STATUS: + case TEGRA210_ADX_RX_INT_STATUS: + case TEGRA210_ADX_RX_INT_SET: + case TEGRA210_ADX_TX_STATUS: + case TEGRA210_ADX_TX_INT_STATUS: + case TEGRA210_ADX_TX_INT_SET: + case TEGRA210_ADX_SOFT_RESET: + case TEGRA210_ADX_STATUS: + case TEGRA210_ADX_INT_STATUS: + case TEGRA210_ADX_CFG_RAM_CTRL: + case TEGRA210_ADX_CFG_RAM_DATA: + return true; + default: + break; + } + + return false; +} + +static const struct regmap_config tegra210_adx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_ADX_CFG_RAM_DATA, + .writeable_reg = tegra210_adx_wr_reg, + .readable_reg = tegra210_adx_rd_reg, + .volatile_reg = tegra210_adx_volatile_reg, + .reg_defaults = tegra210_adx_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id tegra210_adx_of_match[] = { + { .compatible = "nvidia,tegra210-adx" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_adx_of_match); + +static int tegra210_adx_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_adx *adx; + void __iomem *regs; + int err; + + adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL); + if (!adx) + return -ENOMEM; + + dev_set_drvdata(dev, adx); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + adx->regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_adx_regmap_config); + if (IS_ERR(adx->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(adx->regmap); + } + + regcache_cache_only(adx->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt, + tegra210_adx_dais, + ARRAY_SIZE(tegra210_adx_dais)); + if (err) { + dev_err(dev, "can't register ADX component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra210_adx_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_adx_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend, + tegra210_adx_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra210_adx_driver = { + .driver = { + .name = "tegra210-adx", + .of_match_table = tegra210_adx_of_match, + .pm = &tegra210_adx_pm_ops, + }, + .probe = tegra210_adx_platform_probe, + .remove = tegra210_adx_platform_remove, +}; +module_platform_driver(tegra210_adx_driver); + +MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); +MODULE_DESCRIPTION("Tegra210 ADX ASoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_adx.h b/sound/soc/tegra/tegra210_adx.h new file mode 100644 index 000000000000..d7dcb6497978 --- /dev/null +++ b/sound/soc/tegra/tegra210_adx.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_adx.h - Definitions for Tegra210 ADX driver + * + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_ADX_H__ +#define __TEGRA210_ADX_H__ + +/* Register offsets from TEGRA210_ADX*_BASE */ +#define TEGRA210_ADX_RX_STATUS 0x0c +#define TEGRA210_ADX_RX_INT_STATUS 0x10 +#define TEGRA210_ADX_RX_INT_MASK 0x14 +#define TEGRA210_ADX_RX_INT_SET 0x18 +#define TEGRA210_ADX_RX_INT_CLEAR 0x1c +#define TEGRA210_ADX_RX_CIF_CTRL 0x20 +#define TEGRA210_ADX_TX_STATUS 0x4c +#define TEGRA210_ADX_TX_INT_STATUS 0x50 +#define TEGRA210_ADX_TX_INT_MASK 0x54 +#define TEGRA210_ADX_TX_INT_SET 0x58 +#define TEGRA210_ADX_TX_INT_CLEAR 0x5c +#define TEGRA210_ADX_TX1_CIF_CTRL 0x60 +#define TEGRA210_ADX_TX2_CIF_CTRL 0x64 +#define TEGRA210_ADX_TX3_CIF_CTRL 0x68 +#define TEGRA210_ADX_TX4_CIF_CTRL 0x6c +#define TEGRA210_ADX_ENABLE 0x80 +#define TEGRA210_ADX_SOFT_RESET 0x84 +#define TEGRA210_ADX_CG 0x88 +#define TEGRA210_ADX_STATUS 0x8c +#define TEGRA210_ADX_INT_STATUS 0x90 +#define TEGRA210_ADX_CTRL 0xa4 +#define TEGRA210_ADX_IN_BYTE_EN0 0xa8 +#define TEGRA210_ADX_IN_BYTE_EN1 0xac +#define TEGRA210_ADX_CFG_RAM_CTRL 0xb8 +#define TEGRA210_ADX_CFG_RAM_DATA 0xbc + +/* Fields in TEGRA210_ADX_ENABLE */ +#define TEGRA210_ADX_ENABLE_SHIFT 0 + +/* Fields in TEGRA210_ADX_CFG_RAM_CTRL */ +#define TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT 0 + +#define TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT 14 +#define TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT) + +#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13 +#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT) + +#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12 +#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT) + +/* Fields in TEGRA210_ADX_SOFT_RESET */ +#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT 0 +#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT) +#define TEGRA210_ADX_SOFT_RESET_SOFT_EN (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT) +#define TEGRA210_ADX_SOFT_RESET_SOFT_DEFAULT (0 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT) + +#define TEGRA210_ADX_AUDIOCIF_CH_STRIDE 4 +#define TEGRA210_ADX_RAM_DEPTH 16 +#define TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT 6 +#define TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT 2 +#define TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT 0 + +struct tegra210_adx { + struct regmap *regmap; + unsigned int map[TEGRA210_ADX_RAM_DEPTH]; + unsigned int byte_mask[2]; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 66287a7c9865..a1989eae2b52 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -105,14 +105,68 @@ static struct snd_soc_dai_driver tegra210_ahub_dais[] = { DAI(ADMAIF8), DAI(ADMAIF9), DAI(ADMAIF10), + /* XBAR <-> I2S <-> Codec */ DAI(I2S1), DAI(I2S2), DAI(I2S3), DAI(I2S4), DAI(I2S5), + /* XBAR <- DMIC <- Codec */ DAI(DMIC1), DAI(DMIC2), DAI(DMIC3), + /* XBAR -> SFC -> XBAR */ + DAI(SFC1 RX), + DAI(SFC1 TX), + DAI(SFC2 RX), + DAI(SFC2 TX), + DAI(SFC3 RX), + DAI(SFC3 TX), + DAI(SFC4 RX), + DAI(SFC4 TX), + /* XBAR -> MVC -> XBAR */ + DAI(MVC1 RX), + DAI(MVC1 TX), + DAI(MVC2 RX), + DAI(MVC2 TX), + /* XBAR -> AMX(4:1) -> XBAR */ + DAI(AMX1 RX1), + DAI(AMX1 RX2), + DAI(AMX1 RX3), + DAI(AMX1 RX4), + DAI(AMX1), + DAI(AMX2 RX1), + DAI(AMX2 RX2), + DAI(AMX2 RX3), + DAI(AMX2 RX4), + DAI(AMX2), + /* XBAR -> ADX(1:4) -> XBAR */ + DAI(ADX1), + DAI(ADX1 TX1), + DAI(ADX1 TX2), + DAI(ADX1 TX3), + DAI(ADX1 TX4), + DAI(ADX2), + DAI(ADX2 TX1), + DAI(ADX2 TX2), + DAI(ADX2 TX3), + DAI(ADX2 TX4), + /* XBAR -> MIXER(10:5) -> XBAR */ + DAI(MIXER1 RX1), + DAI(MIXER1 RX2), + DAI(MIXER1 RX3), + DAI(MIXER1 RX4), + DAI(MIXER1 RX5), + DAI(MIXER1 RX6), + DAI(MIXER1 RX7), + DAI(MIXER1 RX8), + DAI(MIXER1 RX9), + DAI(MIXER1 RX10), + DAI(MIXER1 TX1), + DAI(MIXER1 TX2), + DAI(MIXER1 TX3), + DAI(MIXER1 TX4), + DAI(MIXER1 TX5), }; static struct snd_soc_dai_driver tegra186_ahub_dais[] = { @@ -136,18 +190,93 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = { DAI(ADMAIF18), DAI(ADMAIF19), DAI(ADMAIF20), + /* XBAR <-> I2S <-> Codec */ DAI(I2S1), DAI(I2S2), DAI(I2S3), DAI(I2S4), DAI(I2S5), DAI(I2S6), + /* XBAR <- DMIC <- Codec */ DAI(DMIC1), DAI(DMIC2), DAI(DMIC3), DAI(DMIC4), + /* XBAR -> DSPK -> Codec */ DAI(DSPK1), DAI(DSPK2), + /* XBAR -> SFC -> XBAR */ + DAI(SFC1 RX), + DAI(SFC1 TX), + DAI(SFC2 RX), + DAI(SFC2 TX), + DAI(SFC3 RX), + DAI(SFC3 TX), + DAI(SFC4 RX), + DAI(SFC4 TX), + /* XBAR -> MVC -> XBAR */ + DAI(MVC1 RX), + DAI(MVC1 TX), + DAI(MVC2 RX), + DAI(MVC2 TX), + /* XBAR -> AMX(4:1) -> XBAR */ + DAI(AMX1 RX1), + DAI(AMX1 RX2), + DAI(AMX1 RX3), + DAI(AMX1 RX4), + DAI(AMX1), + DAI(AMX2 RX1), + DAI(AMX2 RX2), + DAI(AMX2 RX3), + DAI(AMX2 RX4), + DAI(AMX2), + DAI(AMX3 RX1), + DAI(AMX3 RX2), + DAI(AMX3 RX3), + DAI(AMX3 RX4), + DAI(AMX3), + DAI(AMX4 RX1), + DAI(AMX4 RX2), + DAI(AMX4 RX3), + DAI(AMX4 RX4), + DAI(AMX4), + /* XBAR -> ADX(1:4) -> XBAR */ + DAI(ADX1), + DAI(ADX1 TX1), + DAI(ADX1 TX2), + DAI(ADX1 TX3), + DAI(ADX1 TX4), + DAI(ADX2), + DAI(ADX2 TX1), + DAI(ADX2 TX2), + DAI(ADX2 TX3), + DAI(ADX2 TX4), + DAI(ADX3), + DAI(ADX3 TX1), + DAI(ADX3 TX2), + DAI(ADX3 TX3), + DAI(ADX3 TX4), + DAI(ADX4), + DAI(ADX4 TX1), + DAI(ADX4 TX2), + DAI(ADX4 TX3), + DAI(ADX4 TX4), + /* XBAR -> MIXER1(10:5) -> XBAR */ + DAI(MIXER1 RX1), + DAI(MIXER1 RX2), + DAI(MIXER1 RX3), + DAI(MIXER1 RX4), + DAI(MIXER1 RX5), + DAI(MIXER1 RX6), + DAI(MIXER1 RX7), + DAI(MIXER1 RX8), + DAI(MIXER1 RX9), + DAI(MIXER1 RX10), + DAI(MIXER1 TX1), + DAI(MIXER1 TX2), + DAI(MIXER1 TX3), + DAI(MIXER1 TX4), + DAI(MIXER1 TX5), }; static const char * const tegra210_ahub_mux_texts[] = { @@ -170,6 +299,27 @@ static const char * const tegra210_ahub_mux_texts[] = { "DMIC1", "DMIC2", "DMIC3", + "SFC1", + "SFC2", + "SFC3", + "SFC4", + "MVC1", + "MVC2", + "AMX1", + "AMX2", + "ADX1 TX1", + "ADX1 TX2", + "ADX1 TX3", + "ADX1 TX4", + "ADX2 TX1", + "ADX2 TX2", + "ADX2 TX3", + "ADX2 TX4", + "MIXER1 TX1", + "MIXER1 TX2", + "MIXER1 TX3", + "MIXER1 TX4", + "MIXER1 TX5", }; static const char * const tegra186_ahub_mux_texts[] = { @@ -204,10 +354,42 @@ static const char * const tegra186_ahub_mux_texts[] = { "DMIC2", "DMIC3", "DMIC4", + "SFC1", + "SFC2", + "SFC3", + "SFC4", + "MVC1", + "MVC2", + "AMX1", + "AMX2", + "AMX3", + "AMX4", + "ADX1 TX1", + "ADX1 TX2", + "ADX1 TX3", + "ADX1 TX4", + "ADX2 TX1", + "ADX2 TX2", + "ADX2 TX3", + "ADX2 TX4", + "ADX3 TX1", + "ADX3 TX2", + "ADX3 TX3", + "ADX3 TX4", + "ADX4 TX1", + "ADX4 TX2", + "ADX4 TX3", + "ADX4 TX4", + "MIXER1 TX1", + "MIXER1 TX2", + "MIXER1 TX3", + "MIXER1 TX4", + "MIXER1 TX5", }; static const unsigned int tegra210_ahub_mux_values[] = { 0, + /* ADMAIF */ MUX_VALUE(0, 0), MUX_VALUE(0, 1), MUX_VALUE(0, 2), @@ -218,18 +400,47 @@ static const unsigned int tegra210_ahub_mux_values[] = { MUX_VALUE(0, 7), MUX_VALUE(0, 8), MUX_VALUE(0, 9), + /* I2S */ MUX_VALUE(0, 16), MUX_VALUE(0, 17), MUX_VALUE(0, 18), MUX_VALUE(0, 19), MUX_VALUE(0, 20), + /* DMIC */ MUX_VALUE(2, 18), MUX_VALUE(2, 19), MUX_VALUE(2, 20), + /* SFC */ + MUX_VALUE(0, 24), + MUX_VALUE(0, 25), + MUX_VALUE(0, 26), + MUX_VALUE(0, 27), + /* MVC */ + MUX_VALUE(2, 8), + MUX_VALUE(2, 9), + /* AMX */ + MUX_VALUE(1, 8), + MUX_VALUE(1, 9), + /* ADX */ + MUX_VALUE(2, 24), + MUX_VALUE(2, 25), + MUX_VALUE(2, 26), + MUX_VALUE(2, 27), + MUX_VALUE(2, 28), + MUX_VALUE(2, 29), + MUX_VALUE(2, 30), + MUX_VALUE(2, 31), + /* MIXER */ + MUX_VALUE(1, 0), + MUX_VALUE(1, 1), + MUX_VALUE(1, 2), + MUX_VALUE(1, 3), + MUX_VALUE(1, 4), }; static const unsigned int tegra186_ahub_mux_values[] = { 0, + /* ADMAIF */ MUX_VALUE(0, 0), MUX_VALUE(0, 1), MUX_VALUE(0, 2), @@ -246,20 +457,59 @@ static const unsigned int tegra186_ahub_mux_values[] = { MUX_VALUE(0, 13), MUX_VALUE(0, 14), MUX_VALUE(0, 15), + /* I2S */ MUX_VALUE(0, 16), MUX_VALUE(0, 17), MUX_VALUE(0, 18), MUX_VALUE(0, 19), MUX_VALUE(0, 20), MUX_VALUE(0, 21), + /* ADMAIF */ MUX_VALUE(3, 16), MUX_VALUE(3, 17), MUX_VALUE(3, 18), MUX_VALUE(3, 19), + /* DMIC */ MUX_VALUE(2, 18), MUX_VALUE(2, 19), MUX_VALUE(2, 20), MUX_VALUE(2, 21), + /* SFC */ + MUX_VALUE(0, 24), + MUX_VALUE(0, 25), + MUX_VALUE(0, 26), + MUX_VALUE(0, 27), + /* MVC */ + MUX_VALUE(2, 8), + MUX_VALUE(2, 9), + /* AMX */ + MUX_VALUE(1, 8), + MUX_VALUE(1, 9), + MUX_VALUE(1, 10), + MUX_VALUE(1, 11), + /* ADX */ + MUX_VALUE(2, 24), + MUX_VALUE(2, 25), + MUX_VALUE(2, 26), + MUX_VALUE(2, 27), + MUX_VALUE(2, 28), + MUX_VALUE(2, 29), + MUX_VALUE(2, 30), + MUX_VALUE(2, 31), + MUX_VALUE(3, 0), + MUX_VALUE(3, 1), + MUX_VALUE(3, 2), + MUX_VALUE(3, 3), + MUX_VALUE(3, 4), + MUX_VALUE(3, 5), + MUX_VALUE(3, 6), + MUX_VALUE(3, 7), + /* MIXER */ + MUX_VALUE(1, 0), + MUX_VALUE(1, 1), + MUX_VALUE(1, 2), + MUX_VALUE(1, 3), + MUX_VALUE(1, 4), }; /* Controls for t210 */ @@ -278,6 +528,32 @@ MUX_ENUM_CTRL_DECL(t210_i2s2_tx, 0x11); MUX_ENUM_CTRL_DECL(t210_i2s3_tx, 0x12); MUX_ENUM_CTRL_DECL(t210_i2s4_tx, 0x13); MUX_ENUM_CTRL_DECL(t210_i2s5_tx, 0x14); +MUX_ENUM_CTRL_DECL(t210_sfc1_tx, 0x18); +MUX_ENUM_CTRL_DECL(t210_sfc2_tx, 0x19); +MUX_ENUM_CTRL_DECL(t210_sfc3_tx, 0x1a); +MUX_ENUM_CTRL_DECL(t210_sfc4_tx, 0x1b); +MUX_ENUM_CTRL_DECL(t210_mvc1_tx, 0x48); +MUX_ENUM_CTRL_DECL(t210_mvc2_tx, 0x49); +MUX_ENUM_CTRL_DECL(t210_amx11_tx, 0x50); +MUX_ENUM_CTRL_DECL(t210_amx12_tx, 0x51); +MUX_ENUM_CTRL_DECL(t210_amx13_tx, 0x52); +MUX_ENUM_CTRL_DECL(t210_amx14_tx, 0x53); +MUX_ENUM_CTRL_DECL(t210_amx21_tx, 0x54); +MUX_ENUM_CTRL_DECL(t210_amx22_tx, 0x55); +MUX_ENUM_CTRL_DECL(t210_amx23_tx, 0x56); +MUX_ENUM_CTRL_DECL(t210_amx24_tx, 0x57); +MUX_ENUM_CTRL_DECL(t210_adx1_tx, 0x58); +MUX_ENUM_CTRL_DECL(t210_adx2_tx, 0x59); +MUX_ENUM_CTRL_DECL(t210_mixer11_tx, 0x20); +MUX_ENUM_CTRL_DECL(t210_mixer12_tx, 0x21); +MUX_ENUM_CTRL_DECL(t210_mixer13_tx, 0x22); +MUX_ENUM_CTRL_DECL(t210_mixer14_tx, 0x23); +MUX_ENUM_CTRL_DECL(t210_mixer15_tx, 0x24); +MUX_ENUM_CTRL_DECL(t210_mixer16_tx, 0x25); +MUX_ENUM_CTRL_DECL(t210_mixer17_tx, 0x26); +MUX_ENUM_CTRL_DECL(t210_mixer18_tx, 0x27); +MUX_ENUM_CTRL_DECL(t210_mixer19_tx, 0x28); +MUX_ENUM_CTRL_DECL(t210_mixer110_tx, 0x29); /* Controls for t186 */ MUX_ENUM_CTRL_DECL_186(t186_admaif1_tx, 0x00); @@ -308,6 +584,42 @@ MUX_ENUM_CTRL_DECL_186(t186_admaif17_tx, 0x68); MUX_ENUM_CTRL_DECL_186(t186_admaif18_tx, 0x69); MUX_ENUM_CTRL_DECL_186(t186_admaif19_tx, 0x6a); MUX_ENUM_CTRL_DECL_186(t186_admaif20_tx, 0x6b); +MUX_ENUM_CTRL_DECL_186(t186_sfc1_tx, 0x18); +MUX_ENUM_CTRL_DECL_186(t186_sfc2_tx, 0x19); +MUX_ENUM_CTRL_DECL_186(t186_sfc3_tx, 0x1a); +MUX_ENUM_CTRL_DECL_186(t186_sfc4_tx, 0x1b); +MUX_ENUM_CTRL_DECL_186(t186_mvc1_tx, 0x48); +MUX_ENUM_CTRL_DECL_186(t186_mvc2_tx, 0x49); +MUX_ENUM_CTRL_DECL_186(t186_amx11_tx, 0x50); +MUX_ENUM_CTRL_DECL_186(t186_amx12_tx, 0x51); +MUX_ENUM_CTRL_DECL_186(t186_amx13_tx, 0x52); +MUX_ENUM_CTRL_DECL_186(t186_amx14_tx, 0x53); +MUX_ENUM_CTRL_DECL_186(t186_amx21_tx, 0x54); +MUX_ENUM_CTRL_DECL_186(t186_amx22_tx, 0x55); +MUX_ENUM_CTRL_DECL_186(t186_amx23_tx, 0x56); +MUX_ENUM_CTRL_DECL_186(t186_amx24_tx, 0x57); +MUX_ENUM_CTRL_DECL_186(t186_amx31_tx, 0x58); +MUX_ENUM_CTRL_DECL_186(t186_amx32_tx, 0x59); +MUX_ENUM_CTRL_DECL_186(t186_amx33_tx, 0x5a); +MUX_ENUM_CTRL_DECL_186(t186_amx34_tx, 0x5b); +MUX_ENUM_CTRL_DECL_186(t186_amx41_tx, 0x64); +MUX_ENUM_CTRL_DECL_186(t186_amx42_tx, 0x65); +MUX_ENUM_CTRL_DECL_186(t186_amx43_tx, 0x66); +MUX_ENUM_CTRL_DECL_186(t186_amx44_tx, 0x67); +MUX_ENUM_CTRL_DECL_186(t186_adx1_tx, 0x60); +MUX_ENUM_CTRL_DECL_186(t186_adx2_tx, 0x61); +MUX_ENUM_CTRL_DECL_186(t186_adx3_tx, 0x62); +MUX_ENUM_CTRL_DECL_186(t186_adx4_tx, 0x63); +MUX_ENUM_CTRL_DECL_186(t186_mixer11_tx, 0x20); +MUX_ENUM_CTRL_DECL_186(t186_mixer12_tx, 0x21); +MUX_ENUM_CTRL_DECL_186(t186_mixer13_tx, 0x22); +MUX_ENUM_CTRL_DECL_186(t186_mixer14_tx, 0x23); +MUX_ENUM_CTRL_DECL_186(t186_mixer15_tx, 0x24); +MUX_ENUM_CTRL_DECL_186(t186_mixer16_tx, 0x25); +MUX_ENUM_CTRL_DECL_186(t186_mixer17_tx, 0x26); +MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27); +MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28); +MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29); /* * The number of entries in, and order of, this array is closely tied to the @@ -333,6 +645,47 @@ static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = { TX_WIDGETS("DMIC1"), TX_WIDGETS("DMIC2"), TX_WIDGETS("DMIC3"), + WIDGETS("SFC1", t210_sfc1_tx), + WIDGETS("SFC2", t210_sfc2_tx), + WIDGETS("SFC3", t210_sfc3_tx), + WIDGETS("SFC4", t210_sfc4_tx), + WIDGETS("MVC1", t210_mvc1_tx), + WIDGETS("MVC2", t210_mvc2_tx), + WIDGETS("AMX1 RX1", t210_amx11_tx), + WIDGETS("AMX1 RX2", t210_amx12_tx), + WIDGETS("AMX1 RX3", t210_amx13_tx), + WIDGETS("AMX1 RX4", t210_amx14_tx), + WIDGETS("AMX2 RX1", t210_amx21_tx), + WIDGETS("AMX2 RX2", t210_amx22_tx), + WIDGETS("AMX2 RX3", t210_amx23_tx), + WIDGETS("AMX2 RX4", t210_amx24_tx), + TX_WIDGETS("AMX1"), + TX_WIDGETS("AMX2"), + WIDGETS("ADX1", t210_adx1_tx), + WIDGETS("ADX2", t210_adx2_tx), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), + WIDGETS("MIXER1 RX1", t210_mixer11_tx), + WIDGETS("MIXER1 RX2", t210_mixer12_tx), + WIDGETS("MIXER1 RX3", t210_mixer13_tx), + WIDGETS("MIXER1 RX4", t210_mixer14_tx), + WIDGETS("MIXER1 RX5", t210_mixer15_tx), + WIDGETS("MIXER1 RX6", t210_mixer16_tx), + WIDGETS("MIXER1 RX7", t210_mixer17_tx), + WIDGETS("MIXER1 RX8", t210_mixer18_tx), + WIDGETS("MIXER1 RX9", t210_mixer19_tx), + WIDGETS("MIXER1 RX10", t210_mixer110_tx), + TX_WIDGETS("MIXER1 TX1"), + TX_WIDGETS("MIXER1 TX2"), + TX_WIDGETS("MIXER1 TX3"), + TX_WIDGETS("MIXER1 TX4"), + TX_WIDGETS("MIXER1 TX5"), }; static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { @@ -368,6 +721,67 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { TX_WIDGETS("DMIC4"), WIDGETS("DSPK1", t186_dspk1_tx), WIDGETS("DSPK2", t186_dspk2_tx), + WIDGETS("SFC1", t186_sfc1_tx), + WIDGETS("SFC2", t186_sfc2_tx), + WIDGETS("SFC3", t186_sfc3_tx), + WIDGETS("SFC4", t186_sfc4_tx), + WIDGETS("MVC1", t186_mvc1_tx), + WIDGETS("MVC2", t186_mvc2_tx), + WIDGETS("AMX1 RX1", t186_amx11_tx), + WIDGETS("AMX1 RX2", t186_amx12_tx), + WIDGETS("AMX1 RX3", t186_amx13_tx), + WIDGETS("AMX1 RX4", t186_amx14_tx), + WIDGETS("AMX2 RX1", t186_amx21_tx), + WIDGETS("AMX2 RX2", t186_amx22_tx), + WIDGETS("AMX2 RX3", t186_amx23_tx), + WIDGETS("AMX2 RX4", t186_amx24_tx), + WIDGETS("AMX3 RX1", t186_amx31_tx), + WIDGETS("AMX3 RX2", t186_amx32_tx), + WIDGETS("AMX3 RX3", t186_amx33_tx), + WIDGETS("AMX3 RX4", t186_amx34_tx), + WIDGETS("AMX4 RX1", t186_amx41_tx), + WIDGETS("AMX4 RX2", t186_amx42_tx), + WIDGETS("AMX4 RX3", t186_amx43_tx), + WIDGETS("AMX4 RX4", t186_amx44_tx), + TX_WIDGETS("AMX1"), + TX_WIDGETS("AMX2"), + TX_WIDGETS("AMX3"), + TX_WIDGETS("AMX4"), + WIDGETS("ADX1", t186_adx1_tx), + WIDGETS("ADX2", t186_adx2_tx), + WIDGETS("ADX3", t186_adx3_tx), + WIDGETS("ADX4", t186_adx4_tx), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), + TX_WIDGETS("ADX3 TX1"), + TX_WIDGETS("ADX3 TX2"), + TX_WIDGETS("ADX3 TX3"), + TX_WIDGETS("ADX3 TX4"), + TX_WIDGETS("ADX4 TX1"), + TX_WIDGETS("ADX4 TX2"), + TX_WIDGETS("ADX4 TX3"), + TX_WIDGETS("ADX4 TX4"), + WIDGETS("MIXER1 RX1", t186_mixer11_tx), + WIDGETS("MIXER1 RX2", t186_mixer12_tx), + WIDGETS("MIXER1 RX3", t186_mixer13_tx), + WIDGETS("MIXER1 RX4", t186_mixer14_tx), + WIDGETS("MIXER1 RX5", t186_mixer15_tx), + WIDGETS("MIXER1 RX6", t186_mixer16_tx), + WIDGETS("MIXER1 RX7", t186_mixer17_tx), + WIDGETS("MIXER1 RX8", t186_mixer18_tx), + WIDGETS("MIXER1 RX9", t186_mixer19_tx), + WIDGETS("MIXER1 RX10", t186_mixer110_tx), + TX_WIDGETS("MIXER1 TX1"), + TX_WIDGETS("MIXER1 TX2"), + TX_WIDGETS("MIXER1 TX3"), + TX_WIDGETS("MIXER1 TX4"), + TX_WIDGETS("MIXER1 TX5"), }; #define TEGRA_COMMON_MUX_ROUTES(name) \ @@ -389,7 +803,28 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { { name " Mux", "I2S5", "I2S5 XBAR-RX" }, \ { name " Mux", "DMIC1", "DMIC1 XBAR-RX" }, \ { name " Mux", "DMIC2", "DMIC2 XBAR-RX" }, \ - { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, + { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \ + { name " Mux", "SFC1", "SFC1 XBAR-RX" }, \ + { name " Mux", "SFC2", "SFC2 XBAR-RX" }, \ + { name " Mux", "SFC3", "SFC3 XBAR-RX" }, \ + { name " Mux", "SFC4", "SFC4 XBAR-RX" }, \ + { name " Mux", "MVC1", "MVC1 XBAR-RX" }, \ + { name " Mux", "MVC2", "MVC2 XBAR-RX" }, \ + { name " Mux", "AMX1", "AMX1 XBAR-RX" }, \ + { name " Mux", "AMX2", "AMX2 XBAR-RX" }, \ + { name " Mux", "ADX1 TX1", "ADX1 TX1 XBAR-RX" }, \ + { name " Mux", "ADX1 TX2", "ADX1 TX2 XBAR-RX" }, \ + { name " Mux", "ADX1 TX3", "ADX1 TX3 XBAR-RX" }, \ + { name " Mux", "ADX1 TX4", "ADX1 TX4 XBAR-RX" }, \ + { name " Mux", "ADX2 TX1", "ADX2 TX1 XBAR-RX" }, \ + { name " Mux", "ADX2 TX2", "ADX2 TX2 XBAR-RX" }, \ + { name " Mux", "ADX2 TX3", "ADX2 TX3 XBAR-RX" }, \ + { name " Mux", "ADX2 TX4", "ADX2 TX4 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX1", "MIXER1 TX1 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX2", "MIXER1 TX2 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX3", "MIXER1 TX3 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX4", "MIXER1 TX4 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX5", "MIXER1 TX5 XBAR-RX" }, #define TEGRA186_ONLY_MUX_ROUTES(name) \ { name " Mux", "ADMAIF11", "ADMAIF11 XBAR-RX" }, \ @@ -403,7 +838,17 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { { name " Mux", "ADMAIF19", "ADMAIF19 XBAR-RX" }, \ { name " Mux", "ADMAIF20", "ADMAIF20 XBAR-RX" }, \ { name " Mux", "I2S6", "I2S6 XBAR-RX" }, \ - { name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, + { name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, \ + { name " Mux", "AMX3", "AMX3 XBAR-RX" }, \ + { name " Mux", "AMX4", "AMX4 XBAR-RX" }, \ + { name " Mux", "ADX3 TX1", "ADX3 TX1 XBAR-RX" }, \ + { name " Mux", "ADX3 TX2", "ADX3 TX2 XBAR-RX" }, \ + { name " Mux", "ADX3 TX3", "ADX3 TX3 XBAR-RX" }, \ + { name " Mux", "ADX3 TX4", "ADX3 TX4 XBAR-RX" }, \ + { name " Mux", "ADX4 TX1", "ADX4 TX1 XBAR-RX" }, \ + { name " Mux", "ADX4 TX2", "ADX4 TX2 XBAR-RX" }, \ + { name " Mux", "ADX4 TX3", "ADX4 TX3 XBAR-RX" }, \ + { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" }, #define TEGRA210_MUX_ROUTES(name) \ TEGRA_COMMON_MUX_ROUTES(name) @@ -450,6 +895,32 @@ static const struct snd_soc_dapm_route tegra210_ahub_routes[] = { TEGRA210_MUX_ROUTES("I2S3") TEGRA210_MUX_ROUTES("I2S4") TEGRA210_MUX_ROUTES("I2S5") + TEGRA210_MUX_ROUTES("SFC1") + TEGRA210_MUX_ROUTES("SFC2") + TEGRA210_MUX_ROUTES("SFC3") + TEGRA210_MUX_ROUTES("SFC4") + TEGRA210_MUX_ROUTES("MVC1") + TEGRA210_MUX_ROUTES("MVC2") + TEGRA210_MUX_ROUTES("AMX1 RX1") + TEGRA210_MUX_ROUTES("AMX1 RX2") + TEGRA210_MUX_ROUTES("AMX1 RX3") + TEGRA210_MUX_ROUTES("AMX1 RX4") + TEGRA210_MUX_ROUTES("AMX2 RX1") + TEGRA210_MUX_ROUTES("AMX2 RX2") + TEGRA210_MUX_ROUTES("AMX2 RX3") + TEGRA210_MUX_ROUTES("AMX2 RX4") + TEGRA210_MUX_ROUTES("ADX1") + TEGRA210_MUX_ROUTES("ADX2") + TEGRA210_MUX_ROUTES("MIXER1 RX1") + TEGRA210_MUX_ROUTES("MIXER1 RX2") + TEGRA210_MUX_ROUTES("MIXER1 RX3") + TEGRA210_MUX_ROUTES("MIXER1 RX4") + TEGRA210_MUX_ROUTES("MIXER1 RX5") + TEGRA210_MUX_ROUTES("MIXER1 RX6") + TEGRA210_MUX_ROUTES("MIXER1 RX7") + TEGRA210_MUX_ROUTES("MIXER1 RX8") + TEGRA210_MUX_ROUTES("MIXER1 RX9") + TEGRA210_MUX_ROUTES("MIXER1 RX10") }; static const struct snd_soc_dapm_route tegra186_ahub_routes[] = { @@ -501,6 +972,42 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = { TEGRA186_MUX_ROUTES("I2S6") TEGRA186_MUX_ROUTES("DSPK1") TEGRA186_MUX_ROUTES("DSPK2") + TEGRA186_MUX_ROUTES("SFC1") + TEGRA186_MUX_ROUTES("SFC2") + TEGRA186_MUX_ROUTES("SFC3") + TEGRA186_MUX_ROUTES("SFC4") + TEGRA186_MUX_ROUTES("MVC1") + TEGRA186_MUX_ROUTES("MVC2") + TEGRA186_MUX_ROUTES("AMX1 RX1") + TEGRA186_MUX_ROUTES("AMX1 RX2") + TEGRA186_MUX_ROUTES("AMX1 RX3") + TEGRA186_MUX_ROUTES("AMX1 RX4") + TEGRA186_MUX_ROUTES("AMX2 RX1") + TEGRA186_MUX_ROUTES("AMX2 RX2") + TEGRA186_MUX_ROUTES("AMX2 RX3") + TEGRA186_MUX_ROUTES("AMX2 RX4") + TEGRA186_MUX_ROUTES("AMX3 RX1") + TEGRA186_MUX_ROUTES("AMX3 RX2") + TEGRA186_MUX_ROUTES("AMX3 RX3") + TEGRA186_MUX_ROUTES("AMX3 RX4") + TEGRA186_MUX_ROUTES("AMX4 RX1") + TEGRA186_MUX_ROUTES("AMX4 RX2") + TEGRA186_MUX_ROUTES("AMX4 RX3") + TEGRA186_MUX_ROUTES("AMX4 RX4") + TEGRA186_MUX_ROUTES("ADX1") + TEGRA186_MUX_ROUTES("ADX2") + TEGRA186_MUX_ROUTES("ADX3") + TEGRA186_MUX_ROUTES("ADX4") + TEGRA186_MUX_ROUTES("MIXER1 RX1") + TEGRA186_MUX_ROUTES("MIXER1 RX2") + TEGRA186_MUX_ROUTES("MIXER1 RX3") + TEGRA186_MUX_ROUTES("MIXER1 RX4") + TEGRA186_MUX_ROUTES("MIXER1 RX5") + TEGRA186_MUX_ROUTES("MIXER1 RX6") + TEGRA186_MUX_ROUTES("MIXER1 RX7") + TEGRA186_MUX_ROUTES("MIXER1 RX8") + TEGRA186_MUX_ROUTES("MIXER1 RX9") + TEGRA186_MUX_ROUTES("MIXER1 RX10") }; static const struct snd_soc_component_driver tegra210_ahub_component = { diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c new file mode 100644 index 000000000000..af9bddfc3120 --- /dev/null +++ b/sound/soc/tegra/tegra210_amx.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_amx.c - Tegra210 AMX driver +// +// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra210_amx.h" +#include "tegra_cif.h" + +/* + * The counter is in terms of AHUB clock cycles. If a frame is not + * received within these clock cycles, the AMX input channel gets + * automatically disabled. For now the counter is calculated as a + * function of sample rate (8 kHz) and AHUB clock (49.152 MHz). + * If later an accurate number is needed, the counter needs to be + * calculated at runtime. + * + * count = ahub_clk / sample_rate + */ +#define TEGRA194_MAX_FRAME_IDLE_COUNT 0x1800 + +#define AMX_CH_REG(id, reg) ((reg) + ((id) * TEGRA210_AMX_AUDIOCIF_CH_STRIDE)) + +static const struct reg_default tegra210_amx_reg_defaults[] = { + { TEGRA210_AMX_RX_INT_MASK, 0x0000000f}, + { TEGRA210_AMX_RX1_CIF_CTRL, 0x00007000}, + { TEGRA210_AMX_RX2_CIF_CTRL, 0x00007000}, + { TEGRA210_AMX_RX3_CIF_CTRL, 0x00007000}, + { TEGRA210_AMX_RX4_CIF_CTRL, 0x00007000}, + { TEGRA210_AMX_TX_INT_MASK, 0x00000001}, + { TEGRA210_AMX_TX_CIF_CTRL, 0x00007000}, + { TEGRA210_AMX_CG, 0x1}, + { TEGRA210_AMX_CFG_RAM_CTRL, 0x00004000}, +}; + +static void tegra210_amx_write_map_ram(struct tegra210_amx *amx) +{ + int i; + + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, + TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN | + TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN | + TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE); + + for (i = 0; i < TEGRA210_AMX_RAM_DEPTH; i++) + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA, + amx->map[i]); + + regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN0, amx->byte_mask[0]); + regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN1, amx->byte_mask[1]); +} + +static int tegra210_amx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai); + unsigned int val; + int err; + + /* Ensure if AMX is disabled */ + err = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_STATUS, val, + !(val & 0x1), 10, 10000); + if (err < 0) { + dev_err(dai->dev, "failed to stop AMX, err = %d\n", err); + return err; + } + + /* + * Soft Reset: Below performs module soft reset which clears + * all FSM logic, flushes flow control of FIFO and resets the + * state register. It also brings module back to disabled + * state (without flushing the data in the pipe). + */ + regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET, + TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK, + TEGRA210_AMX_SOFT_RESET_SOFT_EN); + + err = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_SOFT_RESET, + val, !(val & 0x1), 10, 10000); + if (err < 0) { + dev_err(dai->dev, "failed to reset AMX, err = %d\n", err); + return err; + } + + return 0; +} + +static int __maybe_unused tegra210_amx_runtime_suspend(struct device *dev) +{ + struct tegra210_amx *amx = dev_get_drvdata(dev); + + regcache_cache_only(amx->regmap, true); + regcache_mark_dirty(amx->regmap); + + return 0; +} + +static int __maybe_unused tegra210_amx_runtime_resume(struct device *dev) +{ + struct tegra210_amx *amx = dev_get_drvdata(dev); + + regcache_cache_only(amx->regmap, false); + regcache_sync(amx->regmap); + + regmap_update_bits(amx->regmap, + TEGRA210_AMX_CTRL, + TEGRA210_AMX_CTRL_RX_DEP_MASK, + TEGRA210_AMX_WAIT_ON_ANY << TEGRA210_AMX_CTRL_RX_DEP_SHIFT); + + tegra210_amx_write_map_ram(amx); + + return 0; +} + +static int tegra210_amx_set_audio_cif(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai); + int channels, audio_bits; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + audio_bits = TEGRA_ACIF_BITS_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EINVAL; + } + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = audio_bits; + + tegra_set_cif(amx->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra210_amx_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai); + + if (amx->soc_data->auto_disable) { + regmap_write(amx->regmap, + AMX_CH_REG(dai->id, TEGRA194_AMX_RX1_FRAME_PERIOD), + TEGRA194_MAX_FRAME_IDLE_COUNT); + regmap_write(amx->regmap, TEGRA210_AMX_CYA, 1); + } + + return tegra210_amx_set_audio_cif(dai, params, + AMX_CH_REG(dai->id, TEGRA210_AMX_RX1_CIF_CTRL)); +} + +static int tegra210_amx_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return tegra210_amx_set_audio_cif(dai, params, + TEGRA210_AMX_TX_CIF_CTRL); +} + +static int tegra210_amx_get_byte_map(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt); + unsigned char *bytes_map = (unsigned char *)&amx->map; + int reg = mc->reg; + int enabled; + + if (reg > 31) + enabled = amx->byte_mask[1] & (1 << (reg - 32)); + else + enabled = amx->byte_mask[0] & (1 << reg); + + if (enabled) + ucontrol->value.integer.value[0] = bytes_map[reg]; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int tegra210_amx_put_byte_map(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt); + unsigned char *bytes_map = (unsigned char *)&amx->map; + int reg = mc->reg; + int value = ucontrol->value.integer.value[0]; + + if (value >= 0 && value <= 255) { + /* Update byte map and enable slot */ + bytes_map[reg] = value; + if (reg > 31) + amx->byte_mask[1] |= (1 << (reg - 32)); + else + amx->byte_mask[0] |= (1 << reg); + } else { + /* Reset byte map and disable slot */ + bytes_map[reg] = 0; + if (reg > 31) + amx->byte_mask[1] &= ~(1 << (reg - 32)); + else + amx->byte_mask[0] &= ~(1 << reg); + } + + return 1; +} + +static const struct snd_soc_dai_ops tegra210_amx_out_dai_ops = { + .hw_params = tegra210_amx_out_hw_params, + .startup = tegra210_amx_startup, +}; + +static const struct snd_soc_dai_ops tegra210_amx_in_dai_ops = { + .hw_params = tegra210_amx_in_hw_params, +}; + +#define IN_DAI(id) \ + { \ + .name = "AMX-RX-CIF" #id, \ + .playback = { \ + .stream_name = "RX" #id "-CIF-Playback",\ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "RX" #id "-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra210_amx_in_dai_ops, \ + } + +#define OUT_DAI \ + { \ + .name = "AMX-TX-CIF", \ + .playback = { \ + .stream_name = "TX-CIF-Playback", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "TX-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra210_amx_out_dai_ops, \ + } + +static struct snd_soc_dai_driver tegra210_amx_dais[] = { + IN_DAI(1), + IN_DAI(2), + IN_DAI(3), + IN_DAI(4), + OUT_DAI, +}; + +static const struct snd_soc_dapm_widget tegra210_amx_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, TEGRA210_AMX_CTRL, 0, 0), + SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0), + SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0), + SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0), + SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_AMX_ENABLE, + TEGRA210_AMX_ENABLE_SHIFT, 0), +}; + +#define STREAM_ROUTES(id, sname) \ + { "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \ + { "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname },\ + { "RX" #id, NULL, "RX" #id "-CIF-" sname }, \ + { "TX", NULL, "RX" #id }, \ + { "TX-CIF-" sname, NULL, "TX" }, \ + { "XBAR-" sname, NULL, "TX-CIF-" sname }, \ + { "XBAR-RX", NULL, "XBAR-" sname } + +#define AMX_ROUTES(id) \ + STREAM_ROUTES(id, "Playback"), \ + STREAM_ROUTES(id, "Capture") + +static const struct snd_soc_dapm_route tegra210_amx_routes[] = { + AMX_ROUTES(1), + AMX_ROUTES(2), + AMX_ROUTES(3), + AMX_ROUTES(4), +}; + +#define TEGRA210_AMX_BYTE_MAP_CTRL(reg) \ + SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \ + tegra210_amx_get_byte_map, \ + tegra210_amx_put_byte_map) + +static struct snd_kcontrol_new tegra210_amx_controls[] = { + TEGRA210_AMX_BYTE_MAP_CTRL(0), + TEGRA210_AMX_BYTE_MAP_CTRL(1), + TEGRA210_AMX_BYTE_MAP_CTRL(2), + TEGRA210_AMX_BYTE_MAP_CTRL(3), + TEGRA210_AMX_BYTE_MAP_CTRL(4), + TEGRA210_AMX_BYTE_MAP_CTRL(5), + TEGRA210_AMX_BYTE_MAP_CTRL(6), + TEGRA210_AMX_BYTE_MAP_CTRL(7), + TEGRA210_AMX_BYTE_MAP_CTRL(8), + TEGRA210_AMX_BYTE_MAP_CTRL(9), + TEGRA210_AMX_BYTE_MAP_CTRL(10), + TEGRA210_AMX_BYTE_MAP_CTRL(11), + TEGRA210_AMX_BYTE_MAP_CTRL(12), + TEGRA210_AMX_BYTE_MAP_CTRL(13), + TEGRA210_AMX_BYTE_MAP_CTRL(14), + TEGRA210_AMX_BYTE_MAP_CTRL(15), + TEGRA210_AMX_BYTE_MAP_CTRL(16), + TEGRA210_AMX_BYTE_MAP_CTRL(17), + TEGRA210_AMX_BYTE_MAP_CTRL(18), + TEGRA210_AMX_BYTE_MAP_CTRL(19), + TEGRA210_AMX_BYTE_MAP_CTRL(20), + TEGRA210_AMX_BYTE_MAP_CTRL(21), + TEGRA210_AMX_BYTE_MAP_CTRL(22), + TEGRA210_AMX_BYTE_MAP_CTRL(23), + TEGRA210_AMX_BYTE_MAP_CTRL(24), + TEGRA210_AMX_BYTE_MAP_CTRL(25), + TEGRA210_AMX_BYTE_MAP_CTRL(26), + TEGRA210_AMX_BYTE_MAP_CTRL(27), + TEGRA210_AMX_BYTE_MAP_CTRL(28), + TEGRA210_AMX_BYTE_MAP_CTRL(29), + TEGRA210_AMX_BYTE_MAP_CTRL(30), + TEGRA210_AMX_BYTE_MAP_CTRL(31), + TEGRA210_AMX_BYTE_MAP_CTRL(32), + TEGRA210_AMX_BYTE_MAP_CTRL(33), + TEGRA210_AMX_BYTE_MAP_CTRL(34), + TEGRA210_AMX_BYTE_MAP_CTRL(35), + TEGRA210_AMX_BYTE_MAP_CTRL(36), + TEGRA210_AMX_BYTE_MAP_CTRL(37), + TEGRA210_AMX_BYTE_MAP_CTRL(38), + TEGRA210_AMX_BYTE_MAP_CTRL(39), + TEGRA210_AMX_BYTE_MAP_CTRL(40), + TEGRA210_AMX_BYTE_MAP_CTRL(41), + TEGRA210_AMX_BYTE_MAP_CTRL(42), + TEGRA210_AMX_BYTE_MAP_CTRL(43), + TEGRA210_AMX_BYTE_MAP_CTRL(44), + TEGRA210_AMX_BYTE_MAP_CTRL(45), + TEGRA210_AMX_BYTE_MAP_CTRL(46), + TEGRA210_AMX_BYTE_MAP_CTRL(47), + TEGRA210_AMX_BYTE_MAP_CTRL(48), + TEGRA210_AMX_BYTE_MAP_CTRL(49), + TEGRA210_AMX_BYTE_MAP_CTRL(50), + TEGRA210_AMX_BYTE_MAP_CTRL(51), + TEGRA210_AMX_BYTE_MAP_CTRL(52), + TEGRA210_AMX_BYTE_MAP_CTRL(53), + TEGRA210_AMX_BYTE_MAP_CTRL(54), + TEGRA210_AMX_BYTE_MAP_CTRL(55), + TEGRA210_AMX_BYTE_MAP_CTRL(56), + TEGRA210_AMX_BYTE_MAP_CTRL(57), + TEGRA210_AMX_BYTE_MAP_CTRL(58), + TEGRA210_AMX_BYTE_MAP_CTRL(59), + TEGRA210_AMX_BYTE_MAP_CTRL(60), + TEGRA210_AMX_BYTE_MAP_CTRL(61), + TEGRA210_AMX_BYTE_MAP_CTRL(62), + TEGRA210_AMX_BYTE_MAP_CTRL(63), +}; + +static const struct snd_soc_component_driver tegra210_amx_cmpnt = { + .dapm_widgets = tegra210_amx_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_amx_widgets), + .dapm_routes = tegra210_amx_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_amx_routes), + .controls = tegra210_amx_controls, + .num_controls = ARRAY_SIZE(tegra210_amx_controls), +}; + +static bool tegra210_amx_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_AMX_RX_INT_MASK ... TEGRA210_AMX_RX4_CIF_CTRL: + case TEGRA210_AMX_TX_INT_MASK ... TEGRA210_AMX_CG: + case TEGRA210_AMX_CTRL ... TEGRA210_AMX_CYA: + case TEGRA210_AMX_CFG_RAM_CTRL ... TEGRA210_AMX_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra194_amx_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA194_AMX_RX1_FRAME_PERIOD ... TEGRA194_AMX_RX4_FRAME_PERIOD: + return true; + default: + return tegra210_amx_wr_reg(dev, reg); + } +} + +static bool tegra210_amx_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_AMX_RX_STATUS ... TEGRA210_AMX_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra194_amx_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA194_AMX_RX1_FRAME_PERIOD ... TEGRA194_AMX_RX4_FRAME_PERIOD: + return true; + default: + return tegra210_amx_rd_reg(dev, reg); + } +} + +static bool tegra210_amx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_AMX_RX_STATUS: + case TEGRA210_AMX_RX_INT_STATUS: + case TEGRA210_AMX_RX_INT_SET: + case TEGRA210_AMX_TX_STATUS: + case TEGRA210_AMX_TX_INT_STATUS: + case TEGRA210_AMX_TX_INT_SET: + case TEGRA210_AMX_SOFT_RESET: + case TEGRA210_AMX_STATUS: + case TEGRA210_AMX_INT_STATUS: + case TEGRA210_AMX_CFG_RAM_CTRL: + case TEGRA210_AMX_CFG_RAM_DATA: + return true; + default: + break; + } + + return false; +} + +static const struct regmap_config tegra210_amx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_AMX_CFG_RAM_DATA, + .writeable_reg = tegra210_amx_wr_reg, + .readable_reg = tegra210_amx_rd_reg, + .volatile_reg = tegra210_amx_volatile_reg, + .reg_defaults = tegra210_amx_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_config tegra194_amx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA194_AMX_RX4_LAST_FRAME_PERIOD, + .writeable_reg = tegra194_amx_wr_reg, + .readable_reg = tegra194_amx_rd_reg, + .volatile_reg = tegra210_amx_volatile_reg, + .reg_defaults = tegra210_amx_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct tegra210_amx_soc_data soc_data_tegra210 = { + .regmap_conf = &tegra210_amx_regmap_config, +}; + +static const struct tegra210_amx_soc_data soc_data_tegra194 = { + .regmap_conf = &tegra194_amx_regmap_config, + .auto_disable = true, +}; + +static const struct of_device_id tegra210_amx_of_match[] = { + { .compatible = "nvidia,tegra210-amx", .data = &soc_data_tegra210 }, + { .compatible = "nvidia,tegra194-amx", .data = &soc_data_tegra194 }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_amx_of_match); + +static int tegra210_amx_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_amx *amx; + void __iomem *regs; + int err; + const struct of_device_id *match; + struct tegra210_amx_soc_data *soc_data; + + match = of_match_device(tegra210_amx_of_match, dev); + + soc_data = (struct tegra210_amx_soc_data *)match->data; + + amx = devm_kzalloc(dev, sizeof(*amx), GFP_KERNEL); + if (!amx) + return -ENOMEM; + + amx->soc_data = soc_data; + + dev_set_drvdata(dev, amx); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + amx->regmap = devm_regmap_init_mmio(dev, regs, + soc_data->regmap_conf); + if (IS_ERR(amx->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(amx->regmap); + } + + regcache_cache_only(amx->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra210_amx_cmpnt, + tegra210_amx_dais, + ARRAY_SIZE(tegra210_amx_dais)); + if (err) { + dev_err(dev, "can't register AMX component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra210_amx_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_amx_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_amx_runtime_suspend, + tegra210_amx_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra210_amx_driver = { + .driver = { + .name = "tegra210-amx", + .of_match_table = tegra210_amx_of_match, + .pm = &tegra210_amx_pm_ops, + }, + .probe = tegra210_amx_platform_probe, + .remove = tegra210_amx_platform_remove, +}; +module_platform_driver(tegra210_amx_driver); + +MODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>"); +MODULE_DESCRIPTION("Tegra210 AMX ASoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_amx.h b/sound/soc/tegra/tegra210_amx.h new file mode 100644 index 000000000000..e277741e4258 --- /dev/null +++ b/sound/soc/tegra/tegra210_amx.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_amx.h - Definitions for Tegra210 AMX driver + * + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_AMX_H__ +#define __TEGRA210_AMX_H__ + +/* Register offsets from TEGRA210_AMX*_BASE */ +#define TEGRA210_AMX_RX_STATUS 0x0c +#define TEGRA210_AMX_RX_INT_STATUS 0x10 +#define TEGRA210_AMX_RX_INT_MASK 0x14 +#define TEGRA210_AMX_RX_INT_SET 0x18 +#define TEGRA210_AMX_RX_INT_CLEAR 0x1c +#define TEGRA210_AMX_RX1_CIF_CTRL 0x20 +#define TEGRA210_AMX_RX2_CIF_CTRL 0x24 +#define TEGRA210_AMX_RX3_CIF_CTRL 0x28 +#define TEGRA210_AMX_RX4_CIF_CTRL 0x2c +#define TEGRA210_AMX_TX_STATUS 0x4c +#define TEGRA210_AMX_TX_INT_STATUS 0x50 +#define TEGRA210_AMX_TX_INT_MASK 0x54 +#define TEGRA210_AMX_TX_INT_SET 0x58 +#define TEGRA210_AMX_TX_INT_CLEAR 0x5c +#define TEGRA210_AMX_TX_CIF_CTRL 0x60 +#define TEGRA210_AMX_ENABLE 0x80 +#define TEGRA210_AMX_SOFT_RESET 0x84 +#define TEGRA210_AMX_CG 0x88 +#define TEGRA210_AMX_STATUS 0x8c +#define TEGRA210_AMX_INT_STATUS 0x90 +#define TEGRA210_AMX_CTRL 0xa4 +#define TEGRA210_AMX_OUT_BYTE_EN0 0xa8 +#define TEGRA210_AMX_OUT_BYTE_EN1 0xac +#define TEGRA210_AMX_CYA 0xb0 +#define TEGRA210_AMX_CFG_RAM_CTRL 0xb8 +#define TEGRA210_AMX_CFG_RAM_DATA 0xbc + +#define TEGRA194_AMX_RX1_FRAME_PERIOD 0xc0 +#define TEGRA194_AMX_RX4_FRAME_PERIOD 0xcc +#define TEGRA194_AMX_RX4_LAST_FRAME_PERIOD 0xdc + +/* Fields in TEGRA210_AMX_ENABLE */ +#define TEGRA210_AMX_ENABLE_SHIFT 0 + +/* Fields in TEGRA210_AMX_CTRL */ +#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_SHIFT 14 +#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK (3 << TEGRA210_AMX_CTRL_MSTR_RX_NUM_SHIFT) + +#define TEGRA210_AMX_CTRL_RX_DEP_SHIFT 12 +#define TEGRA210_AMX_CTRL_RX_DEP_MASK (3 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT) + +/* Fields in TEGRA210_AMX_CFG_RAM_CTRL */ +#define TEGRA210_AMX_CFG_RAM_CTRL_RW_SHIFT 14 +#define TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_AMX_CFG_RAM_CTRL_RW_SHIFT) + +#define TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13 +#define TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT) + +#define TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12 +#define TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT) + +#define TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT 0 + +/* Fields in TEGRA210_AMX_SOFT_RESET */ +#define TEGRA210_AMX_SOFT_RESET_SOFT_EN 1 +#define TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK TEGRA210_AMX_SOFT_RESET_SOFT_EN + +#define TEGRA210_AMX_AUDIOCIF_CH_STRIDE 4 +#define TEGRA210_AMX_RAM_DEPTH 16 +#define TEGRA210_AMX_MAP_STREAM_NUM_SHIFT 6 +#define TEGRA210_AMX_MAP_WORD_NUM_SHIFT 2 +#define TEGRA210_AMX_MAP_BYTE_NUM_SHIFT 0 + +enum { + TEGRA210_AMX_WAIT_ON_ALL, + TEGRA210_AMX_WAIT_ON_ANY, +}; + +struct tegra210_amx_soc_data { + const struct regmap_config *regmap_conf; + bool auto_disable; +}; + +struct tegra210_amx { + const struct tegra210_amx_soc_data *soc_data; + unsigned int map[TEGRA210_AMX_RAM_DEPTH]; + struct regmap *regmap; + unsigned int byte_mask[2]; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c new file mode 100644 index 000000000000..55e61776c565 --- /dev/null +++ b/sound/soc/tegra/tegra210_mixer.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_mixer.c - Tegra210 MIXER driver +// +// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra210_mixer.h" +#include "tegra_cif.h" + +#define MIXER_REG(reg, id) ((reg) + ((id) * TEGRA210_MIXER_REG_STRIDE)) +#define MIXER_REG_BASE(reg) ((reg) % TEGRA210_MIXER_REG_STRIDE) + +#define MIXER_GAIN_CFG_RAM_ADDR(id) \ + (TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 + \ + ((id) * TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE)) + +#define MIXER_RX_REG_DEFAULTS(id) \ + { MIXER_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700}, \ + { MIXER_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823}, \ + { MIXER_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0} + +#define MIXER_TX_REG_DEFAULTS(id) \ + { MIXER_REG(TEGRA210_MIXER_TX1_INT_MASK, (id)), 0x00000001}, \ + { MIXER_REG(TEGRA210_MIXER_TX1_CIF_CTRL, (id)), 0x00007700} + +#define REG_DURATION_PARAM(reg, i) ((reg) + NUM_GAIN_POLY_COEFFS + 1 + (i)) + +static const struct reg_default tegra210_mixer_reg_defaults[] = { + /* Inputs */ + MIXER_RX_REG_DEFAULTS(0), + MIXER_RX_REG_DEFAULTS(1), + MIXER_RX_REG_DEFAULTS(2), + MIXER_RX_REG_DEFAULTS(3), + MIXER_RX_REG_DEFAULTS(4), + MIXER_RX_REG_DEFAULTS(5), + MIXER_RX_REG_DEFAULTS(6), + MIXER_RX_REG_DEFAULTS(7), + MIXER_RX_REG_DEFAULTS(8), + MIXER_RX_REG_DEFAULTS(9), + /* Outputs */ + MIXER_TX_REG_DEFAULTS(0), + MIXER_TX_REG_DEFAULTS(1), + MIXER_TX_REG_DEFAULTS(2), + MIXER_TX_REG_DEFAULTS(3), + MIXER_TX_REG_DEFAULTS(4), + + { TEGRA210_MIXER_CG, 0x00000001}, + { TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000}, + { TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000}, + { TEGRA210_MIXER_ENABLE, 0x1 }, +}; + +/* Default gain parameters */ +static const struct tegra210_mixer_gain_params gain_params = { + /* Polynomial coefficients */ + { 0, 0, 0, 0, 0, 0, 0, 0x1000000, 0 }, + /* Gain value */ + 0x10000, + /* Duration Parameters */ + { 0, 0, 0x400, 0x8000000 }, +}; + +static int __maybe_unused tegra210_mixer_runtime_suspend(struct device *dev) +{ + struct tegra210_mixer *mixer = dev_get_drvdata(dev); + + regcache_cache_only(mixer->regmap, true); + regcache_mark_dirty(mixer->regmap); + + return 0; +} + +static int __maybe_unused tegra210_mixer_runtime_resume(struct device *dev) +{ + struct tegra210_mixer *mixer = dev_get_drvdata(dev); + + regcache_cache_only(mixer->regmap, false); + regcache_sync(mixer->regmap); + + return 0; +} + +static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer, + unsigned int addr, + unsigned int coef) +{ + unsigned int reg, val; + int err; + + /* Check if busy */ + err = regmap_read_poll_timeout(mixer->regmap, + TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, + val, !(val & 0x80000000), 10, 10000); + if (err < 0) + return err; + + reg = (addr << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT) & + TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK; + reg |= TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN; + reg |= TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE; + reg |= TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN; + + regmap_write(mixer->regmap, + TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, + reg); + regmap_write(mixer->regmap, + TEGRA210_MIXER_GAIN_CFG_RAM_DATA, + coef); + + return 0; +} + +static int tegra210_mixer_configure_gain(struct snd_soc_component *cmpnt, + unsigned int id, bool instant_gain) +{ + struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt); + unsigned int reg = MIXER_GAIN_CFG_RAM_ADDR(id); + int err, i; + + pm_runtime_get_sync(cmpnt->dev); + + /* Write default gain poly coefficients */ + for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++) { + err = tegra210_mixer_write_ram(mixer, reg + i, + gain_params.poly_coeff[i]); + + if (err < 0) + goto rpm_put; + } + + /* Write stored gain value */ + err = tegra210_mixer_write_ram(mixer, reg + NUM_GAIN_POLY_COEFFS, + mixer->gain_value[id]); + if (err < 0) + goto rpm_put; + + /* Write duration parameters */ + for (i = 0; i < NUM_DURATION_PARMS; i++) { + int val; + + if (instant_gain) + val = 1; + else + val = gain_params.duration[i]; + + err = tegra210_mixer_write_ram(mixer, + REG_DURATION_PARAM(reg, i), + val); + if (err < 0) + goto rpm_put; + } + + /* Trigger to apply gain configurations */ + err = tegra210_mixer_write_ram(mixer, reg + REG_CFG_DONE_TRIGGER, + VAL_CFG_DONE_TRIGGER); + +rpm_put: + pm_runtime_put(cmpnt->dev); + + return err; +} + +static int tegra210_mixer_get_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt); + unsigned int reg = mc->reg; + unsigned int i; + + i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) / + TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE; + + ucontrol->value.integer.value[0] = mixer->gain_value[i]; + + return 0; +} + +static int tegra210_mixer_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt); + unsigned int reg = mc->reg, id; + bool instant_gain = false; + int err; + + if (strstr(kcontrol->id.name, "Instant Gain Volume")) + instant_gain = true; + + /* Save gain value for specific MIXER input */ + id = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) / + TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE; + + mixer->gain_value[id] = ucontrol->value.integer.value[0]; + + err = tegra210_mixer_configure_gain(cmpnt, id, instant_gain); + if (err) { + dev_err(cmpnt->dev, "Failed to apply gain\n"); + return err; + } + + return 1; +} + +static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer, + struct snd_pcm_hw_params *params, + unsigned int reg, + unsigned int id) +{ + unsigned int channels, audio_bits; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EINVAL; + } + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = audio_bits; + + tegra_set_cif(mixer->regmap, + reg + (id * TEGRA210_MIXER_REG_STRIDE), + &cif_conf); + + return 0; +} + +static int tegra210_mixer_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai); + int err; + + err = tegra210_mixer_set_audio_cif(mixer, params, + TEGRA210_MIXER_RX1_CIF_CTRL, + dai->id); + if (err < 0) + return err; + + return tegra210_mixer_configure_gain(dai->component, dai->id, false); +} + +static int tegra210_mixer_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai); + + return tegra210_mixer_set_audio_cif(mixer, params, + TEGRA210_MIXER_TX1_CIF_CTRL, + dai->id - TEGRA210_MIXER_RX_MAX); +} + +static const struct snd_soc_dai_ops tegra210_mixer_out_dai_ops = { + .hw_params = tegra210_mixer_out_hw_params, +}; + +static const struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = { + .hw_params = tegra210_mixer_in_hw_params, +}; + +#define IN_DAI(id) \ + { \ + .name = "MIXER-RX-CIF"#id, \ + .playback = { \ + .stream_name = "RX" #id "-CIF-Playback",\ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "RX" #id "-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra210_mixer_in_dai_ops, \ + } + +#define OUT_DAI(id) \ + { \ + .name = "MIXER-TX-CIF" #id, \ + .playback = { \ + .stream_name = "TX" #id "-CIF-Playback",\ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "TX" #id "-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra210_mixer_out_dai_ops, \ + } + +static struct snd_soc_dai_driver tegra210_mixer_dais[] = { + /* Mixer Input */ + IN_DAI(1), + IN_DAI(2), + IN_DAI(3), + IN_DAI(4), + IN_DAI(5), + IN_DAI(6), + IN_DAI(7), + IN_DAI(8), + IN_DAI(9), + IN_DAI(10), + + /* Mixer Output */ + OUT_DAI(1), + OUT_DAI(2), + OUT_DAI(3), + OUT_DAI(4), + OUT_DAI(5), +}; + +#define ADDER_CTRL_DECL(name, reg) \ + static const struct snd_kcontrol_new name[] = { \ + SOC_DAPM_SINGLE("RX1", reg, 0, 1, 0), \ + SOC_DAPM_SINGLE("RX2", reg, 1, 1, 0), \ + SOC_DAPM_SINGLE("RX3", reg, 2, 1, 0), \ + SOC_DAPM_SINGLE("RX4", reg, 3, 1, 0), \ + SOC_DAPM_SINGLE("RX5", reg, 4, 1, 0), \ + SOC_DAPM_SINGLE("RX6", reg, 5, 1, 0), \ + SOC_DAPM_SINGLE("RX7", reg, 6, 1, 0), \ + SOC_DAPM_SINGLE("RX8", reg, 7, 1, 0), \ + SOC_DAPM_SINGLE("RX9", reg, 8, 1, 0), \ + SOC_DAPM_SINGLE("RX10", reg, 9, 1, 0), \ + } + +ADDER_CTRL_DECL(adder1, TEGRA210_MIXER_TX1_ADDER_CONFIG); +ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_TX2_ADDER_CONFIG); +ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_TX3_ADDER_CONFIG); +ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG); +ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG); + +#define GAIN_CTRL(id) \ + SOC_SINGLE_EXT("RX" #id " Gain Volume", \ + MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \ + 0x20000, 0, tegra210_mixer_get_gain, \ + tegra210_mixer_put_gain), \ + SOC_SINGLE_EXT("RX" #id " Instant Gain Volume", \ + MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \ + 0x20000, 0, tegra210_mixer_get_gain, \ + tegra210_mixer_put_gain), + +/* Volume controls for all MIXER inputs */ +static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = { + GAIN_CTRL(1) + GAIN_CTRL(2) + GAIN_CTRL(3) + GAIN_CTRL(4) + GAIN_CTRL(5) + GAIN_CTRL(6) + GAIN_CTRL(7) + GAIN_CTRL(8) + GAIN_CTRL(9) + GAIN_CTRL(10) +}; + +static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX8", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX9", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX10", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_MIXER_TX1_ENABLE, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_MIXER_TX2_ENABLE, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_MIXER_TX3_ENABLE, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_MIXER_TX4_ENABLE, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0, TEGRA210_MIXER_TX5_ENABLE, 0, 0), + SND_SOC_DAPM_MIXER("Adder1", SND_SOC_NOPM, 1, 0, adder1, + ARRAY_SIZE(adder1)), + SND_SOC_DAPM_MIXER("Adder2", SND_SOC_NOPM, 1, 0, adder2, + ARRAY_SIZE(adder2)), + SND_SOC_DAPM_MIXER("Adder3", SND_SOC_NOPM, 1, 0, adder3, + ARRAY_SIZE(adder3)), + SND_SOC_DAPM_MIXER("Adder4", SND_SOC_NOPM, 1, 0, adder4, + ARRAY_SIZE(adder4)), + SND_SOC_DAPM_MIXER("Adder5", SND_SOC_NOPM, 1, 0, adder5, + ARRAY_SIZE(adder5)), +}; + +#define RX_ROUTES(id, sname) \ + { "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \ + { "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname }, \ + { "RX" #id, NULL, "RX" #id "-CIF-" sname } + +#define MIXER_RX_ROUTES(id) \ + RX_ROUTES(id, "Playback"), \ + RX_ROUTES(id, "Capture") + +#define ADDER_ROUTES(id, sname) \ + { "Adder" #id, "RX1", "RX1" }, \ + { "Adder" #id, "RX2", "RX2" }, \ + { "Adder" #id, "RX3", "RX3" }, \ + { "Adder" #id, "RX4", "RX4" }, \ + { "Adder" #id, "RX5", "RX5" }, \ + { "Adder" #id, "RX6", "RX6" }, \ + { "Adder" #id, "RX7", "RX7" }, \ + { "Adder" #id, "RX8", "RX8" }, \ + { "Adder" #id, "RX9", "RX9" }, \ + { "Adder" #id, "RX10", "RX10" }, \ + { "TX" #id, NULL, "Adder" #id }, \ + { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ + { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ + { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } \ + +#define TX_ROUTES(id, sname) \ + ADDER_ROUTES(1, sname), \ + ADDER_ROUTES(2, sname), \ + ADDER_ROUTES(3, sname), \ + ADDER_ROUTES(4, sname), \ + ADDER_ROUTES(5, sname) + +#define MIXER_TX_ROUTES(id) \ + TX_ROUTES(id, "Playback"), \ + TX_ROUTES(id, "Capture") + +static const struct snd_soc_dapm_route tegra210_mixer_routes[] = { + /* Input */ + MIXER_RX_ROUTES(1), + MIXER_RX_ROUTES(2), + MIXER_RX_ROUTES(3), + MIXER_RX_ROUTES(4), + MIXER_RX_ROUTES(5), + MIXER_RX_ROUTES(6), + MIXER_RX_ROUTES(7), + MIXER_RX_ROUTES(8), + MIXER_RX_ROUTES(9), + MIXER_RX_ROUTES(10), + /* Output */ + MIXER_TX_ROUTES(1), + MIXER_TX_ROUTES(2), + MIXER_TX_ROUTES(3), + MIXER_TX_ROUTES(4), + MIXER_TX_ROUTES(5), +}; + +static const struct snd_soc_component_driver tegra210_mixer_cmpnt = { + .dapm_widgets = tegra210_mixer_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_mixer_widgets), + .dapm_routes = tegra210_mixer_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_mixer_routes), + .controls = tegra210_mixer_gain_ctls, + .num_controls = ARRAY_SIZE(tegra210_mixer_gain_ctls), +}; + +static bool tegra210_mixer_wr_reg(struct device *dev, + unsigned int reg) +{ + if (reg < TEGRA210_MIXER_RX_LIMIT) + reg = MIXER_REG_BASE(reg); + else if (reg < TEGRA210_MIXER_TX_LIMIT) + reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE; + + switch (reg) { + case TEGRA210_MIXER_RX1_SOFT_RESET: + case TEGRA210_MIXER_RX1_CIF_CTRL ... TEGRA210_MIXER_RX1_PEAK_CTRL: + + case TEGRA210_MIXER_TX1_ENABLE: + case TEGRA210_MIXER_TX1_SOFT_RESET: + case TEGRA210_MIXER_TX1_INT_MASK ... TEGRA210_MIXER_TX1_ADDER_CONFIG: + + case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CG: + case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL ... TEGRA210_MIXER_CTRL: + return true; + default: + return false; + } +} + +static bool tegra210_mixer_rd_reg(struct device *dev, + unsigned int reg) +{ + if (reg < TEGRA210_MIXER_RX_LIMIT) + reg = MIXER_REG_BASE(reg); + else if (reg < TEGRA210_MIXER_TX_LIMIT) + reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE; + + switch (reg) { + case TEGRA210_MIXER_RX1_SOFT_RESET ... TEGRA210_MIXER_RX1_SAMPLE_COUNT: + case TEGRA210_MIXER_TX1_ENABLE ... TEGRA210_MIXER_TX1_ADDER_CONFIG: + case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CTRL: + return true; + default: + return false; + } +} + +static bool tegra210_mixer_volatile_reg(struct device *dev, + unsigned int reg) +{ + if (reg < TEGRA210_MIXER_RX_LIMIT) + reg = MIXER_REG_BASE(reg); + else if (reg < TEGRA210_MIXER_TX_LIMIT) + reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE; + + switch (reg) { + case TEGRA210_MIXER_RX1_SOFT_RESET: + case TEGRA210_MIXER_RX1_STATUS: + + case TEGRA210_MIXER_TX1_SOFT_RESET: + case TEGRA210_MIXER_TX1_STATUS: + case TEGRA210_MIXER_TX1_INT_STATUS: + case TEGRA210_MIXER_TX1_INT_SET: + + case TEGRA210_MIXER_SOFT_RESET: + case TEGRA210_MIXER_STATUS: + case TEGRA210_MIXER_INT_STATUS: + case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL: + case TEGRA210_MIXER_GAIN_CFG_RAM_DATA: + case TEGRA210_MIXER_PEAKM_RAM_CTRL: + case TEGRA210_MIXER_PEAKM_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_mixer_precious_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case TEGRA210_MIXER_GAIN_CFG_RAM_DATA: + case TEGRA210_MIXER_PEAKM_RAM_DATA: + return true; + default: + return false; + } +} + +static const struct regmap_config tegra210_mixer_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_MIXER_CTRL, + .writeable_reg = tegra210_mixer_wr_reg, + .readable_reg = tegra210_mixer_rd_reg, + .volatile_reg = tegra210_mixer_volatile_reg, + .precious_reg = tegra210_mixer_precious_reg, + .reg_defaults = tegra210_mixer_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id tegra210_mixer_of_match[] = { + { .compatible = "nvidia,tegra210-amixer" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match); + +static int tegra210_mixer_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_mixer *mixer; + void __iomem *regs; + int err, i; + + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); + if (!mixer) + return -ENOMEM; + + dev_set_drvdata(dev, mixer); + + /* Use default gain value for all MIXER inputs */ + for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++) + mixer->gain_value[i] = gain_params.gain_value; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + mixer->regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_mixer_regmap_config); + if (IS_ERR(mixer->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(mixer->regmap); + } + + regcache_cache_only(mixer->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra210_mixer_cmpnt, + tegra210_mixer_dais, + ARRAY_SIZE(tegra210_mixer_dais)); + if (err) { + dev_err(dev, "can't register MIXER component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra210_mixer_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_mixer_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend, + tegra210_mixer_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra210_mixer_driver = { + .driver = { + .name = "tegra210_mixer", + .of_match_table = tegra210_mixer_of_match, + .pm = &tegra210_mixer_pm_ops, + }, + .probe = tegra210_mixer_platform_probe, + .remove = tegra210_mixer_platform_remove, +}; +module_platform_driver(tegra210_mixer_driver); + +MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); +MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_mixer.h b/sound/soc/tegra/tegra210_mixer.h new file mode 100644 index 000000000000..a330530fbc61 --- /dev/null +++ b/sound/soc/tegra/tegra210_mixer.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_mixer.h - Definitions for Tegra210 MIXER driver + * + * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_MIXER_H__ +#define __TEGRA210_MIXER_H__ + +/* XBAR_RX related MIXER offsets */ +#define TEGRA210_MIXER_RX1_SOFT_RESET 0x04 +#define TEGRA210_MIXER_RX1_STATUS 0x10 +#define TEGRA210_MIXER_RX1_CIF_CTRL 0x24 +#define TEGRA210_MIXER_RX1_CTRL 0x28 +#define TEGRA210_MIXER_RX1_PEAK_CTRL 0x2c +#define TEGRA210_MIXER_RX1_SAMPLE_COUNT 0x30 + +/* XBAR_TX related MIXER offsets */ +#define TEGRA210_MIXER_TX1_ENABLE 0x280 +#define TEGRA210_MIXER_TX1_SOFT_RESET 0x284 +#define TEGRA210_MIXER_TX1_STATUS 0x290 +#define TEGRA210_MIXER_TX1_INT_STATUS 0x294 +#define TEGRA210_MIXER_TX1_INT_MASK 0x298 +#define TEGRA210_MIXER_TX1_INT_SET 0x29c +#define TEGRA210_MIXER_TX1_INT_CLEAR 0x2a0 +#define TEGRA210_MIXER_TX1_CIF_CTRL 0x2a4 +#define TEGRA210_MIXER_TX1_ADDER_CONFIG 0x2a8 + +/* MIXER related offsets */ +#define TEGRA210_MIXER_ENABLE 0x400 +#define TEGRA210_MIXER_SOFT_RESET 0x404 +#define TEGRA210_MIXER_CG 0x408 +#define TEGRA210_MIXER_STATUS 0x410 +#define TEGRA210_MIXER_INT_STATUS 0x414 +#define TEGRA210_MIXER_GAIN_CFG_RAM_CTRL 0x42c +#define TEGRA210_MIXER_GAIN_CFG_RAM_DATA 0x430 +#define TEGRA210_MIXER_PEAKM_RAM_CTRL 0x434 +#define TEGRA210_MIXER_PEAKM_RAM_DATA 0x438 +#define TEGRA210_MIXER_CTRL 0x43c + +#define TEGRA210_MIXER_TX2_ADDER_CONFIG (TEGRA210_MIXER_TX1_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE) +#define TEGRA210_MIXER_TX3_ADDER_CONFIG (TEGRA210_MIXER_TX2_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE) +#define TEGRA210_MIXER_TX4_ADDER_CONFIG (TEGRA210_MIXER_TX3_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE) +#define TEGRA210_MIXER_TX5_ADDER_CONFIG (TEGRA210_MIXER_TX4_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE) + +#define TEGRA210_MIXER_TX2_ENABLE (TEGRA210_MIXER_TX1_ENABLE + TEGRA210_MIXER_REG_STRIDE) +#define TEGRA210_MIXER_TX3_ENABLE (TEGRA210_MIXER_TX2_ENABLE + TEGRA210_MIXER_REG_STRIDE) +#define TEGRA210_MIXER_TX4_ENABLE (TEGRA210_MIXER_TX3_ENABLE + TEGRA210_MIXER_REG_STRIDE) +#define TEGRA210_MIXER_TX5_ENABLE (TEGRA210_MIXER_TX4_ENABLE + TEGRA210_MIXER_REG_STRIDE) + +/* Fields in TEGRA210_MIXER_ENABLE */ +#define TEGRA210_MIXER_ENABLE_SHIFT 0 +#define TEGRA210_MIXER_ENABLE_MASK (1 << TEGRA210_MIXER_ENABLE_SHIFT) +#define TEGRA210_MIXER_EN (1 << TEGRA210_MIXER_ENABLE_SHIFT) + +/* Fields in TEGRA210_MIXER_GAIN_CFG_RAM_CTRL */ +#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 0x0 +#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE 0x10 + +#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT 14 +#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT) +#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE (1 << TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT) + +#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT 13 +#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT) +#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN (1 << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT) + +#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT 12 +#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT) +#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN (1 << TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT) + +#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT 0 +#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK (0x1ff << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT) + +#define TEGRA210_MIXER_REG_STRIDE 0x40 +#define TEGRA210_MIXER_RX_MAX 10 +#define TEGRA210_MIXER_RX_LIMIT (TEGRA210_MIXER_RX_MAX * TEGRA210_MIXER_REG_STRIDE) +#define TEGRA210_MIXER_TX_MAX 5 +#define TEGRA210_MIXER_TX_LIMIT (TEGRA210_MIXER_RX_LIMIT + (TEGRA210_MIXER_TX_MAX * TEGRA210_MIXER_REG_STRIDE)) + +#define REG_CFG_DONE_TRIGGER 0xf +#define VAL_CFG_DONE_TRIGGER 0x1 + +#define NUM_GAIN_POLY_COEFFS 9 +#define NUM_DURATION_PARMS 4 + +struct tegra210_mixer_gain_params { + int poly_coeff[NUM_GAIN_POLY_COEFFS]; + int gain_value; + int duration[NUM_DURATION_PARMS]; +}; + +struct tegra210_mixer { + int gain_value[TEGRA210_MIXER_RX_MAX]; + struct regmap *regmap; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c new file mode 100644 index 000000000000..7b9c7006e419 --- /dev/null +++ b/sound/soc/tegra/tegra210_mvc.c @@ -0,0 +1,645 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_mvc.c - Tegra210 MVC driver +// +// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra210_mvc.h" +#include "tegra_cif.h" + +static const struct reg_default tegra210_mvc_reg_defaults[] = { + { TEGRA210_MVC_RX_INT_MASK, 0x00000001}, + { TEGRA210_MVC_RX_CIF_CTRL, 0x00007700}, + { TEGRA210_MVC_TX_INT_MASK, 0x00000001}, + { TEGRA210_MVC_TX_CIF_CTRL, 0x00007700}, + { TEGRA210_MVC_CG, 0x1}, + { TEGRA210_MVC_CTRL, TEGRA210_MVC_CTRL_DEFAULT}, + { TEGRA210_MVC_INIT_VOL, 0x00800000}, + { TEGRA210_MVC_TARGET_VOL, 0x00800000}, + { TEGRA210_MVC_DURATION, 0x000012c0}, + { TEGRA210_MVC_DURATION_INV, 0x0006d3a0}, + { TEGRA210_MVC_POLY_N1, 0x0000007d}, + { TEGRA210_MVC_POLY_N2, 0x00000271}, + { TEGRA210_MVC_PEAK_CTRL, 0x000012c0}, + { TEGRA210_MVC_CFG_RAM_CTRL, 0x00004000}, +}; + +static const struct tegra210_mvc_gain_params gain_params = { + .poly_coeff = { 23738319, 659403, -3680, + 15546680, 2530732, -120985, + 12048422, 5527252, -785042 }, + .poly_n1 = 16, + .poly_n2 = 63, + .duration = 150, + .duration_inv = 14316558, +}; + +static int __maybe_unused tegra210_mvc_runtime_suspend(struct device *dev) +{ + struct tegra210_mvc *mvc = dev_get_drvdata(dev); + + regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &(mvc->ctrl_value)); + + regcache_cache_only(mvc->regmap, true); + regcache_mark_dirty(mvc->regmap); + + return 0; +} + +static int __maybe_unused tegra210_mvc_runtime_resume(struct device *dev) +{ + struct tegra210_mvc *mvc = dev_get_drvdata(dev); + + regcache_cache_only(mvc->regmap, false); + regcache_sync(mvc->regmap); + + regmap_write(mvc->regmap, TEGRA210_MVC_CTRL, mvc->ctrl_value); + regmap_update_bits(mvc->regmap, + TEGRA210_MVC_SWITCH, + TEGRA210_MVC_VOLUME_SWITCH_MASK, + TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); + + return 0; +} + +static void tegra210_mvc_write_ram(struct regmap *regmap) +{ + int i; + + regmap_write(regmap, TEGRA210_MVC_CFG_RAM_CTRL, + TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN | + TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN | + TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE); + + for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++) + regmap_write(regmap, TEGRA210_MVC_CFG_RAM_DATA, + gain_params.poly_coeff[i]); +} + +static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val) +{ + /* + * Volume control read from mixer control is with + * 100x scaling; for CURVE_POLY the reg range + * is 0-100 (linear, Q24) and for CURVE_LINEAR + * it is -120dB to +40dB (Q8) + */ + if (mvc->curve_type == CURVE_POLY) { + if (val > 10000) + val = 10000; + mvc->volume[chan] = ((val * (1<<8)) / 100) << 16; + } else { + val -= 12000; + mvc->volume[chan] = (val * (1<<8)) / 100; + } +} + +static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + u8 mute_mask; + u32 val; + + pm_runtime_get_sync(cmpnt->dev); + regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val); + pm_runtime_put(cmpnt->dev); + + mute_mask = (val >> TEGRA210_MVC_MUTE_SHIFT) & + TEGRA210_MUTE_MASK_EN; + + ucontrol->value.integer.value[0] = mute_mask; + + return 0; +} + +static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + unsigned int value; + u8 mute_mask; + int err; + + pm_runtime_get_sync(cmpnt->dev); + + /* Check if VOLUME_SWITCH is triggered */ + err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, + value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), + 10, 10000); + if (err < 0) + goto end; + + mute_mask = ucontrol->value.integer.value[0]; + + err = regmap_update_bits(mvc->regmap, mc->reg, + TEGRA210_MVC_MUTE_MASK, + mute_mask << TEGRA210_MVC_MUTE_SHIFT); + if (err < 0) + goto end; + + return 1; + +end: + pm_runtime_put(cmpnt->dev); + return err; +} + +static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; + s32 val = mvc->volume[chan]; + + if (mvc->curve_type == CURVE_POLY) { + val = ((val >> 16) * 100) >> 8; + } else { + val = (val * 100) >> 8; + val += 12000; + } + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + unsigned int reg = mc->reg; + unsigned int value; + u8 chan; + int err; + + pm_runtime_get_sync(cmpnt->dev); + + /* Check if VOLUME_SWITCH is triggered */ + err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, + value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), + 10, 10000); + if (err < 0) + goto end; + + chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; + + tegra210_mvc_conv_vol(mvc, chan, + ucontrol->value.integer.value[0]); + + /* Configure init volume same as target volume */ + regmap_write(mvc->regmap, + TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan), + mvc->volume[chan]); + + regmap_write(mvc->regmap, reg, mvc->volume[chan]); + + regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, + TEGRA210_MVC_VOLUME_SWITCH_MASK, + TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); + + return 1; + +end: + pm_runtime_put(cmpnt->dev); + return err; +} + +static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc, + struct device *dev) +{ + int i; + + /* Change volume to default init for new curve type */ + if (mvc->curve_type == CURVE_POLY) { + for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) + mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY; + } else { + for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) + mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR; + } + + pm_runtime_get_sync(dev); + + /* Program curve type */ + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_CURVE_TYPE_MASK, + mvc->curve_type << + TEGRA210_MVC_CURVE_TYPE_SHIFT); + + /* Init volume for all channels */ + for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) { + regmap_write(mvc->regmap, + TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i), + mvc->volume[i]); + regmap_write(mvc->regmap, + TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i), + mvc->volume[i]); + } + + /* Trigger volume switch */ + regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, + TEGRA210_MVC_VOLUME_SWITCH_MASK, + TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); + + pm_runtime_put(dev); +} + +static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + + ucontrol->value.integer.value[0] = mvc->curve_type; + + return 0; +} + +static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); + int value; + + regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value); + if (value & TEGRA210_MVC_EN) { + dev_err(cmpnt->dev, + "Curve type can't be set when MVC is running\n"); + return -EINVAL; + } + + if (mvc->curve_type == ucontrol->value.integer.value[0]) + return 0; + + mvc->curve_type = ucontrol->value.integer.value[0]; + + tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev); + + return 1; +} + +static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + unsigned int channels, audio_bits; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EINVAL; + } + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = audio_bits; + + tegra_set_cif(mvc->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->dev; + struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai); + int err, val; + + /* + * Soft Reset: Below performs module soft reset which clears + * all FSM logic, flushes flow control of FIFO and resets the + * state register. It also brings module back to disabled + * state (without flushing the data in the pipe). + */ + regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1); + + err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET, + val, !val, 10, 10000); + if (err < 0) { + dev_err(dev, "SW reset failed, err = %d\n", err); + return err; + } + + /* Set RX CIF */ + err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL); + if (err) { + dev_err(dev, "Can't set MVC RX CIF: %d\n", err); + return err; + } + + /* Set TX CIF */ + err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL); + if (err) { + dev_err(dev, "Can't set MVC TX CIF: %d\n", err); + return err; + } + + tegra210_mvc_write_ram(mvc->regmap); + + /* Program poly_n1, poly_n2, duration */ + regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1); + regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2); + regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration); + + /* Program duration_inv */ + regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, + gain_params.duration_inv); + + return 0; +} + +static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = { + .hw_params = tegra210_mvc_hw_params, +}; + +static const char * const tegra210_mvc_curve_type_text[] = { + "Poly", + "Linear", +}; + +static const struct soc_enum tegra210_mvc_curve_type_ctrl = + SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text); + +#define TEGRA210_MVC_VOL_CTRL(chan) \ + SOC_SINGLE_EXT("Channel" #chan " Volume", \ + TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \ + (chan - 1)), \ + 0, 16000, 0, tegra210_mvc_get_vol, \ + tegra210_mvc_put_vol) + +static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = { + /* Per channel volume control */ + TEGRA210_MVC_VOL_CTRL(1), + TEGRA210_MVC_VOL_CTRL(2), + TEGRA210_MVC_VOL_CTRL(3), + TEGRA210_MVC_VOL_CTRL(4), + TEGRA210_MVC_VOL_CTRL(5), + TEGRA210_MVC_VOL_CTRL(6), + TEGRA210_MVC_VOL_CTRL(7), + TEGRA210_MVC_VOL_CTRL(8), + + /* Per channel mute */ + SOC_SINGLE_EXT("Per Chan Mute Mask", + TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0, + tegra210_mvc_get_mute, tegra210_mvc_put_mute), + + SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl, + tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), +}; + +static struct snd_soc_dai_driver tegra210_mvc_dais[] = { + /* Input */ + { + .name = "MVC-RX-CIF", + .playback = { + .stream_name = "RX-CIF-Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "RX-CIF-Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, + + /* Output */ + { + .name = "MVC-TX-CIF", + .playback = { + .stream_name = "TX-CIF-Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "TX-CIF-Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra210_mvc_dai_ops, + } +}; + +static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE, + TEGRA210_MVC_EN_SHIFT, 0), +}; + +#define MVC_ROUTES(sname) \ + { "RX XBAR-" sname, NULL, "XBAR-TX" }, \ + { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \ + { "RX", NULL, "RX-CIF-" sname }, \ + { "TX-CIF-" sname, NULL, "TX" }, \ + { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \ + { "XBAR-RX", NULL, "TX XBAR-" sname } + +static const struct snd_soc_dapm_route tegra210_mvc_routes[] = { + { "TX", NULL, "RX" }, + MVC_ROUTES("Playback"), + MVC_ROUTES("Capture"), +}; + +static const struct snd_soc_component_driver tegra210_mvc_cmpnt = { + .dapm_widgets = tegra210_mvc_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_mvc_widgets), + .dapm_routes = tegra210_mvc_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_mvc_routes), + .controls = tegra210_mvc_vol_ctrl, + .num_controls = ARRAY_SIZE(tegra210_mvc_vol_ctrl), +}; + +static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE: + return true; + default: + return false; + }; +} + +static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL: + case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL: + case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG: + case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_MVC_RX_STATUS: + case TEGRA210_MVC_RX_INT_STATUS: + case TEGRA210_MVC_RX_INT_SET: + + case TEGRA210_MVC_TX_STATUS: + case TEGRA210_MVC_TX_INT_STATUS: + case TEGRA210_MVC_TX_INT_SET: + + case TEGRA210_MVC_SOFT_RESET: + case TEGRA210_MVC_STATUS: + case TEGRA210_MVC_INT_STATUS: + case TEGRA210_MVC_SWITCH: + case TEGRA210_MVC_CFG_RAM_CTRL: + case TEGRA210_MVC_CFG_RAM_DATA: + case TEGRA210_MVC_PEAK_VALUE: + case TEGRA210_MVC_CTRL: + return true; + default: + return false; + } +} + +static const struct regmap_config tegra210_mvc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_MVC_CONFIG_ERR_TYPE, + .writeable_reg = tegra210_mvc_wr_reg, + .readable_reg = tegra210_mvc_rd_reg, + .volatile_reg = tegra210_mvc_volatile_reg, + .reg_defaults = tegra210_mvc_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id tegra210_mvc_of_match[] = { + { .compatible = "nvidia,tegra210-mvc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match); + +static int tegra210_mvc_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_mvc *mvc; + void __iomem *regs; + int err; + + mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL); + if (!mvc) + return -ENOMEM; + + dev_set_drvdata(dev, mvc); + + mvc->curve_type = CURVE_LINEAR; + mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + mvc->regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_mvc_regmap_config); + if (IS_ERR(mvc->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(mvc->regmap); + } + + regcache_cache_only(mvc->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt, + tegra210_mvc_dais, + ARRAY_SIZE(tegra210_mvc_dais)); + if (err) { + dev_err(dev, "can't register MVC component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + tegra210_mvc_reset_vol_settings(mvc, &pdev->dev); + + return 0; +} + +static int tegra210_mvc_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_mvc_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend, + tegra210_mvc_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra210_mvc_driver = { + .driver = { + .name = "tegra210-mvc", + .of_match_table = tegra210_mvc_of_match, + .pm = &tegra210_mvc_pm_ops, + }, + .probe = tegra210_mvc_platform_probe, + .remove = tegra210_mvc_platform_remove, +}; +module_platform_driver(tegra210_mvc_driver) + +MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); +MODULE_DESCRIPTION("Tegra210 MVC ASoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h new file mode 100644 index 000000000000..def29c4c7257 --- /dev/null +++ b/sound/soc/tegra/tegra210_mvc.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_mvc.h - Definitions for Tegra210 MVC driver + * + * Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_MVC_H__ +#define __TEGRA210_MVC_H__ + +/* + * MVC_RX registers are with respect to XBAR. + * The data comes from XBAR to MVC. + */ +#define TEGRA210_MVC_RX_STATUS 0x0c +#define TEGRA210_MVC_RX_INT_STATUS 0x10 +#define TEGRA210_MVC_RX_INT_MASK 0x14 +#define TEGRA210_MVC_RX_INT_SET 0x18 +#define TEGRA210_MVC_RX_INT_CLEAR 0x1c +#define TEGRA210_MVC_RX_CIF_CTRL 0x20 + +/* + * MVC_TX registers are with respect to XBAR. + * The data goes out of MVC. + */ +#define TEGRA210_MVC_TX_STATUS 0x4c +#define TEGRA210_MVC_TX_INT_STATUS 0x50 +#define TEGRA210_MVC_TX_INT_MASK 0x54 +#define TEGRA210_MVC_TX_INT_SET 0x58 +#define TEGRA210_MVC_TX_INT_CLEAR 0x5c +#define TEGRA210_MVC_TX_CIF_CTRL 0x60 + +/* Register offsets from TEGRA210_MVC*_BASE */ +#define TEGRA210_MVC_ENABLE 0x80 +#define TEGRA210_MVC_SOFT_RESET 0x84 +#define TEGRA210_MVC_CG 0x88 +#define TEGRA210_MVC_STATUS 0x90 +#define TEGRA210_MVC_INT_STATUS 0x94 +#define TEGRA210_MVC_CTRL 0xa8 +#define TEGRA210_MVC_SWITCH 0xac +#define TEGRA210_MVC_INIT_VOL 0xb0 +#define TEGRA210_MVC_TARGET_VOL 0xd0 +#define TEGRA210_MVC_DURATION 0xf0 +#define TEGRA210_MVC_DURATION_INV 0xf4 +#define TEGRA210_MVC_POLY_N1 0xf8 +#define TEGRA210_MVC_POLY_N2 0xfc +#define TEGRA210_MVC_PEAK_CTRL 0x100 +#define TEGRA210_MVC_CFG_RAM_CTRL 0x104 +#define TEGRA210_MVC_CFG_RAM_DATA 0x108 +#define TEGRA210_MVC_PEAK_VALUE 0x10c +#define TEGRA210_MVC_CONFIG_ERR_TYPE 0x12c + +/* Fields in TEGRA210_MVC_ENABLE */ +#define TEGRA210_MVC_EN_SHIFT 0 +#define TEGRA210_MVC_EN (1 << TEGRA210_MVC_EN_SHIFT) + +#define TEGRA210_MVC_MUTE_SHIFT 8 +#define TEGRA210_MUTE_MASK_EN 0xff +#define TEGRA210_MVC_MUTE_MASK (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT) +#define TEGRA210_MVC_MUTE_EN (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT) + +#define TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT 30 +#define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT) +#define TEGRA210_MVC_PER_CHAN_CTRL_EN (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT) + +#define TEGRA210_MVC_CURVE_TYPE_SHIFT 1 +#define TEGRA210_MVC_CURVE_TYPE_MASK (1 << TEGRA210_MVC_CURVE_TYPE_SHIFT) + +#define TEGRA210_MVC_VOLUME_SWITCH_SHIFT 2 +#define TEGRA210_MVC_VOLUME_SWITCH_MASK (1 << TEGRA210_MVC_VOLUME_SWITCH_SHIFT) +#define TEGRA210_MVC_VOLUME_SWITCH_TRIGGER (1 << TEGRA210_MVC_VOLUME_SWITCH_SHIFT) +#define TEGRA210_MVC_CTRL_DEFAULT 0x40000003 + +#define TEGRA210_MVC_INIT_VOL_DEFAULT_POLY 0x01000000 +#define TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR 0x00000000 + +/* Fields in TEGRA210_MVC ram ctrl */ +#define TEGRA210_MVC_CFG_RAM_CTRL_RW_SHIFT 14 +#define TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_MVC_CFG_RAM_CTRL_RW_SHIFT) + +#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13 +#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT) + +#define TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12 +#define TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT) + +#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT 0 +#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_MASK (0x1ff << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT) + +#define REG_SIZE 4 +#define TEGRA210_MVC_MAX_CHAN_COUNT 8 +#define TEGRA210_MVC_REG_OFFSET(reg, i) (reg + (REG_SIZE * i)) + +#define NUM_GAIN_POLY_COEFFS 9 + +enum { + CURVE_POLY, + CURVE_LINEAR, +}; + +struct tegra210_mvc_gain_params { + int poly_coeff[NUM_GAIN_POLY_COEFFS]; + int poly_n1; + int poly_n2; + int duration; + int duration_inv; +}; + +struct tegra210_mvc { + int volume[TEGRA210_MVC_MAX_CHAN_COUNT]; + unsigned int curve_type; + unsigned int ctrl_value; + struct regmap *regmap; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c new file mode 100644 index 000000000000..dc477ee1b82c --- /dev/null +++ b/sound/soc/tegra/tegra210_sfc.c @@ -0,0 +1,3549 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_sfc.c - Tegra210 SFC driver +// +// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra210_sfc.h" +#include "tegra_cif.h" + +#define UNSUPP_CONV ((void *)(-EOPNOTSUPP)) +#define BYPASS_CONV NULL + +static const struct reg_default tegra210_sfc_reg_defaults[] = { + { TEGRA210_SFC_RX_INT_MASK, 0x00000001}, + { TEGRA210_SFC_RX_CIF_CTRL, 0x00007700}, + { TEGRA210_SFC_TX_INT_MASK, 0x00000001}, + { TEGRA210_SFC_TX_CIF_CTRL, 0x00007700}, + { TEGRA210_SFC_CG, 0x1}, + { TEGRA210_SFC_CFG_RAM_CTRL, 0x00004000}, +}; + +static const int tegra210_sfc_rates[TEGRA210_SFC_NUM_RATES] = { + 8000, + 11025, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 88200, + 96000, + 176400, + 192000, +}; + +/* coeff RAM tables required for SFC */ +static u32 coef_8to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0018a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003,//output gain + 0x00235204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0000015f,//input gain + 0x00a7909c, 0xff241c71, 0x005f5e00, + 0xffca77f4, 0xff20dd50, 0x006855eb, + 0xff86c552, 0xff18137a, 0x00773648, + 0x00000001//output gain +}; + +static u32 coef_8to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002//output gain +}; + +static u32 coef_8to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0018a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003,//output gain + 0x00230204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000001//output gain +}; + +static u32 coef_8to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x0000a105,//header + 0x000005e1,//input gain + 0x00dca92f, 0xff45647a, 0x0046b59c, + 0x00429d1e, 0xff4fec62, 0x00516d30, + 0xffdea779, 0xff5e08ba, 0x0060185e, + 0xffafbab2, 0xff698d5a, 0x006ce3ae, + 0xff9a82d2, 0xff704674, 0x007633c5, + 0xff923433, 0xff721128, 0x007cff42, + 0x00000003//output gain +}; + +static u32 coef_8to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00006102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002//output gain +}; + +static u32 coef_8to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x0156105,//interpolation + IIR filter + 0x0000d649,//input gain + 0x00e87afb, 0xff5f69d0, 0x003df3cf, + 0x007ce488, 0xff99a5c8, 0x0056a6a0, + 0x00344928, 0xffcba3e5, 0x006be470, + 0x00137aa7, 0xffe60276, 0x00773410, + 0x0005fa2a, 0xfff1ac11, 0x007c795b, + 0x00012d36, 0xfff5eca2, 0x007f10ef, + 0x00000002,//ouptut gain + 0x0021a102,//interpolation + IIR filter + 0x00000e00,//input gain + 0x00e2e000, 0xff6e1a00, 0x002aaa00, + 0x00610a00, 0xff5dda00, 0x003ccc00, + 0x00163a00, 0xff3c0400, 0x00633200, + 0x00000003,//Output gain + 0x00000204,//Farrow filter + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_8to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00156105,//interpolation + IIR Filter + 0x0000d649,//input gain + 0x00e87afb, 0xff5f69d0, 0x003df3cf, + 0x007ce488, 0xff99a5c8, 0x0056a6a0, + 0x00344928, 0xffcba3e5, 0x006be470, + 0x00137aa7, 0xffe60276, 0x00773410, + 0x0005fa2a, 0xfff1ac11, 0x007c795b, + 0x00012d36, 0xfff5eca2, 0x007f10ef, + 0x00000002,//ouptut gain + 0x0000a102,//interpolation + IIR filter + 0x00000e00,//input gain + 0x00e2e000, 0xff6e1a00, 0x002aaa00, + 0x00610a00, 0xff5dda00, 0x003ccc00, + 0x00163a00, 0xff3c0400, 0x00633200, + 0x00000003//output gain +}; + +static u32 coef_8to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x0024a102,//header + 0x0000007d,//input gain + 0x007d1f20, 0xff1a540e, 0x00678bf9, + 0xff916625, 0xff16b0ff, 0x006e433a, + 0xff5af660, 0xff0eb91f, 0x00797356, + 0x00000003,//output gain + 0x00000204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_8to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x0000a102,//header + 0x0000007d,//input gain + 0x007d1f20, 0xff1a540e, 0x00678bf9, + 0xff916625, 0xff16b0ff, 0x006e433a, + 0xff5af660, 0xff0eb91f, 0x00797356, + 0x00000003//output gain +}; + +static u32 coef_11to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0000015f,//input gain + 0x00a7909c, 0xff241c71, 0x005f5e00, + 0xffca77f4, 0xff20dd50, 0x006855eb, + 0xff86c552, 0xff18137a, 0x00773648, + 0x00000002,//output gain + 0x00186102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000002,//output gain + 0x00239204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_11to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00009204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_11to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002//output gain +}; + +static u32 coef_11to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_11to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00009204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_11to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00006102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002//output gain +}; + +static u32 coef_11to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_11to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00006102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002//output gain +}; + +static u32 coef_11to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00000204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_16to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_16to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000fa103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000003,//output gain + 0x001a5204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_16to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0018a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003,//output gain + 0x00235204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0000015f,//input gain + 0x00a7909c, 0xff241c71, 0x005f5e00, + 0xffca77f4, 0xff20dd50, 0x006855eb, + 0xff86c552, 0xff18137a, 0x00773648, + 0x00000001//output gain +}; + +static u32 coef_16to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x0015a105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000003,//output gain + 0x00005105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000001//output gain +}; + +static u32 coef_16to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002//output gain +}; + +static u32 coef_16to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00156105,//interpolation + IIR filter + 0x0000d649,//input gain + 0x00e87afb, 0xff5f69d0, 0x003df3cf, + 0x007ce488, 0xff99a5c8, 0x0056a6a0, + 0x00344928, 0xffcba3e5, 0x006be470, + 0x00137aa7, 0xffe60276, 0x00773410, + 0x0005fa2a, 0xfff1ac11, 0x007c795b, + 0x00012d36, 0xfff5eca2, 0x007f10ef, + 0x00000002,//output gain + 0x0021a102,//interpolation + IIR filter + 0x00000e00,//input gain + 0x00e2e000, 0xff6e1a00, 0x002aaa00, + 0x00610a00, 0xff5dda00, 0x003ccc00, + 0x00163a00, 0xff3c0400, 0x00633200, + 0x00000003,//output gain + 0x002c0204,//Farrow Filter + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005101,//IIR Filter + Decimator + 0x0000203c,//input gain + 0x00f52d35, 0xff2e2162, 0x005a21e0, + 0x00c6f0f0, 0xff2ecd69, 0x006fa78d, + 0x00000001//output gain +}; + +static u32 coef_16to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x0000a105,//interpolation + IIR Filter + 0x00000784,//input gain + 0x00cc516e, 0xff2c9639, 0x005ad5b3, + 0x0013ad0d, 0xff3d4799, 0x0063ce75, + 0xffb6f398, 0xff5138d1, 0x006e9e1f, + 0xff9186e5, 0xff5f96a4, 0x0076a86e, + 0xff82089c, 0xff676b81, 0x007b9f8a, + 0xff7c48a5, 0xff6a31e7, 0x007ebb7b, + 0x00000003//output gain +}; + +static u32 coef_16to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0018a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003,//output gain + 0x00000204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_16to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0000a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003//output gain +}; + +static u32 coef_16to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x0024a102,//header + 0x0000007d,//input gain + 0x007d1f20, 0xff1a540e, 0x00678bf9, + 0xff916625, 0xff16b0ff, 0x006e433a, + 0xff5af660, 0xff0eb91f, 0x00797356, + 0x00000003,//output gain + 0x00000204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_16to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x0000a102,//header + 0x0000007d,//input gain + 0x007d1f20, 0xff1a540e, 0x00678bf9, + 0xff916625, 0xff16b0ff, 0x006e433a, + 0xff5af660, 0xff0eb91f, 0x00797356, + 0x00000003//output gain +}; + +static u32 coef_22to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000002,//output gain + 0x00179204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_22to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_22to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0000015f,//input gain + 0x00a7909c, 0xff241c71, 0x005f5e00, + 0xffca77f4, 0xff20dd50, 0x006855eb, + 0xff86c552, 0xff18137a, 0x00773648, + 0x00000002,//output gain + 0x00186102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000002,//output gain + 0x00239204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_22to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00235204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d029,//input gain + 0x00f2a98b, 0xff92aa71, 0x001fcd16, + 0x00ae9004, 0xffb85140, 0x0041813a, + 0x007f8ed1, 0xffd585fc, 0x006a69e6, + 0x00000001//output gain +}; + +static u32 coef_22to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00009204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_22to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002//output gain +}; + +static u32 coef_22to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_22to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00006102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002//output gain +}; + +static u32 coef_22to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_22to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00006102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002//output gain +}; + +static u32 coef_22to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00000204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_24to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00009105,//header + 0x000005e1,//input gain + 0x00dca92f, 0xff45647a, 0x0046b59c, + 0x00429d1e, 0xff4fec62, 0x00516d30, + 0xffdea779, 0xff5e08ba, 0x0060185e, + 0xffafbab2, 0xff698d5a, 0x006ce3ae, + 0xff9a82d2, 0xff704674, 0x007633c5, + 0xff923433, 0xff721128, 0x007cff42, + 0x00000001//output gain +}; + +static u32 coef_24to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000f6103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002,//output gain + 0x001a5204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_24to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00156105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000002,//output gain + 0x00009105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000001//output gain +}; + +static u32 coef_24to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d029,//input gain + 0x00f2a98b, 0xff92aa71, 0x001fcd16, + 0x00ae9004, 0xffb85140, 0x0041813a, + 0x007f8ed1, 0xffd585fc, 0x006a69e6, + 0x00000002,//output gain + 0x001b6103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002,//output gain + 0x00265204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_24to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00009102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001//output gain +}; + +static u32 coef_24to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00230204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x00001685,//input gain + 0x00f53ae9, 0xff52f196, 0x003e3e08, + 0x00b9f857, 0xff5d8985, 0x0050070a, + 0x008c3e86, 0xff6053f0, 0x006d98ef, + 0x00000001//output gain +}; + +static u32 coef_24to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002//output gain +}; + +static u32 coef_24to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x002f0204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x00000138,//input gain + 0x00d5d232, 0xff2a3bf8, 0x005a785c, + 0x0034001b, 0xff283109, 0x006462a6, + 0xffe6746a, 0xff1fb09c, 0x00758a91, + 0x00000001//output gain +}; + +static u32 coef_24to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00006102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002//output gain +}; + +static u32 coef_24to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00000204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_24to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00006102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002//output gain +}; + +static u32 coef_32to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_32to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000ca102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000003,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x0000d102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001//output gain +}; + +static u32 coef_32to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_32to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000fa103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000003,//output gain + 0x001a5204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_32to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000ca102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000003,//output gain + 0x0000d102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001//output gain +}; + +static u32 coef_32to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0018a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003,//output gain + 0x00235204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0000015f,//input gain + 0x00a7909c, 0xff241c71, 0x005f5e00, + 0xffca77f4, 0xff20dd50, 0x006855eb, + 0xff86c552, 0xff18137a, 0x00773648, + 0x00000001//output gain +}; + +static u32 coef_32to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x0015a105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000003,//output gain + 0x00005105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000001//output gain +}; + +static u32 coef_32to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0018a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003,//output gain + 0x00230204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000001//output gain +}; + +static u32 coef_32to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x0000a105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000003//output gain +}; + +static u32 coef_32to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0018a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003,//output gain + 0x00000204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_32to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x0000a102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000003//output gain +}; + +static u32 coef_44to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00120104,//IIR Filter + 0x00000af2,//input gain + 0x0057eebe, 0xff1e9863, 0x00652604, + 0xff7206ea, 0xff22ad7e, 0x006d47e1, + 0xff42a4d7, 0xff26e722, 0x0075fd83, + 0xff352f66, 0xff29312b, 0x007b986b, + 0xff310a07, 0xff296f51, 0x007eca7c, + 0x00000001,//output gain + 0x001d9204,//Farrow Filter + decimation + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005105,//IIR Filter + Decimator + 0x0000d649,//input gain + 0x00e87afb, 0xff5f69d0, 0x003df3cf, + 0x007ce488, 0xff99a5c8, 0x0056a6a0, + 0x00344928, 0xffcba3e5, 0x006be470, + 0x00137aa7, 0xffe60276, 0x00773410, + 0x0005fa2a, 0xfff1ac11, 0x007c795b, + 0x00012d36, 0xfff5eca2, 0x007f10ef, + 0x00000001//output gain +}; + +static u32 coef_44to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_44to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00126104,//IIR Filter + interpolation + 0x00000af2,//input gain + 0x0057eebe, 0xff1e9863, 0x00652604, + 0xff7206ea, 0xff22ad7e, 0x006d47e1, + 0xff42a4d7, 0xff26e722, 0x0075fd83, + 0xff352f66, 0xff29312b, 0x007b986b, + 0xff310a07, 0xff296f51, 0x007eca7c, + 0x00000002,//output gain + 0x001d9204,//Farrow Filter + decimation + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005105,//IIR Filter + Decimator + 0x0000d649,//input gain + 0x00e87afb, 0xff5f69d0, 0x003df3cf, + 0x007ce488, 0xff99a5c8, 0x0056a6a0, + 0x00344928, 0xffcba3e5, 0x006be470, + 0x00137aa7, 0xffe60276, 0x00773410, + 0x0005fa2a, 0xfff1ac11, 0x007c795b, + 0x00012d36, 0xfff5eca2, 0x007f10ef, + 0x00000001//output gain +}; + +static u32 coef_44to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_44to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x00001685,//input gain + 0x00f53ae9, 0xff52f196, 0x003e3e08, + 0x00b9f857, 0xff5d8985, 0x0050070a, + 0x008c3e86, 0xff6053f0, 0x006d98ef, + 0x00000002,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_44to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0000015f,//input gain + 0x00a7909c, 0xff241c71, 0x005f5e00, + 0xffca77f4, 0xff20dd50, 0x006855eb, + 0xff86c552, 0xff18137a, 0x00773648, + 0x00000002,//output gain + 0x00186102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000002,//output gain + 0x00239204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_44to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00235204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d029,//input gain + 0x00f2a98b, 0xff92aa71, 0x001fcd16, + 0x00ae9004, 0xffb85140, 0x0041813a, + 0x007f8ed1, 0xffd585fc, 0x006a69e6, + 0x00000001//output gain +}; + +static u32 coef_44to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002//output gain +}; + +static u32 coef_44to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_44to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00006102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002//output gain +}; + +static u32 coef_44to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_48to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c9102,//IIR Filter + Decimator + 0x00000e00,//input gain + 0x00e2e000, 0xff6e1a00, 0x002aaa00, + 0x00610a00, 0xff5dda00, 0x003ccc00, + 0x00163a00, 0xff3c0400, 0x00633200, + 0x00000001,//output gain + 0x00005105,//IIR Filter + Decimator + 0x0000d649,//input gain + 0x00e87afb, 0xff5f69d0, 0x003df3cf, + 0x007ce488, 0xff99a5c8, 0x0056a6a0, + 0x00344928, 0xffcba3e5, 0x006be470, + 0x00137aa7, 0xffe60276, 0x00773410, + 0x0005fa2a, 0xfff1ac11, 0x007c795b, + 0x00012d36, 0xfff5eca2, 0x007f10ef, + 0x00000001//output gain +}; + +static u32 coef_48to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000002,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_48to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00009105,//IIR Filter + Decimator + 0x00000784,//input gain + 0x00cc516e, 0xff2c9639, 0x005ad5b3, + 0x0013ad0d, 0xff3d4799, 0x0063ce75, + 0xffb6f398, 0xff5138d1, 0x006e9e1f, + 0xff9186e5, 0xff5f96a4, 0x0076a86e, + 0xff82089c, 0xff676b81, 0x007b9f8a, + 0xff7c48a5, 0xff6a31e7, 0x007ebb7b, + 0x00000001//output gain +}; + +static u32 coef_48to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000f6103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002,//output gain + 0x001a5204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_48to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_48to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00156105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000002,//output gain + 0x00009105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000001//output gain +}; + +static u32 coef_48to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d029,//input gain + 0x00f2a98b, 0xff92aa71, 0x001fcd16, + 0x00ae9004, 0xffb85140, 0x0041813a, + 0x007f8ed1, 0xffd585fc, 0x006a69e6, + 0x00000002,//output gain + 0x001b6103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002,//output gain + 0x00265204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_48to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00230204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x00001685,//input gain + 0x00f53ae9, 0xff52f196, 0x003e3e08, + 0x00b9f857, 0xff5d8985, 0x0050070a, + 0x008c3e86, 0xff6053f0, 0x006d98ef, + 0x00000001//output gain +}; + +static u32 coef_48to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002//output gain +}; + +static u32 coef_48to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00186102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00246102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x002f0204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x00000138,//input gain + 0x00d5d232, 0xff2a3bf8, 0x005a785c, + 0x0034001b, 0xff283109, 0x006462a6, + 0xffe6746a, 0xff1fb09c, 0x00758a91, + 0x00000001//output gain +}; + +static u32 coef_48to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000002,//output gain + 0x00006102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002//output gain +}; + +static u32 coef_88to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x00000057,//input gain + 0x00a8e717, 0xff1c748d, 0x0065b976, + 0xffcbccab, 0xff190aff, 0x006cc1cf, + 0xff871ce1, 0xff10d878, 0x0078cfc5, + 0x00000001,//output gain + 0x00179204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000001,//output gain + 0x00185102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000001,//output gain + 0x00179204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x00001685,//input gain + 0x00f53ae9, 0xff52f196, 0x003e3e08, + 0x00b9f857, 0xff5d8985, 0x0050070a, + 0x008c3e86, 0xff6053f0, 0x006d98ef, + 0x00000001,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000002,//output gain + 0x00179204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x00001685,//input gain + 0x00f53ae9, 0xff52f196, 0x003e3e08, + 0x00b9f857, 0xff5d8985, 0x0050070a, + 0x008c3e86, 0xff6053f0, 0x006d98ef, + 0x00000002,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_88to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_88to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002//output gain +}; + +static u32 coef_88to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000002,//output gain + 0x00186102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_96to8[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c9102,//header + 0x0000007d,//input gain + 0x007d1f20, 0xff1a540e, 0x00678bf9, + 0xff916625, 0xff16b0ff, 0x006e433a, + 0xff5af660, 0xff0eb91f, 0x00797356, + 0x00000001,//output gain + 0x00185102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_96to11[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000001,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_96to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c9102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_96to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000002,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_96to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_96to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00009105,//header + 0x00000292,//input gain + 0x00e4320a, 0xff41d2d9, 0x004911ac, + 0x005dd9e3, 0xff4c7d80, 0x0052103e, + 0xfff8ebef, 0xff5b6fab, 0x005f0a0d, + 0xffc4b414, 0xff68582c, 0x006b38e5, + 0xffabb861, 0xff704bec, 0x0074de52, + 0xffa19f4c, 0xff729059, 0x007c7e90, + 0x00000001//output gain +}; + +static u32 coef_96to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000f6103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002,//output gain + 0x001a5204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_96to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_96to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000f6103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002,//output gain + 0x001a0204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001//output gain +}; + +static u32 coef_96to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000f6103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002,//output gain + 0x001b6102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000002,//output gain + 0x00260204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000001//output gain +}; + +static u32 coef_96to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00006103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000002//output gain +}; + +static u32 coef_176to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x00000057,//input gain + 0x00a8e717, 0xff1c748d, 0x0065b976, + 0xffcbccab, 0xff190aff, 0x006cc1cf, + 0xff871ce1, 0xff10d878, 0x0078cfc5, + 0x00000001,//output gain + 0x00179204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_176to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000001,//output gain + 0x00185102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_176to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x00000138,//input gain + 0x00d5d232, 0xff2a3bf8, 0x005a785c, + 0x0034001b, 0xff283109, 0x006462a6, + 0xffe6746a, 0xff1fb09c, 0x00758a91, + 0x00000001,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_176to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x000005f3,//input gain + 0x00d816d6, 0xff385383, 0x004fe566, + 0x003c548d, 0xff38c23d, 0x005d0b1c, + 0xfff02f7d, 0xff31e983, 0x0072d65d, + 0x00000001,//output gain + 0x00179204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_176to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_176to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x00001685,//input gain + 0x00f53ae9, 0xff52f196, 0x003e3e08, + 0x00b9f857, 0xff5d8985, 0x0050070a, + 0x008c3e86, 0xff6053f0, 0x006d98ef, + 0x00000001,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_176to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001//output gain +}; + +static u32 coef_176to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000001//output gain +}; + +static u32 coef_176to192[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000002,//output gain + 0x00005204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000 +}; + +static u32 coef_192to16[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c9102,//header + 0x0000007d,//input gain + 0x007d1f20, 0xff1a540e, 0x00678bf9, + 0xff916625, 0xff16b0ff, 0x006e433a, + 0xff5af660, 0xff0eb91f, 0x00797356, + 0x00000001,//output gain + 0x00185102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_192to22[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c0102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000001,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_192to24[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000001,//output gain + 0x00185102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_192to32[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c9102,//header + 0x000005d6,//input gain + 0x00c6543e, 0xff342935, 0x0052f116, + 0x000a1d78, 0xff3330c0, 0x005f88a3, + 0xffbee7c0, 0xff2b5ba5, 0x0073eb26, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_192to44[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000002,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00235102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_192to48[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c5102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001,//output gain + 0x00005102,//header + 0x0001d727,//input gain + 0x00fc2fc7, 0xff9bb27b, 0x001c564c, + 0x00e55557, 0xffcadd5b, 0x003d80ba, + 0x00d13397, 0xfff232f8, 0x00683337, + 0x00000001//output gain +}; + +static u32 coef_192to88[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000002,//output gain + 0x00175204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x000013d9,//input gain + 0x00ebd477, 0xff4ce383, 0x0042049d, + 0x0089c278, 0xff54414d, 0x00531ded, + 0x004a5e07, 0xff53cf41, 0x006efbdc, + 0x00000001//output gain +}; + +static u32 coef_192to96[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x00005103,//header + 0x000001e0,//input gain + 0x00de44c0, 0xff380b7f, 0x004ffc73, + 0x00494b44, 0xff3d493a, 0x005908bf, + 0xffe9a3c8, 0xff425647, 0x006745f7, + 0xffc42d61, 0xff40a6c7, 0x00776709, + 0x00000001//output gain +}; + +static u32 coef_192to176[TEGRA210_SFC_COEF_RAM_DEPTH] = { + 0x000c6102,//header + 0x000000af,//input gain + 0x00c65663, 0xff23d2ce, 0x005f97d6, + 0x00086ad6, 0xff20ec4f, 0x00683201, + 0xffbbbef6, 0xff184447, 0x00770963, + 0x00000002,//output gain + 0x00170204,//farrow + 0x000aaaab, + 0xffaaaaab, + 0xfffaaaab, + 0x00555555, + 0xff600000, + 0xfff55555, + 0x00155555, + 0x00055555, + 0xffeaaaab, + 0x00200000, + 0x00005102,//header + 0x0000010a,//input gain + 0x00c93dc4, 0xff26f5f6, 0x005d1041, + 0x001002c4, 0xff245b76, 0x00666002, + 0xffc30a45, 0xff1baecd, 0x00765921, + 0x00000001//output gain +}; + +/* + * Coefficient table for various sample rate conversions. The sample + * rates available are as per tegra210_sfc_rates[]. + */ +static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { + /* Convertions from 8 kHz */ + { + BYPASS_CONV, + coef_8to11, + coef_8to16, + coef_8to22, + coef_8to24, + coef_8to32, + coef_8to44, + coef_8to48, + coef_8to88, + coef_8to96, + UNSUPP_CONV, + UNSUPP_CONV, + }, + /* Convertions from 11.025 kHz */ + { + coef_11to8, + BYPASS_CONV, + coef_11to16, + coef_11to22, + coef_11to24, + coef_11to32, + coef_11to44, + coef_11to48, + coef_11to88, + coef_11to96, + UNSUPP_CONV, + UNSUPP_CONV, + }, + /* Convertions from 16 kHz */ + { + coef_16to8, + coef_16to11, + BYPASS_CONV, + coef_16to22, + coef_16to24, + coef_16to32, + coef_16to44, + coef_16to48, + coef_16to88, + coef_16to96, + coef_16to176, + coef_16to192, + }, + /* Convertions from 22.05 kHz */ + { + coef_22to8, + coef_22to11, + coef_22to16, + BYPASS_CONV, + coef_22to24, + coef_22to32, + coef_22to44, + coef_22to48, + coef_22to88, + coef_22to96, + coef_22to176, + coef_22to192, + }, + /* Convertions from 24 kHz */ + { + coef_24to8, + coef_24to11, + coef_24to16, + coef_24to22, + BYPASS_CONV, + coef_24to32, + coef_24to44, + coef_24to48, + coef_24to88, + coef_24to96, + coef_24to176, + coef_24to192, + }, + /* Convertions from 32 kHz */ + { + coef_32to8, + coef_32to11, + coef_32to16, + coef_32to22, + coef_32to24, + BYPASS_CONV, + coef_32to44, + coef_32to48, + coef_32to88, + coef_32to96, + coef_32to176, + coef_32to192, + }, + /* Convertions from 44.1 kHz */ + { + coef_44to8, + coef_44to11, + coef_44to16, + coef_44to22, + coef_44to24, + coef_44to32, + BYPASS_CONV, + coef_44to48, + coef_44to88, + coef_44to96, + coef_44to176, + coef_44to192, + }, + /* Convertions from 48 kHz */ + { + coef_48to8, + coef_48to11, + coef_48to16, + coef_48to22, + coef_48to24, + coef_48to32, + coef_48to44, + BYPASS_CONV, + coef_48to88, + coef_48to96, + coef_48to176, + coef_48to192, + }, + /* Convertions from 88.2 kHz */ + { + coef_88to8, + coef_88to11, + coef_88to16, + coef_88to22, + coef_88to24, + coef_88to32, + coef_88to44, + coef_88to48, + BYPASS_CONV, + coef_88to96, + coef_88to176, + coef_88to192, + }, + /* Convertions from 96 kHz */ + { coef_96to8, + coef_96to11, + coef_96to16, + coef_96to22, + coef_96to24, + coef_96to32, + coef_96to44, + coef_96to48, + coef_96to88, + BYPASS_CONV, + coef_96to176, + coef_96to192, + }, + /* Convertions from 176.4 kHz */ + { + UNSUPP_CONV, + UNSUPP_CONV, + coef_176to16, + coef_176to22, + coef_176to24, + coef_176to32, + coef_176to44, + coef_176to48, + coef_176to88, + coef_176to96, + BYPASS_CONV, + coef_176to192, + }, + /* Convertions from 192 kHz */ + { + UNSUPP_CONV, + UNSUPP_CONV, + coef_192to16, + coef_192to22, + coef_192to24, + coef_192to32, + coef_192to44, + coef_192to48, + coef_192to88, + coef_192to96, + coef_192to176, + BYPASS_CONV, + }, +}; + +static int __maybe_unused tegra210_sfc_runtime_suspend(struct device *dev) +{ + struct tegra210_sfc *sfc = dev_get_drvdata(dev); + + regcache_cache_only(sfc->regmap, true); + regcache_mark_dirty(sfc->regmap); + + return 0; +} + +static int __maybe_unused tegra210_sfc_runtime_resume(struct device *dev) +{ + struct tegra210_sfc *sfc = dev_get_drvdata(dev); + + regcache_cache_only(sfc->regmap, false); + regcache_sync(sfc->regmap); + + return 0; +} + +static inline void tegra210_sfc_write_ram(struct regmap *regmap, + s32 *data) +{ + int i; + + regmap_write(regmap, TEGRA210_SFC_CFG_RAM_CTRL, + TEGRA210_SFC_RAM_CTRL_SEQ_ACCESS_EN | + TEGRA210_SFC_RAM_CTRL_ADDR_INIT_EN | + TEGRA210_SFC_RAM_CTRL_RW_WRITE); + + for (i = 0; i < TEGRA210_SFC_COEF_RAM_DEPTH; i++) + regmap_write(regmap, TEGRA210_SFC_CFG_RAM_DATA, data[i]); +} + +static int tegra210_sfc_write_coeff_ram(struct snd_soc_component *cmpnt) +{ + struct tegra210_sfc *sfc = dev_get_drvdata(cmpnt->dev); + s32 *coeff_ram; + + /* Bypass */ + if (sfc->srate_in == sfc->srate_out) + return 0; + + coeff_ram = coef_addr_table[sfc->srate_in][sfc->srate_out]; + if (IS_ERR_OR_NULL(coeff_ram)) { + dev_err(cmpnt->dev, + "Conversion from %d to %d Hz is not supported\n", + sfc->srate_in, sfc->srate_out); + + return PTR_ERR_OR_ZERO(coeff_ram); + } + + tegra210_sfc_write_ram(sfc->regmap, coeff_ram); + + regmap_update_bits(sfc->regmap, + TEGRA210_SFC_COEF_RAM, + TEGRA210_SFC_COEF_RAM_EN, + TEGRA210_SFC_COEF_RAM_EN); + + return 0; +} + +static int tegra210_sfc_set_audio_cif(struct tegra210_sfc *sfc, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + unsigned int channels, audio_bits, path; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EOPNOTSUPP; + } + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = TEGRA_ACIF_BITS_32; + + if (reg == TEGRA210_SFC_RX_CIF_CTRL) + path = SFC_RX_PATH; + else + path = SFC_TX_PATH; + + cif_conf.stereo_conv = sfc->stereo_to_mono[path]; + cif_conf.mono_conv = sfc->mono_to_stereo[path]; + + tegra_set_cif(sfc->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra210_sfc_soft_reset(struct tegra210_sfc *sfc) +{ + u32 val; + + /* + * Soft Reset: Below performs module soft reset which clears + * all FSM logic, flushes flow control of FIFO and resets the + * state register. It also brings module back to disabled + * state (without flushing the data in the pipe). + */ + regmap_update_bits(sfc->regmap, TEGRA210_SFC_SOFT_RESET, + TEGRA210_SFC_SOFT_RESET_EN, 1); + + return regmap_read_poll_timeout(sfc->regmap, + TEGRA210_SFC_SOFT_RESET, + val, + !(val & TEGRA210_SFC_SOFT_RESET_EN), + 10, 10000); +} + +static int tegra210_sfc_rate_to_idx(struct device *dev, int rate, + int *rate_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tegra210_sfc_rates); i++) { + if (rate == tegra210_sfc_rates[i]) { + *rate_idx = i; + + return 0; + } + } + + dev_err(dev, "Sample rate %d Hz is not supported\n", rate); + + return -EOPNOTSUPP; +} + +static int tegra210_sfc_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai); + int err; + + regmap_update_bits(sfc->regmap, TEGRA210_SFC_COEF_RAM, + TEGRA210_SFC_COEF_RAM_EN, 0); + + err = tegra210_sfc_soft_reset(sfc); + if (err < 0) { + dev_err(dai->dev, "Failed to reset SFC in %s, err = %d\n", + __func__, err); + + return err; + } + + return 0; +} + +static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai); + struct device *dev = dai->dev; + int err; + + err = tegra210_sfc_rate_to_idx(dev, params_rate(params), + &sfc->srate_in); + if (err < 0) + return err; + + err = tegra210_sfc_set_audio_cif(sfc, params, TEGRA210_SFC_RX_CIF_CTRL); + if (err < 0) { + dev_err(dev, "Can't set SFC RX CIF: %d\n", err); + return err; + } + + regmap_write(sfc->regmap, TEGRA210_SFC_RX_FREQ, sfc->srate_in); + + return err; +} + +static int tegra210_sfc_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai); + struct device *dev = dai->dev; + int err; + + err = tegra210_sfc_rate_to_idx(dev, params_rate(params), + &sfc->srate_out); + if (err < 0) + return err; + + err = tegra210_sfc_set_audio_cif(sfc, params, TEGRA210_SFC_TX_CIF_CTRL); + if (err < 0) { + dev_err(dev, "Can't set SFC TX CIF: %d\n", err); + return err; + } + + regmap_write(sfc->regmap, TEGRA210_SFC_TX_FREQ, sfc->srate_out); + + return 0; +} + +static int tegra210_sfc_init(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + return tegra210_sfc_write_coeff_ram(cmpnt); +} + +static int tegra210_sfc_get_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_sfc *sfc = snd_soc_component_get_drvdata(cmpnt); + + if (strstr(kcontrol->id.name, "Input Stereo To Mono")) + ucontrol->value.integer.value[0] = + sfc->stereo_to_mono[SFC_RX_PATH]; + else if (strstr(kcontrol->id.name, "Input Mono To Stereo")) + ucontrol->value.integer.value[0] = + sfc->mono_to_stereo[SFC_RX_PATH]; + else if (strstr(kcontrol->id.name, "Output Stereo To Mono")) + ucontrol->value.integer.value[0] = + sfc->stereo_to_mono[SFC_TX_PATH]; + else if (strstr(kcontrol->id.name, "Output Mono To Stereo")) + ucontrol->value.integer.value[0] = + sfc->mono_to_stereo[SFC_TX_PATH]; + + return 0; +} + +static int tegra210_sfc_put_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_sfc *sfc = snd_soc_component_get_drvdata(cmpnt); + int value = ucontrol->value.integer.value[0]; + + if (strstr(kcontrol->id.name, "Input Stereo To Mono")) + sfc->stereo_to_mono[SFC_RX_PATH] = value; + else if (strstr(kcontrol->id.name, "Input Mono To Stereo")) + sfc->mono_to_stereo[SFC_RX_PATH] = value; + else if (strstr(kcontrol->id.name, "Output Stereo To Mono")) + sfc->stereo_to_mono[SFC_TX_PATH] = value; + else if (strstr(kcontrol->id.name, "Output Mono To Stereo")) + sfc->mono_to_stereo[SFC_TX_PATH] = value; + else + return 0; + + return 1; +} + +static const struct snd_soc_dai_ops tegra210_sfc_in_dai_ops = { + .hw_params = tegra210_sfc_in_hw_params, + .startup = tegra210_sfc_startup, +}; + +static const struct snd_soc_dai_ops tegra210_sfc_out_dai_ops = { + .hw_params = tegra210_sfc_out_hw_params, +}; + +static struct snd_soc_dai_driver tegra210_sfc_dais[] = { + { + .name = "SFC-RX-CIF", + .playback = { + .stream_name = "RX-CIF-Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "RX-CIF-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra210_sfc_in_dai_ops, + }, + { + .name = "SFC-TX-CIF", + .playback = { + .stream_name = "TX-CIF-Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "TX-CIF-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra210_sfc_out_dai_ops, + }, +}; + +static const struct snd_soc_dapm_widget tegra210_sfc_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("TX", NULL, 0, TEGRA210_SFC_ENABLE, + TEGRA210_SFC_EN_SHIFT, 0, + tegra210_sfc_init, SND_SOC_DAPM_PRE_PMU), +}; + +#define RESAMPLE_ROUTE(sname) \ + { "RX XBAR-" sname, NULL, "XBAR-TX" }, \ + { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \ + { "RX", NULL, "RX-CIF-" sname }, \ + { "TX-CIF-" sname, NULL, "TX" }, \ + { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \ + { "XBAR-RX", NULL, "TX XBAR-" sname } + +static const struct snd_soc_dapm_route tegra210_sfc_routes[] = { + { "TX", NULL, "RX" }, + RESAMPLE_ROUTE("Playback"), + RESAMPLE_ROUTE("Capture"), +}; + +static const char * const tegra210_sfc_stereo_conv_text[] = { + "CH0", "CH1", "AVG", +}; + +static const char * const tegra210_sfc_mono_conv_text[] = { + "Zero", "Copy", +}; + +static const struct soc_enum tegra210_sfc_stereo_conv_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(tegra210_sfc_stereo_conv_text), + tegra210_sfc_stereo_conv_text); + +static const struct soc_enum tegra210_sfc_mono_conv_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(tegra210_sfc_mono_conv_text), + tegra210_sfc_mono_conv_text); + +static const struct snd_kcontrol_new tegra210_sfc_controls[] = { + SOC_ENUM_EXT("Input Stereo To Mono", tegra210_sfc_stereo_conv_enum, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_ENUM_EXT("Input Mono To Stereo", tegra210_sfc_mono_conv_enum, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_ENUM_EXT("Output Stereo To Mono", tegra210_sfc_stereo_conv_enum, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_ENUM_EXT("Output Mono To Stereo", tegra210_sfc_mono_conv_enum, + tegra210_sfc_get_control, tegra210_sfc_put_control), +}; + +static const struct snd_soc_component_driver tegra210_sfc_cmpnt = { + .dapm_widgets = tegra210_sfc_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_sfc_widgets), + .dapm_routes = tegra210_sfc_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_sfc_routes), + .controls = tegra210_sfc_controls, + .num_controls = ARRAY_SIZE(tegra210_sfc_controls), +}; + +static bool tegra210_sfc_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_SFC_RX_INT_MASK ... TEGRA210_SFC_RX_FREQ: + case TEGRA210_SFC_TX_INT_MASK ... TEGRA210_SFC_TX_FREQ: + case TEGRA210_SFC_ENABLE ... TEGRA210_SFC_CG: + case TEGRA210_SFC_COEF_RAM ... TEGRA210_SFC_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_sfc_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_SFC_RX_STATUS ... TEGRA210_SFC_RX_FREQ: + case TEGRA210_SFC_TX_STATUS ... TEGRA210_SFC_TX_FREQ: + case TEGRA210_SFC_ENABLE ... TEGRA210_SFC_INT_STATUS: + case TEGRA210_SFC_COEF_RAM ... TEGRA210_SFC_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_sfc_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_SFC_RX_STATUS: + case TEGRA210_SFC_RX_INT_STATUS: + case TEGRA210_SFC_RX_INT_SET: + + case TEGRA210_SFC_TX_STATUS: + case TEGRA210_SFC_TX_INT_STATUS: + case TEGRA210_SFC_TX_INT_SET: + + case TEGRA210_SFC_SOFT_RESET: + case TEGRA210_SFC_STATUS: + case TEGRA210_SFC_INT_STATUS: + case TEGRA210_SFC_CFG_RAM_CTRL: + case TEGRA210_SFC_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_sfc_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_SFC_CFG_RAM_DATA: + return true; + default: + return false; + } +} + +static const struct regmap_config tegra210_sfc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_SFC_CFG_RAM_DATA, + .writeable_reg = tegra210_sfc_wr_reg, + .readable_reg = tegra210_sfc_rd_reg, + .volatile_reg = tegra210_sfc_volatile_reg, + .precious_reg = tegra210_sfc_precious_reg, + .reg_defaults = tegra210_sfc_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_sfc_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id tegra210_sfc_of_match[] = { + { .compatible = "nvidia,tegra210-sfc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_sfc_of_match); + +static int tegra210_sfc_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_sfc *sfc; + void __iomem *regs; + int err; + + sfc = devm_kzalloc(dev, sizeof(*sfc), GFP_KERNEL); + if (!sfc) + return -ENOMEM; + + dev_set_drvdata(dev, sfc); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + sfc->regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_sfc_regmap_config); + if (IS_ERR(sfc->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(sfc->regmap); + } + + regcache_cache_only(sfc->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra210_sfc_cmpnt, + tegra210_sfc_dais, + ARRAY_SIZE(tegra210_sfc_dais)); + if (err) { + dev_err(dev, "can't register SFC component, err: %d\n", err); + return err; + } + + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static int tegra210_sfc_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_sfc_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_sfc_runtime_suspend, + tegra210_sfc_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra210_sfc_driver = { + .driver = { + .name = "tegra210-sfc", + .of_match_table = tegra210_sfc_of_match, + .pm = &tegra210_sfc_pm_ops, + }, + .probe = tegra210_sfc_platform_probe, + .remove = tegra210_sfc_platform_remove, +}; +module_platform_driver(tegra210_sfc_driver) + +MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); +MODULE_DESCRIPTION("Tegra210 SFC ASoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_sfc.h b/sound/soc/tegra/tegra210_sfc.h new file mode 100644 index 000000000000..5a6b66e297d8 --- /dev/null +++ b/sound/soc/tegra/tegra210_sfc.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_sfc.h - Definitions for Tegra210 SFC driver + * + * Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_SFC_H__ +#define __TEGRA210_SFC_H__ + +/* + * SFC_RX registers are with respect to XBAR. + * The data comes from XBAR to SFC. + */ +#define TEGRA210_SFC_RX_STATUS 0x0c +#define TEGRA210_SFC_RX_INT_STATUS 0x10 +#define TEGRA210_SFC_RX_INT_MASK 0x14 +#define TEGRA210_SFC_RX_INT_SET 0x18 +#define TEGRA210_SFC_RX_INT_CLEAR 0x1c +#define TEGRA210_SFC_RX_CIF_CTRL 0x20 +#define TEGRA210_SFC_RX_FREQ 0x24 + +/* + * SFC_TX registers are with respect to XBAR. + * The data goes out of SFC. + */ +#define TEGRA210_SFC_TX_STATUS 0x4c +#define TEGRA210_SFC_TX_INT_STATUS 0x50 +#define TEGRA210_SFC_TX_INT_MASK 0x54 +#define TEGRA210_SFC_TX_INT_SET 0x58 +#define TEGRA210_SFC_TX_INT_CLEAR 0x5c +#define TEGRA210_SFC_TX_CIF_CTRL 0x60 +#define TEGRA210_SFC_TX_FREQ 0x64 + +/* Register offsets from TEGRA210_SFC*_BASE */ +#define TEGRA210_SFC_ENABLE 0x80 +#define TEGRA210_SFC_SOFT_RESET 0x84 +#define TEGRA210_SFC_CG 0x88 +#define TEGRA210_SFC_STATUS 0x8c +#define TEGRA210_SFC_INT_STATUS 0x90 +#define TEGRA210_SFC_COEF_RAM 0xbc +#define TEGRA210_SFC_CFG_RAM_CTRL 0xc0 +#define TEGRA210_SFC_CFG_RAM_DATA 0xc4 + +/* Fields in TEGRA210_SFC_ENABLE */ +#define TEGRA210_SFC_EN_SHIFT 0 +#define TEGRA210_SFC_EN (1 << TEGRA210_SFC_EN_SHIFT) + +#define TEGRA210_SFC_NUM_RATES 12 + +/* Fields in TEGRA210_SFC_COEF_RAM */ +#define TEGRA210_SFC_COEF_RAM_EN BIT(0) + +#define TEGRA210_SFC_SOFT_RESET_EN BIT(0) + +/* Coefficients */ +#define TEGRA210_SFC_COEF_RAM_DEPTH 64 +#define TEGRA210_SFC_RAM_CTRL_RW_WRITE (1 << 14) +#define TEGRA210_SFC_RAM_CTRL_ADDR_INIT_EN (1 << 13) +#define TEGRA210_SFC_RAM_CTRL_SEQ_ACCESS_EN (1 << 12) + + +enum tegra210_sfc_path { + SFC_RX_PATH, + SFC_TX_PATH, + SFC_PATHS, +}; + +struct tegra210_sfc { + unsigned int mono_to_stereo[SFC_PATHS]; + unsigned int stereo_to_mono[SFC_PATHS]; + unsigned int srate_out; + unsigned int srate_in; + struct regmap *regmap; +}; + +#endif diff --git a/sound/soc/tegra/tegra_asoc_machine.c b/sound/soc/tegra/tegra_asoc_machine.c index 735909310a26..b95438c3dbf7 100644 --- a/sound/soc/tegra/tegra_asoc_machine.c +++ b/sound/soc/tegra/tegra_asoc_machine.c @@ -313,7 +313,7 @@ static int tegra_machine_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops tegra_machine_snd_ops = { +static const struct snd_soc_ops tegra_machine_snd_ops = { .hw_params = tegra_machine_hw_params, }; @@ -341,9 +341,34 @@ tegra_machine_parse_phandle(struct device *dev, const char *name) return np; } +static void tegra_machine_unregister_codec(void *pdev) +{ + platform_device_unregister(pdev); +} + +static int tegra_machine_register_codec(struct device *dev, const char *name) +{ + struct platform_device *pdev; + int err; + + if (!name) + return 0; + + pdev = platform_device_register_simple(name, -1, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + err = devm_add_action_or_reset(dev, tegra_machine_unregister_codec, + pdev); + if (err) + return err; + + return 0; +} + int tegra_asoc_machine_probe(struct platform_device *pdev) { - struct device_node *np_codec, *np_i2s; + struct device_node *np_codec, *np_i2s, *np_ac97; const struct tegra_asoc_data *asoc; struct device *dev = &pdev->dev; struct tegra_machine *machine; @@ -404,17 +429,30 @@ int tegra_asoc_machine_probe(struct platform_device *pdev) return err; } - np_codec = tegra_machine_parse_phandle(dev, "nvidia,audio-codec"); - if (IS_ERR(np_codec)) - return PTR_ERR(np_codec); + if (asoc->set_ac97) { + err = tegra_machine_register_codec(dev, asoc->codec_dev_name); + if (err) + return err; + + np_ac97 = tegra_machine_parse_phandle(dev, "nvidia,ac97-controller"); + if (IS_ERR(np_ac97)) + return PTR_ERR(np_ac97); - np_i2s = tegra_machine_parse_phandle(dev, "nvidia,i2s-controller"); - if (IS_ERR(np_i2s)) - return PTR_ERR(np_i2s); + card->dai_link->cpus->of_node = np_ac97; + card->dai_link->platforms->of_node = np_ac97; + } else { + np_codec = tegra_machine_parse_phandle(dev, "nvidia,audio-codec"); + if (IS_ERR(np_codec)) + return PTR_ERR(np_codec); - card->dai_link->cpus->of_node = np_i2s; - card->dai_link->codecs->of_node = np_codec; - card->dai_link->platforms->of_node = np_i2s; + np_i2s = tegra_machine_parse_phandle(dev, "nvidia,i2s-controller"); + if (IS_ERR(np_i2s)) + return PTR_ERR(np_i2s); + + card->dai_link->cpus->of_node = np_i2s; + card->dai_link->codecs->of_node = np_codec; + card->dai_link->platforms->of_node = np_i2s; + } if (asoc->add_common_controls) { card->controls = tegra_machine_controls; @@ -589,6 +627,7 @@ static struct snd_soc_card snd_soc_tegra_wm9712 = { static const struct tegra_asoc_data tegra_wm9712_data = { .card = &snd_soc_tegra_wm9712, .add_common_dapm_widgets = true, + .codec_dev_name = "wm9712-codec", .set_ac97 = true, }; @@ -686,6 +725,7 @@ static struct snd_soc_dai_link tegra_tlv320aic23_dai = { }; static struct snd_soc_card snd_soc_tegra_trimslice = { + .name = "tegra-trimslice", .components = "codec:tlv320aic23", .dai_link = &tegra_tlv320aic23_dai, .num_links = 1, diff --git a/sound/soc/tegra/tegra_asoc_machine.h b/sound/soc/tegra/tegra_asoc_machine.h index 8ee0ec814f67..d6a8d1320551 100644 --- a/sound/soc/tegra/tegra_asoc_machine.h +++ b/sound/soc/tegra/tegra_asoc_machine.h @@ -13,6 +13,7 @@ struct snd_soc_pcm_runtime; struct tegra_asoc_data { unsigned int (*mclk_rate)(unsigned int srate); + const char *codec_dev_name; struct snd_soc_card *card; unsigned int mclk_id; bool hp_jack_gpio_active_low; |