diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index ea076010..5830274b 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,17 +1,42 @@ # SPDX-License-Identifier: GPL-2.0-only -# SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. snd-soc-tegra-utils-oot-objs := tegra_asoc_utils.o tegra_asoc_machine.o \ tegra_isomgr_bw.o tegra_codecs.o +snd-soc-tegra210-ahub-oot-objs := tegra210_ahub.o +snd-soc-tegra210-dmic-oot-objs := tegra210_dmic.o +snd-soc-tegra210-i2s-oot-objs := tegra210_i2s.o +snd-soc-tegra186-dspk-oot-objs := tegra186_dspk.o +snd-soc-tegra210-admaif-oot-objs := tegra210_admaif.o +snd-soc-tegra210-amx-oot-objs := tegra210_amx.o +snd-soc-tegra210-adx-oot-objs := tegra210_adx.o +snd-soc-tegra210-mixer-oot-objs := tegra210_mixer.o +snd-soc-tegra210-sfc-oot-objs := tegra210_sfc.o snd-soc-tegra210-afc-oot-objs := tegra210_afc.o +snd-soc-tegra210-mvc-oot-objs := tegra210_mvc.o snd-soc-tegra210-iqc-oot-objs := tegra210_iqc.o +snd-soc-tegra186-asrc-oot-objs := tegra186_asrc.o snd-soc-tegra186-arad-oot-objs := tegra186_arad.o +snd-soc-tegra210-ope-oot-objs := tegra210_ope.o tegra210_peq.o \ + tegra210_mbdrc.o snd-soc-tegra-machine-driver-oot-objs := tegra_machine_driver.o snd-soc-tegra-controls-objs := tegra_mixer_control.o obj-m += snd-soc-tegra-utils-oot.o +obj-m += snd-soc-tegra210-dmic-oot.o +obj-m += snd-soc-tegra210-ahub-oot.o +obj-m += snd-soc-tegra210-i2s-oot.o +obj-m += snd-soc-tegra186-dspk-oot.o +obj-m += snd-soc-tegra210-admaif-oot.o +obj-m += snd-soc-tegra210-amx-oot.o +obj-m += snd-soc-tegra210-adx-oot.o +obj-m += snd-soc-tegra210-mixer-oot.o +obj-m += snd-soc-tegra210-sfc-oot.o obj-m += snd-soc-tegra210-afc-oot.o +obj-m += snd-soc-tegra210-mvc-oot.o obj-m += snd-soc-tegra210-iqc-oot.o +obj-m += snd-soc-tegra210-ope-oot.o obj-m += snd-soc-tegra186-arad-oot.o +obj-m += snd-soc-tegra186-asrc-oot.o obj-m += snd-soc-tegra-machine-driver-oot.o obj-m += snd-soc-tegra-controls.o diff --git a/sound/soc/tegra/tegra186_arad.c b/sound/soc/tegra/tegra186_arad.c index 26ca3e3f..c9c6b655 100644 --- a/sound/soc/tegra/tegra186_arad.c +++ b/sound/soc/tegra/tegra186_arad.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -// SPDX-FileCopyrightText: Copyright (c) 2015-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // tegra186_arad.c - Tegra186 ARAD driver +// +// Copyright (c) 2015-2023, NVIDIA CORPORATION. All rights reserved. #include @@ -26,10 +27,7 @@ #include #include "tegra186_arad.h" - -#ifdef CONFIG_SND_SOC_TEGRA186_ASRC_WAR #include "tegra186_asrc.h" -#endif static struct device *arad_dev; diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c new file mode 100644 index 00000000..6e7f96f2 --- /dev/null +++ b/sound/soc/tegra/tegra186_asrc.c @@ -0,0 +1,1179 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra186_asrc.c - Tegra186 ASRC driver +// +// Copyright (c) 2015-2023, NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_TEGRA186_AHC +#include +#endif +#include +#include +#include +#include + +#include + +#include "tegra186_arad.h" +#include "tegra186_asrc.h" +#include "tegra210_ahub.h" + +#define RATIO_ARAD 0 +#define RATIO_SW 1 + +#define ASRC_STREAM_SOURCE_SELECT(id) \ + (TEGRA186_ASRC_STREAM1_CONFIG + id*TEGRA186_ASRC_STREAM_STRIDE) + +#define ASRC_STREAM_REG(reg, id) (reg + (id * TEGRA186_ASRC_STREAM_STRIDE)) + +#define ASRC_STREAM_REG_DEFAULTS(id) \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, id), ((id+1)<<4)}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, id), 0x1}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, id), 0x0}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_MUTE_UNMUTE_DURATION, id), 0x400}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RX_CIF_CTRL, id), 0x7500}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_TX_CIF_CTRL, id), 0x7500}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_ENABLE, id), 0x0}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_SOFT_RESET, id), 0x0}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_STATEBUF_ADDR, id), 0x0}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_STATEBUF_CONFIG, id), 0x445}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_INSAMPLEBUF_ADDR, id), 0x0}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_INSAMPLEBUF_CONFIG, id), 0x64}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_ADDR, id), 0x4b0}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_CONFIG, id), 0x64} +static struct device *asrc_dev; + +static const struct reg_default tegra186_asrc_reg_defaults[] = { + ASRC_STREAM_REG_DEFAULTS(0), + ASRC_STREAM_REG_DEFAULTS(1), + ASRC_STREAM_REG_DEFAULTS(2), + ASRC_STREAM_REG_DEFAULTS(3), + ASRC_STREAM_REG_DEFAULTS(4), + ASRC_STREAM_REG_DEFAULTS(5), + + { TEGRA186_ASRC_GLOBAL_ENB, 0}, + { TEGRA186_ASRC_GLOBAL_SOFT_RESET, 0}, + { TEGRA186_ASRC_GLOBAL_CG, 1}, + { TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR, 0}, + { TEGRA186_ASRC_GLOBAL_SCRATCH_CONFIG, 0x0c207980}, + { TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL, 0x00115500}, + { TEGRA186_ASRC_RATIO_UPD_RX_STATUS, 0}, + { TEGRA186_ASRC_GLOBAL_STATUS, 0}, + { TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS, 0}, + { TEGRA186_ASRC_GLOBAL_INT_MASK, 0x0}, + { TEGRA186_ASRC_GLOBAL_INT_SET, 0x0}, + { TEGRA186_ASRC_GLOBAL_INT_CLEAR, 0x0}, + { TEGRA186_ASRC_GLOBAL_APR_CTRL, 0x0}, + { TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL, 0x0}, + { TEGRA186_ASRC_GLOBAL_DISARM_APR, 0x0}, + { TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL, 0x0}, + { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS, 0x0}, + { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL, 0x0}, + { TEGRA186_ASRC_CYA, 0x0}, +}; + + +static int tegra186_asrc_get_stream_enable_status(struct tegra186_asrc *asrc, + unsigned int lane_id) +{ + int val; + + regmap_read(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_STATUS, lane_id), + &val); + + return val & 0x01; + +} + +static int tegra186_asrc_get_ratio_lock_status(struct tegra186_asrc *asrc, + unsigned int lane_id) +{ + int val; + + regmap_read(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS, lane_id), + &val); + + return val & 0x1; +} + +static void tegra186_asrc_set_ratio_lock_status(struct tegra186_asrc *asrc, + unsigned int lane_id) +{ + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS, lane_id), 1); +} + +int tegra186_asrc_set_source(int id, int source) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(asrc_dev); + + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, id), + TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK, (source & 1)); + + return 0; +} + +int tegra186_asrc_update_ratio(int id, int inte, int frac) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(asrc_dev); + + regmap_write(asrc->regmap, ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, id), + inte); + regmap_write(asrc->regmap, ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, id), + frac); + + tegra186_asrc_set_ratio_lock_status(asrc, (unsigned int)id); + + return 0; +} + +static int tegra186_asrc_runtime_suspend(struct device *dev) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + +#ifdef CONFIG_TEGRA186_AHC + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_INT_MASK, 0x1); +#endif + regcache_cache_only(asrc->regmap, true); + regcache_mark_dirty(asrc->regmap); + + return 0; +} + +static int tegra186_asrc_runtime_resume(struct device *dev) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + int lane_id; + + regcache_cache_only(asrc->regmap, false); + regcache_sync(asrc->regmap); + + /* HW needs sw reset to make sure previous + transaction was clean */ + regmap_write(asrc->regmap, + TEGRA186_ASRC_GLOBAL_SOFT_RESET, 0x1); + /* Set global starting address of the buffer in ARAM */ + regmap_write(asrc->regmap, + TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR, + asrc->soc_data->aram_start_addr); + + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_INT_MASK, + 0x01); + /* set global enable */ + regmap_write(asrc->regmap, + TEGRA186_ASRC_GLOBAL_ENB, TEGRA186_ASRC_GLOBAL_EN); + + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_INT_CLEAR, + 0x01); + /** + * Hw Bug:200208400 - asrc interrupt status gets cleared when + * it is cleared twice. This WAR is only applicable for T186 + */ + if (of_machine_is_compatible("nvidia,tegra186-asrc")) + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_INT_CLEAR, 0x1); + + for (lane_id = 0; lane_id < 6; lane_id++) { + if (asrc->lane[lane_id].ratio_source == RATIO_SW) { + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, + lane_id), + asrc->lane[lane_id].int_part); + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, + lane_id), + asrc->lane[lane_id].frac_part); + tegra186_asrc_set_ratio_lock_status(asrc, lane_id); + } + } +#ifdef CONFIG_TEGRA186_AHC + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_INT_MASK, 0x0); +#endif + + return 0; +} + +static int tegra186_asrc_set_audio_cif(struct tegra186_asrc *asrc, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + 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_S24_LE: + 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 = TEGRA_ACIF_BITS_24; + tegra_set_cif(asrc->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra186_asrc_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->dev; + struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai); + int ret, lane_id = dai->id; + + /* set threshold */ + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RX_THRESHOLD, dai->id), + asrc->lane[lane_id].input_thresh); + + ret = tegra186_asrc_set_audio_cif(asrc, params, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RX_CIF_CTRL, dai->id)); + if (ret) { + dev_err(dev, "Can't set ASRC RX%d CIF: %d\n", dai->id, ret); + return ret; + } + + return ret; +} + +static int tegra186_asrc_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->dev; + struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai); + int ret, lane_id = dai->id - 7, dcnt = 10; + + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, lane_id), + 1, asrc->lane[lane_id].ratio_source); + + ret = tegra186_asrc_set_audio_cif(asrc, params, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_TX_CIF_CTRL, lane_id)); + if (ret) { + dev_err(dev, "Can't set ASRC TX%d CIF: %d\n", lane_id, ret); + return ret; + } + + /* set ENABLE_HW_RATIO_COMP */ + if (asrc->lane[lane_id].hwcomp_disable) { + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, lane_id), + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK, + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE); + } else { + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, lane_id), + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK, + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE); + + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_COMP, lane_id), + TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE); + } + + /* set lock */ + if (asrc->lane[lane_id].ratio_source == RATIO_SW) { + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, + lane_id), + asrc->lane[lane_id].int_part); + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, lane_id), + asrc->lane[lane_id].frac_part); + tegra186_asrc_set_ratio_lock_status(asrc, lane_id); + } else + while (!tegra186_asrc_get_ratio_lock_status(asrc, lane_id) && + dcnt--) + udelay(100); + + /* set threshold */ + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_TX_THRESHOLD, lane_id), + asrc->lane[lane_id].output_thresh); + + return ret; +} + +static int tegra186_asrc_get_ratio_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *asrc_private = + (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + /* get the source of a lane in asrc */ + ucontrol->value.integer.value[0] = asrc->lane[id].ratio_source; + + return 0; +} + +static int tegra186_asrc_put_ratio_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *asrc_private = + (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + /* update the source of the lane */ + asrc->lane[id].ratio_source = ucontrol->value.integer.value[0]; + regmap_update_bits(asrc->regmap, asrc_private->reg, + TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK, + asrc->lane[id].ratio_source); + + return 0; +} + +static int tegra186_asrc_get_enable_stream(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int enable; + + regmap_read(asrc->regmap, asrc_private->reg, &enable); + ucontrol->value.integer.value[0] = enable; + + return 0; +} + +static int tegra186_asrc_put_enable_stream(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int enable = 0; + + enable = ucontrol->value.integer.value[0]; + regmap_write(asrc->regmap, asrc_private->reg, enable); + + return 0; +} + +static int tegra186_asrc_get_ratio_int(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + regmap_read(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, id), + &asrc->lane[id].int_part); + ucontrol->value.integer.value[0] = asrc->lane[id].int_part; + + return 0; +} + +static int tegra186_asrc_put_ratio_int(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + asrc->lane[id].int_part = ucontrol->value.integer.value[0]; + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, id), + asrc->lane[id].int_part); + tegra186_asrc_set_ratio_lock_status(asrc, id); + + return 0; +} + +static int tegra186_asrc_get_ratio_frac(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mreg_control *asrc_private = + (struct soc_mreg_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE; + + regmap_read(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, id), + &asrc->lane[id].frac_part); + ucontrol->value.integer.value[0] = asrc->lane[id].frac_part; + + return 0; +} + +static int tegra186_asrc_put_ratio_frac(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mreg_control *asrc_private = + (struct soc_mreg_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE; + + asrc->lane[id].frac_part = ucontrol->value.integer.value[0]; + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, id), + asrc->lane[id].frac_part); + tegra186_asrc_set_ratio_lock_status(asrc, id); + + return 0; +} + +static int tegra186_asrc_get_hwcomp_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + ucontrol->value.integer.value[0] = asrc->lane[id].hwcomp_disable; + + return 0; +} + +static int tegra186_asrc_put_hwcomp_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + asrc->lane[id].hwcomp_disable = ucontrol->value.integer.value[0]; + + return 0; +} + +static int tegra186_asrc_get_input_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + ucontrol->value.integer.value[0] = (asrc->lane[id].input_thresh & 0x3); + + return 0; +} + +static int tegra186_asrc_put_input_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + asrc->lane[id].input_thresh = (asrc->lane[id].input_thresh & ~(0x3)) + | ucontrol->value.integer.value[0]; + return 0; +} + +static int tegra186_asrc_get_output_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + ucontrol->value.integer.value[0] = (asrc->lane[id].output_thresh & 0x3); + + return 0; +} + +static int tegra186_asrc_put_output_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + asrc->lane[id].output_thresh = (asrc->lane[id].output_thresh & ~(0x3)) + | ucontrol->value.integer.value[0]; + + return 0; +} +static int tegra186_asrc_req_arad_ratio(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct device *dev = cmpnt->dev; + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + int ret = 0; + unsigned int lane_id = 0; + lane_id = (w->reg - TEGRA186_ASRC_STREAM1_ENABLE) / + TEGRA186_ASRC_STREAM_STRIDE; + + if (event == SND_SOC_DAPM_POST_PMD) { + regmap_write(asrc->regmap, ASRC_STREAM_REG + (TEGRA186_ASRC_STREAM1_SOFT_RESET, lane_id), + 0x1); + return ret; + } + if (asrc->lane[lane_id].ratio_source == RATIO_ARAD) + tegra186_arad_send_ratio(); + + return ret; +} + +static struct snd_soc_dai_ops tegra186_asrc_in_dai_ops = { + .hw_params = tegra186_asrc_in_hw_params, +}; + +static struct snd_soc_dai_ops tegra186_asrc_out_dai_ops = { + .hw_params = tegra186_asrc_out_hw_params, +}; + +#define IN_DAI(sname, id, dai_ops) \ + { \ + .name = #sname #id, \ + .playback = { \ + .stream_name = #sname #id " Receive", \ + .channels_min = 1, \ + .channels_max = 12, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = dai_ops, \ + } + +#define OUT_DAI(sname, id, dai_ops) \ + { \ + .name = #sname #id, \ + .capture = { \ + .stream_name = #sname #id " Transmit", \ + .channels_min = 1, \ + .channels_max = 12, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = dai_ops, \ + } + +static struct snd_soc_dai_driver tegra186_asrc_dais[] = { + IN_DAI(RX, 1, &tegra186_asrc_in_dai_ops), + IN_DAI(RX, 2, &tegra186_asrc_in_dai_ops), + IN_DAI(RX, 3, &tegra186_asrc_in_dai_ops), + IN_DAI(RX, 4, &tegra186_asrc_in_dai_ops), + IN_DAI(RX, 5, &tegra186_asrc_in_dai_ops), + IN_DAI(RX, 6, &tegra186_asrc_in_dai_ops), + IN_DAI(RX, 7, &tegra186_asrc_in_dai_ops), + OUT_DAI(TX, 1, &tegra186_asrc_out_dai_ops), + OUT_DAI(TX, 2, &tegra186_asrc_out_dai_ops), + OUT_DAI(TX, 3, &tegra186_asrc_out_dai_ops), + OUT_DAI(TX, 4, &tegra186_asrc_out_dai_ops), + OUT_DAI(TX, 5, &tegra186_asrc_out_dai_ops), + OUT_DAI(TX, 6, &tegra186_asrc_out_dai_ops), +}; + +#define SND_SOC_DAPM_IN(wname, wevent) \ +{ .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} + +static const struct snd_soc_dapm_widget tegra186_asrc_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_OUT_E("TX1", NULL, 0, TEGRA186_ASRC_STREAM1_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_req_arad_ratio, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("TX2", NULL, 0, TEGRA186_ASRC_STREAM2_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_req_arad_ratio, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("TX3", NULL, 0, TEGRA186_ASRC_STREAM3_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_req_arad_ratio, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("TX4", NULL, 0, TEGRA186_ASRC_STREAM4_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_req_arad_ratio, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("TX5", NULL, 0, TEGRA186_ASRC_STREAM5_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_req_arad_ratio, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("TX6", NULL, 0, TEGRA186_ASRC_STREAM6_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_req_arad_ratio, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_IN("RX7", NULL), +}; + +static const struct snd_soc_dapm_route tegra186_asrc_routes[] = { + { "RX1", NULL, "RX1 Receive" }, + { "TX1", NULL, "RX1" }, + { "TX1 Transmit", NULL, "TX1" }, + { "RX2", NULL, "RX2 Receive" }, + { "TX2", NULL, "RX2" }, + { "TX2 Transmit", NULL, "TX2" }, + { "RX3", NULL, "RX3 Receive" }, + { "TX3", NULL, "RX3" }, + { "TX3 Transmit", NULL, "TX3" }, + { "RX4", NULL, "RX4 Receive" }, + { "TX4", NULL, "RX4" }, + { "TX4 Transmit", NULL, "TX4" }, + { "RX5", NULL, "RX5 Receive" }, + { "TX5", NULL, "RX5" }, + { "TX5 Transmit", NULL, "TX5" }, + { "RX6", NULL, "RX6 Receive" }, + { "TX6", NULL, "RX6" }, + { "TX6 Transmit", NULL, "TX6" }, + { "RX7", NULL, "RX7 Receive" }, +}; + +static const char * const tegra186_asrc_ratio_source_text[] = { + "ARAD", + "SW", +}; + +#define ASRC_SOURCE_DECL(name, id) \ + static const struct soc_enum name = \ + SOC_ENUM_SINGLE(ASRC_STREAM_SOURCE_SELECT(id), \ + 0, 2, tegra186_asrc_ratio_source_text) + +ASRC_SOURCE_DECL(src_select1, 0); +ASRC_SOURCE_DECL(src_select2, 1); +ASRC_SOURCE_DECL(src_select3, 2); +ASRC_SOURCE_DECL(src_select4, 3); +ASRC_SOURCE_DECL(src_select5, 4); +ASRC_SOURCE_DECL(src_select6, 5); + +#define SOC_SINGLE_EXT_FRAC(xname, xregbase, \ + xmax, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_xr_sx, .get = xget, \ + .put = xput, \ + .private_value = (unsigned long)&(struct soc_mreg_control) \ + {.regbase = xregbase, .regcount = 1, .nbits = 32, \ + .invert = 0, .min = 0, .max = xmax} } + +static const struct snd_kcontrol_new tegra186_asrc_controls[] = { + SOC_SINGLE_EXT("Ratio1 Integer Part", TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK, 0, + tegra186_asrc_get_ratio_int, tegra186_asrc_put_ratio_int), + SOC_SINGLE_EXT_FRAC("Ratio1 Fractional Part", + TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio2 Integer Part", TEGRA186_ASRC_STREAM2_RATIO_INTEGER_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK, 0, + tegra186_asrc_get_ratio_int, tegra186_asrc_put_ratio_int), + SOC_SINGLE_EXT_FRAC("Ratio2 Fractional Part", + TEGRA186_ASRC_STREAM2_RATIO_FRAC_PART, + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio3 Integer Part", TEGRA186_ASRC_STREAM3_RATIO_INTEGER_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK, 0, + tegra186_asrc_get_ratio_int, tegra186_asrc_put_ratio_int), + SOC_SINGLE_EXT_FRAC("Ratio3 Fractional Part", + TEGRA186_ASRC_STREAM3_RATIO_FRAC_PART, + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio4 Integer Part", TEGRA186_ASRC_STREAM4_RATIO_INTEGER_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK, 0, + tegra186_asrc_get_ratio_int, tegra186_asrc_put_ratio_int), + SOC_SINGLE_EXT_FRAC("Ratio4 Fractional Part", + TEGRA186_ASRC_STREAM4_RATIO_FRAC_PART, + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio5 Integer Part", TEGRA186_ASRC_STREAM5_RATIO_INTEGER_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK, 0, + tegra186_asrc_get_ratio_int, tegra186_asrc_put_ratio_int), + SOC_SINGLE_EXT_FRAC("Ratio5 Fractional Part", + TEGRA186_ASRC_STREAM5_RATIO_FRAC_PART, + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio6 Integer Part", TEGRA186_ASRC_STREAM6_RATIO_INTEGER_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK, 0, + tegra186_asrc_get_ratio_int, tegra186_asrc_put_ratio_int), + SOC_SINGLE_EXT_FRAC("Ratio6 Fractional Part", + TEGRA186_ASRC_STREAM6_RATIO_FRAC_PART, + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + + SOC_ENUM_EXT("Ratio1 Source", src_select1, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio2 Source", src_select2, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio3 Source", src_select3, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio4 Source", src_select4, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio5 Source", src_select5, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio6 Source", src_select6, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + + SOC_SINGLE_EXT("Stream1 Enable", + TEGRA186_ASRC_STREAM1_ENABLE, 0, 1, 0, + tegra186_asrc_get_enable_stream, tegra186_asrc_put_enable_stream), + SOC_SINGLE_EXT("Stream2 Enable", + TEGRA186_ASRC_STREAM2_ENABLE, 0, 1, 0, + tegra186_asrc_get_enable_stream, tegra186_asrc_put_enable_stream), + SOC_SINGLE_EXT("Stream3 Enable", + TEGRA186_ASRC_STREAM3_ENABLE, 0, 1, 0, + tegra186_asrc_get_enable_stream, tegra186_asrc_put_enable_stream), + SOC_SINGLE_EXT("Stream4 Enable", + TEGRA186_ASRC_STREAM4_ENABLE, 0, 1, 0, + tegra186_asrc_get_enable_stream, tegra186_asrc_put_enable_stream), + SOC_SINGLE_EXT("Stream5 Enable", + TEGRA186_ASRC_STREAM5_ENABLE, 0, 1, 0, + tegra186_asrc_get_enable_stream, tegra186_asrc_put_enable_stream), + SOC_SINGLE_EXT("Stream6 Enable", + TEGRA186_ASRC_STREAM6_ENABLE, 0, 1, 0, + tegra186_asrc_get_enable_stream, tegra186_asrc_put_enable_stream), + SOC_SINGLE_EXT("Stream1 Hwcomp Disable", + TEGRA186_ASRC_STREAM1_CONFIG, + 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, tegra186_asrc_put_hwcomp_disable), + SOC_SINGLE_EXT("Stream2 Hwcomp Disable", + TEGRA186_ASRC_STREAM2_CONFIG, + 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, tegra186_asrc_put_hwcomp_disable), + SOC_SINGLE_EXT("Stream3 Hwcomp Disable", + TEGRA186_ASRC_STREAM3_CONFIG, + 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, tegra186_asrc_put_hwcomp_disable), + SOC_SINGLE_EXT("Stream4 Hwcomp Disable", + TEGRA186_ASRC_STREAM4_CONFIG, + 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, tegra186_asrc_put_hwcomp_disable), + SOC_SINGLE_EXT("Stream5 Hwcomp Disable", + TEGRA186_ASRC_STREAM5_CONFIG, + 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, tegra186_asrc_put_hwcomp_disable), + SOC_SINGLE_EXT("Stream6 Hwcomp Disable", + TEGRA186_ASRC_STREAM6_CONFIG, + 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, tegra186_asrc_put_hwcomp_disable), + SOC_SINGLE_EXT("Stream1 Input Thresh", + TEGRA186_ASRC_STREAM1_RX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_input_threshold, tegra186_asrc_put_input_threshold), + SOC_SINGLE_EXT("Stream2 Input Thresh", + TEGRA186_ASRC_STREAM2_RX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_input_threshold, tegra186_asrc_put_input_threshold), + SOC_SINGLE_EXT("Stream3 Input Thresh", + TEGRA186_ASRC_STREAM3_RX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_input_threshold, tegra186_asrc_put_input_threshold), + SOC_SINGLE_EXT("Stream4 Input Thresh", + TEGRA186_ASRC_STREAM4_RX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_input_threshold, tegra186_asrc_put_input_threshold), + SOC_SINGLE_EXT("Stream5 Input Thresh", + TEGRA186_ASRC_STREAM5_RX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_input_threshold, tegra186_asrc_put_input_threshold), + SOC_SINGLE_EXT("Stream6 Input Thresh", + TEGRA186_ASRC_STREAM6_RX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_input_threshold, tegra186_asrc_put_input_threshold), + SOC_SINGLE_EXT("Stream1 Output Thresh", + TEGRA186_ASRC_STREAM1_TX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_output_threshold, tegra186_asrc_put_output_threshold), + SOC_SINGLE_EXT("Stream2 Output Thresh", + TEGRA186_ASRC_STREAM2_TX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_output_threshold, tegra186_asrc_put_output_threshold), + SOC_SINGLE_EXT("Stream3 Output Thresh", + TEGRA186_ASRC_STREAM3_TX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_output_threshold, tegra186_asrc_put_output_threshold), + SOC_SINGLE_EXT("Stream4 Output Thresh", + TEGRA186_ASRC_STREAM4_TX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_output_threshold, tegra186_asrc_put_output_threshold), + SOC_SINGLE_EXT("Stream5 Output Thresh", + TEGRA186_ASRC_STREAM5_TX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_output_threshold, tegra186_asrc_put_output_threshold), + SOC_SINGLE_EXT("Stream6 Output Thresh", + TEGRA186_ASRC_STREAM6_TX_THRESHOLD, + 0, 3, 0, + tegra186_asrc_get_output_threshold, tegra186_asrc_put_output_threshold), +}; + +static struct snd_soc_component_driver tegra186_asrc_cmpnt = { + .dapm_widgets = tegra186_asrc_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra186_asrc_widgets), + .dapm_routes = tegra186_asrc_routes, + .num_dapm_routes = ARRAY_SIZE(tegra186_asrc_routes), + .controls = tegra186_asrc_controls, + .num_controls = ARRAY_SIZE(tegra186_asrc_controls), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static bool tegra186_asrc_wr_reg(struct device *dev, unsigned int reg) +{ + if (reg < TEGRA186_ASRC_STREAM_LIMIT) + reg %= TEGRA186_ASRC_STREAM_STRIDE; + + switch (reg) { + case TEGRA186_ASRC_STREAM1_CONFIG: + case TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART: + case TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART: + case TEGRA186_ASRC_STREAM1_RX_THRESHOLD: + case TEGRA186_ASRC_STREAM1_TX_THRESHOLD: + case TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS: + case TEGRA186_ASRC_STREAM1_MUTE_UNMUTE_DURATION: + case TEGRA186_ASRC_STREAM1_RATIO_COMP: + case TEGRA186_ASRC_STREAM1_RX_CIF_CTRL: + case TEGRA186_ASRC_STREAM1_TX_CIF_CTRL: + case TEGRA186_ASRC_STREAM1_ENABLE: + case TEGRA186_ASRC_STREAM1_SOFT_RESET: + case TEGRA186_ASRC_STREAM1_STATEBUF_ADDR: + case TEGRA186_ASRC_STREAM1_STATEBUF_CONFIG: + case TEGRA186_ASRC_STREAM1_INSAMPLEBUF_ADDR: + case TEGRA186_ASRC_STREAM1_INSAMPLEBUF_CONFIG: + case TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_ADDR: + case TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_CONFIG: + + case TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL: + + case TEGRA186_ASRC_GLOBAL_ENB: + case TEGRA186_ASRC_GLOBAL_SOFT_RESET: + case TEGRA186_ASRC_GLOBAL_CG: + case TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR: + case TEGRA186_ASRC_GLOBAL_SCRATCH_CONFIG: + + case TEGRA186_ASRC_GLOBAL_INT_MASK: + case TEGRA186_ASRC_GLOBAL_INT_SET: + case TEGRA186_ASRC_GLOBAL_INT_CLEAR: + case TEGRA186_ASRC_GLOBAL_APR_CTRL: + case TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL: + case TEGRA186_ASRC_GLOBAL_DISARM_APR: + case TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL: + case TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS: + case TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL: + case TEGRA186_ASRC_CYA: + return true; + default: + return false; + } +} + +static bool tegra186_asrc_rd_reg(struct device *dev, unsigned int reg) +{ + if (reg < TEGRA186_ASRC_STREAM_LIMIT) + reg %= TEGRA186_ASRC_STREAM_STRIDE; + + switch (reg) { + case TEGRA186_ASRC_STREAM1_CONFIG: + case TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART: + case TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART: + case TEGRA186_ASRC_STREAM1_RX_THRESHOLD: + case TEGRA186_ASRC_STREAM1_TX_THRESHOLD: + case TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS: + case TEGRA186_ASRC_STREAM1_MUTE_UNMUTE_DURATION: + case TEGRA186_ASRC_STREAM1_RATIO_COMP: + case TEGRA186_ASRC_STREAM1_RX_STATUS: + case TEGRA186_ASRC_STREAM1_RX_CIF_CTRL: + case TEGRA186_ASRC_STREAM1_TX_STATUS: + case TEGRA186_ASRC_STREAM1_TX_CIF_CTRL: + case TEGRA186_ASRC_STREAM1_ENABLE: + case TEGRA186_ASRC_STREAM1_SOFT_RESET: + case TEGRA186_ASRC_STREAM1_STATUS: + case TEGRA186_ASRC_STREAM1_BUFFER_STATUS: + case TEGRA186_ASRC_STREAM1_CONFIG_ERR_TYPE: + case TEGRA186_ASRC_STREAM1_STATEBUF_ADDR: + case TEGRA186_ASRC_STREAM1_STATEBUF_CONFIG: + case TEGRA186_ASRC_STREAM1_INSAMPLEBUF_ADDR: + case TEGRA186_ASRC_STREAM1_INSAMPLEBUF_CONFIG: + case TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_ADDR: + case TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_CONFIG: + + case TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL: + case TEGRA186_ASRC_RATIO_UPD_RX_STATUS: + + case TEGRA186_ASRC_GLOBAL_ENB: + case TEGRA186_ASRC_GLOBAL_SOFT_RESET: + case TEGRA186_ASRC_GLOBAL_CG: + case TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR: + case TEGRA186_ASRC_GLOBAL_SCRATCH_CONFIG: + + case TEGRA186_ASRC_GLOBAL_STATUS: + case TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS: + case TEGRA186_ASRC_GLOBAL_INT_STATUS: + case TEGRA186_ASRC_GLOBAL_INT_MASK: + case TEGRA186_ASRC_GLOBAL_INT_SET: + case TEGRA186_ASRC_GLOBAL_INT_CLEAR: + case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG: + case TEGRA186_ASRC_GLOBAL_APR_CTRL: + case TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL: + case TEGRA186_ASRC_GLOBAL_DISARM_APR: + case TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL: + case TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS: + case TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL: + case TEGRA186_ASRC_CYA: + return true; + default: + return false; + } +} + +static bool tegra186_asrc_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg < TEGRA186_ASRC_STREAM_LIMIT) + reg %= TEGRA186_ASRC_STREAM_STRIDE; + + switch (reg) { + case TEGRA186_ASRC_STREAM1_RX_STATUS: + case TEGRA186_ASRC_STREAM1_TX_STATUS: + case TEGRA186_ASRC_STREAM1_SOFT_RESET: + case TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART: + case TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART: + case TEGRA186_ASRC_STREAM1_STATUS: + case TEGRA186_ASRC_STREAM1_BUFFER_STATUS: + case TEGRA186_ASRC_STREAM1_CONFIG_ERR_TYPE: + case TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS: + case TEGRA186_ASRC_RATIO_UPD_RX_STATUS: + case TEGRA186_ASRC_GLOBAL_SOFT_RESET: + case TEGRA186_ASRC_GLOBAL_STATUS: + case TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS: + case TEGRA186_ASRC_GLOBAL_INT_STATUS: + case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG: + return true; + default: + return false; + } +} + +void tegra186_asrc_handle_arad_unlock(int stream_id, int action) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(asrc_dev); + int dcnt = 10; + + regmap_write(asrc->regmap, ASRC_STREAM_REG + (TEGRA186_ASRC_STREAM1_ENABLE, stream_id), action); + if (!action) + udelay(2000); + + while ((tegra186_asrc_get_stream_enable_status(asrc, + stream_id) != action) && dcnt--) + udelay(100); +} + +static const struct regmap_config tegra186_asrc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA186_ASRC_CYA, + .writeable_reg = tegra186_asrc_wr_reg, + .readable_reg = tegra186_asrc_rd_reg, + .volatile_reg = tegra186_asrc_volatile_reg, + .reg_defaults = tegra186_asrc_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra186_asrc_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +#ifdef CONFIG_TEGRA186_AHC +static void tegra186_asrc_ahc_cb(void *data) +{ + struct device *dev = (struct device *)data; + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + + regcache_cache_bypass(asrc->regmap, true); + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_INT_CLEAR, 0x1); + + /** + * Hw Bug:200208400 - asrc interrupt status gets cleared when + * it is cleared twice. This WAR is only applicable for T186 + */ + if (of_machine_is_compatible("nvidia,tegra186-asrc")) + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_INT_CLEAR, 0x1); + + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_ENB, 0x0); + udelay(100); + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_ENB, 0x1); + regcache_cache_bypass(asrc->regmap, false); +} +#endif + +static const struct tegra_asrc_soc_data soc_data_tegra186 = { + .aram_start_addr = 0x3F800000, +}; + +static const struct tegra_asrc_soc_data soc_data_tegra239 = { + .aram_start_addr = 0x70000000, +}; + +static const struct of_device_id tegra186_asrc_of_match[] = { + { .compatible = "nvidia,tegra186-asrc", .data = &soc_data_tegra186 }, + { .compatible = "nvidia,tegra194-asrc", .data = &soc_data_tegra186 }, + { .compatible = "nvidia,tegra239-asrc", .data = &soc_data_tegra239 }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra186_asrc_of_match); + +static int tegra186_asrc_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra186_asrc *asrc; + void __iomem *regs; + int err; + unsigned int i; + + asrc_dev = dev; + + asrc = devm_kzalloc(dev, sizeof(*asrc), GFP_KERNEL); + if (!asrc) + return -ENOMEM; + + dev_set_drvdata(dev, asrc); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + asrc->regmap = devm_regmap_init_mmio(dev, regs, + &tegra186_asrc_regmap_config); + if (IS_ERR(asrc->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(asrc->regmap); + } + + asrc->soc_data = of_device_get_match_data(&pdev->dev); + + regcache_cache_only(asrc->regmap, true); + +#ifdef CONFIG_TEGRA186_AHC + tegra186_ahc_register_cb(tegra186_asrc_ahc_cb, TEGRA186_AHC_ASRC1_CB, + dev); +#endif + + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_CONFIG, + TEGRA186_ASRC_GLOBAL_CONFIG_FRAC_32BIT_PRECISION); + + /* initialize default output srate */ + for (i = 0; i < 6; i++) { + asrc->lane[i].int_part = 1; + asrc->lane[i].frac_part = 0; + asrc->lane[i].ratio_source = RATIO_SW; + asrc->lane[i].hwcomp_disable = 0; + asrc->lane[i].input_thresh = + TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CONFIG; + asrc->lane[i].output_thresh = + TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CONFIG; + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, i), 1, 1); + } + + err = devm_snd_soc_register_component(dev, &tegra186_asrc_cmpnt, + tegra186_asrc_dais, + ARRAY_SIZE(tegra186_asrc_dais)); + if (err) { + dev_err(dev, "can't register ASRC component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra186_asrc_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra186_asrc_pm_ops = { + SET_RUNTIME_PM_OPS(tegra186_asrc_runtime_suspend, + tegra186_asrc_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra186_asrc_driver = { + .driver = { + .name = "tegra186-asrc", + .of_match_table = tegra186_asrc_of_match, + .pm = &tegra186_asrc_pm_ops, + }, + .probe = tegra186_asrc_platform_probe, + .remove = tegra186_asrc_platform_remove, +}; +module_platform_driver(tegra186_asrc_driver) + +MODULE_AUTHOR("Junghyun Kim "); +MODULE_DESCRIPTION("Tegra186 ASRC ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra186_asrc.h b/sound/soc/tegra/tegra186_asrc.h new file mode 100644 index 00000000..7f85a0cb --- /dev/null +++ b/sound/soc/tegra/tegra186_asrc.h @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra186_asrc.h - Definitions for Tegra186 ASRC driver + * + * Copyright (c) 2015-2022, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA186_ASRC_H__ +#define __TEGRA186_ASRC_H__ + +#define TEGRA186_ASRC_STREAM_STRIDE 0x80 +#define TEGRA186_ASRC_STREAM_MAX 6 +#define TEGRA186_ASRC_STREAM_LIMIT 0x2f0 + +/* ASRC stream related offset */ +#define TEGRA186_ASRC_STREAM1_CONFIG 0x0 +#define TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART 0x4 +#define TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART 0x8 +#define TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS 0xc +#define TEGRA186_ASRC_STREAM1_MUTE_UNMUTE_DURATION 0x10 +#define TEGRA186_ASRC_STREAM1_TX_THRESHOLD 0x14 +#define TEGRA186_ASRC_STREAM1_RX_THRESHOLD 0x18 +#define TEGRA186_ASRC_STREAM1_RATIO_COMP 0x1C +#define TEGRA186_ASRC_STREAM1_RX_STATUS 0x20 +#define TEGRA186_ASRC_STREAM1_RX_CIF_CTRL 0x24 +#define TEGRA186_ASRC_STREAM1_TX_STATUS 0x2c +#define TEGRA186_ASRC_STREAM1_TX_CIF_CTRL 0x30 +#define TEGRA186_ASRC_STREAM1_ENABLE 0x38 +#define TEGRA186_ASRC_STREAM1_SOFT_RESET 0x3c +#define TEGRA186_ASRC_STREAM1_STATUS 0x4c +#define TEGRA186_ASRC_STREAM1_BUFFER_STATUS 0x50 +#define TEGRA186_ASRC_STREAM1_CONFIG_ERR_TYPE 0x54 +#define TEGRA186_ASRC_STREAM1_STATEBUF_ADDR 0x5c +#define TEGRA186_ASRC_STREAM1_STATEBUF_CONFIG 0x60 +#define TEGRA186_ASRC_STREAM1_INSAMPLEBUF_ADDR 0x64 +#define TEGRA186_ASRC_STREAM1_INSAMPLEBUF_CONFIG 0x68 +#define TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_ADDR 0x6c +#define TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_CONFIG 0x70 + +#define TEGRA186_ASRC_STREAM2_RATIO_INTEGER_PART 0x84 +#define TEGRA186_ASRC_STREAM2_RATIO_FRAC_PART 0x88 +#define TEGRA186_ASRC_STREAM3_RATIO_INTEGER_PART 0x104 +#define TEGRA186_ASRC_STREAM3_RATIO_FRAC_PART 0x108 +#define TEGRA186_ASRC_STREAM4_RATIO_INTEGER_PART 0x184 +#define TEGRA186_ASRC_STREAM4_RATIO_FRAC_PART 0x188 +#define TEGRA186_ASRC_STREAM5_RATIO_INTEGER_PART 0x204 +#define TEGRA186_ASRC_STREAM5_RATIO_FRAC_PART 0x208 +#define TEGRA186_ASRC_STREAM6_RATIO_INTEGER_PART 0x284 +#define TEGRA186_ASRC_STREAM6_RATIO_FRAC_PART 0x288 + +#define TEGRA186_ASRC_STREAM1_ENABLE 0x38 +#define TEGRA186_ASRC_STREAM2_ENABLE 0xb8 +#define TEGRA186_ASRC_STREAM3_ENABLE 0x138 +#define TEGRA186_ASRC_STREAM4_ENABLE 0x1b8 +#define TEGRA186_ASRC_STREAM5_ENABLE 0x238 +#define TEGRA186_ASRC_STREAM6_ENABLE 0x2b8 + +#define TEGRA186_ASRC_STREAM2_CONFIG 0x80 +#define TEGRA186_ASRC_STREAM3_CONFIG 0x100 +#define TEGRA186_ASRC_STREAM4_CONFIG 0x180 +#define TEGRA186_ASRC_STREAM5_CONFIG 0x200 +#define TEGRA186_ASRC_STREAM6_CONFIG 0x280 + +#define TEGRA186_ASRC_STREAM2_TX_THRESHOLD 0x94 +#define TEGRA186_ASRC_STREAM3_TX_THRESHOLD 0x114 +#define TEGRA186_ASRC_STREAM4_TX_THRESHOLD 0x194 +#define TEGRA186_ASRC_STREAM5_TX_THRESHOLD 0x214 +#define TEGRA186_ASRC_STREAM6_TX_THRESHOLD 0x294 +#define TEGRA186_ASRC_STREAM2_RX_THRESHOLD 0x98 +#define TEGRA186_ASRC_STREAM3_RX_THRESHOLD 0x118 +#define TEGRA186_ASRC_STREAM4_RX_THRESHOLD 0x198 +#define TEGRA186_ASRC_STREAM5_RX_THRESHOLD 0x218 +#define TEGRA186_ASRC_STREAM6_RX_THRESHOLD 0x298 + + + + + +/* ASRC UPD related offset */ +#define TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL 0x30c +#define TEGRA186_ASRC_RATIO_UPD_RX_STATUS 0x310 + +/* ASRC Global registers offset */ +#define TEGRA186_ASRC_GLOBAL_ENB 0x2f4 +#define TEGRA186_ASRC_GLOBAL_SOFT_RESET 0x2f8 +#define TEGRA186_ASRC_GLOBAL_CG 0x2fc +#define TEGRA186_ASRC_GLOBAL_CONFIG 0x300 +#define TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR 0x304 +#define TEGRA186_ASRC_GLOBAL_SCRATCH_CONFIG 0x308 +#define TEGRA186_ASRC_GLOBAL_STATUS 0x314 +#define TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS 0x318 +#define TEGRA186_ASRC_GLOBAL_INT_STATUS 0x324 +#define TEGRA186_ASRC_GLOBAL_INT_MASK 0x328 +#define TEGRA186_ASRC_GLOBAL_INT_SET 0x32c +#define TEGRA186_ASRC_GLOBAL_INT_CLEAR 0x330 +#define TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG 0x334 +#define TEGRA186_ASRC_GLOBAL_APR_CTRL 0x1000 +#define TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL 0x1004 +#define TEGRA186_ASRC_GLOBAL_DISARM_APR 0x1008 +#define TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL 0x100c +#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS 0x1010 +#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL 0x1014 +#define TEGRA186_ASRC_CYA 0x1018 + +#define TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE 0xaaaa +#define TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CONFIG 0x00201002 +#define TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CONFIG 0x00201002 + +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT 31 +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT) +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT) +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE (0 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT) + +#define TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK (1 << TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT) + +#define TEGRA186_ASRC_STREAM_EN_SHIFT 0 +#define TEGRA186_ASRC_STREAM_EN (1 << TEGRA186_ASRC_STREAM_EN_SHIFT) +#define TEGRA186_ASRC_GLOBAL_EN_SHIFT 0 +#define TEGRA186_ASRC_GLOBAL_EN (1 << TEGRA186_ASRC_GLOBAL_EN_SHIFT) + +#define TEGRA186_ASRC_STREAM_STATEBUF_CONFIG_SIZE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_STATEBUF_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_STATEBUF_CONFIG_SIZE_SHIFT) +#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CONFIG_SIZE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_INSAMPLEBUF_CONFIG_SIZE_SHIFT) +#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CONFIG_SIZE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CONFIG_SIZE_SHIFT) + +#define TEGRA186_ASRC_GLOBAL_CONFIG_FRAC_28BIT_PRECISION 0 +#define TEGRA186_ASRC_GLOBAL_CONFIG_FRAC_32BIT_PRECISION 1 + +#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_MAX_CHANNELS_SHIFT 24 +#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_MAX_CHANNELS_MASK (0xFF << TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_MAX_CHANNELS_SHIFT) +#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_BLOCK_SIZE_SHIFT 16 +#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_BLOCK_SIZE_MASK (0xFF << TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_BLOCK_SIZE_SHIFT) +#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_SIZE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_SIZE_SHIFT) + +#define TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK 0x1F +#define TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK 0xFFFFFFFF + +#define TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MAX 0x7FFFFFFF +enum asrc_task_event { + STREAM_DISABLE, + STREAM_ENABLE, +}; + +struct tegra_cif_conf; + +struct tegra186_asrc_lane { + unsigned int int_part; + unsigned int frac_part; + int ratio_source; + unsigned int hwcomp_disable; + unsigned int input_thresh; + unsigned int output_thresh; +}; + +struct tegra_asrc_soc_data { + unsigned int aram_start_addr; +}; + +struct tegra186_asrc { + const struct tegra_asrc_soc_data *soc_data; + struct regmap *regmap; + struct tegra186_asrc_lane lane[6]; + struct tasklet_struct tasklet; + struct list_head task_desc; + int active_dai_count; +}; +int tegra186_asrc_set_source(int id, int source); +int tegra186_asrc_event(int id, enum asrc_task_event event, int status); +int tegra186_asrc_update_ratio(int id, int inte, int frac); +void tegra186_asrc_handle_arad_unlock(int stream_id, int action); +#endif diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c new file mode 100644 index 00000000..01916c4d --- /dev/null +++ b/sound/soc/tegra/tegra186_dspk.c @@ -0,0 +1,711 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra186_dspk.c - Tegra186 DSPK driver +// +// Copyright (c) 2020-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra186_dspk.h" + +static const struct reg_default tegra186_dspk_reg_defaults[] = { + { TEGRA186_DSPK_RX_INT_MASK, 0x00000007 }, + { TEGRA186_DSPK_RX_CIF_CTRL, 0x00007700 }, + { TEGRA186_DSPK_CG, 0x00000001 }, + { TEGRA186_DSPK_CORE_CTRL, 0x00000310 }, + { TEGRA186_DSPK_CODEC_CTRL, 0x03000000 }, +}; + +static int tegra186_dspk_get_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = dspk->srate_override; + + return 0; +} + +static int tegra186_dspk_put_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + if (dspk->srate_override == value) + return 0; + + dspk->srate_override = value; + + return 1; +} + +static int tegra186_dspk_get_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = dspk->audio_fmt_override; + + return 0; +} + +static int tegra186_dspk_put_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (dspk->audio_fmt_override == value) + return 0; + + dspk->audio_fmt_override = value; + + return 1; +} + +static int tegra186_dspk_get_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = dspk->audio_ch_override; + + return 0; +} + +static int tegra186_dspk_put_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + if (dspk->audio_ch_override == value) + return 0; + + dspk->audio_ch_override = value; + + return 1; +} + +static int tegra186_dspk_get_fifo_th(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = dspk->rx_fifo_th; + + return 0; +} + +static int tegra186_dspk_put_fifo_th(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + if (value == dspk->rx_fifo_th) + return 0; + + dspk->rx_fifo_th = value; + + return 1; +} + +static int tegra186_dspk_get_osr_val(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = dspk->osr_val; + + return 0; +} + +static int tegra186_dspk_put_osr_val(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dspk->osr_val) + return 0; + + dspk->osr_val = value; + + return 1; +} + +static int tegra186_dspk_get_pol_sel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = dspk->lrsel; + + return 0; +} + +static int tegra186_dspk_put_pol_sel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dspk->lrsel) + return 0; + + dspk->lrsel = value; + + return 1; +} + +static int tegra186_dspk_get_ch_sel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = dspk->ch_sel; + + return 0; +} + +static int tegra186_dspk_put_ch_sel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dspk->ch_sel) + return 0; + + dspk->ch_sel = value; + + return 1; +} + +static int tegra186_dspk_get_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = dspk->mono_to_stereo; + + return 0; +} + +static int tegra186_dspk_put_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dspk->mono_to_stereo) + return 0; + + dspk->mono_to_stereo = value; + + return 1; +} + +static int tegra186_dspk_get_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = dspk->stereo_to_mono; + + return 0; +} + +static int tegra186_dspk_put_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dspk->stereo_to_mono) + return 0; + + dspk->stereo_to_mono = value; + + return 1; +} + +static int __maybe_unused tegra186_dspk_runtime_suspend(struct device *dev) +{ + struct tegra186_dspk *dspk = dev_get_drvdata(dev); + + regcache_cache_only(dspk->regmap, true); + regcache_mark_dirty(dspk->regmap); + + clk_disable_unprepare(dspk->clk_dspk); + + return 0; +} + +static int __maybe_unused tegra186_dspk_runtime_resume(struct device *dev) +{ + struct tegra186_dspk *dspk = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(dspk->clk_dspk); + if (err) { + dev_err(dev, "failed to enable DSPK clock, err: %d\n", err); + return err; + } + + regcache_cache_only(dspk->regmap, false); + regcache_sync(dspk->regmap); + + return 0; +} + +static const unsigned int tegra186_dspk_fmts[] = { + 0, + TEGRA_ACIF_BITS_16, + TEGRA_ACIF_BITS_32, +}; + +static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra186_dspk *dspk = snd_soc_dai_get_drvdata(dai); + unsigned int channels, srate, dspk_clk; + struct device *dev = dai->dev; + struct tegra_cif_conf cif_conf; + unsigned int max_th; + int err; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + cif_conf.audio_ch = channels; + + /* Override audio channel */ + if (dspk->audio_ch_override) + cif_conf.audio_ch = dspk->audio_ch_override; + + /* Client channel */ + switch (dspk->ch_sel) { + case DSPK_CH_SELECT_LEFT: + case DSPK_CH_SELECT_RIGHT: + cif_conf.client_ch = 1; + break; + case DSPK_CH_SELECT_STEREO: + cif_conf.client_ch = 2; + break; + default: + dev_err(dev, "Invalid DSPK client channels\n"); + return -EINVAL; + } + + cif_conf.client_bits = TEGRA_ACIF_BITS_24; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + cif_conf.client_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + dev_err(dev, "unsupported format!\n"); + return -ENOTSUPP; + } + + /* Audio bit format override */ + if (dspk->audio_fmt_override) + cif_conf.audio_bits = + tegra186_dspk_fmts[dspk->audio_fmt_override]; + + srate = params_rate(params); + /* Sample rate override */ + if (dspk->srate_override) + srate = dspk->srate_override; + + /* RX FIFO threshold in terms of frames */ + max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1; + + if (dspk->rx_fifo_th > max_th) + dspk->rx_fifo_th = max_th; + + cif_conf.threshold = dspk->rx_fifo_th; + cif_conf.mono_conv = dspk->mono_to_stereo; + cif_conf.stereo_conv = dspk->stereo_to_mono; + + tegra_set_cif(dspk->regmap, TEGRA186_DSPK_RX_CIF_CTRL, + &cif_conf); + + /* + * DSPK clock and PDM codec clock should be synchronous with 4:1 ratio, + * this is because it takes 4 clock cycles to send out one sample to + * codec by sigma delta modulator. Finally the clock rate is a multiple + * of 'Over Sampling Ratio', 'Sample Rate' and 'Interface Clock Ratio'. + */ + dspk_clk = (DSPK_OSR_FACTOR << dspk->osr_val) * srate * DSPK_CLK_RATIO; + + err = clk_set_rate(dspk->clk_dspk, dspk_clk); + if (err) { + dev_err(dev, "can't set DSPK clock rate %u, err: %d\n", + dspk_clk, err); + + return err; + } + + regmap_update_bits(dspk->regmap, + /* Reg */ + TEGRA186_DSPK_CORE_CTRL, + /* Mask */ + TEGRA186_DSPK_OSR_MASK | + TEGRA186_DSPK_CHANNEL_SELECT_MASK | + TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK, + /* Value */ + (dspk->osr_val << DSPK_OSR_SHIFT) | + ((dspk->ch_sel + 1) << CH_SEL_SHIFT) | + (dspk->lrsel << LRSEL_POL_SHIFT)); + + return 0; +} + +static const struct snd_soc_dai_ops tegra186_dspk_dai_ops = { + .hw_params = tegra186_dspk_hw_params, +}; + +/* + * Three DAIs are exposed + * 1. "CIF" DAI for connecting with XBAR + * 2. "DAP" DAI for connecting with CODEC + * 3. "DUMMY_SINK" can be used when no external + * codec connection is available. In such case + * "DAP" is connected with "DUMMY_SINK" + * Order of these DAIs should not be changed, since DAI links in DT refer + * to these DAIs depending on the index. + */ +static struct snd_soc_dai_driver tegra186_dspk_dais[] = { + { + .name = "DSPK-CIF", + .playback = { + .stream_name = "CIF-Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, + { + .name = "DSPK-DAP", +#if IS_ENABLED(CONFIG_TEGRA_DPCM) + .playback = { + .stream_name = "DAP-Playback", +#else + .capture = { + .stream_name = "DAP-Capture", +#endif + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra186_dspk_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "DUMMY_SINK", + .playback = { + .stream_name = "Dummy-Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, +}; + +static const struct snd_soc_dapm_widget tegra186_dspk_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0), + SND_SOC_DAPM_SPK("SPK", NULL), +}; + +static const struct snd_soc_dapm_route tegra186_dspk_routes[] = { +#if IS_ENABLED(CONFIG_TEGRA_DPCM) + { "XBAR-Playback", NULL, "XBAR-TX" }, + { "CIF-Playback", NULL, "XBAR-Playback" }, + { "RX", NULL, "CIF-Playback" }, + { "DAP-Playback", NULL, "RX" }, + { "SPK", NULL, "DAP-Playback" }, +#else + { "RX", NULL, "CIF-Playback" }, + { "DAP-Capture", NULL, "RX" }, + { "SPK", NULL, "Dummy-Playback" }, +#endif +}; + +static const char * const tegra186_dspk_format_text[] = { + "None", + "16", + "32", +}; + +static const struct soc_enum tegra186_dspk_format_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_format_text), + tegra186_dspk_format_text); + +static const char * const tegra186_dspk_ch_sel_text[] = { + "Left", "Right", "Stereo", +}; + +static const struct soc_enum tegra186_dspk_ch_sel_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_ch_sel_text), + tegra186_dspk_ch_sel_text); + +static const char * const tegra186_dspk_osr_text[] = { + "OSR_32", "OSR_64", "OSR_128", "OSR_256", +}; + +static const struct soc_enum tegra186_dspk_osr_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_osr_text), + tegra186_dspk_osr_text); + +static const char * const tegra186_dspk_lrsel_text[] = { + "Left", "Right", +}; + +static const char * const tegra186_dspk_mono_conv_text[] = { + "Zero", "Copy", +}; + +static const struct soc_enum tegra186_dspk_mono_conv_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(tegra186_dspk_mono_conv_text), + tegra186_dspk_mono_conv_text); + +static const char * const tegra186_dspk_stereo_conv_text[] = { + "CH0", "CH1", "AVG", +}; + +static const struct soc_enum tegra186_dspk_stereo_conv_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(tegra186_dspk_stereo_conv_text), + tegra186_dspk_stereo_conv_text); + +static const struct soc_enum tegra186_dspk_lrsel_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_lrsel_text), + tegra186_dspk_lrsel_text); + +static const struct snd_kcontrol_new tegrat186_dspk_controls[] = { + SOC_SINGLE_EXT("FIFO Threshold", SND_SOC_NOPM, 0, + TEGRA186_DSPK_RX_FIFO_DEPTH - 1, 0, + tegra186_dspk_get_fifo_th, tegra186_dspk_put_fifo_th), + SOC_ENUM_EXT("OSR Value", tegra186_dspk_osr_enum, + tegra186_dspk_get_osr_val, tegra186_dspk_put_osr_val), + SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum, + tegra186_dspk_get_pol_sel, tegra186_dspk_put_pol_sel), + SOC_SINGLE_EXT("Sample Rate", SND_SOC_NOPM, 0, 48000, 0, + tegra186_dspk_get_sample_rate, + tegra186_dspk_put_sample_rate), + SOC_SINGLE_EXT("Audio Channels", SND_SOC_NOPM, 0, 2, 0, + tegra186_dspk_get_audio_ch, + tegra186_dspk_put_audio_ch), + SOC_ENUM_EXT("Audio Bit Format", tegra186_dspk_format_enum, + tegra186_dspk_get_audio_bitfmt, + tegra186_dspk_put_audio_bitfmt), + SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum, + tegra186_dspk_get_ch_sel, tegra186_dspk_put_ch_sel), + SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum, + tegra186_dspk_get_mono_to_stereo, + tegra186_dspk_put_mono_to_stereo), + SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum, + tegra186_dspk_get_stereo_to_mono, + tegra186_dspk_put_stereo_to_mono), +}; + +static const struct snd_soc_component_driver tegra186_dspk_cmpnt = { + .dapm_widgets = tegra186_dspk_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra186_dspk_widgets), + .dapm_routes = tegra186_dspk_routes, + .num_dapm_routes = ARRAY_SIZE(tegra186_dspk_routes), + .controls = tegrat186_dspk_controls, + .num_controls = ARRAY_SIZE(tegrat186_dspk_controls), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static bool tegra186_dspk_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA186_DSPK_RX_INT_MASK ... TEGRA186_DSPK_RX_CIF_CTRL: + case TEGRA186_DSPK_ENABLE ... TEGRA186_DSPK_CG: + case TEGRA186_DSPK_CORE_CTRL ... TEGRA186_DSPK_CODEC_CTRL: + return true; + default: + return false; + }; +} + +static bool tegra186_dspk_rd_reg(struct device *dev, unsigned int reg) +{ + if (tegra186_dspk_wr_reg(dev, reg)) + return true; + + switch (reg) { + case TEGRA186_DSPK_RX_STATUS: + case TEGRA186_DSPK_RX_INT_STATUS: + case TEGRA186_DSPK_STATUS: + case TEGRA186_DSPK_INT_STATUS: + return true; + default: + return false; + }; +} + +static bool tegra186_dspk_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA186_DSPK_RX_STATUS: + case TEGRA186_DSPK_RX_INT_STATUS: + case TEGRA186_DSPK_STATUS: + case TEGRA186_DSPK_INT_STATUS: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra186_dspk_regmap = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA186_DSPK_CODEC_CTRL, + .writeable_reg = tegra186_dspk_wr_reg, + .readable_reg = tegra186_dspk_rd_reg, + .volatile_reg = tegra186_dspk_volatile_reg, + .reg_defaults = tegra186_dspk_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra186_dspk_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id tegra186_dspk_of_match[] = { + { .compatible = "nvidia,tegra186-dspk" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra186_dspk_of_match); + +static int tegra186_dspk_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra186_dspk *dspk; + void __iomem *regs; + int err; + + dspk = devm_kzalloc(dev, sizeof(*dspk), GFP_KERNEL); + if (!dspk) + return -ENOMEM; + + dspk->osr_val = DSPK_OSR_64; + dspk->lrsel = DSPK_LRSEL_LEFT; + dspk->ch_sel = DSPK_CH_SELECT_STEREO; + dspk->mono_to_stereo = 0; /* "Zero" */ + + dev_set_drvdata(dev, dspk); + + dspk->clk_dspk = devm_clk_get(dev, "dspk"); + if (IS_ERR(dspk->clk_dspk)) { + dev_err(dev, "can't retrieve DSPK clock\n"); + return PTR_ERR(dspk->clk_dspk); + } + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dspk->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_dspk_regmap); + if (IS_ERR(dspk->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(dspk->regmap); + } + + regcache_cache_only(dspk->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra186_dspk_cmpnt, + tegra186_dspk_dais, + ARRAY_SIZE(tegra186_dspk_dais)); + if (err) { + dev_err(dev, "can't register DSPK component, err: %d\n", + err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra186_dspk_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra186_dspk_pm_ops = { + SET_RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend, + tegra186_dspk_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra186_dspk_driver = { + .driver = { + .name = "tegra186-dspk", + .of_match_table = tegra186_dspk_of_match, + .pm = &tegra186_dspk_pm_ops, + }, + .probe = tegra186_dspk_platform_probe, + .remove = tegra186_dspk_platform_remove, +}; +module_platform_driver(tegra186_dspk_driver); + +MODULE_AUTHOR("Mohan Kumar "); +MODULE_AUTHOR("Sameer Pujar "); +MODULE_DESCRIPTION("Tegra186 ASoC DSPK driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra186_dspk.h b/sound/soc/tegra/tegra186_dspk.h new file mode 100644 index 00000000..38f75505 --- /dev/null +++ b/sound/soc/tegra/tegra186_dspk.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra186_dspk.h - Definitions for Tegra186 DSPK driver + * + * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA186_DSPK_H__ +#define __TEGRA186_DSPK_H__ + +/* Register offsets from DSPK BASE */ +#define TEGRA186_DSPK_RX_STATUS 0x0c +#define TEGRA186_DSPK_RX_INT_STATUS 0x10 +#define TEGRA186_DSPK_RX_INT_MASK 0x14 +#define TEGRA186_DSPK_RX_INT_SET 0x18 +#define TEGRA186_DSPK_RX_INT_CLEAR 0x1c +#define TEGRA186_DSPK_RX_CIF_CTRL 0x20 +#define TEGRA186_DSPK_ENABLE 0x40 +#define TEGRA186_DSPK_SOFT_RESET 0x44 +#define TEGRA186_DSPK_CG 0x48 +#define TEGRA186_DSPK_STATUS 0x4c +#define TEGRA186_DSPK_INT_STATUS 0x50 +#define TEGRA186_DSPK_CORE_CTRL 0x60 +#define TEGRA186_DSPK_CODEC_CTRL 0x64 + +/* DSPK CORE CONTROL fields */ +#define CH_SEL_SHIFT 8 +#define TEGRA186_DSPK_CHANNEL_SELECT_MASK (0x3 << CH_SEL_SHIFT) +#define DSPK_OSR_SHIFT 4 +#define TEGRA186_DSPK_OSR_MASK (0x3 << DSPK_OSR_SHIFT) +#define LRSEL_POL_SHIFT 0 +#define TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK (0x1 << LRSEL_POL_SHIFT) +#define TEGRA186_DSPK_RX_FIFO_DEPTH 64 + +#define DSPK_OSR_FACTOR 32 + +/* DSPK interface clock ratio */ +#define DSPK_CLK_RATIO 4 + +enum tegra_dspk_osr { + DSPK_OSR_32, + DSPK_OSR_64, + DSPK_OSR_128, + DSPK_OSR_256, +}; + +enum tegra_dspk_ch_sel { + DSPK_CH_SELECT_LEFT, + DSPK_CH_SELECT_RIGHT, + DSPK_CH_SELECT_STEREO, +}; + +enum tegra_dspk_lrsel { + DSPK_LRSEL_LEFT, + DSPK_LRSEL_RIGHT, +}; + +struct tegra186_dspk { + unsigned int rx_fifo_th; + unsigned int osr_val; + unsigned int lrsel; + unsigned int srate_override; + unsigned int audio_ch_override; + unsigned int audio_fmt_override; + unsigned int ch_sel; + unsigned int mono_to_stereo; + unsigned int stereo_to_mono; + struct clk *clk_dspk; + struct regmap *regmap; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c new file mode 100644 index 00000000..155373c0 --- /dev/null +++ b/sound/soc/tegra/tegra210_admaif.c @@ -0,0 +1,1410 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_admaif.c - Tegra ADMAIF driver +// +// Copyright (c) 2020-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tegra210_admaif.h" +#include "tegra_isomgr_bw.h" + +#define CH_REG(offset, reg, id) \ + ((offset) + (reg) + (TEGRA_ADMAIF_CHANNEL_REG_STRIDE * (id))) + +#define CH_TX_REG(reg, id) CH_REG(admaif->soc_data->tx_base, reg, id) + +#define CH_RX_REG(reg, id) CH_REG(admaif->soc_data->rx_base, reg, id) + +#define REG_DEFAULTS(id, rx_ctrl, tx_ctrl, tx_base, rx_base) \ + { CH_REG(rx_base, TEGRA_ADMAIF_RX_INT_MASK, id), 0x00000001 }, \ + { CH_REG(rx_base, TEGRA_ADMAIF_CH_ACIF_RX_CTRL, id), 0x00007700 }, \ + { CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl }, \ + { CH_REG(tx_base, TEGRA_ADMAIF_TX_INT_MASK, id), 0x00000001 }, \ + { CH_REG(tx_base, TEGRA_ADMAIF_CH_ACIF_TX_CTRL, id), 0x00007700 }, \ + { CH_REG(tx_base, TEGRA_ADMAIF_TX_FIFO_CTRL, id), tx_ctrl } + +#define ADMAIF_REG_DEFAULTS(id, chip) \ + REG_DEFAULTS((id) - 1, \ + chip ## _ADMAIF_RX ## id ## _FIFO_CTRL_REG_DEFAULT, \ + chip ## _ADMAIF_TX ## id ## _FIFO_CTRL_REG_DEFAULT, \ + chip ## _ADMAIF_TX_BASE, \ + chip ## _ADMAIF_RX_BASE) + +static const struct reg_default tegra186_admaif_reg_defaults[] = { + {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003}, + ADMAIF_REG_DEFAULTS(1, TEGRA186), + ADMAIF_REG_DEFAULTS(2, TEGRA186), + ADMAIF_REG_DEFAULTS(3, TEGRA186), + ADMAIF_REG_DEFAULTS(4, TEGRA186), + ADMAIF_REG_DEFAULTS(5, TEGRA186), + ADMAIF_REG_DEFAULTS(6, TEGRA186), + ADMAIF_REG_DEFAULTS(7, TEGRA186), + ADMAIF_REG_DEFAULTS(8, TEGRA186), + ADMAIF_REG_DEFAULTS(9, TEGRA186), + ADMAIF_REG_DEFAULTS(10, TEGRA186), + ADMAIF_REG_DEFAULTS(11, TEGRA186), + ADMAIF_REG_DEFAULTS(12, TEGRA186), + ADMAIF_REG_DEFAULTS(13, TEGRA186), + ADMAIF_REG_DEFAULTS(14, TEGRA186), + ADMAIF_REG_DEFAULTS(15, TEGRA186), + ADMAIF_REG_DEFAULTS(16, TEGRA186), + ADMAIF_REG_DEFAULTS(17, TEGRA186), + ADMAIF_REG_DEFAULTS(18, TEGRA186), + ADMAIF_REG_DEFAULTS(19, TEGRA186), + ADMAIF_REG_DEFAULTS(20, TEGRA186) +}; + +static const struct reg_default tegra210_admaif_reg_defaults[] = { + {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA210_ADMAIF_GLOBAL_BASE), 0x00000003}, + ADMAIF_REG_DEFAULTS(1, TEGRA210), + ADMAIF_REG_DEFAULTS(2, TEGRA210), + ADMAIF_REG_DEFAULTS(3, TEGRA210), + ADMAIF_REG_DEFAULTS(4, TEGRA210), + ADMAIF_REG_DEFAULTS(5, TEGRA210), + ADMAIF_REG_DEFAULTS(6, TEGRA210), + ADMAIF_REG_DEFAULTS(7, TEGRA210), + ADMAIF_REG_DEFAULTS(8, TEGRA210), + ADMAIF_REG_DEFAULTS(9, TEGRA210), + ADMAIF_REG_DEFAULTS(10, TEGRA210) +}; + +static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE; + unsigned int num_ch = admaif->soc_data->num_ch; + unsigned int rx_base = admaif->soc_data->rx_base; + unsigned int tx_base = admaif->soc_data->tx_base; + unsigned int global_base = admaif->soc_data->global_base; + unsigned int reg_max = admaif->soc_data->regmap_conf->max_register; + unsigned int rx_max = rx_base + (num_ch * ch_stride); + unsigned int tx_max = tx_base + (num_ch * ch_stride); + + if ((reg >= rx_base) && (reg < rx_max)) { + reg = (reg - rx_base) % ch_stride; + if ((reg == TEGRA_ADMAIF_RX_ENABLE) || + (reg == TEGRA_ADMAIF_RX_FIFO_CTRL) || + (reg == TEGRA_ADMAIF_RX_SOFT_RESET) || + (reg == TEGRA_ADMAIF_CH_ACIF_RX_CTRL)) + return true; + } else if ((reg >= tx_base) && (reg < tx_max)) { + reg = (reg - tx_base) % ch_stride; + if ((reg == TEGRA_ADMAIF_TX_ENABLE) || + (reg == TEGRA_ADMAIF_TX_FIFO_CTRL) || + (reg == TEGRA_ADMAIF_TX_SOFT_RESET) || + (reg == TEGRA_ADMAIF_CH_ACIF_TX_CTRL)) + return true; + } else if ((reg >= global_base) && (reg < reg_max)) { + if (reg == (global_base + TEGRA_ADMAIF_GLOBAL_ENABLE)) + return true; + } + + return false; +} + +static bool tegra_admaif_rd_reg(struct device *dev, unsigned int reg) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE; + unsigned int num_ch = admaif->soc_data->num_ch; + unsigned int rx_base = admaif->soc_data->rx_base; + unsigned int tx_base = admaif->soc_data->tx_base; + unsigned int global_base = admaif->soc_data->global_base; + unsigned int reg_max = admaif->soc_data->regmap_conf->max_register; + unsigned int rx_max = rx_base + (num_ch * ch_stride); + unsigned int tx_max = tx_base + (num_ch * ch_stride); + + if ((reg >= rx_base) && (reg < rx_max)) { + reg = (reg - rx_base) % ch_stride; + if ((reg == TEGRA_ADMAIF_RX_ENABLE) || + (reg == TEGRA_ADMAIF_RX_STATUS) || + (reg == TEGRA_ADMAIF_RX_INT_STATUS) || + (reg == TEGRA_ADMAIF_RX_FIFO_CTRL) || + (reg == TEGRA_ADMAIF_RX_SOFT_RESET) || + (reg == TEGRA_ADMAIF_CH_ACIF_RX_CTRL)) + return true; + } else if ((reg >= tx_base) && (reg < tx_max)) { + reg = (reg - tx_base) % ch_stride; + if ((reg == TEGRA_ADMAIF_TX_ENABLE) || + (reg == TEGRA_ADMAIF_TX_STATUS) || + (reg == TEGRA_ADMAIF_TX_INT_STATUS) || + (reg == TEGRA_ADMAIF_TX_FIFO_CTRL) || + (reg == TEGRA_ADMAIF_TX_SOFT_RESET) || + (reg == TEGRA_ADMAIF_CH_ACIF_TX_CTRL)) + return true; + } else if ((reg >= global_base) && (reg < reg_max)) { + if ((reg == (global_base + TEGRA_ADMAIF_GLOBAL_ENABLE)) || + (reg == (global_base + TEGRA_ADMAIF_GLOBAL_CG_0)) || + (reg == (global_base + TEGRA_ADMAIF_GLOBAL_STATUS)) || + (reg == (global_base + + TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS)) || + (reg == (global_base + + TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS))) + return true; + } + + return false; +} + +static bool tegra_admaif_volatile_reg(struct device *dev, unsigned int reg) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE; + unsigned int num_ch = admaif->soc_data->num_ch; + unsigned int rx_base = admaif->soc_data->rx_base; + unsigned int tx_base = admaif->soc_data->tx_base; + unsigned int global_base = admaif->soc_data->global_base; + unsigned int reg_max = admaif->soc_data->regmap_conf->max_register; + unsigned int rx_max = rx_base + (num_ch * ch_stride); + unsigned int tx_max = tx_base + (num_ch * ch_stride); + + if ((reg >= rx_base) && (reg < rx_max)) { + reg = (reg - rx_base) % ch_stride; + if ((reg == TEGRA_ADMAIF_RX_ENABLE) || + (reg == TEGRA_ADMAIF_RX_STATUS) || + (reg == TEGRA_ADMAIF_RX_INT_STATUS) || + (reg == TEGRA_ADMAIF_RX_SOFT_RESET)) + return true; + } else if ((reg >= tx_base) && (reg < tx_max)) { + reg = (reg - tx_base) % ch_stride; + if ((reg == TEGRA_ADMAIF_TX_ENABLE) || + (reg == TEGRA_ADMAIF_TX_STATUS) || + (reg == TEGRA_ADMAIF_TX_INT_STATUS) || + (reg == TEGRA_ADMAIF_TX_SOFT_RESET)) + return true; + } else if ((reg >= global_base) && (reg < reg_max)) { + if ((reg == (global_base + TEGRA_ADMAIF_GLOBAL_STATUS)) || + (reg == (global_base + + TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS)) || + (reg == (global_base + + TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS))) + return true; + } + + return false; +} + +static const struct regmap_config tegra210_admaif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_ADMAIF_LAST_REG, + .writeable_reg = tegra_admaif_wr_reg, + .readable_reg = tegra_admaif_rd_reg, + .volatile_reg = tegra_admaif_volatile_reg, + .reg_defaults = tegra210_admaif_reg_defaults, + .num_reg_defaults = TEGRA210_ADMAIF_CHANNEL_COUNT * 6 + 1, + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_config tegra186_admaif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA186_ADMAIF_LAST_REG, + .writeable_reg = tegra_admaif_wr_reg, + .readable_reg = tegra_admaif_rd_reg, + .volatile_reg = tegra_admaif_volatile_reg, + .reg_defaults = tegra186_admaif_reg_defaults, + .num_reg_defaults = TEGRA186_ADMAIF_CHANNEL_COUNT * 6 + 1, + .cache_type = REGCACHE_FLAT, +}; + +static int __maybe_unused tegra_admaif_runtime_suspend(struct device *dev) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + + regcache_cache_only(admaif->regmap, true); + regcache_mark_dirty(admaif->regmap); + + return 0; +} + +static int __maybe_unused tegra_admaif_runtime_resume(struct device *dev) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + + regcache_cache_only(admaif->regmap, false); + regcache_sync(admaif->regmap); + + return 0; +} + +static int tegra_admaif_set_pack_mode(struct regmap *map, unsigned int reg, + int valid_bit) +{ + switch (valid_bit) { + case DATA_8BIT: + regmap_update_bits(map, reg, PACK8_EN_MASK, PACK8_EN); + regmap_update_bits(map, reg, PACK16_EN_MASK, 0); + break; + case DATA_16BIT: + regmap_update_bits(map, reg, PACK16_EN_MASK, PACK16_EN); + regmap_update_bits(map, reg, PACK8_EN_MASK, 0); + break; + case DATA_32BIT: + regmap_update_bits(map, reg, PACK16_EN_MASK, 0); + regmap_update_bits(map, reg, PACK8_EN_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra_admaif_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + tegra_isomgr_adma_setbw(substream, true); + + return 0; +} + +static void tegra_admaif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + tegra_isomgr_adma_setbw(substream, false); +} + +static int tegra_admaif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->dev; + struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); + struct tegra_cif_conf cif_conf; + unsigned int reg, path; + int valid_bit, channels; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + cif_conf.audio_bits = TEGRA_ACIF_BITS_8; + cif_conf.client_bits = TEGRA_ACIF_BITS_8; + valid_bit = DATA_8BIT; + break; + case SNDRV_PCM_FORMAT_S16_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + cif_conf.client_bits = TEGRA_ACIF_BITS_16; + valid_bit = DATA_16BIT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + cif_conf.client_bits = TEGRA_ACIF_BITS_24; + valid_bit = DATA_32BIT; + break; + case SNDRV_PCM_FORMAT_S32_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + cif_conf.client_bits = TEGRA_ACIF_BITS_32; + valid_bit = DATA_32BIT; + break; + default: + dev_err(dev, "unsupported format!\n"); + return -EOPNOTSUPP; + } + + channels = params_channels(params); + cif_conf.client_ch = channels; + cif_conf.audio_ch = channels; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + path = ADMAIF_TX_PATH; + reg = CH_TX_REG(TEGRA_ADMAIF_CH_ACIF_TX_CTRL, dai->id); + } else { + path = ADMAIF_RX_PATH; + reg = CH_RX_REG(TEGRA_ADMAIF_CH_ACIF_RX_CTRL, dai->id); + } + + if (admaif->audio_ch_override[path][dai->id]) + cif_conf.audio_ch = admaif->audio_ch_override[path][dai->id]; + + if (admaif->client_ch_override[path][dai->id]) + cif_conf.client_ch = admaif->client_ch_override[path][dai->id]; + + cif_conf.mono_conv = admaif->mono_to_stereo[path][dai->id]; + cif_conf.stereo_conv = admaif->stereo_to_mono[path][dai->id]; + + tegra_admaif_set_pack_mode(admaif->regmap, reg, valid_bit); + + tegra_set_cif(admaif->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra_admaif_start(struct snd_soc_dai *dai, int direction) +{ + struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); + unsigned int reg, mask, val; + + switch (direction) { + case SNDRV_PCM_STREAM_PLAYBACK: + mask = TX_ENABLE_MASK; + val = TX_ENABLE; + reg = CH_TX_REG(TEGRA_ADMAIF_TX_ENABLE, dai->id); + break; + case SNDRV_PCM_STREAM_CAPTURE: + mask = RX_ENABLE_MASK; + val = RX_ENABLE; + reg = CH_RX_REG(TEGRA_ADMAIF_RX_ENABLE, dai->id); + break; + default: + return -EINVAL; + } + + regmap_update_bits(admaif->regmap, reg, mask, val); + + return 0; +} + +static int tegra_admaif_stop(struct snd_soc_dai *dai, int direction) +{ + struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); + unsigned int enable_reg, status_reg, reset_reg, mask, val; + char *dir_name; + int err, enable; + + switch (direction) { + case SNDRV_PCM_STREAM_PLAYBACK: + mask = TX_ENABLE_MASK; + enable = TX_ENABLE; + dir_name = "TX"; + enable_reg = CH_TX_REG(TEGRA_ADMAIF_TX_ENABLE, dai->id); + status_reg = CH_TX_REG(TEGRA_ADMAIF_TX_STATUS, dai->id); + reset_reg = CH_TX_REG(TEGRA_ADMAIF_TX_SOFT_RESET, dai->id); + break; + case SNDRV_PCM_STREAM_CAPTURE: + mask = RX_ENABLE_MASK; + enable = RX_ENABLE; + dir_name = "RX"; + enable_reg = CH_RX_REG(TEGRA_ADMAIF_RX_ENABLE, dai->id); + status_reg = CH_RX_REG(TEGRA_ADMAIF_RX_STATUS, dai->id); + reset_reg = CH_RX_REG(TEGRA_ADMAIF_RX_SOFT_RESET, dai->id); + break; + default: + return -EINVAL; + } + + /* Disable TX/RX channel */ + regmap_update_bits(admaif->regmap, enable_reg, mask, ~enable); + + /* Wait until ADMAIF TX/RX status is disabled */ + err = regmap_read_poll_timeout_atomic(admaif->regmap, status_reg, val, + !(val & enable), 10, 10000); + if (err < 0) + dev_warn(dai->dev, "timeout: failed to disable ADMAIF%d_%s\n", + dai->id + 1, dir_name); + + /* SW reset */ + regmap_update_bits(admaif->regmap, reset_reg, SW_RESET_MASK, SW_RESET); + + /* Wait till SW reset is complete */ + err = regmap_read_poll_timeout_atomic(admaif->regmap, reset_reg, val, + !(val & SW_RESET_MASK & SW_RESET), + 10, 10000); + if (err) { + dev_err(dai->dev, "timeout: SW reset failed for ADMAIF%d_%s\n", + dai->id + 1, dir_name); + return err; + } + + return 0; +} + +static int tegra_admaif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int err; + + err = snd_dmaengine_pcm_trigger(substream, cmd); + if (err) + return err; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + return tegra_admaif_start(dai, substream->stream); + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + return tegra_admaif_stop(dai, substream->stream); + default: + return -EINVAL; + } +} + +static void tegra_admaif_reg_dump(struct device *dev) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + int tx_offset = admaif->soc_data->tx_base; + int i, stride, ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "parent get_sync failed: %d\n", ret); + return; + } + + dev_info(dev, "=========ADMAIF reg dump=========\n"); + + for (i = 0; i < admaif->soc_data->num_ch; i++) { + stride = (i * TEGRA_ADMAIF_CHANNEL_REG_STRIDE); + + dev_info(dev, "RX%d_Enable = %#x\n", i + 1, + readl(admaif->base_addr + + TEGRA_ADMAIF_RX_ENABLE + stride)); + + dev_info(dev, "RX%d_STATUS = %#x\n", i + 1, + readl(admaif->base_addr + + TEGRA_ADMAIF_RX_STATUS + stride)); + + dev_info(dev, "RX%d_CIF_CTRL = %#x\n", i + 1, + readl(admaif->base_addr + + TEGRA_ADMAIF_CH_ACIF_RX_CTRL + stride)); + + dev_info(dev, "RX%d_FIFO_CTRL = %#x\n", i + 1, + readl(admaif->base_addr + + TEGRA_ADMAIF_RX_FIFO_CTRL + stride)); + + dev_info(dev, "TX%d_Enable = %#x\n", i + 1, + readl(admaif->base_addr + tx_offset + + TEGRA_ADMAIF_TX_ENABLE + stride)); + + dev_info(dev, "TX%d_STATUS = %#x\n", i + 1, + readl(admaif->base_addr + tx_offset + + TEGRA_ADMAIF_TX_STATUS + stride)); + + dev_info(dev, "TX%d_CIF_CTRL = %#x\n", i + 1, + readl(admaif->base_addr + tx_offset + + TEGRA_ADMAIF_CH_ACIF_TX_CTRL + stride)); + + dev_info(dev, "TX%d_FIFO_CTRL = %#x\n", i + 1, + readl(admaif->base_addr + tx_offset + + TEGRA_ADMAIF_TX_FIFO_CTRL + stride)); + } + + pm_runtime_put_sync(dev); +} + +static int tegra210_admaif_pget_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = + admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg]; + + return 0; +} + +static int tegra210_admaif_pput_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value = ucontrol->value.integer.value[0]; + + if (value == admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg]) + return 0; + + admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg] = value; + + return 1; +} + +static int tegra210_admaif_cget_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = + admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg]; + + return 0; +} + +static int tegra210_admaif_cput_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value = ucontrol->value.integer.value[0]; + + if (admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg] == value) + return 0; + + admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg] = value; + + return 1; +} + +static int tegra210_admaif_pget_client_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = + admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg]; + + return 0; +} + +static int tegra210_admaif_pput_client_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value = ucontrol->value.integer.value[0]; + + if (admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg] == value) + return 0; + + admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg] = value; + + return 1; +} + +static int tegra210_admaif_cget_client_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = + admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg]; + + return 0; +} + +static int tegra210_admaif_cput_client_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value = ucontrol->value.integer.value[0]; + + if (admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg] == value) + return 0; + + admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg] = value; + + return 1; +} + +static int tegra210_admaif_get_reg_dump(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + + ucontrol->value.integer.value[0] = admaif->reg_dump_flag; + + return 0; +} + +static int tegra210_admaif_put_reg_dump(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + int value = ucontrol->value.integer.value[0]; + + if (admaif->reg_dump_flag == value) + return 0; + + admaif->reg_dump_flag = ucontrol->value.integer.value[0]; + + if (admaif->reg_dump_flag) { + /* + * Below call is implemented by downstream ADMA driver. + * For Kernel OOT this causes build errors since upstream + * ADMA driver is used there. Disable below call for now + * until future for this is decided (Bug 3798682). + */ +//#if IS_ENABLED(CONFIG_TEGRA210_ADMA) +// tegra_adma_dump_ch_reg(); +//#endif + + tegra_admaif_reg_dump(cmpnt->dev); + } + + return 1; +} + +static int tegra210_admaif_pget_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + + ucontrol->value.enumerated.item[0] = + admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg]; + + return 0; +} + +static int tegra210_admaif_pput_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg]) + return 0; + + admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg] = value; + + return 1; +} + +static int tegra210_admaif_cget_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + + ucontrol->value.enumerated.item[0] = + admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg]; + + return 0; +} + +static int tegra210_admaif_cput_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg]) + return 0; + + admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg] = value; + + return 1; +} + +static int tegra210_admaif_pget_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + + ucontrol->value.enumerated.item[0] = + admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg]; + + return 0; +} + +static int tegra210_admaif_pput_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg]) + return 0; + + admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg] = value; + + return 1; +} + +static int tegra210_admaif_cget_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + + ucontrol->value.enumerated.item[0] = + admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg]; + + return 0; +} + +static int tegra210_admaif_cput_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg]) + return 0; + + admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg] = value; + + return 1; +} + +static int tegra_admaif_dai_probe(struct snd_soc_dai *dai) +{ + struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &admaif->playback_dma_data[dai->id], + &admaif->capture_dma_data[dai->id]); + + return 0; +} + +static const struct snd_soc_dai_ops tegra_admaif_dai_ops = { +#if defined(NV_SND_SOC_DAI_OPS_STRUCT_HAS_PROBE_PRESENT) /* Linux 6.5 */ + .probe = tegra_admaif_dai_probe, +#endif + .hw_params = tegra_admaif_hw_params, + .trigger = tegra_admaif_trigger, + .shutdown = tegra_admaif_shutdown, + .prepare = tegra_admaif_prepare, +}; + +#if defined(NV_SND_SOC_DAI_OPS_STRUCT_HAS_PROBE_PRESENT) /* Linux 6.5 */ +#define DAI(dai_name) \ + { \ + .name = dai_name, \ + .playback = { \ + .stream_name = dai_name " Playback", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = dai_name " Capture", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra_admaif_dai_ops, \ + } +#else +#define DAI(dai_name) \ + { \ + .name = dai_name, \ + .probe = tegra_admaif_dai_probe, \ + .playback = { \ + .stream_name = dai_name " Playback", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = dai_name " Capture", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra_admaif_dai_ops, \ + } +#endif + + +#define ADMAIF_CODEC_FIFO_DAI(id) \ + { \ + .name = "ADMAIF" #id " FIFO", \ + .playback = { \ + .stream_name = "ADMAIF" #id " FIFO Transmit", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "ADMAIF" #id " FIFO Receive", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra_admaif_dai_ops, \ + } + +#define ADMAIF_CODEC_CIF_DAI(id) \ + { \ + .name = "ADMAIF" #id " CIF", \ + .playback = { \ + .stream_name = "ADMAIF" #id " CIF Transmit", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "ADMAIF" #id " CIF Receive", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + } + +static struct snd_soc_dai_driver tegra210_admaif_cmpnt_dais[] = { + DAI("ADMAIF1"), + DAI("ADMAIF2"), + DAI("ADMAIF3"), + DAI("ADMAIF4"), + DAI("ADMAIF5"), + DAI("ADMAIF6"), + DAI("ADMAIF7"), + DAI("ADMAIF8"), + DAI("ADMAIF9"), + DAI("ADMAIF10"), + ADMAIF_CODEC_FIFO_DAI(1), + ADMAIF_CODEC_FIFO_DAI(2), + ADMAIF_CODEC_FIFO_DAI(3), + ADMAIF_CODEC_FIFO_DAI(4), + ADMAIF_CODEC_FIFO_DAI(5), + ADMAIF_CODEC_FIFO_DAI(6), + ADMAIF_CODEC_FIFO_DAI(7), + ADMAIF_CODEC_FIFO_DAI(8), + ADMAIF_CODEC_FIFO_DAI(9), + ADMAIF_CODEC_FIFO_DAI(10), + ADMAIF_CODEC_CIF_DAI(1), + ADMAIF_CODEC_CIF_DAI(2), + ADMAIF_CODEC_CIF_DAI(3), + ADMAIF_CODEC_CIF_DAI(4), + ADMAIF_CODEC_CIF_DAI(5), + ADMAIF_CODEC_CIF_DAI(6), + ADMAIF_CODEC_CIF_DAI(7), + ADMAIF_CODEC_CIF_DAI(8), + ADMAIF_CODEC_CIF_DAI(9), + ADMAIF_CODEC_CIF_DAI(10), +}; + +static struct snd_soc_dai_driver tegra186_admaif_cmpnt_dais[] = { + DAI("ADMAIF1"), + DAI("ADMAIF2"), + DAI("ADMAIF3"), + DAI("ADMAIF4"), + DAI("ADMAIF5"), + DAI("ADMAIF6"), + DAI("ADMAIF7"), + DAI("ADMAIF8"), + DAI("ADMAIF9"), + DAI("ADMAIF10"), + DAI("ADMAIF11"), + DAI("ADMAIF12"), + DAI("ADMAIF13"), + DAI("ADMAIF14"), + DAI("ADMAIF15"), + DAI("ADMAIF16"), + DAI("ADMAIF17"), + DAI("ADMAIF18"), + DAI("ADMAIF19"), + DAI("ADMAIF20"), + ADMAIF_CODEC_FIFO_DAI(1), + ADMAIF_CODEC_FIFO_DAI(2), + ADMAIF_CODEC_FIFO_DAI(3), + ADMAIF_CODEC_FIFO_DAI(4), + ADMAIF_CODEC_FIFO_DAI(5), + ADMAIF_CODEC_FIFO_DAI(6), + ADMAIF_CODEC_FIFO_DAI(7), + ADMAIF_CODEC_FIFO_DAI(8), + ADMAIF_CODEC_FIFO_DAI(9), + ADMAIF_CODEC_FIFO_DAI(10), + ADMAIF_CODEC_FIFO_DAI(11), + ADMAIF_CODEC_FIFO_DAI(12), + ADMAIF_CODEC_FIFO_DAI(13), + ADMAIF_CODEC_FIFO_DAI(14), + ADMAIF_CODEC_FIFO_DAI(15), + ADMAIF_CODEC_FIFO_DAI(16), + ADMAIF_CODEC_FIFO_DAI(17), + ADMAIF_CODEC_FIFO_DAI(18), + ADMAIF_CODEC_FIFO_DAI(19), + ADMAIF_CODEC_FIFO_DAI(20), + ADMAIF_CODEC_CIF_DAI(1), + ADMAIF_CODEC_CIF_DAI(2), + ADMAIF_CODEC_CIF_DAI(3), + ADMAIF_CODEC_CIF_DAI(4), + ADMAIF_CODEC_CIF_DAI(5), + ADMAIF_CODEC_CIF_DAI(6), + ADMAIF_CODEC_CIF_DAI(7), + ADMAIF_CODEC_CIF_DAI(8), + ADMAIF_CODEC_CIF_DAI(9), + ADMAIF_CODEC_CIF_DAI(10), + ADMAIF_CODEC_CIF_DAI(11), + ADMAIF_CODEC_CIF_DAI(12), + ADMAIF_CODEC_CIF_DAI(13), + ADMAIF_CODEC_CIF_DAI(14), + ADMAIF_CODEC_CIF_DAI(15), + ADMAIF_CODEC_CIF_DAI(16), + ADMAIF_CODEC_CIF_DAI(17), + ADMAIF_CODEC_CIF_DAI(18), + ADMAIF_CODEC_CIF_DAI(19), + ADMAIF_CODEC_CIF_DAI(20), +}; + +#define ADMAIF_WIDGETS(id) \ + SND_SOC_DAPM_AIF_IN("ADMAIF" #id " FIFO RX", NULL, 0, \ + SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_AIF_OUT("ADMAIF" #id " FIFO TX", NULL, 0, \ + SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_AIF_IN("ADMAIF" #id " CIF RX", NULL, 0, \ + SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_AIF_OUT("ADMAIF" #id " CIF TX", NULL, 0, \ + SND_SOC_NOPM, 0, 0) + +static const struct snd_soc_dapm_widget tegra_admaif_widgets[] = { + ADMAIF_WIDGETS(1), + ADMAIF_WIDGETS(2), + ADMAIF_WIDGETS(3), + ADMAIF_WIDGETS(4), + ADMAIF_WIDGETS(5), + ADMAIF_WIDGETS(6), + ADMAIF_WIDGETS(7), + ADMAIF_WIDGETS(8), + ADMAIF_WIDGETS(9), + ADMAIF_WIDGETS(10), + ADMAIF_WIDGETS(11), + ADMAIF_WIDGETS(12), + ADMAIF_WIDGETS(13), + ADMAIF_WIDGETS(14), + ADMAIF_WIDGETS(15), + ADMAIF_WIDGETS(16), + ADMAIF_WIDGETS(17), + ADMAIF_WIDGETS(18), + ADMAIF_WIDGETS(19), + ADMAIF_WIDGETS(20) +}; + +#define ADMAIF_ROUTES(id) \ + { "ADMAIF" #id " FIFO RX", NULL, "ADMAIF" #id " FIFO Transmit" }, \ + { "ADMAIF" #id " CIF TX", NULL, "ADMAIF" #id " FIFO RX" }, \ + { "ADMAIF" #id " CIF Receive", NULL, "ADMAIF" #id " CIF TX" }, \ + { "ADMAIF" #id " CIF RX", NULL, "ADMAIF" #id " CIF Transmit" }, \ + { "ADMAIF" #id " FIFO TX", NULL, "ADMAIF" #id " CIF RX" }, \ + { "ADMAIF" #id " FIFO Receive", NULL, "ADMAIF" #id " FIFO TX" } \ + +static const struct snd_soc_dapm_route tegra_admaif_routes[] = { + ADMAIF_ROUTES(1), + ADMAIF_ROUTES(2), + ADMAIF_ROUTES(3), + ADMAIF_ROUTES(4), + ADMAIF_ROUTES(5), + ADMAIF_ROUTES(6), + ADMAIF_ROUTES(7), + ADMAIF_ROUTES(8), + ADMAIF_ROUTES(9), + ADMAIF_ROUTES(10), + ADMAIF_ROUTES(11), + ADMAIF_ROUTES(12), + ADMAIF_ROUTES(13), + ADMAIF_ROUTES(14), + ADMAIF_ROUTES(15), + ADMAIF_ROUTES(16), + ADMAIF_ROUTES(17), + ADMAIF_ROUTES(18), + ADMAIF_ROUTES(19), + ADMAIF_ROUTES(20) +}; + +static const char * const tegra_admaif_stereo_conv_text[] = { + "CH0", "CH1", "AVG", +}; + +static const char * const tegra_admaif_mono_conv_text[] = { + "Zero", "Copy", +}; + +#define TEGRA_ADMAIF_CHANNEL_CTRL(reg) \ + SOC_SINGLE_EXT("ADMAIF" #reg " Playback Audio Channels", reg - 1, \ + 0, 16, 0, tegra210_admaif_pget_audio_ch, \ + tegra210_admaif_pput_audio_ch), \ + SOC_SINGLE_EXT("ADMAIF" #reg " Capture Audio Channels", reg - 1, \ + 0, 16, 0, tegra210_admaif_cget_audio_ch, \ + tegra210_admaif_cput_audio_ch), \ + SOC_SINGLE_EXT("ADMAIF" #reg " Playback Client Channels", reg - 1,\ + 0, 16, 0, tegra210_admaif_pget_client_ch, \ + tegra210_admaif_pput_client_ch), \ + SOC_SINGLE_EXT("ADMAIF" #reg " Capture Client Channels", reg - 1, \ + 0, 16, 0, tegra210_admaif_cget_client_ch, \ + tegra210_admaif_cput_client_ch) + +/* + * Below macro is added to avoid looping over all ADMAIFx controls related + * to mono/stereo conversions in get()/put() callbacks. + */ +#define NV_SOC_ENUM_EXT(xname, xreg, xhandler_get, xhandler_put, xenum_text) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .info = snd_soc_info_enum_double, \ + .name = xname, \ + .get = xhandler_get, \ + .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_enum) \ + SOC_ENUM_SINGLE(xreg, 0, ARRAY_SIZE(xenum_text), xenum_text) \ +} + +#define TEGRA_ADMAIF_CIF_CTRL(reg) \ + NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback Mono To Stereo", reg - 1, \ + tegra210_admaif_pget_mono_to_stereo, \ + tegra210_admaif_pput_mono_to_stereo, \ + tegra_admaif_mono_conv_text), \ + NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback Stereo To Mono", reg - 1, \ + tegra210_admaif_pget_stereo_to_mono, \ + tegra210_admaif_pput_stereo_to_mono, \ + tegra_admaif_stereo_conv_text), \ + NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture Mono To Stereo", reg - 1, \ + tegra210_admaif_cget_mono_to_stereo, \ + tegra210_admaif_cput_mono_to_stereo, \ + tegra_admaif_mono_conv_text), \ + NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture Stereo To Mono", reg - 1, \ + tegra210_admaif_cget_stereo_to_mono, \ + tegra210_admaif_cput_stereo_to_mono, \ + tegra_admaif_stereo_conv_text) + +static struct snd_kcontrol_new tegra210_admaif_controls[] = { + TEGRA_ADMAIF_CHANNEL_CTRL(1), + TEGRA_ADMAIF_CHANNEL_CTRL(2), + TEGRA_ADMAIF_CHANNEL_CTRL(3), + TEGRA_ADMAIF_CHANNEL_CTRL(4), + TEGRA_ADMAIF_CHANNEL_CTRL(5), + TEGRA_ADMAIF_CHANNEL_CTRL(6), + TEGRA_ADMAIF_CHANNEL_CTRL(7), + TEGRA_ADMAIF_CHANNEL_CTRL(8), + TEGRA_ADMAIF_CHANNEL_CTRL(9), + TEGRA_ADMAIF_CHANNEL_CTRL(10), + TEGRA_ADMAIF_CIF_CTRL(1), + TEGRA_ADMAIF_CIF_CTRL(2), + TEGRA_ADMAIF_CIF_CTRL(3), + TEGRA_ADMAIF_CIF_CTRL(4), + TEGRA_ADMAIF_CIF_CTRL(5), + TEGRA_ADMAIF_CIF_CTRL(6), + TEGRA_ADMAIF_CIF_CTRL(7), + TEGRA_ADMAIF_CIF_CTRL(8), + TEGRA_ADMAIF_CIF_CTRL(9), + TEGRA_ADMAIF_CIF_CTRL(10), + SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0, + tegra210_admaif_get_reg_dump, + tegra210_admaif_put_reg_dump), +}; + +static struct snd_kcontrol_new tegra186_admaif_controls[] = { + TEGRA_ADMAIF_CHANNEL_CTRL(1), + TEGRA_ADMAIF_CHANNEL_CTRL(2), + TEGRA_ADMAIF_CHANNEL_CTRL(3), + TEGRA_ADMAIF_CHANNEL_CTRL(4), + TEGRA_ADMAIF_CHANNEL_CTRL(5), + TEGRA_ADMAIF_CHANNEL_CTRL(6), + TEGRA_ADMAIF_CHANNEL_CTRL(7), + TEGRA_ADMAIF_CHANNEL_CTRL(8), + TEGRA_ADMAIF_CHANNEL_CTRL(9), + TEGRA_ADMAIF_CHANNEL_CTRL(10), + TEGRA_ADMAIF_CHANNEL_CTRL(11), + TEGRA_ADMAIF_CHANNEL_CTRL(12), + TEGRA_ADMAIF_CHANNEL_CTRL(13), + TEGRA_ADMAIF_CHANNEL_CTRL(14), + TEGRA_ADMAIF_CHANNEL_CTRL(15), + TEGRA_ADMAIF_CHANNEL_CTRL(16), + TEGRA_ADMAIF_CHANNEL_CTRL(17), + TEGRA_ADMAIF_CHANNEL_CTRL(18), + TEGRA_ADMAIF_CHANNEL_CTRL(19), + TEGRA_ADMAIF_CHANNEL_CTRL(20), + TEGRA_ADMAIF_CIF_CTRL(1), + TEGRA_ADMAIF_CIF_CTRL(2), + TEGRA_ADMAIF_CIF_CTRL(3), + TEGRA_ADMAIF_CIF_CTRL(4), + TEGRA_ADMAIF_CIF_CTRL(5), + TEGRA_ADMAIF_CIF_CTRL(6), + TEGRA_ADMAIF_CIF_CTRL(7), + TEGRA_ADMAIF_CIF_CTRL(8), + TEGRA_ADMAIF_CIF_CTRL(9), + TEGRA_ADMAIF_CIF_CTRL(10), + TEGRA_ADMAIF_CIF_CTRL(11), + TEGRA_ADMAIF_CIF_CTRL(12), + TEGRA_ADMAIF_CIF_CTRL(13), + TEGRA_ADMAIF_CIF_CTRL(14), + TEGRA_ADMAIF_CIF_CTRL(15), + TEGRA_ADMAIF_CIF_CTRL(16), + TEGRA_ADMAIF_CIF_CTRL(17), + TEGRA_ADMAIF_CIF_CTRL(18), + TEGRA_ADMAIF_CIF_CTRL(19), + TEGRA_ADMAIF_CIF_CTRL(20), + SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0, + tegra210_admaif_get_reg_dump, + tegra210_admaif_put_reg_dump), +}; + +static const struct snd_soc_component_driver tegra210_admaif_cmpnt = { + .dapm_widgets = tegra_admaif_widgets, + .num_dapm_widgets = TEGRA210_ADMAIF_CHANNEL_COUNT * 4, + .dapm_routes = tegra_admaif_routes, + .num_dapm_routes = TEGRA210_ADMAIF_CHANNEL_COUNT * 6, + .controls = tegra210_admaif_controls, + .num_controls = ARRAY_SIZE(tegra210_admaif_controls), + .pcm_construct = tegra_pcm_construct, + .open = tegra_pcm_open, + .close = tegra_pcm_close, + .hw_params = tegra_pcm_hw_params, + .pointer = tegra_pcm_pointer, + .use_dai_pcm_id = 1, +}; + +static const struct snd_soc_component_driver tegra186_admaif_cmpnt = { + .dapm_widgets = tegra_admaif_widgets, + .num_dapm_widgets = TEGRA186_ADMAIF_CHANNEL_COUNT * 4, + .dapm_routes = tegra_admaif_routes, + .num_dapm_routes = TEGRA186_ADMAIF_CHANNEL_COUNT * 6, + .controls = tegra186_admaif_controls, + .num_controls = ARRAY_SIZE(tegra186_admaif_controls), + .pcm_construct = tegra_pcm_construct, + .open = tegra_pcm_open, + .close = tegra_pcm_close, + .hw_params = tegra_pcm_hw_params, + .pointer = tegra_pcm_pointer, + .use_dai_pcm_id = 1, +}; + +static const struct tegra_admaif_soc_data soc_data_tegra210 = { + .num_ch = TEGRA210_ADMAIF_CHANNEL_COUNT, + .cmpnt = &tegra210_admaif_cmpnt, + .dais = tegra210_admaif_cmpnt_dais, + .regmap_conf = &tegra210_admaif_regmap_config, + .global_base = TEGRA210_ADMAIF_GLOBAL_BASE, + .tx_base = TEGRA210_ADMAIF_TX_BASE, + .rx_base = TEGRA210_ADMAIF_RX_BASE, +}; + +static const struct tegra_admaif_soc_data soc_data_tegra186 = { + .num_ch = TEGRA186_ADMAIF_CHANNEL_COUNT, + .cmpnt = &tegra186_admaif_cmpnt, + .dais = tegra186_admaif_cmpnt_dais, + .regmap_conf = &tegra186_admaif_regmap_config, + .global_base = TEGRA186_ADMAIF_GLOBAL_BASE, + .tx_base = TEGRA186_ADMAIF_TX_BASE, + .rx_base = TEGRA186_ADMAIF_RX_BASE, +}; + +static const struct of_device_id tegra_admaif_of_match[] = { + { .compatible = "nvidia,tegra210-admaif", .data = &soc_data_tegra210 }, + { .compatible = "nvidia,tegra186-admaif", .data = &soc_data_tegra186 }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra_admaif_of_match); + +static int tegra_admaif_probe(struct platform_device *pdev) +{ + struct tegra_admaif *admaif; + void __iomem *regs; + struct resource *res; + int err, i; + + admaif = devm_kzalloc(&pdev->dev, sizeof(*admaif), GFP_KERNEL); + if (!admaif) + return -ENOMEM; + + admaif->soc_data = of_device_get_match_data(&pdev->dev); + + dev_set_drvdata(&pdev->dev, admaif); + + admaif->capture_dma_data = + devm_kcalloc(&pdev->dev, + admaif->soc_data->num_ch, + sizeof(struct snd_dmaengine_dai_dma_data), + GFP_KERNEL); + if (!admaif->capture_dma_data) + return -ENOMEM; + + admaif->playback_dma_data = + devm_kcalloc(&pdev->dev, + admaif->soc_data->num_ch, + sizeof(struct snd_dmaengine_dai_dma_data), + GFP_KERNEL); + if (!admaif->playback_dma_data) + return -ENOMEM; + + for (i = 0; i < ADMAIF_PATHS; i++) { + admaif->audio_ch_override[i] = + devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch, + sizeof(unsigned int), GFP_KERNEL); + if (!admaif->audio_ch_override[i]) + return -ENOMEM; + + admaif->client_ch_override[i] = + devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch, + sizeof(unsigned int), GFP_KERNEL); + if (!admaif->client_ch_override[i]) + return -ENOMEM; + + admaif->mono_to_stereo[i] = + devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch, + sizeof(unsigned int), GFP_KERNEL); + if (!admaif->mono_to_stereo[i]) + return -ENOMEM; + + admaif->stereo_to_mono[i] = + devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch, + sizeof(unsigned int), GFP_KERNEL); + if (!admaif->stereo_to_mono[i]) + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + admaif->base_addr = regs; + + admaif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + admaif->soc_data->regmap_conf); + if (IS_ERR(admaif->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(admaif->regmap); + } + + regcache_cache_only(admaif->regmap, true); + + tegra_isomgr_adma_register(&pdev->dev); + + regmap_update_bits(admaif->regmap, admaif->soc_data->global_base + + TEGRA_ADMAIF_GLOBAL_ENABLE, 1, 1); + + for (i = 0; i < admaif->soc_data->num_ch; i++) { + admaif->playback_dma_data[i].addr = res->start + + CH_TX_REG(TEGRA_ADMAIF_TX_FIFO_WRITE, i); + + admaif->capture_dma_data[i].addr = res->start + + CH_RX_REG(TEGRA_ADMAIF_RX_FIFO_READ, i); + + admaif->playback_dma_data[i].addr_width = 32; + + if (of_property_read_string_index(pdev->dev.of_node, + "dma-names", (i * 2) + 1, + &admaif->playback_dma_data[i].chan_name) < 0) { + dev_err(&pdev->dev, + "missing property nvidia,dma-names\n"); + + return -ENODEV; + } + + admaif->capture_dma_data[i].addr_width = 32; + + if (of_property_read_string_index(pdev->dev.of_node, + "dma-names", + (i * 2), + &admaif->capture_dma_data[i].chan_name) < 0) { + dev_err(&pdev->dev, + "missing property nvidia,dma-names\n"); + + return -ENODEV; + } + } + + err = devm_snd_soc_register_component(&pdev->dev, + admaif->soc_data->cmpnt, + admaif->soc_data->dais, + admaif->soc_data->num_ch * 3); + if (err) { + dev_err(&pdev->dev, + "can't register ADMAIF component, err: %d\n", err); + return err; + } + + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static int tegra_admaif_remove(struct platform_device *pdev) +{ + tegra_isomgr_adma_unregister(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra_admaif_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_admaif_runtime_suspend, + tegra_admaif_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra_admaif_driver = { + .probe = tegra_admaif_probe, + .remove = tegra_admaif_remove, + .driver = { + .name = "tegra210-admaif", + .of_match_table = tegra_admaif_of_match, + .pm = &tegra_admaif_pm_ops, + }, +}; +module_platform_driver(tegra_admaif_driver); + +MODULE_AUTHOR("Songhee Baek "); +MODULE_DESCRIPTION("Tegra210 ASoC ADMAIF driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_admaif.h b/sound/soc/tegra/tegra210_admaif.h new file mode 100644 index 00000000..44b2df64 --- /dev/null +++ b/sound/soc/tegra/tegra210_admaif.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_admaif.h - Tegra ADMAIF registers + * + * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA_ADMAIF_H__ +#define __TEGRA_ADMAIF_H__ + +#define TEGRA_ADMAIF_CHANNEL_REG_STRIDE 0x40 +/* Tegra210 specific */ +#define TEGRA210_ADMAIF_LAST_REG 0x75f +#define TEGRA210_ADMAIF_CHANNEL_COUNT 10 +#define TEGRA210_ADMAIF_RX_BASE 0x0 +#define TEGRA210_ADMAIF_TX_BASE 0x300 +#define TEGRA210_ADMAIF_GLOBAL_BASE 0x700 +/* Tegra186 specific */ +#define TEGRA186_ADMAIF_LAST_REG 0xd5f +#define TEGRA186_ADMAIF_CHANNEL_COUNT 20 +#define TEGRA186_ADMAIF_RX_BASE 0x0 +#define TEGRA186_ADMAIF_TX_BASE 0x500 +#define TEGRA186_ADMAIF_GLOBAL_BASE 0xd00 +/* Global registers */ +#define TEGRA_ADMAIF_GLOBAL_ENABLE 0x0 +#define TEGRA_ADMAIF_GLOBAL_CG_0 0x8 +#define TEGRA_ADMAIF_GLOBAL_STATUS 0x10 +#define TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS 0x20 +#define TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS 0x24 +/* RX channel registers */ +#define TEGRA_ADMAIF_RX_ENABLE 0x0 +#define TEGRA_ADMAIF_RX_SOFT_RESET 0x4 +#define TEGRA_ADMAIF_RX_STATUS 0xc +#define TEGRA_ADMAIF_RX_INT_STATUS 0x10 +#define TEGRA_ADMAIF_RX_INT_MASK 0x14 +#define TEGRA_ADMAIF_RX_INT_SET 0x18 +#define TEGRA_ADMAIF_RX_INT_CLEAR 0x1c +#define TEGRA_ADMAIF_CH_ACIF_RX_CTRL 0x20 +#define TEGRA_ADMAIF_RX_FIFO_CTRL 0x28 +#define TEGRA_ADMAIF_RX_FIFO_READ 0x2c +/* TX channel registers */ +#define TEGRA_ADMAIF_TX_ENABLE 0x0 +#define TEGRA_ADMAIF_TX_SOFT_RESET 0x4 +#define TEGRA_ADMAIF_TX_STATUS 0xc +#define TEGRA_ADMAIF_TX_INT_STATUS 0x10 +#define TEGRA_ADMAIF_TX_INT_MASK 0x14 +#define TEGRA_ADMAIF_TX_INT_SET 0x18 +#define TEGRA_ADMAIF_TX_INT_CLEAR 0x1c +#define TEGRA_ADMAIF_CH_ACIF_TX_CTRL 0x20 +#define TEGRA_ADMAIF_TX_FIFO_CTRL 0x28 +#define TEGRA_ADMAIF_TX_FIFO_WRITE 0x2c +/* Bit fields */ +#define PACK8_EN_SHIFT 31 +#define PACK8_EN_MASK BIT(PACK8_EN_SHIFT) +#define PACK8_EN BIT(PACK8_EN_SHIFT) +#define PACK16_EN_SHIFT 30 +#define PACK16_EN_MASK BIT(PACK16_EN_SHIFT) +#define PACK16_EN BIT(PACK16_EN_SHIFT) +#define TX_ENABLE_SHIFT 0 +#define TX_ENABLE_MASK BIT(TX_ENABLE_SHIFT) +#define TX_ENABLE BIT(TX_ENABLE_SHIFT) +#define RX_ENABLE_SHIFT 0 +#define RX_ENABLE_MASK BIT(RX_ENABLE_SHIFT) +#define RX_ENABLE BIT(RX_ENABLE_SHIFT) +#define SW_RESET_MASK 1 +#define SW_RESET 1 +/* Default values - Tegra210 */ +#define TEGRA210_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT 0x00000300 +#define TEGRA210_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT 0x00000304 +#define TEGRA210_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT 0x00000208 +#define TEGRA210_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT 0x0000020b +#define TEGRA210_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT 0x0000020e +#define TEGRA210_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT 0x00000211 +#define TEGRA210_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT 0x00000214 +#define TEGRA210_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT 0x00000217 +#define TEGRA210_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT 0x0000021a +#define TEGRA210_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT 0x0000021d +#define TEGRA210_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT 0x02000300 +#define TEGRA210_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT 0x02000304 +#define TEGRA210_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT 0x01800208 +#define TEGRA210_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT 0x0180020b +#define TEGRA210_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT 0x0180020e +#define TEGRA210_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT 0x01800211 +#define TEGRA210_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT 0x01800214 +#define TEGRA210_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT 0x01800217 +#define TEGRA210_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT 0x0180021a +#define TEGRA210_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT 0x0180021d +/* Default values - Tegra186 */ +#define TEGRA186_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT 0x00000300 +#define TEGRA186_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT 0x00000304 +#define TEGRA186_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT 0x00000308 +#define TEGRA186_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT 0x0000030c +#define TEGRA186_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT 0x00000210 +#define TEGRA186_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT 0x00000213 +#define TEGRA186_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT 0x00000216 +#define TEGRA186_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT 0x00000219 +#define TEGRA186_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT 0x0000021c +#define TEGRA186_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT 0x0000021f +#define TEGRA186_ADMAIF_RX11_FIFO_CTRL_REG_DEFAULT 0x00000222 +#define TEGRA186_ADMAIF_RX12_FIFO_CTRL_REG_DEFAULT 0x00000225 +#define TEGRA186_ADMAIF_RX13_FIFO_CTRL_REG_DEFAULT 0x00000228 +#define TEGRA186_ADMAIF_RX14_FIFO_CTRL_REG_DEFAULT 0x0000022b +#define TEGRA186_ADMAIF_RX15_FIFO_CTRL_REG_DEFAULT 0x0000022e +#define TEGRA186_ADMAIF_RX16_FIFO_CTRL_REG_DEFAULT 0x00000231 +#define TEGRA186_ADMAIF_RX17_FIFO_CTRL_REG_DEFAULT 0x00000234 +#define TEGRA186_ADMAIF_RX18_FIFO_CTRL_REG_DEFAULT 0x00000237 +#define TEGRA186_ADMAIF_RX19_FIFO_CTRL_REG_DEFAULT 0x0000023a +#define TEGRA186_ADMAIF_RX20_FIFO_CTRL_REG_DEFAULT 0x0000023d +#define TEGRA186_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT 0x02000300 +#define TEGRA186_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT 0x02000304 +#define TEGRA186_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT 0x02000308 +#define TEGRA186_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT 0x0200030c +#define TEGRA186_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT 0x01800210 +#define TEGRA186_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT 0x01800213 +#define TEGRA186_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT 0x01800216 +#define TEGRA186_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT 0x01800219 +#define TEGRA186_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT 0x0180021c +#define TEGRA186_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT 0x0180021f +#define TEGRA186_ADMAIF_TX11_FIFO_CTRL_REG_DEFAULT 0x01800222 +#define TEGRA186_ADMAIF_TX12_FIFO_CTRL_REG_DEFAULT 0x01800225 +#define TEGRA186_ADMAIF_TX13_FIFO_CTRL_REG_DEFAULT 0x01800228 +#define TEGRA186_ADMAIF_TX14_FIFO_CTRL_REG_DEFAULT 0x0180022b +#define TEGRA186_ADMAIF_TX15_FIFO_CTRL_REG_DEFAULT 0x0180022e +#define TEGRA186_ADMAIF_TX16_FIFO_CTRL_REG_DEFAULT 0x01800231 +#define TEGRA186_ADMAIF_TX17_FIFO_CTRL_REG_DEFAULT 0x01800234 +#define TEGRA186_ADMAIF_TX18_FIFO_CTRL_REG_DEFAULT 0x01800237 +#define TEGRA186_ADMAIF_TX19_FIFO_CTRL_REG_DEFAULT 0x0180023a +#define TEGRA186_ADMAIF_TX20_FIFO_CTRL_REG_DEFAULT 0x0180023d + +enum { + DATA_8BIT, + DATA_16BIT, + DATA_32BIT +}; + +enum { + ADMAIF_RX_PATH, + ADMAIF_TX_PATH, + ADMAIF_PATHS, +}; + +struct tegra_admaif_soc_data { + const struct snd_soc_component_driver *cmpnt; + const struct regmap_config *regmap_conf; + struct snd_soc_dai_driver *dais; + unsigned int global_base; + unsigned int tx_base; + unsigned int rx_base; + unsigned int num_ch; +}; + +struct tegra_admaif { + struct snd_dmaengine_dai_dma_data *capture_dma_data; + struct snd_dmaengine_dai_dma_data *playback_dma_data; + const struct tegra_admaif_soc_data *soc_data; + unsigned int *audio_ch_override[ADMAIF_PATHS]; + unsigned int *client_ch_override[ADMAIF_PATHS]; + int reg_dump_flag; + void __iomem *base_addr; + unsigned int *mono_to_stereo[ADMAIF_PATHS]; + unsigned int *stereo_to_mono[ADMAIF_PATHS]; + struct regmap *regmap; +}; + +extern void tegra_adma_dump_ch_reg(void); + +#endif diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c new file mode 100644 index 00000000..e21d3ee2 --- /dev/null +++ b/sound/soc/tegra/tegra210_adx.c @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_adx.c - Tegra210 ADX driver +// +// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra210_adx.h" +#include "tegra210_ahub.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}, +}; + +/** + * tegra210_adx_enable_outstream - enable output stream + * @adx: struct of tegra210_adx + * @stream_id: adx output stream id for enabling + */ +static void tegra210_adx_enable_outstream(struct tegra210_adx *adx, + unsigned int stream_id) +{ + int reg; + + reg = TEGRA210_ADX_CTRL; + + regmap_update_bits(adx->regmap, reg, + TEGRA210_ADX_TX_ENABLE << stream_id, + TEGRA210_ADX_TX_ENABLE << stream_id); +} + +/** + * tegra210_adx_disable_outstream - disable output stream + * @adx: struct of tegra210_adx + * @stream_id: adx output stream id for disabling + */ +static void tegra210_adx_disable_outstream(struct tegra210_adx *adx, + unsigned int stream_id) +{ + int reg; + + reg = TEGRA210_ADX_CTRL; + + regmap_update_bits(adx->regmap, reg, + TEGRA210_ADX_TX_ENABLE << stream_id, + TEGRA210_ADX_TX_DISABLE); +} + +/** + * tegra210_adx_set_in_byte_mask - set byte mask for input frame + * @adx: struct of tegra210_adx + * @mask1: enable for bytes 31 ~ 0 of input frame + * @mask2: enable for bytes 63 ~ 32 of input frame + */ +static void tegra210_adx_set_in_byte_mask(struct tegra210_adx *adx) +{ + 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]); +} + +/** + * tegra210_adx_set_map_table - set map table not RAM + * @adx: struct of tegra210_adx + * @out_byte_addr: byte address in one frame + * @stream_id: input stream id + * @nth_word: n-th word in the input stream + * @nth_byte: n-th byte in the word + */ +static void tegra210_adx_set_map_table(struct tegra210_adx *adx, + unsigned int out_byte_addr, + unsigned int stream_id, + unsigned int nth_word, + unsigned int nth_byte) +{ + unsigned char *bytes_map = (unsigned char *)&adx->map; + + bytes_map[out_byte_addr] = + (stream_id << TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT) | + (nth_word << TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT) | + (nth_byte << TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT); +} + +/** + * tegra210_adx_write_map_ram - write map information in RAM + * @adx: struct of tegra210_adx + * @addr: n-th word of input stream + * @val : bytes mapping information of the word + */ +static void tegra210_adx_write_map_ram(struct tegra210_adx *adx, + unsigned int addr, + unsigned int val) +{ + unsigned int reg; + + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, + (addr << TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT)); + + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA, val); + + regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, ®); + reg |= TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN; + + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, reg); + + regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, ®); + reg |= TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE; + + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, reg); +} + +static void tegra210_adx_update_map_ram(struct tegra210_adx *adx) +{ + int i; + + for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++) + tegra210_adx_write_map_ram(adx, i, adx->map[i]); +} + +static int tegra210_adx_stop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct device *dev = cmpnt->dev; + struct tegra210_adx *adx = dev_get_drvdata(dev); + 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(dev, "failed to stop ADX, err = %d\n", err); + return err; + } + + /* SW reset */ + 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(dev, "failed to reset ADX, err = %d\n", err); + return err; + } + + regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET, + TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK, + TEGRA210_ADX_SOFT_RESET_SOFT_DEFAULT); + + return 0; +} + +static unsigned int __maybe_unused + tegra210_adx_read_map_ram(struct tegra210_adx *adx, unsigned int addr) +{ + unsigned int val; + int err; + + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, + (addr << TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT)); + + regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, &val); + val |= TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN; + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, val); + regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, &val); + val &= ~(TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE); + regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, val); + + err = regmap_read_poll_timeout(adx->regmap, + TEGRA210_ADX_CFG_RAM_CTRL, + val, !(val & 0x80000000), 10, 10000); + if (err < 0) + return err; + + regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA, &val); + + return val; +} + +static int 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 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); + /* update the map ram */ + tegra210_adx_update_map_ram(adx); + tegra210_adx_set_in_byte_mask(adx); + + return 0; +} + +static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai, + int channels, 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_S24_LE: + 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) +{ + struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); + int channels; + + if (adx->output_channels[dai->id] > 0) + channels = adx->output_channels[dai->id]; + else + channels = params_channels(params); + + return tegra210_adx_set_audio_cif(dai, channels, params_format(params), + TEGRA210_ADX_TX1_CIF_CTRL + + (dai->id * TEGRA210_ADX_AUDIOCIF_CH_STRIDE)); +} + +static int tegra210_adx_out_trigger(struct snd_pcm_substream *substream, + int cmd, + struct snd_soc_dai *dai) +{ + struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tegra210_adx_enable_outstream(adx, dai->id); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tegra210_adx_disable_outstream(adx, dai->id); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); + int channels; + + if (adx->input_channels > 0) + channels = adx->input_channels; + else + channels = params_channels(params); + + return tegra210_adx_set_audio_cif(dai, channels, params_format(params), + TEGRA210_ADX_RX_CIF_CTRL); +} + +static int tegra210_adx_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct device *dev = dai->dev; + struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); + unsigned int out_stream_idx, out_ch_idx, out_byte_idx; + int i; + + if ((rx_num < 1) || (rx_num > 64)) { + dev_err(dev, "Doesn't support %d rx_num, need to be 1 to 64\n", + rx_num); + return -EINVAL; + } + + if (!rx_slot) { + dev_err(dev, "rx_slot is NULL\n"); + return -EINVAL; + } + + memset(adx->map, 0, sizeof(adx->map)); + memset(adx->byte_mask, 0, sizeof(adx->byte_mask)); + + for (i = 0; i < rx_num; i++) { + if (rx_slot[i] != 0) { + /* getting mapping information */ + /* n-th output stream : 0 to 3 */ + out_stream_idx = (rx_slot[i] >> 16) & 0x3; + /* n-th audio channel of output stream : 1 to 16 */ + out_ch_idx = (rx_slot[i] >> 8) & 0x1f; + /* n-th byte of audio channel : 0 to 3 */ + out_byte_idx = rx_slot[i] & 0x3; + tegra210_adx_set_map_table(adx, i, out_stream_idx, + out_ch_idx - 1, + out_byte_idx); + + /* making byte_mask */ + if (i > 31) + adx->byte_mask[1] |= (1 << (i - 32)); + else + adx->byte_mask[0] |= (1 << i); + } + } + + return 0; +} + +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] = 256; + + 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); + struct soc_mixer_control *mc; + unsigned char *bytes_map = (unsigned char *)&adx->map; + int value = ucontrol->value.integer.value[0]; + + 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 0; +} + +static int tegra210_adx_get_in_channels(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); + + ucontrol->value.integer.value[0] = adx->input_channels; + + return 0; +} + +static int tegra210_adx_put_in_channels(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); + int value = ucontrol->value.integer.value[0]; + + if (value < 0 || value > 16) + return -EINVAL; + + adx->input_channels = value; + + return 0; +} + +static int tegra210_adx_get_out_channels(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; + + mc = (struct soc_mixer_control *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = adx->output_channels[mc->reg - 1]; + + return 0; +} + +static int tegra210_adx_put_out_channels(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; + int value = ucontrol->value.integer.value[0]; + + mc = (struct soc_mixer_control *)kcontrol->private_value; + + if (value < 0 || value > 16) + return -EINVAL; + + adx->output_channels[mc->reg - 1] = value; + + return 0; +} + +static struct snd_soc_dai_ops tegra210_adx_in_dai_ops = { + .hw_params = tegra210_adx_in_hw_params, + .set_channel_map = tegra210_adx_set_channel_map, +}; + +static struct snd_soc_dai_ops tegra210_adx_out_dai_ops = { + .hw_params = tegra210_adx_out_hw_params, + .trigger = tegra210_adx_out_trigger, +}; + +#define OUT_DAI(id) \ + { \ + .name = "OUT" #id, \ + .capture = { \ + .stream_name = "OUT" #id " Transmit", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_96000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = &tegra210_adx_out_dai_ops, \ + } + +#define IN_DAI(sname, dai_ops) \ + { \ + .name = #sname, \ + .playback = { \ + .stream_name = #sname " Receive", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_96000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = dai_ops, \ + } + +static struct snd_soc_dai_driver tegra210_adx_dais[] = { + OUT_DAI(1), + OUT_DAI(2), + OUT_DAI(3), + OUT_DAI(4), + IN_DAI(IN, &tegra210_adx_in_dai_ops), +}; + +static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("IN", NULL, 0, TEGRA210_ADX_ENABLE, + TEGRA210_ADX_ENABLE_SHIFT, 0, + tegra210_adx_stop, SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("OUT1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0), + SND_SOC_DAPM_AIF_OUT("OUT2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0), + SND_SOC_DAPM_AIF_OUT("OUT3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0), + SND_SOC_DAPM_AIF_OUT("OUT4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0), +}; + +static const struct snd_soc_dapm_route tegra210_adx_routes[] = { + { "IN", NULL, "IN Receive" }, + { "OUT1", NULL, "IN" }, + { "OUT2", NULL, "IN" }, + { "OUT3", NULL, "IN" }, + { "OUT4", NULL, "IN" }, + { "OUT1 Transmit", NULL, "OUT1" }, + { "OUT2 Transmit", NULL, "OUT2" }, + { "OUT3 Transmit", NULL, "OUT3" }, + { "OUT4 Transmit", NULL, "OUT4" }, +}; + +#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) + +#define TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(reg) \ + SOC_SINGLE_EXT("Output" #reg " Audio Channels", reg, 0, 16, 0, \ + tegra210_adx_get_out_channels, \ + tegra210_adx_put_out_channels) + +#define TEGRA210_ADX_INPUT_CHANNELS_CTRL(reg) \ + SOC_SINGLE_EXT("Input Audio Channels", reg, 0, 16, 0, \ + tegra210_adx_get_in_channels, \ + tegra210_adx_put_in_channels) + +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), + + TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(1), + TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(2), + TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(3), + TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(4), + TEGRA210_ADX_INPUT_CHANNELS_CTRL(1), +}; + +static 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), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +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_CYA: + 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 "); +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 00000000..49d45d87 --- /dev/null +++ b/sound/soc/tegra/tegra210_adx.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_adx.h - Definitions for Tegra210 ADX driver + * + * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_ADX_H__ +#define __TEGRA210_ADX_H__ + +#define TEGRA210_ADX_AUDIOCIF_CH_STRIDE 4 + +/* 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_CYA 0xb0 +#define TEGRA210_ADX_DBG 0xb4 +#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_RW_SHIFT 14 +#define TEGRA210_ADX_CFG_RAM_CTRL_RW_MASK (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT) +#define TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT) + +#define TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT 0 + +#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13 +#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_MASK (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT) +#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_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) + +/* + * These defines are not in register field. + */ +#define TEGRA210_ADX_NUM_OUTPUTS 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 + +enum { + TEGRA210_ADX_TX_DISABLE, + TEGRA210_ADX_TX_ENABLE, +}; + +enum { + /* Code assumes that OUT_STREAM values of ADX start at 0 */ + /* OUT_STREAM# is equilvant to hw OUT_CH# */ + TEGRA210_ADX_OUT_STREAM0 = 0, + TEGRA210_ADX_OUT_STREAM1, + TEGRA210_ADX_OUT_STREAM2, + TEGRA210_ADX_OUT_STREAM3, + TEGRA210_ADX_IN_STREAM, + TEGRA210_ADX_TOTAL_STREAM +}; + +struct tegra210_adx { + struct regmap *regmap; + unsigned int map[TEGRA210_ADX_RAM_DEPTH]; + unsigned int byte_mask[2]; + int input_channels; + int output_channels[TEGRA210_ADX_NUM_OUTPUTS]; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_afc.c b/sound/soc/tegra/tegra210_afc.c index d7fefe2f..42adc3f9 100644 --- a/sound/soc/tegra/tegra210_afc.c +++ b/sound/soc/tegra/tegra210_afc.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -// SPDX-FileCopyrightText: Copyright (c) 2014-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // tegra210_afc.c - Tegra210 AFC driver +// +// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved. #include @@ -22,6 +23,7 @@ #include #include "tegra210_afc.h" +#include "tegra210_ahub.h" static const struct reg_default tegra210_afc_reg_defaults[] = { { TEGRA210_AFC_AXBAR_RX_CIF_CTRL, 0x00007700}, diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c new file mode 100644 index 00000000..f0c6f38a --- /dev/null +++ b/sound/soc/tegra/tegra210_ahub.c @@ -0,0 +1,1696 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_ahub.c - Tegra210 AHUB driver +// +// Copyright (c) 2020-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra210_ahub.h" + +static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl); + struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *e = (struct soc_enum *)kctl->private_value; + unsigned int reg, i, bit_pos = 0; + + /* + * Find the bit position of current MUX input. + * If nothing is set, position would be 0 and it corresponds to 'None'. + */ + for (i = 0; i < ahub->soc_data->reg_count; i++) { + unsigned int reg_val; + + reg = e->reg + (TEGRA210_XBAR_PART1_RX * i); + reg_val = snd_soc_component_read(cmpnt, reg); + reg_val &= ahub->soc_data->mask[i]; + + if (reg_val) { + bit_pos = ffs(reg_val) + + (8 * cmpnt->val_bytes * i); + break; + } + } + + /* Find index related to the item in array *_ahub_mux_texts[] */ + for (i = 0; i < e->items; i++) { + if (bit_pos == e->values[i]) { + uctl->value.enumerated.item[0] = i; + break; + } + } + + return 0; +} + +static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl); + struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctl); + struct soc_enum *e = (struct soc_enum *)kctl->private_value; + struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { }; + unsigned int *item = uctl->value.enumerated.item; + unsigned int value = e->values[item[0]]; + unsigned int i, bit_pos, reg_idx = 0, reg_val = 0; + int change = 0; + + if (item[0] >= e->items) + return -EINVAL; + + if (value) { + /* Get the register index and value to set */ + reg_idx = (value - 1) / (8 * cmpnt->val_bytes); + bit_pos = (value - 1) % (8 * cmpnt->val_bytes); + reg_val = BIT(bit_pos); + } + + /* + * Run through all parts of a MUX register to find the state changes. + * There will be an additional update if new MUX input value is from + * different part of the MUX register. + */ + for (i = 0; i < ahub->soc_data->reg_count; i++) { + update[i].reg = e->reg + (TEGRA210_XBAR_PART1_RX * i); + update[i].val = (i == reg_idx) ? reg_val : 0; + update[i].mask = ahub->soc_data->mask[i]; + update[i].kcontrol = kctl; + + /* Update widget power if state has changed */ + if (snd_soc_component_test_bits(cmpnt, update[i].reg, + update[i].mask, + update[i].val)) + change |= snd_soc_dapm_mux_update_power(dapm, kctl, + item[0], e, + &update[i]); + } + + return change; +} + +void tegra210_ahub_write_ram(struct regmap *regmap, unsigned int reg_ctrl, + unsigned int reg_data, unsigned int ram_offset, + unsigned int *data, size_t size) +{ + unsigned int val = 0; + int i = 0; + + val = ram_offset & TEGRA210_AHUBRAMCTL_CTRL_RAM_ADDR_MASK; + val |= TEGRA210_AHUBRAMCTL_CTRL_ADDR_INIT_EN; + val |= TEGRA210_AHUBRAMCTL_CTRL_SEQ_ACCESS_EN; + val |= TEGRA210_AHUBRAMCTL_CTRL_RW_WRITE; + + regmap_write(regmap, reg_ctrl, val); + for (i = 0; i < size; i++) + regmap_write(regmap, reg_data, data[i]); + + return; +} +EXPORT_SYMBOL_GPL(tegra210_ahub_write_ram); + +void tegra210_ahub_read_ram(struct regmap *regmap, unsigned int reg_ctrl, + unsigned int reg_data, unsigned int ram_offset, + unsigned int *data, size_t size) +{ + unsigned int val = 0; + int i = 0; + + val = ram_offset & TEGRA210_AHUBRAMCTL_CTRL_RAM_ADDR_MASK; + val |= TEGRA210_AHUBRAMCTL_CTRL_ADDR_INIT_EN; + val |= TEGRA210_AHUBRAMCTL_CTRL_SEQ_ACCESS_EN; + val |= TEGRA210_AHUBRAMCTL_CTRL_RW_READ; + + regmap_write(regmap, reg_ctrl, val); + /* Since all ahub non-io modules work under same ahub clock it is not + necessary to check ahub read busy bit after every read */ + for (i = 0; i < size; i++) + regmap_read(regmap, reg_data, &data[i]); + + return; +} +EXPORT_SYMBOL_GPL(tegra210_ahub_read_ram); + +/* + * TODO As per downstream kernel code there will be routing issue + * if DAI names are updated for SFC, MVC and OPE input and + * output. Due to that added those modules output DAIs just to keep + * similar to upstream kernel. Single DAI is used for input and + * output. + * + * Once the routing changes are done for above mentioned modules + * as per upstream, suffix the common dai name with RX. + */ +static struct snd_soc_dai_driver tegra210_ahub_dais[] = { + DAI(ADMAIF1), + DAI(ADMAIF2), + DAI(ADMAIF3), + DAI(ADMAIF4), + DAI(ADMAIF5), + DAI(ADMAIF6), + DAI(ADMAIF7), + DAI(ADMAIF8), + DAI(ADMAIF9), + DAI(ADMAIF10), + DAI(I2S1), + DAI(I2S2), + DAI(I2S3), + DAI(I2S4), + DAI(I2S5), + DAI(DMIC1), + DAI(DMIC2), + DAI(DMIC3), + DAI(SFC1), + DAI(SFC1 TX), + DAI(SFC2), + DAI(SFC2 TX), + DAI(SFC3), + DAI(SFC3 TX), + DAI(SFC4), + DAI(SFC4 TX), + DAI(MVC1), + DAI(MVC1 TX), + DAI(MVC2), + DAI(MVC2 TX), + 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(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(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), + DAI(OPE1), + DAI(OPE1 TX), + DAI(OPE2), + DAI(OPE2 TX), + DAI(AFC1), + DAI(AFC2), + DAI(AFC3), + DAI(AFC4), + DAI(AFC5), + DAI(AFC6), + DAI(SPKPROT1), + DAI(IQC1-1), + DAI(IQC1-2), + DAI(IQC2-1), + DAI(IQC2-2), +}; + +static struct snd_soc_dai_driver tegra186_ahub_dais[] = { + DAI(ADMAIF1), + DAI(ADMAIF2), + DAI(ADMAIF3), + DAI(ADMAIF4), + DAI(ADMAIF5), + DAI(ADMAIF6), + DAI(ADMAIF7), + DAI(ADMAIF8), + DAI(ADMAIF9), + DAI(ADMAIF10), + DAI(ADMAIF11), + DAI(ADMAIF12), + DAI(ADMAIF13), + DAI(ADMAIF14), + DAI(ADMAIF15), + DAI(ADMAIF16), + DAI(ADMAIF17), + DAI(ADMAIF18), + DAI(ADMAIF19), + DAI(ADMAIF20), + DAI(I2S1), + DAI(I2S2), + DAI(I2S3), + DAI(I2S4), + DAI(I2S5), + DAI(I2S6), + DAI(DMIC1), + DAI(DMIC2), + DAI(DMIC3), + DAI(DMIC4), + DAI(DSPK1), + DAI(DSPK2), + DAI(SFC1), + DAI(SFC1 TX), + DAI(SFC2), + DAI(SFC2 TX), + DAI(SFC3), + DAI(SFC3 TX), + DAI(SFC4), + DAI(SFC4 TX), + DAI(MVC1), + DAI(MVC1 TX), + DAI(MVC2), + DAI(MVC2 TX), + 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), + 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), + 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), + DAI(ASRC1 RX1), + DAI(ASRC1 TX1), + DAI(ASRC1 RX2), + DAI(ASRC1 TX2), + DAI(ASRC1 RX3), + DAI(ASRC1 TX3), + DAI(ASRC1 RX4), + DAI(ASRC1 TX4), + DAI(ASRC1 RX5), + DAI(ASRC1 TX5), + DAI(ASRC1 RX6), + DAI(ASRC1 TX6), + DAI(ASRC1 RX7), + DAI(OPE1), + DAI(OPE1 TX), + DAI(AFC1), + DAI(AFC2), + DAI(AFC3), + DAI(AFC4), + DAI(AFC5), + DAI(AFC6), + DAI(SPKPROT1), + DAI(IQC1-1), + DAI(IQC1-2), + DAI(IQC2-1), + DAI(IQC2-2), + DAI(ARAD1), +}; + +static const char * const tegra210_ahub_mux_texts[] = { + "None", + "ADMAIF1", + "ADMAIF2", + "ADMAIF3", + "ADMAIF4", + "ADMAIF5", + "ADMAIF6", + "ADMAIF7", + "ADMAIF8", + "ADMAIF9", + "ADMAIF10", + "I2S1", + "I2S2", + "I2S3", + "I2S4", + "I2S5", + "SFC1", + "SFC2", + "SFC3", + "SFC4", + /* index 0..19 above are inputs of PART0 Mux */ + "MIXER1 TX1", + "MIXER1 TX2", + "MIXER1 TX3", + "MIXER1 TX4", + "MIXER1 TX5", + "AMX1", + "AMX2", + "AFC1", + "AFC2", + "AFC3", + "AFC4", + "AFC5", + "AFC6", + /* index 20..34 above are inputs of PART1 Mux */ + "OPE1", + "OPE2", + "SPKPROT1", + "MVC1", + "MVC2", + "IQC1-1", + "IQC1-2", + "IQC2-1", + "IQC2-2", + "DMIC1", + "DMIC2", + "DMIC3", + "ADX1 TX1", + "ADX1 TX2", + "ADX1 TX3", + "ADX1 TX4", + "ADX2 TX1", + "ADX2 TX2", + "ADX2 TX3", + "ADX2 TX4", + /* index 35..53 above are inputs of PART2 Mux */ +}; + +static const char * const tegra186_ahub_mux_texts[] = { + "None", + "ADMAIF1", + "ADMAIF2", + "ADMAIF3", + "ADMAIF4", + "ADMAIF5", + "ADMAIF6", + "ADMAIF7", + "ADMAIF8", + "ADMAIF9", + "ADMAIF10", + "ADMAIF11", + "ADMAIF12", + "ADMAIF13", + "ADMAIF14", + "ADMAIF15", + "ADMAIF16", + "I2S1", + "I2S2", + "I2S3", + "I2S4", + "I2S5", + "I2S6", + "SFC1", + "SFC2", + "SFC3", + "SFC4", + /* index 0..19 above are inputs of PART0 Mux */ + "MIXER1 TX1", + "MIXER1 TX2", + "MIXER1 TX3", + "MIXER1 TX4", + "MIXER1 TX5", + "AMX1", + "AMX2", + "AMX3", + "AMX4", + "ARAD1", + "AFC1", + "AFC2", + "AFC3", + "AFC4", + "AFC5", + "AFC6", + /* index 20..34 above are inputs of PART1 Mux */ + "OPE1", + "SPKPROT1", + "MVC1", + "MVC2", + "IQC1-1", + "IQC1-2", + "IQC2-1", + "IQC2-2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", + "ADX1 TX1", + "ADX1 TX2", + "ADX1 TX3", + "ADX1 TX4", + "ADX2 TX1", + "ADX2 TX2", + "ADX2 TX3", + "ADX2 TX4", + /* index 35..53 above are inputs of PART2 Mux */ + "ADX3 TX1", + "ADX3 TX2", + "ADX3 TX3", + "ADX3 TX4", + "ADX4 TX1", + "ADX4 TX2", + "ADX4 TX3", + "ADX4 TX4", + "ADMAIF17", + "ADMAIF18", + "ADMAIF19", + "ADMAIF20", + "ASRC1 TX1", + "ASRC1 TX2", + "ASRC1 TX3", + "ASRC1 TX4", + "ASRC1 TX5", + "ASRC1 TX6", + /* index 54..71 above are inputs of PART3 Mux */ +}; + +static const unsigned int tegra210_ahub_mux_values[] = { + 0, + MUX_VALUE(0, 0), + MUX_VALUE(0, 1), + MUX_VALUE(0, 2), + MUX_VALUE(0, 3), + MUX_VALUE(0, 4), + MUX_VALUE(0, 5), + MUX_VALUE(0, 6), + MUX_VALUE(0, 7), + MUX_VALUE(0, 8), + MUX_VALUE(0, 9), + MUX_VALUE(0, 16), + MUX_VALUE(0, 17), + MUX_VALUE(0, 18), + MUX_VALUE(0, 19), + MUX_VALUE(0, 20), + MUX_VALUE(0, 24), + MUX_VALUE(0, 25), + MUX_VALUE(0, 26), + MUX_VALUE(0, 27), + /* index 0..19 above are inputs of PART0 Mux */ + MUX_VALUE(1, 0), + MUX_VALUE(1, 1), + MUX_VALUE(1, 2), + MUX_VALUE(1, 3), + MUX_VALUE(1, 4), + MUX_VALUE(1, 8), + MUX_VALUE(1, 9), + MUX_VALUE(1, 24), + MUX_VALUE(1, 25), + MUX_VALUE(1, 26), + MUX_VALUE(1, 27), + MUX_VALUE(1, 28), + MUX_VALUE(1, 29), + /* index 20..34 above are inputs of PART1 Mux */ + MUX_VALUE(2, 0), + MUX_VALUE(2, 1), + MUX_VALUE(2, 4), + MUX_VALUE(2, 8), + MUX_VALUE(2, 9), + MUX_VALUE(2, 12), + MUX_VALUE(2, 13), + MUX_VALUE(2, 14), + MUX_VALUE(2, 15), + MUX_VALUE(2, 18), + MUX_VALUE(2, 19), + MUX_VALUE(2, 20), + 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), + /* index 35..53 above are inputs of PART2 Mux */ +}; + +static const unsigned int tegra186_ahub_mux_values[] = { + 0, + MUX_VALUE(0, 0), + MUX_VALUE(0, 1), + MUX_VALUE(0, 2), + MUX_VALUE(0, 3), + MUX_VALUE(0, 4), + MUX_VALUE(0, 5), + MUX_VALUE(0, 6), + MUX_VALUE(0, 7), + MUX_VALUE(0, 8), + MUX_VALUE(0, 9), + MUX_VALUE(0, 10), + MUX_VALUE(0, 11), + MUX_VALUE(0, 12), + MUX_VALUE(0, 13), + MUX_VALUE(0, 14), + MUX_VALUE(0, 15), + MUX_VALUE(0, 16), + MUX_VALUE(0, 17), + MUX_VALUE(0, 18), + MUX_VALUE(0, 19), + MUX_VALUE(0, 20), + MUX_VALUE(0, 21), + MUX_VALUE(0, 24), + MUX_VALUE(0, 25), + MUX_VALUE(0, 26), + MUX_VALUE(0, 27), + /* index 0..19 above are inputs of PART0 Mux */ + MUX_VALUE(1, 0), + MUX_VALUE(1, 1), + MUX_VALUE(1, 2), + MUX_VALUE(1, 3), + MUX_VALUE(1, 4), + MUX_VALUE(1, 8), + MUX_VALUE(1, 9), + MUX_VALUE(1, 10), + MUX_VALUE(1, 11), + MUX_VALUE(1, 16), + MUX_VALUE(1, 24), + MUX_VALUE(1, 25), + MUX_VALUE(1, 26), + MUX_VALUE(1, 27), + MUX_VALUE(1, 28), + MUX_VALUE(1, 29), + /* index 20..34 above are inputs of PART1 Mux */ + MUX_VALUE(2, 0), + MUX_VALUE(2, 4), + MUX_VALUE(2, 8), + MUX_VALUE(2, 9), + MUX_VALUE(2, 12), + MUX_VALUE(2, 13), + MUX_VALUE(2, 14), + MUX_VALUE(2, 15), + MUX_VALUE(2, 18), + MUX_VALUE(2, 19), + MUX_VALUE(2, 20), + MUX_VALUE(2, 21), + 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), + /* index 35..53 above are inputs of PART2 Mux */ + 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), + MUX_VALUE(3, 16), + MUX_VALUE(3, 17), + MUX_VALUE(3, 18), + MUX_VALUE(3, 19), + MUX_VALUE(3, 24), + MUX_VALUE(3, 25), + MUX_VALUE(3, 26), + MUX_VALUE(3, 27), + MUX_VALUE(3, 28), + MUX_VALUE(3, 29), + /* index 54..71 above are inputs of PART3 Mux */ +}; + +/* Controls for t210 */ +MUX_ENUM_CTRL_DECL(t210_admaif1_tx, 0x00); +MUX_ENUM_CTRL_DECL(t210_admaif2_tx, 0x01); +MUX_ENUM_CTRL_DECL(t210_admaif3_tx, 0x02); +MUX_ENUM_CTRL_DECL(t210_admaif4_tx, 0x03); +MUX_ENUM_CTRL_DECL(t210_admaif5_tx, 0x04); +MUX_ENUM_CTRL_DECL(t210_admaif6_tx, 0x05); +MUX_ENUM_CTRL_DECL(t210_admaif7_tx, 0x06); +MUX_ENUM_CTRL_DECL(t210_admaif8_tx, 0x07); +MUX_ENUM_CTRL_DECL(t210_admaif9_tx, 0x08); +MUX_ENUM_CTRL_DECL(t210_admaif10_tx, 0x09); +MUX_ENUM_CTRL_DECL(t210_i2s1_tx, 0x10); +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_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); +MUX_ENUM_CTRL_DECL(t210_afc1_tx, 0x34); +MUX_ENUM_CTRL_DECL(t210_afc2_tx, 0x35); +MUX_ENUM_CTRL_DECL(t210_afc3_tx, 0x36); +MUX_ENUM_CTRL_DECL(t210_afc4_tx, 0x37); +MUX_ENUM_CTRL_DECL(t210_afc5_tx, 0x38); +MUX_ENUM_CTRL_DECL(t210_afc6_tx, 0x39); +MUX_ENUM_CTRL_DECL(t210_ope1_tx, 0x40); +MUX_ENUM_CTRL_DECL(t210_ope2_tx, 0x41); +MUX_ENUM_CTRL_DECL(t210_spkprot_tx, 0x44); +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); + +/* Controls for t186 */ +MUX_ENUM_CTRL_DECL_186(t186_admaif1_tx, 0x00); +MUX_ENUM_CTRL_DECL_186(t186_admaif2_tx, 0x01); +MUX_ENUM_CTRL_DECL_186(t186_admaif3_tx, 0x02); +MUX_ENUM_CTRL_DECL_186(t186_admaif4_tx, 0x03); +MUX_ENUM_CTRL_DECL_186(t186_admaif5_tx, 0x04); +MUX_ENUM_CTRL_DECL_186(t186_admaif6_tx, 0x05); +MUX_ENUM_CTRL_DECL_186(t186_admaif7_tx, 0x06); +MUX_ENUM_CTRL_DECL_186(t186_admaif8_tx, 0x07); +MUX_ENUM_CTRL_DECL_186(t186_admaif9_tx, 0x08); +MUX_ENUM_CTRL_DECL_186(t186_admaif10_tx, 0x09); +MUX_ENUM_CTRL_DECL_186(t186_i2s1_tx, 0x10); +MUX_ENUM_CTRL_DECL_186(t186_i2s2_tx, 0x11); +MUX_ENUM_CTRL_DECL_186(t186_i2s3_tx, 0x12); +MUX_ENUM_CTRL_DECL_186(t186_i2s4_tx, 0x13); +MUX_ENUM_CTRL_DECL_186(t186_i2s5_tx, 0x14); +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_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); +MUX_ENUM_CTRL_DECL_186(t186_afc1_tx, 0x38); +MUX_ENUM_CTRL_DECL_186(t186_afc2_tx, 0x39); +MUX_ENUM_CTRL_DECL_186(t186_afc3_tx, 0x3a); +MUX_ENUM_CTRL_DECL_186(t186_afc4_tx, 0x3b); +MUX_ENUM_CTRL_DECL_186(t186_afc5_tx, 0x3c); +MUX_ENUM_CTRL_DECL_186(t186_afc6_tx, 0x3d); +MUX_ENUM_CTRL_DECL_186(t186_ope1_tx, 0x40); +MUX_ENUM_CTRL_DECL_186(t186_spkprot_tx, 0x44); +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_adx1_tx, 0x60); +MUX_ENUM_CTRL_DECL_186(t186_adx2_tx, 0x61); +MUX_ENUM_CTRL_DECL_186(t186_dspk1_tx, 0x30); +MUX_ENUM_CTRL_DECL_186(t186_dspk2_tx, 0x31); +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_admaif11_tx, 0x0a); +MUX_ENUM_CTRL_DECL_186(t186_admaif12_tx, 0x0b); +MUX_ENUM_CTRL_DECL_186(t186_admaif13_tx, 0x0c); +MUX_ENUM_CTRL_DECL_186(t186_admaif14_tx, 0x0d); +MUX_ENUM_CTRL_DECL_186(t186_admaif15_tx, 0x0e); +MUX_ENUM_CTRL_DECL_186(t186_admaif16_tx, 0x0f); +MUX_ENUM_CTRL_DECL_186(t186_i2s6_tx, 0x15); +MUX_ENUM_CTRL_DECL_186(t186_adx3_tx, 0x62); +MUX_ENUM_CTRL_DECL_186(t186_adx4_tx, 0x63); +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_asrc11_tx, 0x6c); +MUX_ENUM_CTRL_DECL_186(t186_asrc12_tx, 0x6d); +MUX_ENUM_CTRL_DECL_186(t186_asrc13_tx, 0x6e); +MUX_ENUM_CTRL_DECL_186(t186_asrc14_tx, 0x6f); +MUX_ENUM_CTRL_DECL_186(t186_asrc15_tx, 0x70); +MUX_ENUM_CTRL_DECL_186(t186_asrc16_tx, 0x71); +MUX_ENUM_CTRL_DECL_186(t186_asrc17_tx, 0x72); + +/*T234 specific Controls */ +MUX_ENUM_CTRL_DECL_186(t234_afc1_tx, 0x34); +MUX_ENUM_CTRL_DECL_186(t234_afc2_tx, 0x35); +MUX_ENUM_CTRL_DECL_186(t234_afc3_tx, 0x36); +MUX_ENUM_CTRL_DECL_186(t234_afc4_tx, 0x37); +MUX_ENUM_CTRL_DECL_186(t234_afc5_tx, 0x38); +MUX_ENUM_CTRL_DECL_186(t234_afc6_tx, 0x39); +MUX_ENUM_CTRL_DECL_186(t234_spkprot_tx, 0x41); +MUX_ENUM_CTRL_DECL_186(t234_mvc1_tx, 0x44); +MUX_ENUM_CTRL_DECL_186(t234_mvc2_tx, 0x45); +MUX_ENUM_CTRL_DECL_186(t234_amx11_tx, 0x48); +MUX_ENUM_CTRL_DECL_186(t234_amx12_tx, 0x49); +MUX_ENUM_CTRL_DECL_186(t234_amx13_tx, 0x4a); +MUX_ENUM_CTRL_DECL_186(t234_amx14_tx, 0x4b); +MUX_ENUM_CTRL_DECL_186(t234_amx21_tx, 0x4c); +MUX_ENUM_CTRL_DECL_186(t234_amx22_tx, 0x4d); +MUX_ENUM_CTRL_DECL_186(t234_amx23_tx, 0x4e); +MUX_ENUM_CTRL_DECL_186(t234_amx24_tx, 0x4f); +MUX_ENUM_CTRL_DECL_186(t234_amx31_tx, 0x50); +MUX_ENUM_CTRL_DECL_186(t234_amx32_tx, 0x51); +MUX_ENUM_CTRL_DECL_186(t234_amx33_tx, 0x52); +MUX_ENUM_CTRL_DECL_186(t234_amx34_tx, 0x53); +MUX_ENUM_CTRL_DECL_186(t234_adx1_tx, 0x58); +MUX_ENUM_CTRL_DECL_186(t234_adx2_tx, 0x59); +MUX_ENUM_CTRL_DECL_186(t234_adx3_tx, 0x5a); +MUX_ENUM_CTRL_DECL_186(t234_adx4_tx, 0x5b); +MUX_ENUM_CTRL_DECL_186(t234_amx41_tx, 0x5c); +MUX_ENUM_CTRL_DECL_186(t234_amx42_tx, 0x5d); +MUX_ENUM_CTRL_DECL_186(t234_amx43_tx, 0x5e); +MUX_ENUM_CTRL_DECL_186(t234_amx44_tx, 0x5f); +MUX_ENUM_CTRL_DECL_186(t234_admaif17_tx, 0x60); +MUX_ENUM_CTRL_DECL_186(t234_admaif18_tx, 0x61); +MUX_ENUM_CTRL_DECL_186(t234_admaif19_tx, 0x62); +MUX_ENUM_CTRL_DECL_186(t234_admaif20_tx, 0x63); +MUX_ENUM_CTRL_DECL_186(t234_asrc11_tx, 0x64); +MUX_ENUM_CTRL_DECL_186(t234_asrc12_tx, 0x65); +MUX_ENUM_CTRL_DECL_186(t234_asrc13_tx, 0x66); +MUX_ENUM_CTRL_DECL_186(t234_asrc14_tx, 0x67); +MUX_ENUM_CTRL_DECL_186(t234_asrc15_tx, 0x68); +MUX_ENUM_CTRL_DECL_186(t234_asrc16_tx, 0x69); +MUX_ENUM_CTRL_DECL_186(t234_asrc17_tx, 0x6a); + +/* + * The number of entries in, and order of, this array is closely tied to the + * calculation of tegra210_ahub_codec.num_dapm_widgets near the end of + * tegra210_ahub_probe() + */ +static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = { + WIDGETS("ADMAIF1", t210_admaif1_tx), + WIDGETS("ADMAIF2", t210_admaif2_tx), + WIDGETS("ADMAIF3", t210_admaif3_tx), + WIDGETS("ADMAIF4", t210_admaif4_tx), + WIDGETS("ADMAIF5", t210_admaif5_tx), + WIDGETS("ADMAIF6", t210_admaif6_tx), + WIDGETS("ADMAIF7", t210_admaif7_tx), + WIDGETS("ADMAIF8", t210_admaif8_tx), + WIDGETS("ADMAIF9", t210_admaif9_tx), + WIDGETS("ADMAIF10", t210_admaif10_tx), + WIDGETS("I2S1", t210_i2s1_tx), + WIDGETS("I2S2", t210_i2s2_tx), + WIDGETS("I2S3", t210_i2s3_tx), + WIDGETS("I2S4", t210_i2s4_tx), + WIDGETS("I2S5", t210_i2s5_tx), + WIDGETS("SFC1", t210_sfc1_tx), + WIDGETS("SFC2", t210_sfc2_tx), + WIDGETS("SFC3", t210_sfc3_tx), + WIDGETS("SFC4", t210_sfc4_tx), + 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"), + WIDGETS("AFC1", t210_afc1_tx), + WIDGETS("AFC2", t210_afc2_tx), + WIDGETS("AFC3", t210_afc3_tx), + WIDGETS("AFC4", t210_afc4_tx), + WIDGETS("AFC5", t210_afc5_tx), + WIDGETS("AFC6", t210_afc6_tx), + WIDGETS("OPE1", t210_ope1_tx), + WIDGETS("OPE2", t210_ope2_tx), + WIDGETS("SPKPROT1", t210_spkprot_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), + WIDGETS("ADX1", t210_adx1_tx), + WIDGETS("ADX2", t210_adx2_tx), + TX_WIDGETS("IQC1-1"), + TX_WIDGETS("IQC1-2"), + TX_WIDGETS("IQC2-1"), + TX_WIDGETS("IQC2-2"), + TX_WIDGETS("DMIC1"), + TX_WIDGETS("DMIC2"), + TX_WIDGETS("DMIC3"), + TX_WIDGETS("AMX1"), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("AMX2"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), +}; + +static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { + WIDGETS("ADMAIF1", t186_admaif1_tx), + WIDGETS("ADMAIF2", t186_admaif2_tx), + WIDGETS("ADMAIF3", t186_admaif3_tx), + WIDGETS("ADMAIF4", t186_admaif4_tx), + WIDGETS("ADMAIF5", t186_admaif5_tx), + WIDGETS("ADMAIF6", t186_admaif6_tx), + WIDGETS("ADMAIF7", t186_admaif7_tx), + WIDGETS("ADMAIF8", t186_admaif8_tx), + WIDGETS("ADMAIF9", t186_admaif9_tx), + WIDGETS("ADMAIF10", t186_admaif10_tx), + WIDGETS("I2S1", t186_i2s1_tx), + WIDGETS("I2S2", t186_i2s2_tx), + WIDGETS("I2S3", t186_i2s3_tx), + WIDGETS("I2S4", t186_i2s4_tx), + WIDGETS("I2S5", t186_i2s5_tx), + WIDGETS("SFC1", t186_sfc1_tx), + WIDGETS("SFC2", t186_sfc2_tx), + WIDGETS("SFC3", t186_sfc3_tx), + WIDGETS("SFC4", t186_sfc4_tx), + 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"), + WIDGETS("AFC1", t186_afc1_tx), + WIDGETS("AFC2", t186_afc2_tx), + WIDGETS("AFC3", t186_afc3_tx), + WIDGETS("AFC4", t186_afc4_tx), + WIDGETS("AFC5", t186_afc5_tx), + WIDGETS("AFC6", t186_afc6_tx), + WIDGETS("OPE1", t186_ope1_tx), + WIDGETS("SPKPROT1", t186_spkprot_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("ADX1", t186_adx1_tx), + WIDGETS("ADX2", t186_adx2_tx), + TX_WIDGETS("IQC1-1"), + TX_WIDGETS("IQC1-2"), + TX_WIDGETS("IQC2-1"), + TX_WIDGETS("IQC2-2"), + TX_WIDGETS("DMIC1"), + TX_WIDGETS("DMIC2"), + TX_WIDGETS("DMIC3"), + TX_WIDGETS("AMX1"), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("AMX2"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), + WIDGETS("ADMAIF11", t186_admaif11_tx), + WIDGETS("ADMAIF12", t186_admaif12_tx), + WIDGETS("ADMAIF13", t186_admaif13_tx), + WIDGETS("ADMAIF14", t186_admaif14_tx), + WIDGETS("ADMAIF15", t186_admaif15_tx), + WIDGETS("ADMAIF16", t186_admaif16_tx), + WIDGETS("ADMAIF17", t186_admaif17_tx), + WIDGETS("ADMAIF18", t186_admaif18_tx), + WIDGETS("ADMAIF19", t186_admaif19_tx), + WIDGETS("ADMAIF20", t186_admaif20_tx), + WIDGETS("I2S6", t186_i2s6_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), + WIDGETS("ADX3", t186_adx3_tx), + WIDGETS("ADX4", t186_adx4_tx), + WIDGETS("ASRC1 RX1", t186_asrc11_tx), + WIDGETS("ASRC1 RX2", t186_asrc12_tx), + WIDGETS("ASRC1 RX3", t186_asrc13_tx), + WIDGETS("ASRC1 RX4", t186_asrc14_tx), + WIDGETS("ASRC1 RX5", t186_asrc15_tx), + WIDGETS("ASRC1 RX6", t186_asrc16_tx), + WIDGETS("ASRC1 RX7", t186_asrc17_tx), + TX_WIDGETS("ASRC1 TX1"), + TX_WIDGETS("ASRC1 TX2"), + TX_WIDGETS("ASRC1 TX3"), + TX_WIDGETS("ASRC1 TX4"), + TX_WIDGETS("ASRC1 TX5"), + TX_WIDGETS("ASRC1 TX6"), + WIDGETS("DSPK1", t186_dspk1_tx), + WIDGETS("DSPK2", t186_dspk2_tx), + TX_WIDGETS("AMX3"), + TX_WIDGETS("ADX3 TX1"), + TX_WIDGETS("ADX3 TX2"), + TX_WIDGETS("ADX3 TX3"), + TX_WIDGETS("ADX3 TX4"), + TX_WIDGETS("AMX4"), + TX_WIDGETS("ADX4 TX1"), + TX_WIDGETS("ADX4 TX2"), + TX_WIDGETS("ADX4 TX3"), + TX_WIDGETS("ADX4 TX4"), + TX_WIDGETS("DMIC4"), + TX_WIDGETS("ARAD1"), +}; + +/*TODO: Remove SPKPROT and IQC entries for all chips */ +static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = { + WIDGETS("ADMAIF1", t186_admaif1_tx), + WIDGETS("ADMAIF2", t186_admaif2_tx), + WIDGETS("ADMAIF3", t186_admaif3_tx), + WIDGETS("ADMAIF4", t186_admaif4_tx), + WIDGETS("ADMAIF5", t186_admaif5_tx), + WIDGETS("ADMAIF6", t186_admaif6_tx), + WIDGETS("ADMAIF7", t186_admaif7_tx), + WIDGETS("ADMAIF8", t186_admaif8_tx), + WIDGETS("ADMAIF9", t186_admaif9_tx), + WIDGETS("ADMAIF10", t186_admaif10_tx), + WIDGETS("I2S1", t186_i2s1_tx), + WIDGETS("I2S2", t186_i2s2_tx), + WIDGETS("I2S3", t186_i2s3_tx), + WIDGETS("I2S4", t186_i2s4_tx), + WIDGETS("I2S5", t186_i2s5_tx), + WIDGETS("SFC1", t186_sfc1_tx), + WIDGETS("SFC2", t186_sfc2_tx), + WIDGETS("SFC3", t186_sfc3_tx), + WIDGETS("SFC4", t186_sfc4_tx), + 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"), + WIDGETS("AFC1", t234_afc1_tx), + WIDGETS("AFC2", t234_afc2_tx), + WIDGETS("AFC3", t234_afc3_tx), + WIDGETS("AFC4", t234_afc4_tx), + WIDGETS("AFC5", t234_afc5_tx), + WIDGETS("AFC6", t234_afc6_tx), + WIDGETS("OPE1", t186_ope1_tx), + WIDGETS("SPKPROT1", t234_spkprot_tx), + WIDGETS("MVC1", t234_mvc1_tx), + WIDGETS("MVC2", t234_mvc2_tx), + WIDGETS("AMX1 RX1", t234_amx11_tx), + WIDGETS("AMX1 RX2", t234_amx12_tx), + WIDGETS("AMX1 RX3", t234_amx13_tx), + WIDGETS("AMX1 RX4", t234_amx14_tx), + WIDGETS("AMX2 RX1", t234_amx21_tx), + WIDGETS("AMX2 RX2", t234_amx22_tx), + WIDGETS("AMX2 RX3", t234_amx23_tx), + WIDGETS("AMX2 RX4", t234_amx24_tx), + WIDGETS("ADX1", t234_adx1_tx), + WIDGETS("ADX2", t234_adx2_tx), + TX_WIDGETS("IQC1-1"), + TX_WIDGETS("IQC1-2"), + TX_WIDGETS("IQC2-1"), + TX_WIDGETS("IQC2-2"), + TX_WIDGETS("DMIC1"), + TX_WIDGETS("DMIC2"), + TX_WIDGETS("DMIC3"), + TX_WIDGETS("AMX1"), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("AMX2"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), + WIDGETS("ADMAIF11", t186_admaif11_tx), + WIDGETS("ADMAIF12", t186_admaif12_tx), + WIDGETS("ADMAIF13", t186_admaif13_tx), + WIDGETS("ADMAIF14", t186_admaif14_tx), + WIDGETS("ADMAIF15", t186_admaif15_tx), + WIDGETS("ADMAIF16", t186_admaif16_tx), + WIDGETS("ADMAIF17", t234_admaif17_tx), + WIDGETS("ADMAIF18", t234_admaif18_tx), + WIDGETS("ADMAIF19", t234_admaif19_tx), + WIDGETS("ADMAIF20", t234_admaif20_tx), + WIDGETS("I2S6", t186_i2s6_tx), + WIDGETS("AMX3 RX1", t234_amx31_tx), + WIDGETS("AMX3 RX2", t234_amx32_tx), + WIDGETS("AMX3 RX3", t234_amx33_tx), + WIDGETS("AMX3 RX4", t234_amx34_tx), + WIDGETS("AMX4 RX1", t234_amx41_tx), + WIDGETS("AMX4 RX2", t234_amx42_tx), + WIDGETS("AMX4 RX3", t234_amx43_tx), + WIDGETS("AMX4 RX4", t234_amx44_tx), + WIDGETS("ADX3", t234_adx3_tx), + WIDGETS("ADX4", t234_adx4_tx), + WIDGETS("ASRC1 RX1", t234_asrc11_tx), + WIDGETS("ASRC1 RX2", t234_asrc12_tx), + WIDGETS("ASRC1 RX3", t234_asrc13_tx), + WIDGETS("ASRC1 RX4", t234_asrc14_tx), + WIDGETS("ASRC1 RX5", t234_asrc15_tx), + WIDGETS("ASRC1 RX6", t234_asrc16_tx), + WIDGETS("ASRC1 RX7", t234_asrc17_tx), + TX_WIDGETS("ASRC1 TX1"), + TX_WIDGETS("ASRC1 TX2"), + TX_WIDGETS("ASRC1 TX3"), + TX_WIDGETS("ASRC1 TX4"), + TX_WIDGETS("ASRC1 TX5"), + TX_WIDGETS("ASRC1 TX6"), + WIDGETS("DSPK1", t186_dspk1_tx), + WIDGETS("DSPK2", t186_dspk2_tx), + TX_WIDGETS("AMX3"), + TX_WIDGETS("ADX3 TX1"), + TX_WIDGETS("ADX3 TX2"), + TX_WIDGETS("ADX3 TX3"), + TX_WIDGETS("ADX3 TX4"), + TX_WIDGETS("AMX4"), + TX_WIDGETS("ADX4 TX1"), + TX_WIDGETS("ADX4 TX2"), + TX_WIDGETS("ADX4 TX3"), + TX_WIDGETS("ADX4 TX4"), + TX_WIDGETS("DMIC4"), + TX_WIDGETS("ARAD1"), +}; + +#define TEGRA_COMMON_MUX_ROUTES(name) \ + { name " XBAR-RX", NULL, name " XBAR-Playback"}, \ + { name " XBAR-Capture", NULL, name " XBAR-TX"}, \ + { name " XBAR-TX", NULL, name " Mux"}, \ + { name " Mux", "ADMAIF1", "ADMAIF1 XBAR-RX" }, \ + { name " Mux", "ADMAIF2", "ADMAIF2 XBAR-RX" }, \ + { name " Mux", "ADMAIF3", "ADMAIF3 XBAR-RX" }, \ + { name " Mux", "ADMAIF4", "ADMAIF4 XBAR-RX" }, \ + { name " Mux", "ADMAIF5", "ADMAIF5 XBAR-RX" }, \ + { name " Mux", "ADMAIF6", "ADMAIF6 XBAR-RX" }, \ + { name " Mux", "ADMAIF7", "ADMAIF7 XBAR-RX" }, \ + { name " Mux", "ADMAIF8", "ADMAIF8 XBAR-RX" }, \ + { name " Mux", "ADMAIF9", "ADMAIF9 XBAR-RX" }, \ + { name " Mux", "ADMAIF10", "ADMAIF10 XBAR-RX" }, \ + { name " Mux", "I2S1", "I2S1 XBAR-RX" }, \ + { name " Mux", "I2S2", "I2S2 XBAR-RX" }, \ + { name " Mux", "I2S3", "I2S3 XBAR-RX" }, \ + { name " Mux", "I2S4", "I2S4 XBAR-RX" }, \ + { name " Mux", "I2S5", "I2S5 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", "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" }, \ + { name " Mux", "AFC1", "AFC1 XBAR-RX" }, \ + { name " Mux", "AFC2", "AFC2 XBAR-RX" }, \ + { name " Mux", "AFC3", "AFC3 XBAR-RX" }, \ + { name " Mux", "AFC4", "AFC4 XBAR-RX" }, \ + { name " Mux", "AFC5", "AFC5 XBAR-RX" }, \ + { name " Mux", "AFC6", "AFC6 XBAR-RX" }, \ + { name " Mux", "OPE1", "OPE1 XBAR-RX" }, \ + { name " Mux", "MVC1", "MVC1 XBAR-RX" }, \ + { name " Mux", "MVC2", "MVC2 XBAR-RX" }, \ + { name " Mux", "IQC1-1", "IQC1-1 XBAR-RX" }, \ + { name " Mux", "IQC1-2", "IQC1-2 XBAR-RX" }, \ + { name " Mux", "IQC2-1", "IQC2-1 XBAR-RX" }, \ + { name " Mux", "IQC2-2", "IQC2-2 XBAR-RX" }, \ + { name " Mux", "DMIC1", "DMIC1 XBAR-RX" }, \ + { name " Mux", "DMIC2", "DMIC2 XBAR-RX" }, \ + { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \ + { name " Mux", "AMX1", "AMX1 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", "AMX2", "AMX2 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" }, + +#define TEGRA210_ONLY_MUX_ROUTES(name) \ + { name " Mux", "OPE2", "OPE2 XBAR-RX" }, + +#define TEGRA186_ONLY_MUX_ROUTES(name) \ + { name " Mux", "ADMAIF11", "ADMAIF11 XBAR-RX" }, \ + { name " Mux", "ADMAIF12", "ADMAIF12 XBAR-RX" }, \ + { name " Mux", "ADMAIF13", "ADMAIF13 XBAR-RX" }, \ + { name " Mux", "ADMAIF14", "ADMAIF14 XBAR-RX" }, \ + { name " Mux", "ADMAIF15", "ADMAIF15 XBAR-RX" }, \ + { name " Mux", "ADMAIF16", "ADMAIF16 XBAR-RX" }, \ + { name " Mux", "ADMAIF17", "ADMAIF17 XBAR-RX" }, \ + { name " Mux", "ADMAIF18", "ADMAIF18 XBAR-RX" }, \ + { name " Mux", "ADMAIF19", "ADMAIF19 XBAR-RX" }, \ + { name " Mux", "ADMAIF20", "ADMAIF20 XBAR-RX" }, \ + { name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, \ + { name " Mux", "I2S6", "I2S6 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX1", "ASRC1 TX1 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX2", "ASRC1 TX2 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX3", "ASRC1 TX3 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX4", "ASRC1 TX4 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX5", "ASRC1 TX5 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX6", "ASRC1 TX6 XBAR-RX" }, \ + { name " Mux", "AMX3", "AMX3 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", "AMX4", "AMX4 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" }, \ + { name " Mux", "ARAD1", "ARAD1 XBAR-RX" }, + +#define TEGRA210_MUX_ROUTES(name) \ + TEGRA_COMMON_MUX_ROUTES(name) \ + TEGRA210_ONLY_MUX_ROUTES(name) + +#define TEGRA186_MUX_ROUTES(name) \ + TEGRA_COMMON_MUX_ROUTES(name) \ + TEGRA186_ONLY_MUX_ROUTES(name) + +/* Connect FEs with XBAR */ +#define TEGRA_FE_ROUTES(name) \ + { name " XBAR-Playback",NULL, name " Playback" }, \ + { name " XBAR-RX", NULL, name " XBAR-Playback"}, \ + { name " XBAR-Capture", NULL, name " XBAR-TX" }, \ + { name " Capture", NULL, name " XBAR-Capture" }, + +static const struct snd_soc_dapm_route tegra210_ahub_routes[] = { +#if IS_ENABLED(CONFIG_TEGRA_DPCM) + TEGRA_FE_ROUTES("ADMAIF1") + TEGRA_FE_ROUTES("ADMAIF2") + TEGRA_FE_ROUTES("ADMAIF3") + TEGRA_FE_ROUTES("ADMAIF4") + TEGRA_FE_ROUTES("ADMAIF5") + TEGRA_FE_ROUTES("ADMAIF6") + TEGRA_FE_ROUTES("ADMAIF7") + TEGRA_FE_ROUTES("ADMAIF8") + TEGRA_FE_ROUTES("ADMAIF9") + TEGRA_FE_ROUTES("ADMAIF10") +#endif + TEGRA210_MUX_ROUTES("ADMAIF1") + TEGRA210_MUX_ROUTES("ADMAIF2") + TEGRA210_MUX_ROUTES("ADMAIF3") + TEGRA210_MUX_ROUTES("ADMAIF4") + TEGRA210_MUX_ROUTES("ADMAIF5") + TEGRA210_MUX_ROUTES("ADMAIF6") + TEGRA210_MUX_ROUTES("ADMAIF7") + TEGRA210_MUX_ROUTES("ADMAIF8") + TEGRA210_MUX_ROUTES("ADMAIF9") + TEGRA210_MUX_ROUTES("ADMAIF10") + TEGRA210_MUX_ROUTES("I2S1") + TEGRA210_MUX_ROUTES("I2S2") + 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("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") + IN_OUT_ROUTES("MIXER1 TX1") + IN_OUT_ROUTES("MIXER1 TX2") + IN_OUT_ROUTES("MIXER1 TX3") + IN_OUT_ROUTES("MIXER1 TX4") + IN_OUT_ROUTES("MIXER1 TX5") + TEGRA210_MUX_ROUTES("AFC1") + TEGRA210_MUX_ROUTES("AFC2") + TEGRA210_MUX_ROUTES("AFC3") + TEGRA210_MUX_ROUTES("AFC4") + TEGRA210_MUX_ROUTES("AFC5") + TEGRA210_MUX_ROUTES("AFC6") + TEGRA210_MUX_ROUTES("OPE1") + TEGRA210_MUX_ROUTES("OPE2") + TEGRA210_MUX_ROUTES("SPKPROT1") + 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") + IN_OUT_ROUTES("IQC1-1") + IN_OUT_ROUTES("IQC1-2") + IN_OUT_ROUTES("IQC2-1") + IN_OUT_ROUTES("IQC2-1") + IN_OUT_ROUTES("DMIC1") + IN_OUT_ROUTES("DMIC2") + IN_OUT_ROUTES("DMIC3") + IN_OUT_ROUTES("AMX1") + IN_OUT_ROUTES("AMX2") + IN_OUT_ROUTES("ADX1 TX1") + IN_OUT_ROUTES("ADX1 TX2") + IN_OUT_ROUTES("ADX1 TX3") + IN_OUT_ROUTES("ADX1 TX4") + IN_OUT_ROUTES("ADX2 TX1") + IN_OUT_ROUTES("ADX2 TX2") + IN_OUT_ROUTES("ADX2 TX3") + IN_OUT_ROUTES("ADX2 TX4") +}; + +static const struct snd_soc_dapm_route tegra186_ahub_routes[] = { +#if IS_ENABLED(CONFIG_TEGRA_DPCM) + TEGRA_FE_ROUTES("ADMAIF1") + TEGRA_FE_ROUTES("ADMAIF2") + TEGRA_FE_ROUTES("ADMAIF3") + TEGRA_FE_ROUTES("ADMAIF4") + TEGRA_FE_ROUTES("ADMAIF5") + TEGRA_FE_ROUTES("ADMAIF6") + TEGRA_FE_ROUTES("ADMAIF7") + TEGRA_FE_ROUTES("ADMAIF8") + TEGRA_FE_ROUTES("ADMAIF9") + TEGRA_FE_ROUTES("ADMAIF10") + TEGRA_FE_ROUTES("ADMAIF11") + TEGRA_FE_ROUTES("ADMAIF12") + TEGRA_FE_ROUTES("ADMAIF13") + TEGRA_FE_ROUTES("ADMAIF14") + TEGRA_FE_ROUTES("ADMAIF15") + TEGRA_FE_ROUTES("ADMAIF16") + TEGRA_FE_ROUTES("ADMAIF17") + TEGRA_FE_ROUTES("ADMAIF18") + TEGRA_FE_ROUTES("ADMAIF19") + TEGRA_FE_ROUTES("ADMAIF20") +#endif + TEGRA186_MUX_ROUTES("ADMAIF1") + TEGRA186_MUX_ROUTES("ADMAIF2") + TEGRA186_MUX_ROUTES("ADMAIF3") + TEGRA186_MUX_ROUTES("ADMAIF4") + TEGRA186_MUX_ROUTES("ADMAIF5") + TEGRA186_MUX_ROUTES("ADMAIF6") + TEGRA186_MUX_ROUTES("ADMAIF7") + TEGRA186_MUX_ROUTES("ADMAIF8") + TEGRA186_MUX_ROUTES("ADMAIF9") + TEGRA186_MUX_ROUTES("ADMAIF10") + TEGRA186_MUX_ROUTES("I2S1") + TEGRA186_MUX_ROUTES("I2S2") + TEGRA186_MUX_ROUTES("I2S3") + TEGRA186_MUX_ROUTES("I2S4") + TEGRA186_MUX_ROUTES("I2S5") + TEGRA186_MUX_ROUTES("SFC1") + TEGRA186_MUX_ROUTES("SFC2") + TEGRA186_MUX_ROUTES("SFC3") + TEGRA186_MUX_ROUTES("SFC4") + 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") + IN_OUT_ROUTES("MIXER1 TX1") + IN_OUT_ROUTES("MIXER1 TX2") + IN_OUT_ROUTES("MIXER1 TX3") + IN_OUT_ROUTES("MIXER1 TX4") + IN_OUT_ROUTES("MIXER1 TX5") + TEGRA186_MUX_ROUTES("AFC1") + TEGRA186_MUX_ROUTES("AFC2") + TEGRA186_MUX_ROUTES("AFC3") + TEGRA186_MUX_ROUTES("AFC4") + TEGRA186_MUX_ROUTES("AFC5") + TEGRA186_MUX_ROUTES("AFC6") + TEGRA186_MUX_ROUTES("OPE1") + TEGRA186_MUX_ROUTES("SPKPROT1") + 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("ADX1") + TEGRA186_MUX_ROUTES("ADX2") + IN_OUT_ROUTES("IQC1-1") + IN_OUT_ROUTES("IQC1-2") + IN_OUT_ROUTES("IQC2-1") + IN_OUT_ROUTES("IQC2-1") + IN_OUT_ROUTES("DMIC1") + IN_OUT_ROUTES("DMIC2") + IN_OUT_ROUTES("DMIC3") + IN_OUT_ROUTES("AMX1") + IN_OUT_ROUTES("AMX2") + IN_OUT_ROUTES("ADX1 TX1") + IN_OUT_ROUTES("ADX1 TX2") + IN_OUT_ROUTES("ADX1 TX3") + IN_OUT_ROUTES("ADX1 TX4") + IN_OUT_ROUTES("ADX2 TX1") + IN_OUT_ROUTES("ADX2 TX2") + IN_OUT_ROUTES("ADX2 TX3") + IN_OUT_ROUTES("ADX2 TX4") + TEGRA186_MUX_ROUTES("ADMAIF11") + TEGRA186_MUX_ROUTES("ADMAIF12") + TEGRA186_MUX_ROUTES("ADMAIF13") + TEGRA186_MUX_ROUTES("ADMAIF14") + TEGRA186_MUX_ROUTES("ADMAIF15") + TEGRA186_MUX_ROUTES("ADMAIF16") + TEGRA186_MUX_ROUTES("ADMAIF17") + TEGRA186_MUX_ROUTES("ADMAIF18") + TEGRA186_MUX_ROUTES("ADMAIF19") + TEGRA186_MUX_ROUTES("ADMAIF20") + 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("ADX3") + TEGRA186_MUX_ROUTES("ADX4") + TEGRA186_MUX_ROUTES("I2S6") + TEGRA186_MUX_ROUTES("ASRC1 RX1") + TEGRA186_MUX_ROUTES("ASRC1 RX2") + TEGRA186_MUX_ROUTES("ASRC1 RX3") + TEGRA186_MUX_ROUTES("ASRC1 RX4") + TEGRA186_MUX_ROUTES("ASRC1 RX5") + TEGRA186_MUX_ROUTES("ASRC1 RX6") + TEGRA186_MUX_ROUTES("ASRC1 RX7") + IN_OUT_ROUTES("ASRC1 TX1") + IN_OUT_ROUTES("ASRC1 TX2") + IN_OUT_ROUTES("ASRC1 TX3") + IN_OUT_ROUTES("ASRC1 TX4") + IN_OUT_ROUTES("ASRC1 TX5") + IN_OUT_ROUTES("ASRC1 TX6") + TEGRA186_MUX_ROUTES("DSPK1") + TEGRA186_MUX_ROUTES("DSPK2") + IN_OUT_ROUTES("DMIC4") + IN_OUT_ROUTES("AMX3") + IN_OUT_ROUTES("AMX4") + IN_OUT_ROUTES("ADX3 TX1") + IN_OUT_ROUTES("ADX3 TX2") + IN_OUT_ROUTES("ADX3 TX3") + IN_OUT_ROUTES("ADX3 TX4") + IN_OUT_ROUTES("ADX4 TX1") + IN_OUT_ROUTES("ADX4 TX2") + IN_OUT_ROUTES("ADX4 TX3") + IN_OUT_ROUTES("ADX4 TX4") + IN_OUT_ROUTES("ARAD1") +}; + +static const struct snd_soc_component_driver tegra210_ahub_component = { + .dapm_widgets = tegra210_ahub_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_ahub_widgets), + .dapm_routes = tegra210_ahub_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_ahub_routes), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static const struct snd_soc_component_driver tegra186_ahub_component = { + .dapm_widgets = tegra186_ahub_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra186_ahub_widgets), + .dapm_routes = tegra186_ahub_routes, + .num_dapm_routes = ARRAY_SIZE(tegra186_ahub_routes), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static const struct snd_soc_component_driver tegra234_ahub_component = { + .dapm_widgets = tegra234_ahub_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra234_ahub_widgets), + .dapm_routes = tegra186_ahub_routes, + .num_dapm_routes = ARRAY_SIZE(tegra186_ahub_routes), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static const struct regmap_config tegra210_ahub_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TEGRA210_MAX_REGISTER_ADDR, + .cache_type = REGCACHE_FLAT, +}; + +static const struct regmap_config tegra186_ahub_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TEGRA186_MAX_REGISTER_ADDR, + .cache_type = REGCACHE_FLAT, +}; + +static const struct tegra_ahub_soc_data soc_data_tegra210 = { + .cmpnt_drv = &tegra210_ahub_component, + .dai_drv = tegra210_ahub_dais, + .num_dais = ARRAY_SIZE(tegra210_ahub_dais), + .regmap_config = &tegra210_ahub_regmap_config, + .mask[0] = TEGRA210_XBAR_REG_MASK_0, + .mask[1] = TEGRA210_XBAR_REG_MASK_1, + .mask[2] = TEGRA210_XBAR_REG_MASK_2, + .mask[3] = TEGRA210_XBAR_REG_MASK_3, + .reg_count = TEGRA210_XBAR_UPDATE_MAX_REG, +}; + +static const struct tegra_ahub_soc_data soc_data_tegra186 = { + .cmpnt_drv = &tegra186_ahub_component, + .dai_drv = tegra186_ahub_dais, + .num_dais = ARRAY_SIZE(tegra186_ahub_dais), + .regmap_config = &tegra186_ahub_regmap_config, + .mask[0] = TEGRA186_XBAR_REG_MASK_0, + .mask[1] = TEGRA186_XBAR_REG_MASK_1, + .mask[2] = TEGRA186_XBAR_REG_MASK_2, + .mask[3] = TEGRA186_XBAR_REG_MASK_3, + .reg_count = TEGRA186_XBAR_UPDATE_MAX_REG, +}; + +static const struct tegra_ahub_soc_data soc_data_tegra234 = { + .cmpnt_drv = &tegra234_ahub_component, + .dai_drv = tegra186_ahub_dais, + .num_dais = ARRAY_SIZE(tegra186_ahub_dais), + .regmap_config = &tegra186_ahub_regmap_config, + .mask[0] = TEGRA186_XBAR_REG_MASK_0, + .mask[1] = TEGRA186_XBAR_REG_MASK_1, + .mask[2] = TEGRA186_XBAR_REG_MASK_2, + .mask[3] = TEGRA186_XBAR_REG_MASK_3, + .reg_count = TEGRA186_XBAR_UPDATE_MAX_REG, +}; + +static const struct of_device_id tegra_ahub_of_match[] = { + { .compatible = "nvidia,tegra210-ahub", .data = &soc_data_tegra210 }, + { .compatible = "nvidia,tegra186-ahub", .data = &soc_data_tegra186 }, + { .compatible = "nvidia,tegra234-ahub", .data = &soc_data_tegra234 }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra_ahub_of_match); + +static int __maybe_unused tegra_ahub_runtime_suspend(struct device *dev) +{ + struct tegra_ahub *ahub = dev_get_drvdata(dev); + + regcache_cache_only(ahub->regmap, true); + regcache_mark_dirty(ahub->regmap); + + clk_disable_unprepare(ahub->clk); + + return 0; +} + +static int __maybe_unused tegra_ahub_runtime_resume(struct device *dev) +{ + struct tegra_ahub *ahub = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(ahub->clk); + if (err) { + dev_err(dev, "failed to enable AHUB clock, err: %d\n", err); + return err; + } + + regcache_cache_only(ahub->regmap, false); + regcache_sync(ahub->regmap); + + return 0; +} + +static int tegra_ahub_probe(struct platform_device *pdev) +{ + struct tegra_ahub *ahub; + void __iomem *regs; + int err; + + ahub = devm_kzalloc(&pdev->dev, sizeof(*ahub), GFP_KERNEL); + if (!ahub) + return -ENOMEM; + + ahub->soc_data = of_device_get_match_data(&pdev->dev); + + platform_set_drvdata(pdev, ahub); + + ahub->clk = devm_clk_get(&pdev->dev, "ahub"); + if (IS_ERR(ahub->clk)) { + dev_err(&pdev->dev, "can't retrieve AHUB clock\n"); + return PTR_ERR(ahub->clk); + } + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + ahub->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + ahub->soc_data->regmap_config); + if (IS_ERR(ahub->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(ahub->regmap); + } + + regcache_cache_only(ahub->regmap, true); + + err = devm_snd_soc_register_component(&pdev->dev, + ahub->soc_data->cmpnt_drv, + ahub->soc_data->dai_drv, + ahub->soc_data->num_dais); + if (err) { + dev_err(&pdev->dev, "can't register AHUB component, err: %d\n", + err); + return err; + } + + pm_runtime_enable(&pdev->dev); + + err = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (err) { + pm_runtime_disable(&pdev->dev); + return err; + } + + return 0; +} + +static int tegra_ahub_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra_ahub_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_ahub_runtime_suspend, + tegra_ahub_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra_ahub_driver = { + .probe = tegra_ahub_probe, + .remove = tegra_ahub_remove, + .driver = { + .name = "tegra210-ahub", + .of_match_table = tegra_ahub_of_match, + .pm = &tegra_ahub_pm_ops, + }, +}; +module_platform_driver(tegra_ahub_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_AUTHOR("Mohan Kumar "); +MODULE_DESCRIPTION("Tegra210 ASoC AHUB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_ahub.h b/sound/soc/tegra/tegra210_ahub.h new file mode 100644 index 00000000..fae7f0ae --- /dev/null +++ b/sound/soc/tegra/tegra210_ahub.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_ahub.h - TEGRA210 AHUB + * + * Copyright (c) 2020-2021 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_AHUB__H__ +#define __TEGRA210_AHUB__H__ + +/* Tegra210 specific */ +#define TEGRA210_XBAR_PART1_RX 0x200 +#define TEGRA210_XBAR_PART2_RX 0x400 +#define TEGRA210_XBAR_RX_STRIDE 0x4 +#define TEGRA210_XBAR_AUDIO_RX_COUNT 90 +#define TEGRA210_XBAR_REG_MASK_0 0xf1f03ff +#define TEGRA210_XBAR_REG_MASK_1 0x3f30031f +#define TEGRA210_XBAR_REG_MASK_2 0xff1cf313 +#define TEGRA210_XBAR_REG_MASK_3 0x0 +#define TEGRA210_XBAR_UPDATE_MAX_REG 3 +/* Tegra186 specific */ +#define TEGRA186_XBAR_PART3_RX 0x600 +#define TEGRA186_XBAR_AUDIO_RX_COUNT 115 +#define TEGRA186_XBAR_REG_MASK_0 0xf3fffff +#define TEGRA186_XBAR_REG_MASK_1 0x3f310f1f +#define TEGRA186_XBAR_REG_MASK_2 0xff3cf311 +#define TEGRA186_XBAR_REG_MASK_3 0x3f0f00ff +#define TEGRA186_XBAR_UPDATE_MAX_REG 4 + +/* Fields in *AHUBRAMCTL_CTRL; used by different AHUB modules */ +#define TEGRA210_AHUBRAMCTL_CTRL_RW_READ 0 +#define TEGRA210_AHUBRAMCTL_CTRL_RW_WRITE (1 << 14) +#define TEGRA210_AHUBRAMCTL_CTRL_ADDR_INIT_EN (1 << 13) +#define TEGRA210_AHUBRAMCTL_CTRL_SEQ_ACCESS_EN (1 << 12) +#define TEGRA210_AHUBRAMCTL_CTRL_RAM_ADDR_MASK 0x1ff + +#define TEGRA_XBAR_UPDATE_MAX_REG (TEGRA186_XBAR_UPDATE_MAX_REG) + +#define TEGRA186_MAX_REGISTER_ADDR (TEGRA186_XBAR_PART3_RX + \ + (TEGRA210_XBAR_RX_STRIDE * (TEGRA186_XBAR_AUDIO_RX_COUNT - 1))) + +#define TEGRA210_MAX_REGISTER_ADDR (TEGRA210_XBAR_PART2_RX + \ + (TEGRA210_XBAR_RX_STRIDE * (TEGRA210_XBAR_AUDIO_RX_COUNT - 1))) + +#define MUX_REG(id) (TEGRA210_XBAR_RX_STRIDE * (id)) + +#define MUX_VALUE(npart, nbit) (1 + (nbit) + (npart) * 32) + +#define SOC_VALUE_ENUM_WIDE(xreg, shift, xmax, xtexts, xvalues) \ + { \ + .reg = xreg, \ + .shift_l = shift, \ + .shift_r = shift, \ + .items = xmax, \ + .texts = xtexts, \ + .values = xvalues, \ + .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0 \ + } + +#define SOC_VALUE_ENUM_WIDE_DECL(name, xreg, shift, xtexts, xvalues) \ + static struct soc_enum name = \ + SOC_VALUE_ENUM_WIDE(xreg, shift, ARRAY_SIZE(xtexts), \ + xtexts, xvalues) + +#define MUX_ENUM_CTRL_DECL(ename, id) \ + SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0, \ + tegra210_ahub_mux_texts, \ + tegra210_ahub_mux_values); \ + static const struct snd_kcontrol_new ename##_control = \ + SOC_DAPM_ENUM_EXT("Route", ename##_enum, \ + tegra_ahub_get_value_enum, \ + tegra_ahub_put_value_enum) + +#define MUX_ENUM_CTRL_DECL_186(ename, id) \ + SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0, \ + tegra186_ahub_mux_texts, \ + tegra186_ahub_mux_values); \ + static const struct snd_kcontrol_new ename##_control = \ + SOC_DAPM_ENUM_EXT("Route", ename##_enum, \ + tegra_ahub_get_value_enum, \ + tegra_ahub_put_value_enum) + +#define IN_OUT_ROUTES(name) \ + { name " XBAR-RX", NULL, name " XBAR-Playback" }, \ + { name " XBAR-Capture", NULL, name " XBAR-TX" }, + +#define WIDGETS(sname, ename) \ + SND_SOC_DAPM_AIF_IN(sname " XBAR-RX", NULL, 0, SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_AIF_OUT(sname " XBAR-TX", NULL, 0, SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_MUX(sname " Mux", SND_SOC_NOPM, 0, 0, \ + &ename##_control) + +#define TX_WIDGETS(sname) \ + SND_SOC_DAPM_AIF_IN(sname " XBAR-RX", NULL, 0, SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_AIF_OUT(sname " XBAR-TX", NULL, 0, SND_SOC_NOPM, 0, 0) + +#define DAI(sname) \ + { \ + .name = "XBAR-" #sname, \ + .playback = { \ + .stream_name = #sname " XBAR-Playback", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = #sname " XBAR-Capture", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_KNOT, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + } + +struct tegra_ahub_soc_data { + const struct regmap_config *regmap_config; + const struct snd_soc_component_driver *cmpnt_drv; + struct snd_soc_dai_driver *dai_drv; + unsigned int mask[4]; + unsigned int reg_count; + unsigned int num_dais; +}; + +struct tegra_ahub { + const struct tegra_ahub_soc_data *soc_data; + struct regmap *regmap; + struct clk *clk; +}; + +void tegra210_ahub_write_ram(struct regmap *regmap, unsigned int reg_ctrl, + unsigned int reg_data, unsigned int ram_offset, + unsigned int *data, size_t size); +void tegra210_ahub_read_ram(struct regmap *regmap, unsigned int reg_ctrl, + unsigned int reg_data, unsigned int ram_offset, + unsigned int *data, size_t size); + +#endif diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c new file mode 100644 index 00000000..adbe9ff9 --- /dev/null +++ b/sound/soc/tegra/tegra210_amx.c @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_amx.c - Tegra210 AMX driver +// +// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra210_ahub.h" +#include "tegra210_amx.h" + +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}, +}; + +/** + * tegra210_amx_set_master_stream - set master stream and dependency + * @amx: struct of tegra210_amx + * @stream_id: one of input stream id to be a master + * @dependency: master dependency for tansferring + * 0 - wait on all, 1 - wait on any + * + * This dependency matter on starting point not every frame. + * Once amx starts to run, it is work as wait on all. + */ +static void tegra210_amx_set_master_stream(struct tegra210_amx *amx, + unsigned int stream_id, + unsigned int dependency) +{ + unsigned int mask, val; + + mask = (TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK | + TEGRA210_AMX_CTRL_RX_DEP_MASK); + + val = (stream_id << TEGRA210_AMX_CTRL_MSTR_RX_NUN_SHIFT) | + (dependency << TEGRA210_AMX_CTRL_RX_DEP_SHIFT); + + regmap_update_bits(amx->regmap, TEGRA210_AMX_CTRL, mask, val); +} + +/** + * tegra210_amx_enable_instream - enable input stream + * @amx: struct of tegra210_amx + * @stream_id: amx input stream id for enabling + */ +static void tegra210_amx_enable_instream(struct tegra210_amx *amx, + unsigned int stream_id) +{ + int reg; + + reg = TEGRA210_AMX_CTRL; + + regmap_update_bits(amx->regmap, reg, + TEGRA210_AMX_RX_ENABLE << stream_id, + TEGRA210_AMX_RX_ENABLE << stream_id); +} + +/** + * tegra210_amx_disable_instream - disable input stream + * @amx: struct of tegra210_amx + * @stream_id: amx input stream id for disabling + */ +static void tegra210_amx_disable_instream(struct tegra210_amx *amx, + unsigned int stream_id) +{ + int reg; + + reg = TEGRA210_AMX_CTRL; + + regmap_update_bits(amx->regmap, reg, + TEGRA210_AMX_RX_ENABLE << stream_id, + TEGRA210_AMX_RX_DISABLE); +} + +/** + * tegra210_amx_set_out_byte_mask - set byte mask for output frame + * @amx: struct of tegra210_amx + * @mask1: enable for bytes 31 ~ 0 + * @mask2: enable for bytes 63 ~ 32 + */ +static void tegra210_amx_set_out_byte_mask(struct tegra210_amx *amx) +{ + 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]); +} + +/** + * tegra210_amx_set_map_table - set map table not RAM + * @amx: struct of tegra210_amx + * @out_byte_addr: byte address in one frame + * @stream_id: input stream id (0 to 3) + * @nth_word: n-th word in the input stream (1 to 16) + * @nth_byte: n-th byte in the word (0 to 3) + */ +static void tegra210_amx_set_map_table(struct tegra210_amx *amx, + unsigned int out_byte_addr, + unsigned int stream_id, + unsigned int nth_word, + unsigned int nth_byte) +{ + unsigned char *bytes_map = (unsigned char *)&amx->map; + + bytes_map[out_byte_addr] = + (stream_id << TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT) | + (nth_word << TEGRA210_AMX_MAP_WORD_NUMBER_SHIFT) | + (nth_byte << TEGRA210_AMX_MAP_BYTE_NUMBER_SHIFT); +} + +/** + * tegra210_amx_write_map_ram - write map information in RAM + * @amx: struct of tegra210_amx + * @addr: n-th word of input stream + * @val : bytes mapping information of the word + */ +static void tegra210_amx_write_map_ram(struct tegra210_amx *amx, + unsigned int addr, + unsigned int val) +{ + unsigned int reg; + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, + (addr << TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT)); + + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA, val); + + regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, ®); + reg |= TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN; + + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, reg); + + regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, ®); + reg |= TEGRA210_AMX_CFG_CTRL_RW_WRITE; + + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, reg); +} + +static void tegra210_amx_update_map_ram(struct tegra210_amx *amx) +{ + int i; + + for (i = 0; i < TEGRA210_AMX_RAM_DEPTH; i++) + tegra210_amx_write_map_ram(amx, i, amx->map[i]); +} + +static int tegra210_amx_stop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct device *dev = cmpnt->dev; + struct tegra210_amx *amx = dev_get_drvdata(dev); + 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(dev, "failed to stop AMX, err = %d\n", err); + return err; + } + + /* SW reset */ + 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(dev, "failed to reset AMX, err = %d\n", err); + return err; + } + + regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET, + TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK, + TEGRA210_AMX_SOFT_RESET_SOFT_DEFAULT); + + return 0; +} + +static int 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 unsigned int __maybe_unused + tegra210_amx_read_map_ram(struct tegra210_amx *amx, unsigned int addr) +{ + unsigned int val; + int err; + + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, + (addr << TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT)); + + regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, &val); + val |= TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN; + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, val); + regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, &val); + val &= ~(TEGRA210_AMX_CFG_CTRL_RW_WRITE); + regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, val); + + err = regmap_read_poll_timeout(amx->regmap, + TEGRA210_AMX_CFG_RAM_CTRL, + val, !(val & 0x80000000), 10, 10000); + if (err < 0) + return err; + + regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA, &val); + + return val; +} + +static int 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); + /* update map ram */ + tegra210_amx_set_master_stream(amx, 0, TEGRA210_AMX_WAIT_ON_ANY); + tegra210_amx_update_map_ram(amx); + tegra210_amx_set_out_byte_mask(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); + + if (strstr(dai->name, "OUT")) { + channels = amx->output_channels > 0 ? + amx->output_channels : channels; + } else { + channels = amx->input_channels[dai->id] > 0 ? + amx->input_channels[dai->id] : channels; + } + + if (channels < 1 || channels > 16) + return -EINVAL; + + 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_S24_LE: + 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) +{ + int err; + struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai); + + + /* For T19x soc frame period disable counter can be programmed as: + * counter = 1 * ahub_clk_rate + * ------------------------- + * sample_rate + * + * TODO: read actual sample_rate & ahub_clk_rate + * For now using: + * sample_rate = 8000 + * ahub_clk_rate = 49152000 + */ + if (amx->soc_data->is_auto_disable_supported) { + regmap_write(amx->regmap, + TEGRA194_AMX_RX1_FRAME_PERIOD + + (dai->id * TEGRA210_AMX_AUDIOCIF_CH_STRIDE), + 0x1800); + regmap_write(amx->regmap, TEGRA210_AMX_CYA, 1); + } + + err = tegra210_amx_set_audio_cif(dai, params, + TEGRA210_AMX_RX1_CIF_CTRL + + (dai->id * TEGRA210_AMX_AUDIOCIF_CH_STRIDE)); + + return err; +} + +static int tegra210_amx_in_trigger(struct snd_pcm_substream *substream, + int cmd, + struct snd_soc_dai *dai) +{ + struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tegra210_amx_enable_instream(amx, dai->id); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tegra210_amx_disable_instream(amx, dai->id); + break; + default: + return -EINVAL; + } + + return 0; +} + +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_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct device *dev = dai->dev; + struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai); + unsigned int in_stream_idx, in_ch_idx, in_byte_idx; + int i; + + if ((tx_num < 1) || (tx_num > 64)) { + dev_err(dev, "Doesn't support %d tx_num, need to be 1 to 64\n", + tx_num); + return -EINVAL; + } + + if (!tx_slot) { + dev_err(dev, "tx_slot is NULL\n"); + return -EINVAL; + } + + memset(amx->map, 0, sizeof(amx->map)); + memset(amx->byte_mask, 0, sizeof(amx->byte_mask)); + + for (i = 0; i < tx_num; i++) { + if (tx_slot[i] != 0) { + /* getting mapping information */ + /* n-th input stream : 0 to 3 */ + in_stream_idx = (tx_slot[i] >> 16) & 0x3; + /* n-th audio channel of input stream : 1 to 16 */ + in_ch_idx = (tx_slot[i] >> 8) & 0x1f; + /* n-th byte of audio channel : 0 to 3 */ + in_byte_idx = tx_slot[i] & 0x3; + tegra210_amx_set_map_table(amx, i, in_stream_idx, + in_ch_idx - 1, + in_byte_idx); + + /* making byte_mask */ + if (i > 31) + amx->byte_mask[1] |= (1 << (i - 32)); + else + amx->byte_mask[0] |= (1 << i); + } + } + + return 0; +} + +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] = 256; + + 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 0; +} + +static int tegra210_amx_get_channels(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); + int reg = mc->reg; + char buf[50]; + + snprintf(buf, 50, "Input%d Audio Channels", reg); + if (strstr(kcontrol->id.name, buf)) + ucontrol->value.integer.value[0] = amx->input_channels[reg - 1]; + else if (strstr(kcontrol->id.name, "Output Audio Channels")) + ucontrol->value.integer.value[0] = amx->output_channels; + + return 0; +} + +static int tegra210_amx_put_channels(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); + int reg = mc->reg; + int value = ucontrol->value.integer.value[0]; + char buf[50]; + + snprintf(buf, 50, "Input%d Audio Channels", reg); + if (strstr(kcontrol->id.name, buf)) { + if (value >= 0 && value <= 16) + amx->input_channels[reg - 1] = value; + else + return -EINVAL; + } else if (strstr(kcontrol->id.name, "Output Audio Channels")) { + if (value >= 0 && value <= 16) + amx->output_channels = value; + else + return -EINVAL; + } + return 0; +} + +static struct snd_soc_dai_ops tegra210_amx_out_dai_ops = { + .hw_params = tegra210_amx_out_hw_params, + .set_channel_map = tegra210_amx_set_channel_map, +}; + +static struct snd_soc_dai_ops tegra210_amx_in_dai_ops = { + .hw_params = tegra210_amx_in_hw_params, + .trigger = tegra210_amx_in_trigger, +}; + +#define IN_DAI(id) \ + { \ + .name = "IN" #id, \ + .playback = { \ + .stream_name = "IN" #id " Receive", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_96000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = &tegra210_amx_in_dai_ops, \ + } + +#define OUT_DAI(sname, dai_ops) \ + { \ + .name = #sname, \ + .capture = { \ + .stream_name = #sname " Transmit", \ + .channels_min = 1, \ + .channels_max = 16, \ + .rates = SNDRV_PCM_RATE_8000_96000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = 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(OUT, &tegra210_amx_out_dai_ops), +}; + +static const struct snd_soc_dapm_widget tegra210_amx_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN1", NULL, 0, TEGRA210_AMX_CTRL, 0, 0), + SND_SOC_DAPM_AIF_IN("IN2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0), + SND_SOC_DAPM_AIF_IN("IN3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0), + SND_SOC_DAPM_AIF_IN("IN4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0), + SND_SOC_DAPM_AIF_OUT_E("OUT", NULL, 0, TEGRA210_AMX_ENABLE, + TEGRA210_AMX_ENABLE_SHIFT, 0, + tegra210_amx_stop, SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route tegra210_amx_routes[] = { + { "IN1", NULL, "IN1 Receive" }, + { "IN2", NULL, "IN2 Receive" }, + { "IN3", NULL, "IN3 Receive" }, + { "IN4", NULL, "IN4 Receive" }, + { "OUT", NULL, "IN1" }, + { "OUT", NULL, "IN2" }, + { "OUT", NULL, "IN3" }, + { "OUT", NULL, "IN4" }, + { "OUT Transmit", NULL, "OUT" }, +}; + +#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) + +#define TEGRA210_AMX_OUTPUT_CHANNELS_CTRL(reg) \ + SOC_SINGLE_EXT("Output Audio Channels", reg, 0, 16, 0, \ + tegra210_amx_get_channels, \ + tegra210_amx_put_channels) + +#define TEGRA210_AMX_INPUT_CHANNELS_CTRL(reg) \ + SOC_SINGLE_EXT("Input" #reg " Audio Channels", reg, 0, 16, 0, \ + tegra210_amx_get_channels, \ + tegra210_amx_put_channels) + +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), + + TEGRA210_AMX_OUTPUT_CHANNELS_CTRL(1), + TEGRA210_AMX_INPUT_CHANNELS_CTRL(1), + TEGRA210_AMX_INPUT_CHANNELS_CTRL(2), + TEGRA210_AMX_INPUT_CHANNELS_CTRL(3), + TEGRA210_AMX_INPUT_CHANNELS_CTRL(4), +}; + +static 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), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +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, + .is_auto_disable_supported = false, +}; + +static const struct tegra210_amx_soc_data soc_data_tegra194 = { + .regmap_conf = &tegra194_amx_regmap_config, + .is_auto_disable_supported = 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; + memset(amx->map, 0, sizeof(amx->map)); + memset(amx->byte_mask, 0, sizeof(amx->byte_mask)); + 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 "); +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 00000000..4c49e725 --- /dev/null +++ b/sound/soc/tegra/tegra210_amx.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_amx.h - Definitions for Tegra210 AMX driver + * + * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_AMX_H__ +#define __TEGRA210_AMX_H__ + +#define TEGRA210_AMX_AUDIOCIF_CH_STRIDE 4 + +/* 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_DBG 0xb4 +#define TEGRA210_AMX_CFG_RAM_CTRL 0xb8 +#define TEGRA210_AMX_CFG_RAM_DATA 0xbc + +#define TEGRA194_AMX_RX1_FRAME_PERIOD 0xc0 +#define TEGRA194_AMX_RX2_FRAME_PERIOD 0xc4 +#define TEGRA194_AMX_RX3_FRAME_PERIOD 0xc8 +#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_NUN_SHIFT 14 +#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK (3 << TEGRA210_AMX_CTRL_MSTR_RX_NUN_SHIFT) + +#define TEGRA210_AMX_CTRL_RX_DEP_SHIFT 12 +#define TEGRA210_AMX_CTRL_RX_DEP_MASK (3 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT) +#define TEGRA210_AMX_CTRL_RX_DEP_WT_ON_ALL 0 +#define TEGRA210_AMX_CTRL_RX_DEP_WT_ON_ANY (1 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT) +#define TEGRA210_AMX_CTRL_RX_DEP_RSVD (3 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT) + +/* Fields in TEGRA210_AMX_CFG_RAM_CTRL */ +#define TEGRA210_AMX_CFG_CTRL_RW_SHIFT 14 +#define TEGRA210_AMX_CFG_CTRL_RW_MASK (1 << TEGRA210_AMX_CFG_CTRL_RW_SHIFT) +#define TEGRA210_AMX_CFG_CTRL_RW_WRITE (1 << TEGRA210_AMX_CFG_CTRL_RW_SHIFT) + +#define TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_SHIFT 13 +#define TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_MASK (1 << TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_SHIFT) +#define TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN (1 << TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_SHIFT) + +#define TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT 0 +#define TEGRA210_AMX_CFG_CTRL_RAM_ADDR_MASK (0xff << TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT) + +/* Fields in TEGRA210_AMX_SOFT_RESET */ +#define TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT 0 +#define TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK (1 << TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT) +#define TEGRA210_AMX_SOFT_RESET_SOFT_EN (1 << TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT) +#define TEGRA210_AMX_SOFT_RESET_SOFT_DEFAULT (0 << TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT) + +/* + * Those defines are not in register field. + */ +#define TEGRA210_AMX_NUM_INPUTS 4 +#define TEGRA210_AMX_RAM_DEPTH 16 +#define TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT 6 +#define TEGRA210_AMX_MAP_STREAM_NUMBER_MASK (0x3 << TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT) +#define TEGRA210_AMX_MAP_WORD_NUMBER_SHIFT 2 +#define TEGRA210_AMX_MAP_WORD_NUMBER_MASK (0xF << TEGRA210_AMX_MAP_WORD_NUMBER_SHIFT) +#define TEGRA210_AMX_MAP_BYTE_NUMBER_SHIFT 0 +#define TEGRA210_AMX_MAP_BYTE_NUMBER_MASK (0x3 << TEGRA210_AMX_MAP_BYTE_NUMBER_SHIFT) + +enum { + TEGRA210_AMX_WAIT_ON_ALL, + TEGRA210_AMX_WAIT_ON_ANY, +}; + +enum { + TEGRA210_AMX_RX_DISABLE, + TEGRA210_AMX_RX_ENABLE, +}; + +struct tegra210_amx_soc_data { + bool is_auto_disable_supported; + const struct regmap_config *regmap_conf; +}; + +struct tegra210_amx { + struct regmap *regmap; + unsigned int map[TEGRA210_AMX_RAM_DEPTH]; + unsigned int byte_mask[2]; + int input_channels[TEGRA210_AMX_NUM_INPUTS]; + int output_channels; + const struct tegra210_amx_soc_data *soc_data; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_dmic.c b/sound/soc/tegra/tegra210_dmic.c new file mode 100644 index 00000000..3ee630a1 --- /dev/null +++ b/sound/soc/tegra/tegra210_dmic.c @@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_dmic.c - Tegra210 DMIC driver +// +// Copyright (c) 2020-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra210_dmic.h" + +#include + +static const struct reg_default tegra210_dmic_reg_defaults[] = { + { TEGRA210_DMIC_TX_INT_MASK, 0x00000001 }, + { TEGRA210_DMIC_TX_CIF_CTRL, 0x00007700 }, + { TEGRA210_DMIC_CG, 0x1 }, + { TEGRA210_DMIC_CTRL, 0x00000301 }, + /* Below enables all filters - DCR, LP and SC */ + { TEGRA210_DMIC_DBG_CTRL, 0xe }, + /* Below as per latest POR value */ + { TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4, 0x0 }, + /* LP filter is configured for pass through and used to apply gain */ + { TEGRA210_DMIC_LP_BIQUAD_0_COEF_0, 0x00800000 }, + { TEGRA210_DMIC_LP_BIQUAD_0_COEF_1, 0x0 }, + { TEGRA210_DMIC_LP_BIQUAD_0_COEF_2, 0x0 }, + { TEGRA210_DMIC_LP_BIQUAD_0_COEF_3, 0x0 }, + { TEGRA210_DMIC_LP_BIQUAD_0_COEF_4, 0x0 }, + { TEGRA210_DMIC_LP_BIQUAD_1_COEF_0, 0x00800000 }, + { TEGRA210_DMIC_LP_BIQUAD_1_COEF_1, 0x0 }, + { TEGRA210_DMIC_LP_BIQUAD_1_COEF_2, 0x0 }, + { TEGRA210_DMIC_LP_BIQUAD_1_COEF_3, 0x0 }, + { TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 0x0 }, +}; + +static int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev) +{ + struct tegra210_dmic *dmic = dev_get_drvdata(dev); + + regcache_cache_only(dmic->regmap, true); + regcache_mark_dirty(dmic->regmap); + + clk_disable_unprepare(dmic->clk_dmic); + + return 0; +} + +static int __maybe_unused tegra210_dmic_runtime_resume(struct device *dev) +{ + struct tegra210_dmic *dmic = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(dmic->clk_dmic); + if (err) { + dev_err(dev, "failed to enable DMIC clock, err: %d\n", err); + return err; + } + + regcache_cache_only(dmic->regmap, false); + regcache_sync(dmic->regmap); + + return 0; +} + +static const unsigned int tegra210_dmic_fmts[] = { + 0, + TEGRA_ACIF_BITS_16, + TEGRA_ACIF_BITS_32, +}; + +static int tegra210_dmic_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_dmic *dmic = snd_soc_dai_get_drvdata(dai); + unsigned int srate, clk_rate, channels; + struct tegra_cif_conf cif_conf; + unsigned long long gain_q23 = DEFAULT_GAIN_Q23; + int err; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + + cif_conf.audio_ch = channels; + if (dmic->audio_ch_override) + cif_conf.audio_ch = dmic->audio_ch_override; + + switch (dmic->ch_select) { + case DMIC_CH_SELECT_LEFT: + case DMIC_CH_SELECT_RIGHT: + cif_conf.client_ch = 1; + break; + case DMIC_CH_SELECT_STEREO: + cif_conf.client_ch = 2; + break; + default: + dev_err(dai->dev, "invalid DMIC client channels\n"); + return -EINVAL; + } + + srate = params_rate(params); + if (dmic->srate_override) + srate = dmic->srate_override; + + /* + * DMIC clock rate is a multiple of 'Over Sampling Ratio' and + * 'Sample Rate'. The supported OSR values are 64, 128 and 256. + */ + clk_rate = (DMIC_OSR_FACTOR << dmic->osr_val) * srate; + + err = clk_set_rate(dmic->clk_dmic, clk_rate); + if (err) { + dev_err(dai->dev, "can't set DMIC clock rate %u, err: %d\n", + clk_rate, err); + return err; + } + + regmap_update_bits(dmic->regmap, + /* Reg */ + TEGRA210_DMIC_CTRL, + /* Mask */ + TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK | + TEGRA210_DMIC_CTRL_OSR_MASK | + TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK, + /* Value */ + (dmic->lrsel << LRSEL_POL_SHIFT) | + (dmic->osr_val << OSR_SHIFT) | + ((dmic->ch_select + 1) << CH_SEL_SHIFT)); + + /* + * Use LP filter gain register to apply boost. + * Boost Gain Volume control has 100x factor. + */ + if (dmic->boost_gain) + gain_q23 = div_u64(gain_q23 * dmic->boost_gain, 100); + + regmap_write(dmic->regmap, TEGRA210_DMIC_LP_FILTER_GAIN, + (unsigned int)gain_q23); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + dev_err(dai->dev, "unsupported format!\n"); + return -EOPNOTSUPP; + } + + if (dmic->audio_bits_override) + cif_conf.audio_bits = + tegra210_dmic_fmts[dmic->audio_bits_override]; + + cif_conf.client_bits = TEGRA_ACIF_BITS_24; + cif_conf.mono_conv = dmic->mono_to_stereo; + cif_conf.stereo_conv = dmic->stereo_to_mono; + + tegra_set_cif(dmic->regmap, TEGRA210_DMIC_TX_CIF_CTRL, &cif_conf); + + return 0; +} + +static int tegra210_dmic_get_boost_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.integer.value[0] = dmic->boost_gain; + + return 0; +} + +static int tegra210_dmic_put_boost_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + int value = ucontrol->value.integer.value[0]; + + if (value == dmic->boost_gain) + return 0; + + dmic->boost_gain = value; + + return 1; +} + +static int tegra210_dmic_get_ch_select(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = dmic->ch_select; + + return 0; +} + +static int tegra210_dmic_put_ch_select(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dmic->ch_select) + return 0; + + dmic->ch_select = value; + + return 1; +} + +static int tegra210_dmic_get_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = dmic->mono_to_stereo; + + return 0; +} + +static int tegra210_dmic_put_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dmic->mono_to_stereo) + return 0; + + dmic->mono_to_stereo = value; + + return 1; +} + +static int tegra210_dmic_get_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = dmic->stereo_to_mono; + + return 0; +} + +static int tegra210_dmic_put_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dmic->stereo_to_mono) + return 0; + + dmic->stereo_to_mono = value; + + return 1; +} + +static int tegra210_dmic_get_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = dmic->audio_bits_override; + + return 0; +} + +static int tegra210_dmic_put_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (dmic->audio_bits_override == value) + return 0; + + dmic->audio_bits_override = value; + + return 1; +} + +static int tegra210_dmic_get_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.integer.value[0] = dmic->audio_ch_override; + + return 0; +} + +static int tegra210_dmic_put_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + int value = ucontrol->value.integer.value[0]; + + if (dmic->audio_ch_override == value) + return 0; + + dmic->audio_ch_override = value; + + return 1; +} + +static int tegra210_dmic_get_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.integer.value[0] = dmic->srate_override; + + return 0; +} + +static int tegra210_dmic_put_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + int value = ucontrol->value.integer.value[0]; + + if (dmic->srate_override == value) + return 0; + + dmic->srate_override = value; + + return 1; +} + +static int tegra210_dmic_get_osr_val(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = dmic->osr_val; + + return 0; +} + +static int tegra210_dmic_put_osr_val(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dmic->osr_val) + return 0; + + dmic->osr_val = value; + + return 1; +} + +static int tegra210_dmic_get_pol_sel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = dmic->lrsel; + + return 0; +} + +static int tegra210_dmic_put_pol_sel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == dmic->lrsel) + return 0; + + dmic->lrsel = value; + + return 1; +} + +static const struct snd_soc_dai_ops tegra210_dmic_dai_ops = { + .hw_params = tegra210_dmic_hw_params, +}; + +/* + * Three DAIs are exposed + * 1. "CIF" DAI for connecting with XBAR + * 2. "DAP" DAI for connecting with CODEC + * 3. "DUMMY_SOURCE" can be used when no external + * codec connection is available. In such case + * "DAP" is connected with "DUMMY_SOURCE" + */ +static struct snd_soc_dai_driver tegra210_dmic_dais[] = { + { + .name = "DMIC-CIF", + .capture = { + .stream_name = "CIF-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, + { + .name = "DMIC-DAP", +#if IS_ENABLED(CONFIG_TEGRA_DPCM) + .capture = { + .stream_name = "DAP-Capture", +#else + .playback = { + .stream_name = "DAP-Playback", +#endif + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra210_dmic_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "DUMMY_SOURCE", + .capture = { + .stream_name = "Dummy-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, +}; + +static const struct snd_soc_dapm_widget tegra210_dmic_widgets[] = { + SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_DMIC_ENABLE, 0, 0), + SND_SOC_DAPM_MIC("MIC", NULL), +}; + +static const struct snd_soc_dapm_route tegra210_dmic_routes[] = { +#if IS_ENABLED(CONFIG_TEGRA_DPCM) + { "XBAR-RX", NULL, "XBAR-Capture" }, + { "XBAR-Capture", NULL, "CIF-Capture" }, + { "CIF-Capture", NULL, "TX" }, + { "TX", NULL, "DAP-Capture" }, + { "DAP-Capture", NULL, "MIC" }, +#else + { "CIF-Capture", NULL, "TX" }, + { "TX", NULL, "DAP-Playback" }, + { "Dummy-Capture", NULL, "MIC" }, +#endif +}; + +static const char * const tegra210_dmic_ch_select[] = { + "Left", "Right", "Stereo", +}; + +static const struct soc_enum tegra210_dmic_ch_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_ch_select), + tegra210_dmic_ch_select); + +static const char * const tegra210_dmic_mono_conv_text[] = { + "Zero", "Copy", +}; + +static const char * const tegra210_dmic_stereo_conv_text[] = { + "CH0", "CH1", "AVG", +}; + +static const struct soc_enum tegra210_dmic_mono_conv_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_mono_conv_text), + tegra210_dmic_mono_conv_text); + +static const struct soc_enum tegra210_dmic_stereo_conv_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_stereo_conv_text), + tegra210_dmic_stereo_conv_text); + +static const char * const tegra210_dmic_format_text[] = { + "None", + "16", + "32", +}; + +static const struct soc_enum tegra210_dmic_format_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_format_text), + tegra210_dmic_format_text); + +static const char * const tegra210_dmic_osr_text[] = { + "OSR_64", "OSR_128", "OSR_256", +}; + +static const struct soc_enum tegra210_dmic_osr_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_osr_text), + tegra210_dmic_osr_text); + +static const char * const tegra210_dmic_lrsel_text[] = { + "Left", "Right", +}; + +static const struct soc_enum tegra210_dmic_lrsel_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_lrsel_text), + tegra210_dmic_lrsel_text); + +static const struct snd_kcontrol_new tegra210_dmic_controls[] = { + SOC_SINGLE_EXT("Boost Gain Volume", 0, 0, MAX_BOOST_GAIN, 0, + tegra210_dmic_get_boost_gain, + tegra210_dmic_put_boost_gain), + SOC_ENUM_EXT("Channel Select", tegra210_dmic_ch_enum, + tegra210_dmic_get_ch_select, tegra210_dmic_put_ch_select), + SOC_ENUM_EXT("Mono To Stereo", + tegra210_dmic_mono_conv_enum, + tegra210_dmic_get_mono_to_stereo, + tegra210_dmic_put_mono_to_stereo), + SOC_ENUM_EXT("Stereo To Mono", + tegra210_dmic_stereo_conv_enum, + tegra210_dmic_get_stereo_to_mono, + tegra210_dmic_put_stereo_to_mono), + SOC_ENUM_EXT("Audio Bit Format", tegra210_dmic_format_enum, + tegra210_dmic_get_audio_bitfmt, + tegra210_dmic_put_audio_bitfmt), + SOC_SINGLE_EXT("Sample Rate", 0, 0, 48000, 0, + tegra210_dmic_get_sample_rate, + tegra210_dmic_put_sample_rate), + SOC_SINGLE_EXT("Audio Channels", 0, 0, 2, 0, tegra210_dmic_get_audio_ch, + tegra210_dmic_put_audio_ch), + SOC_ENUM_EXT("OSR Value", tegra210_dmic_osr_enum, + tegra210_dmic_get_osr_val, tegra210_dmic_put_osr_val), + SOC_ENUM_EXT("LR Polarity Select", tegra210_dmic_lrsel_enum, + tegra210_dmic_get_pol_sel, tegra210_dmic_put_pol_sel), +}; + +static const struct snd_soc_component_driver tegra210_dmic_compnt = { + .dapm_widgets = tegra210_dmic_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_dmic_widgets), + .dapm_routes = tegra210_dmic_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_dmic_routes), + .controls = tegra210_dmic_controls, + .num_controls = ARRAY_SIZE(tegra210_dmic_controls), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static bool tegra210_dmic_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_DMIC_TX_INT_MASK ... TEGRA210_DMIC_TX_CIF_CTRL: + case TEGRA210_DMIC_ENABLE ... TEGRA210_DMIC_CG: + case TEGRA210_DMIC_CTRL: + case TEGRA210_DMIC_DBG_CTRL: + case TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 ... TEGRA210_DMIC_LP_BIQUAD_1_COEF_4: + return true; + default: + return false; + }; +} + +static bool tegra210_dmic_rd_reg(struct device *dev, unsigned int reg) +{ + if (tegra210_dmic_wr_reg(dev, reg)) + return true; + + switch (reg) { + case TEGRA210_DMIC_TX_STATUS: + case TEGRA210_DMIC_TX_INT_STATUS: + case TEGRA210_DMIC_STATUS: + case TEGRA210_DMIC_INT_STATUS: + return true; + default: + return false; + }; +} + +static bool tegra210_dmic_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_DMIC_TX_STATUS: + case TEGRA210_DMIC_TX_INT_STATUS: + case TEGRA210_DMIC_TX_INT_SET: + case TEGRA210_DMIC_SOFT_RESET: + case TEGRA210_DMIC_STATUS: + case TEGRA210_DMIC_INT_STATUS: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra210_dmic_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, + .writeable_reg = tegra210_dmic_wr_reg, + .readable_reg = tegra210_dmic_rd_reg, + .volatile_reg = tegra210_dmic_volatile_reg, + .reg_defaults = tegra210_dmic_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int tegra210_dmic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_dmic *dmic; + void __iomem *regs; + int err; + + dmic = devm_kzalloc(dev, sizeof(*dmic), GFP_KERNEL); + if (!dmic) + return -ENOMEM; + + dmic->osr_val = DMIC_OSR_64; + dmic->ch_select = DMIC_CH_SELECT_STEREO; + dmic->lrsel = DMIC_LRSEL_LEFT; + dmic->boost_gain = 0; + dmic->stereo_to_mono = 0; /* "CH0" */ + + dev_set_drvdata(dev, dmic); + + dmic->clk_dmic = devm_clk_get(dev, "dmic"); + if (IS_ERR(dmic->clk_dmic)) { + dev_err(dev, "can't retrieve DMIC clock\n"); + return PTR_ERR(dmic->clk_dmic); + } + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dmic->regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_dmic_regmap_config); + if (IS_ERR(dmic->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(dmic->regmap); + } + + regcache_cache_only(dmic->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra210_dmic_compnt, + tegra210_dmic_dais, + ARRAY_SIZE(tegra210_dmic_dais)); + if (err) { + dev_err(dev, "can't register DMIC component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra210_dmic_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_dmic_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend, + tegra210_dmic_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct of_device_id tegra210_dmic_of_match[] = { + { .compatible = "nvidia,tegra210-dmic" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_dmic_of_match); + +static struct platform_driver tegra210_dmic_driver = { + .driver = { + .name = "tegra210-dmic", + .of_match_table = tegra210_dmic_of_match, + .pm = &tegra210_dmic_pm_ops, + }, + .probe = tegra210_dmic_probe, + .remove = tegra210_dmic_remove, +}; +module_platform_driver(tegra210_dmic_driver) + +MODULE_AUTHOR("Rahul Mittal "); +MODULE_DESCRIPTION("Tegra210 ASoC DMIC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_dmic.h b/sound/soc/tegra/tegra210_dmic.h new file mode 100644 index 00000000..a08d1364 --- /dev/null +++ b/sound/soc/tegra/tegra210_dmic.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_dmic.h - Definitions for Tegra210 DMIC driver + * + * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_DMIC_H__ +#define __TEGRA210_DMIC_H__ + +/* Register offsets from DMIC BASE */ +#define TEGRA210_DMIC_TX_STATUS 0x0c +#define TEGRA210_DMIC_TX_INT_STATUS 0x10 +#define TEGRA210_DMIC_TX_INT_MASK 0x14 +#define TEGRA210_DMIC_TX_INT_SET 0x18 +#define TEGRA210_DMIC_TX_INT_CLEAR 0x1c +#define TEGRA210_DMIC_TX_CIF_CTRL 0x20 +#define TEGRA210_DMIC_ENABLE 0x40 +#define TEGRA210_DMIC_SOFT_RESET 0x44 +#define TEGRA210_DMIC_CG 0x48 +#define TEGRA210_DMIC_STATUS 0x4c +#define TEGRA210_DMIC_INT_STATUS 0x50 +#define TEGRA210_DMIC_CTRL 0x64 +#define TEGRA210_DMIC_DBG_CTRL 0x70 +#define TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 0x88 +#define TEGRA210_DMIC_LP_FILTER_GAIN 0x8c +#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_0 0x90 +#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_1 0x94 +#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_2 0x98 +#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_3 0x9c +#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_4 0xa0 +#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_0 0xa4 +#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_1 0xa8 +#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_2 0xac +#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_3 0xb0 +#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_4 0xb4 + +/* Fields in TEGRA210_DMIC_CTRL */ +#define CH_SEL_SHIFT 8 +#define TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK (0x3 << CH_SEL_SHIFT) +#define LRSEL_POL_SHIFT 4 +#define TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK (0x1 << LRSEL_POL_SHIFT) +#define OSR_SHIFT 0 +#define TEGRA210_DMIC_CTRL_OSR_MASK (0x3 << OSR_SHIFT) + +#define DMIC_OSR_FACTOR 64 + +#define DEFAULT_GAIN_Q23 0x800000 + +/* Max boost gain factor used for mixer control */ +#define MAX_BOOST_GAIN 25599 + +enum tegra_dmic_ch_select { + DMIC_CH_SELECT_LEFT, + DMIC_CH_SELECT_RIGHT, + DMIC_CH_SELECT_STEREO, +}; + +enum tegra_dmic_osr { + DMIC_OSR_64, + DMIC_OSR_128, + DMIC_OSR_256, +}; + +enum tegra_dmic_lrsel { + DMIC_LRSEL_LEFT, + DMIC_LRSEL_RIGHT, +}; + +struct tegra210_dmic { + struct clk *clk_dmic; + struct regmap *regmap; + unsigned int audio_ch_override; + unsigned int audio_bits_override; + unsigned int srate_override; + unsigned int mono_to_stereo; + unsigned int stereo_to_mono; + unsigned int boost_gain; + unsigned int ch_select; + unsigned int osr_val; + unsigned int lrsel; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c new file mode 100644 index 00000000..57e629f5 --- /dev/null +++ b/sound/soc/tegra/tegra210_i2s.c @@ -0,0 +1,1295 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_i2s.c - Tegra210 I2S driver +// +// Copyright (c) 2020-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra210_i2s.h" + +#if IS_ENABLED(CONFIG_TEGRA_DPCM) +#define TEGRA_PLAYBACK_STREAM SNDRV_PCM_STREAM_PLAYBACK +#else +#define TEGRA_PLAYBACK_STREAM SNDRV_PCM_STREAM_CAPTURE +#endif + +static const struct reg_default tegra210_i2s_reg_defaults[] = { + { TEGRA210_I2S_RX_INT_MASK, 0x00000003 }, + { TEGRA210_I2S_RX_CIF_CTRL, 0x00007700 }, + { TEGRA210_I2S_TX_INT_MASK, 0x00000003 }, + { TEGRA210_I2S_TX_CIF_CTRL, 0x00007700 }, + { TEGRA210_I2S_CG, 0x1 }, + { TEGRA210_I2S_TIMING, 0x0000001f }, + { TEGRA210_I2S_ENABLE, 0x1 }, + /* + * Below update does not have any effect on Tegra186 and Tegra194. + * On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update + * is required to select i2s4b for it to be functional for I2S + * operation. + */ + { TEGRA210_I2S_CYA, 0x1 }, +}; + +static void tegra210_i2s_set_slot_ctrl(struct regmap *regmap, + unsigned int total_slots, + unsigned int tx_slot_mask, + unsigned int rx_slot_mask) +{ + regmap_write(regmap, TEGRA210_I2S_SLOT_CTRL, total_slots - 1); + regmap_write(regmap, TEGRA210_I2S_TX_SLOT_CTRL, tx_slot_mask); + regmap_write(regmap, TEGRA210_I2S_RX_SLOT_CTRL, rx_slot_mask); +} + +static int tegra210_i2s_set_clock_rate(struct device *dev, + unsigned int clock_rate) +{ + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + unsigned int val; + int err; + + regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val); + + /* No need to set rates if I2S is being operated in slave */ + if (!(val & I2S_CTRL_MASTER_EN)) + return 0; + + err = clk_set_rate(i2s->clk_i2s, clock_rate); + if (err) { + dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n", + clock_rate, err); + return err; + } + + if (!IS_ERR(i2s->clk_sync_input)) { + /* + * Other I/O modules in AHUB can use i2s bclk as reference + * clock. Below sets sync input clock rate as per bclk, + * which can be used as input to other I/O modules. + */ + err = clk_set_rate(i2s->clk_sync_input, clock_rate); + if (err) { + dev_err(dev, + "can't set I2S sync input rate %u, err = %d\n", + clock_rate, err); + return err; + } + } + + return 0; +} + +static int tegra210_i2s_sw_reset(struct snd_soc_component *compnt, + bool is_playback) +{ + struct device *dev = compnt->dev; + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + unsigned int reset_mask = I2S_SOFT_RESET_MASK; + unsigned int reset_en = I2S_SOFT_RESET_EN; + unsigned int reset_reg, cif_reg, stream_reg; + unsigned int cif_ctrl, stream_ctrl, i2s_ctrl, val; + int err; + + if (is_playback) { + reset_reg = TEGRA210_I2S_RX_SOFT_RESET; + cif_reg = TEGRA210_I2S_RX_CIF_CTRL; + stream_reg = TEGRA210_I2S_RX_CTRL; + } else { + reset_reg = TEGRA210_I2S_TX_SOFT_RESET; + cif_reg = TEGRA210_I2S_TX_CIF_CTRL; + stream_reg = TEGRA210_I2S_TX_CTRL; + } + + /* Store CIF and I2S control values */ + regmap_read(i2s->regmap, cif_reg, &cif_ctrl); + regmap_read(i2s->regmap, stream_reg, &stream_ctrl); + regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &i2s_ctrl); + + /* Reset to make sure the previous transactions are clean */ + regmap_update_bits(i2s->regmap, reset_reg, reset_mask, reset_en); + + err = regmap_read_poll_timeout(i2s->regmap, reset_reg, val, + !(val & reset_mask & reset_en), + 10, 10000); + if (err) { + dev_err(dev, "timeout: failed to reset I2S for %s\n", + is_playback ? "playback" : "capture"); + return err; + } + + /* Restore CIF and I2S control values */ + regmap_write(i2s->regmap, cif_reg, cif_ctrl); + regmap_write(i2s->regmap, stream_reg, stream_ctrl); + regmap_write(i2s->regmap, TEGRA210_I2S_CTRL, i2s_ctrl); + + return 0; +} + +static int tegra210_i2s_init(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *compnt = snd_soc_dapm_to_component(w->dapm); + struct device *dev = compnt->dev; + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + unsigned int val, status_reg; + bool is_playback; + int err; + + switch (w->reg) { + case TEGRA210_I2S_RX_ENABLE: + is_playback = true; + status_reg = TEGRA210_I2S_RX_STATUS; + break; + case TEGRA210_I2S_TX_ENABLE: + is_playback = false; + status_reg = TEGRA210_I2S_TX_STATUS; + break; + default: + return -EINVAL; + } + + /* Ensure I2S is in disabled state before new session */ + err = regmap_read_poll_timeout(i2s->regmap, status_reg, val, + !(val & I2S_EN_MASK & I2S_EN), + 10, 10000); + if (err) { + dev_err(dev, "timeout: previous I2S %s is still active\n", + is_playback ? "playback" : "capture"); + return err; + } + + return tegra210_i2s_sw_reset(compnt, is_playback); +} + +static int __maybe_unused tegra210_i2s_runtime_suspend(struct device *dev) +{ + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + + regcache_cache_only(i2s->regmap, true); + regcache_mark_dirty(i2s->regmap); + + clk_disable_unprepare(i2s->clk_i2s); + + return 0; +} + +static int __maybe_unused tegra210_i2s_runtime_resume(struct device *dev) +{ + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(i2s->clk_i2s); + if (err) { + dev_err(dev, "failed to enable I2S bit clock, err: %d\n", err); + return err; + } + + regcache_cache_only(i2s->regmap, false); + regcache_sync(i2s->regmap); + + return 0; +} + +static void tegra210_i2s_set_data_offset(struct tegra210_i2s *i2s, + unsigned int data_offset) +{ + /* Capture path */ + regmap_update_bits(i2s->regmap, TEGRA210_I2S_TX_CTRL, + I2S_CTRL_DATA_OFFSET_MASK, + data_offset << I2S_DATA_SHIFT); + + /* Playback path */ + regmap_update_bits(i2s->regmap, TEGRA210_I2S_RX_CTRL, + I2S_CTRL_DATA_OFFSET_MASK, + data_offset << I2S_DATA_SHIFT); +} + +static int tegra210_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int mask, val; + + mask = I2S_CTRL_MASTER_EN_MASK; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val = I2S_CTRL_MASTER_EN; + break; + default: + return -EINVAL; + } + + mask |= I2S_CTRL_FRAME_FMT_MASK | I2S_CTRL_LRCK_POL_MASK; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE; + val |= I2S_CTRL_LRCK_POL_HIGH; + tegra210_i2s_set_data_offset(i2s, 1); + break; + case SND_SOC_DAIFMT_DSP_B: + val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE; + val |= I2S_CTRL_LRCK_POL_HIGH; + tegra210_i2s_set_data_offset(i2s, 0); + break; + /* I2S mode has data offset of 1 */ + case SND_SOC_DAIFMT_I2S: + val |= I2S_CTRL_FRAME_FMT_LRCK_MODE; + val |= I2S_CTRL_LRCK_POL_LOW; + tegra210_i2s_set_data_offset(i2s, 1); + break; + /* + * For RJ mode data offset is dependent on the sample size + * and the bclk ratio, and so is set when hw_params is called. + */ + case SND_SOC_DAIFMT_RIGHT_J: + val |= I2S_CTRL_FRAME_FMT_LRCK_MODE; + val |= I2S_CTRL_LRCK_POL_HIGH; + break; + case SND_SOC_DAIFMT_LEFT_J: + val |= I2S_CTRL_FRAME_FMT_LRCK_MODE; + val |= I2S_CTRL_LRCK_POL_HIGH; + tegra210_i2s_set_data_offset(i2s, 0); + break; + default: + return -EINVAL; + } + + mask |= I2S_CTRL_EDGE_CTRL_MASK; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val |= I2S_CTRL_EDGE_CTRL_POS_EDGE; + break; + case SND_SOC_DAIFMT_NB_IF: + val |= I2S_CTRL_EDGE_CTRL_POS_EDGE; + val ^= I2S_CTRL_LRCK_POL_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE; + break; + case SND_SOC_DAIFMT_IB_IF: + val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE; + val ^= I2S_CTRL_LRCK_POL_MASK; + break; + default: + return -EINVAL; + } + + regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, mask, val); + + i2s->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + return 0; +} + +static int tegra210_i2s_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + /* Copy the required tx and rx mask */ + i2s->tx_mask = (tx_mask > DEFAULT_I2S_SLOT_MASK) ? + DEFAULT_I2S_SLOT_MASK : tx_mask; + i2s->rx_mask = (rx_mask > DEFAULT_I2S_SLOT_MASK) ? + DEFAULT_I2S_SLOT_MASK : rx_mask; + + return 0; +} + +static int tegra210_i2s_get_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->srate_override; + + return 0; +} + +static int tegra210_i2s_put_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (i2s->srate_override == value) + return 0; + + i2s->srate_override = value; + + return 1; +} + +static int tegra210_i2s_pget_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.enumerated.item[0] = + i2s->audio_fmt_override[I2S_RX_PATH]; + + return 0; +} + +static int tegra210_i2s_pput_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (i2s->audio_fmt_override[I2S_RX_PATH] == value) + return 0; + + i2s->audio_fmt_override[I2S_RX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_cget_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.enumerated.item[0] = + i2s->audio_fmt_override[I2S_TX_PATH]; + + return 0; +} + +static int tegra210_i2s_cput_audio_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (i2s->audio_fmt_override[I2S_TX_PATH] == value) + return 0; + + i2s->audio_fmt_override[I2S_TX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_get_client_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.enumerated.item[0] = i2s->client_fmt_override; + + return 0; +} + +static int tegra210_i2s_put_client_bitfmt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (i2s->client_fmt_override == value) + return 0; + + i2s->client_fmt_override = value; + + return 1; +} + +static int tegra210_i2s_pget_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->audio_ch_override[I2S_RX_PATH]; + + return 0; +} + +static int tegra210_i2s_pput_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (i2s->audio_ch_override[I2S_RX_PATH] == value) + return 0; + + i2s->audio_ch_override[I2S_RX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_cget_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->audio_ch_override[I2S_TX_PATH]; + + return 0; +} + +static int tegra210_i2s_cput_audio_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (i2s->audio_ch_override[I2S_TX_PATH] == value) + return 0; + + i2s->audio_ch_override[I2S_TX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_get_client_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->client_ch_override; + + return 0; +} + +static int tegra210_i2s_put_client_ch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (i2s->client_ch_override == value) + return 0; + + i2s->client_ch_override = value; + + return 1; +} + +static int tegra210_i2s_get_loopback(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->loopback; + + return 0; +} + +static int tegra210_i2s_put_loopback(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (value == i2s->loopback) + return 0; + + i2s->loopback = value; + + regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, I2S_CTRL_LPBK_MASK, + i2s->loopback << I2S_CTRL_LPBK_SHIFT); + + return 1; +} + +static int tegra210_i2s_get_fsync_width(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->fsync_width; + + return 0; +} + +static int tegra210_i2s_put_fsync_width(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (value == i2s->fsync_width) + return 0; + + i2s->fsync_width = value; + + /* + * Frame sync width is used only for FSYNC modes and not + * applicable for LRCK modes. Reset value for this field is "0", + * which means the width is one bit clock wide. + * The width requirement may depend on the codec and in such + * cases mixer control is used to update custom values. A value + * of "N" here means, width is "N + 1" bit clock wide. + */ + regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, + I2S_CTRL_FSYNC_WIDTH_MASK, + i2s->fsync_width << I2S_FSYNC_WIDTH_SHIFT); + + return 1; +} + +static int tegra210_i2s_cget_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.enumerated.item[0] = i2s->stereo_to_mono[I2S_TX_PATH]; + + return 0; +} + +static int tegra210_i2s_cput_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == i2s->stereo_to_mono[I2S_TX_PATH]) + return 0; + + i2s->stereo_to_mono[I2S_TX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_cget_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.enumerated.item[0] = i2s->mono_to_stereo[I2S_TX_PATH]; + + return 0; +} + +static int tegra210_i2s_cput_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == i2s->mono_to_stereo[I2S_TX_PATH]) + return 0; + + i2s->mono_to_stereo[I2S_TX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_pget_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.enumerated.item[0] = i2s->stereo_to_mono[I2S_RX_PATH]; + + return 0; +} + +static int tegra210_i2s_pput_stereo_to_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == i2s->stereo_to_mono[I2S_RX_PATH]) + return 0; + + i2s->stereo_to_mono[I2S_RX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_pget_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.enumerated.item[0] = i2s->mono_to_stereo[I2S_RX_PATH]; + + return 0; +} + +static int tegra210_i2s_pput_mono_to_stereo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + unsigned int value = ucontrol->value.enumerated.item[0]; + + if (value == i2s->mono_to_stereo[I2S_RX_PATH]) + return 0; + + i2s->mono_to_stereo[I2S_RX_PATH] = value; + + return 1; +} + +static int tegra210_i2s_pget_fifo_th(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->rx_fifo_th; + + return 0; +} + +static int tegra210_i2s_pput_fifo_th(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (value == i2s->rx_fifo_th) + return 0; + + i2s->rx_fifo_th = value; + + return 1; +} + +static int tegra210_i2s_get_bclk_ratio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + + ucontrol->value.integer.value[0] = i2s->bclk_ratio; + + return 0; +} + +static int tegra210_i2s_put_bclk_ratio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); + int value = ucontrol->value.integer.value[0]; + + if (value == i2s->bclk_ratio) + return 0; + + i2s->bclk_ratio = value; + + return 1; +} + +static int tegra210_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, + unsigned int ratio) +{ + struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + i2s->bclk_ratio = ratio; + + return 0; +} + +static const char * const tegra210_i2s_format_text[] = { + "None", + "16", + "32", +}; + +static const unsigned int tegra210_cif_fmt[] = { + 0, + TEGRA_ACIF_BITS_16, + TEGRA_ACIF_BITS_32, +}; + +static const unsigned int tegra210_i2s_bit_fmt[] = { + 0, + I2S_BITS_16, + I2S_BITS_32, +}; + +static const unsigned int tegra210_i2s_sample_size[] = { + 0, + 16, + 32, +}; + +static const struct soc_enum tegra210_i2s_format_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_format_text), + tegra210_i2s_format_text); + +static int tegra210_i2s_set_timing_params(struct device *dev, + unsigned int sample_size, + unsigned int srate, + unsigned int channels) +{ + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + unsigned int val, bit_count, bclk_rate, num_bclk = sample_size; + int err; + + if (i2s->bclk_ratio) + num_bclk *= i2s->bclk_ratio; + + if (i2s->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) + tegra210_i2s_set_data_offset(i2s, num_bclk - sample_size); + + /* I2S bit clock rate */ + bclk_rate = srate * channels * num_bclk; + + err = tegra210_i2s_set_clock_rate(dev, bclk_rate); + if (err) { + dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n", + bclk_rate, err); + return err; + } + + regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val); + + /* + * For LRCK mode, channel bit count depends on number of bit clocks + * on the left channel, where as for FSYNC mode bit count depends on + * the number of bit clocks in both left and right channels for DSP + * mode or the number of bit clocks in one TDM frame. + * + */ + switch (val & I2S_CTRL_FRAME_FMT_MASK) { + case I2S_CTRL_FRAME_FMT_LRCK_MODE: + bit_count = (bclk_rate / (srate * 2)) - 1; + break; + case I2S_CTRL_FRAME_FMT_FSYNC_MODE: + bit_count = (bclk_rate / srate) - 1; + + tegra210_i2s_set_slot_ctrl(i2s->regmap, channels, + i2s->tx_mask, i2s->rx_mask); + break; + default: + dev_err(dev, "invalid I2S frame format\n"); + return -EINVAL; + } + + if (bit_count > I2S_TIMING_CH_BIT_CNT_MASK) { + dev_err(dev, "invalid I2S channel bit count %u\n", bit_count); + return -EINVAL; + } + + regmap_write(i2s->regmap, TEGRA210_I2S_TIMING, + bit_count << I2S_TIMING_CH_BIT_CNT_SHIFT); + + return 0; +} + +static int tegra210_i2s_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_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int sample_size, channels, srate, val, reg, path; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + if (channels < 1) { + dev_err(dev, "invalid I2S %d channel configuration\n", + channels); + return -EINVAL; + } + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + val = I2S_BITS_8; + sample_size = 8; + cif_conf.audio_bits = TEGRA_ACIF_BITS_8; + cif_conf.client_bits = TEGRA_ACIF_BITS_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + val = I2S_BITS_16; + sample_size = 16; + cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + cif_conf.client_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = I2S_BITS_24; + + /* + * I2S bit clock is derived from PLLA_OUT0 and size of + * 24 bits results in fractional value and the clock + * is not accurate with this. To have integer clock + * division below is used. It means there are additional + * bit clocks (8 cycles) which are ignored. Codec picks + * up data for other channel when LRCK signal toggles. + */ + sample_size = 32; + + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + cif_conf.client_bits = TEGRA_ACIF_BITS_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val = I2S_BITS_32; + sample_size = 32; + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + cif_conf.client_bits = TEGRA_ACIF_BITS_32; + break; + default: + dev_err(dev, "unsupported format!\n"); + return -EOPNOTSUPP; + } + + if (i2s->client_fmt_override) { + val = tegra210_i2s_bit_fmt[i2s->client_fmt_override]; + sample_size = + tegra210_i2s_sample_size[i2s->client_fmt_override]; + cif_conf.client_bits = + tegra210_cif_fmt[i2s->client_fmt_override]; + } + + /* Program sample size */ + regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, + I2S_CTRL_BIT_SIZE_MASK, val); + + srate = params_rate(params); + + /* Override rate, channel and audio bit params as applicable */ + if (i2s->srate_override) + srate = i2s->srate_override; + + /* For playback I2S RX-CIF and for capture TX-CIF is used */ + if (substream->stream == TEGRA_PLAYBACK_STREAM) + path = I2S_RX_PATH; + else + path = I2S_TX_PATH; + + if (i2s->audio_ch_override[path]) + cif_conf.audio_ch = i2s->audio_ch_override[path]; + + if (i2s->client_ch_override) + cif_conf.client_ch = i2s->client_ch_override; + + if (i2s->audio_fmt_override[path]) + cif_conf.audio_bits = + tegra210_cif_fmt[i2s->audio_fmt_override[path]]; + + if (substream->stream == TEGRA_PLAYBACK_STREAM) { + unsigned int max_th; + + /* FIFO threshold in terms of frames */ + max_th = (I2S_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1; + + if (i2s->rx_fifo_th > max_th) + i2s->rx_fifo_th = max_th; + + cif_conf.threshold = i2s->rx_fifo_th; + + reg = TEGRA210_I2S_RX_CIF_CTRL; + } else { + reg = TEGRA210_I2S_TX_CIF_CTRL; + } + + cif_conf.mono_conv = i2s->mono_to_stereo[path]; + cif_conf.stereo_conv = i2s->stereo_to_mono[path]; + + tegra_set_cif(i2s->regmap, reg, &cif_conf); + + return tegra210_i2s_set_timing_params(dev, sample_size, srate, + cif_conf.client_ch); +} + +static const struct snd_soc_dai_ops tegra210_i2s_dai_ops = { + .set_fmt = tegra210_i2s_set_fmt, + .hw_params = tegra210_i2s_hw_params, + .set_bclk_ratio = tegra210_i2s_set_dai_bclk_ratio, + .set_tdm_slot = tegra210_i2s_set_tdm_slot, +}; + +/* + * Three DAIs are exposed + * 1. "CIF" DAI for connecting with XBAR + * 2. "DAP" DAI for connecting with CODEC + * 3. "DUMMY" can be used when no external codec connection is + * available. In such case "DAP" is connected with "DUMMY". + * Order of these DAIs should not be changed, since DAI links in DT refer + * to these DAIs depending on the index. + */ +static struct snd_soc_dai_driver tegra210_i2s_dais[] = { + { + .name = "I2S-CIF", + .playback = { + .stream_name = "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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, + { + .name = "I2S-DAP", + .playback = { + .stream_name = "DAP-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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "DAP-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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra210_i2s_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "DUMMY", + .playback = { + .stream_name = "Dummy-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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Dummy-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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, +}; + +static const char * const tegra210_i2s_stereo_conv_text[] = { + "CH0", "CH1", "AVG", +}; + +static const char * const tegra210_i2s_mono_conv_text[] = { + "Zero", "Copy", +}; + +static const struct soc_enum tegra210_i2s_mono_conv_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_mono_conv_text), + tegra210_i2s_mono_conv_text); + +static const struct soc_enum tegra210_i2s_stereo_conv_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_stereo_conv_text), + tegra210_i2s_stereo_conv_text); + +static const struct snd_kcontrol_new tegra210_i2s_controls[] = { + SOC_SINGLE_EXT("Sample Rate", 0, 0, 192000, 0, + tegra210_i2s_get_sample_rate, + tegra210_i2s_put_sample_rate), + SOC_ENUM_EXT("Playback Audio Bit Format", tegra210_i2s_format_enum, + tegra210_i2s_pget_audio_bitfmt, + tegra210_i2s_pput_audio_bitfmt), + SOC_ENUM_EXT("Capture Audio Bit Format", tegra210_i2s_format_enum, + tegra210_i2s_cget_audio_bitfmt, + tegra210_i2s_cput_audio_bitfmt), + SOC_ENUM_EXT("Client Bit Format", tegra210_i2s_format_enum, + tegra210_i2s_get_client_bitfmt, + tegra210_i2s_put_client_bitfmt), + SOC_SINGLE_EXT("Playback Audio Channels", 0, 0, 16, 0, + tegra210_i2s_pget_audio_ch, tegra210_i2s_pput_audio_ch), + SOC_SINGLE_EXT("Capture Audio Channels", 0, 0, 16, 0, + tegra210_i2s_cget_audio_ch, tegra210_i2s_cput_audio_ch), + SOC_SINGLE_EXT("Client Channels", 0, 0, 16, 0, + tegra210_i2s_get_client_ch, tegra210_i2s_put_client_ch), + SOC_SINGLE_EXT("Loopback", 0, 0, 1, 0, tegra210_i2s_get_loopback, + tegra210_i2s_put_loopback), + SOC_SINGLE_EXT("FSYNC Width", 0, 0, 255, 0, + tegra210_i2s_get_fsync_width, + tegra210_i2s_put_fsync_width), + SOC_ENUM_EXT("Capture Stereo To Mono", tegra210_i2s_stereo_conv_enum, + tegra210_i2s_cget_stereo_to_mono, + tegra210_i2s_cput_stereo_to_mono), + SOC_ENUM_EXT("Capture Mono To Stereo", tegra210_i2s_mono_conv_enum, + tegra210_i2s_cget_mono_to_stereo, + tegra210_i2s_cput_mono_to_stereo), + SOC_ENUM_EXT("Playback Stereo To Mono", tegra210_i2s_stereo_conv_enum, + tegra210_i2s_pget_mono_to_stereo, + tegra210_i2s_pput_mono_to_stereo), + SOC_ENUM_EXT("Playback Mono To Stereo", tegra210_i2s_mono_conv_enum, + tegra210_i2s_pget_stereo_to_mono, + tegra210_i2s_pput_stereo_to_mono), + SOC_SINGLE_EXT("Playback FIFO Threshold", 0, 0, I2S_RX_FIFO_DEPTH - 1, + 0, tegra210_i2s_pget_fifo_th, tegra210_i2s_pput_fifo_th), + SOC_SINGLE_EXT("BCLK Ratio", 0, 0, INT_MAX, 0, + tegra210_i2s_get_bclk_ratio, + tegra210_i2s_put_bclk_ratio), +}; + +static const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("RX", NULL, 0, TEGRA210_I2S_RX_ENABLE, + 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_AIF_OUT_E("TX", NULL, 0, TEGRA210_I2S_TX_ENABLE, + 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIC("MIC", NULL), + SND_SOC_DAPM_SPK("SPK", NULL), + +}; + +static const struct snd_soc_dapm_route tegra210_i2s_routes[] = { +#if IS_ENABLED(CONFIG_TEGRA_DPCM) + /* Playback route from XBAR */ + { "XBAR-Playback", NULL, "XBAR-TX" }, + { "CIF-Playback", NULL, "XBAR-Playback" }, + /* Capture route to XBAR */ + { "XBAR-RX", NULL, "XBAR-Capture" }, + { "XBAR-Capture", NULL, "CIF-Capture" }, + + { "RX", NULL, "CIF-Playback" }, + { "DAP-Playback", NULL, "RX" }, + { "SPK", NULL, "DAP-Playback" }, + { "CIF-Capture", NULL, "TX" }, + { "TX", NULL, "DAP-Capture" }, + { "DAP-Capture", NULL, "MIC" }, +#else + { "RX", NULL, "CIF-Playback" }, + { "DAP-Capture", NULL, "RX" }, + { "CIF-Capture", NULL, "TX" }, + { "TX", NULL, "DAP-Playback" }, + { "Dummy-Capture", NULL, "MIC" }, + { "SPK", NULL, "Dummy-Playback" }, +#endif +}; + +static const struct snd_soc_component_driver tegra210_i2s_cmpnt = { + .dapm_widgets = tegra210_i2s_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_i2s_widgets), + .dapm_routes = tegra210_i2s_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_i2s_routes), + .controls = tegra210_i2s_controls, + .num_controls = ARRAY_SIZE(tegra210_i2s_controls), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static bool tegra210_i2s_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_I2S_RX_ENABLE ... TEGRA210_I2S_RX_SOFT_RESET: + case TEGRA210_I2S_RX_INT_MASK ... TEGRA210_I2S_RX_CLK_TRIM: + case TEGRA210_I2S_TX_ENABLE ... TEGRA210_I2S_TX_SOFT_RESET: + case TEGRA210_I2S_TX_INT_MASK ... TEGRA210_I2S_TX_CLK_TRIM: + case TEGRA210_I2S_ENABLE ... TEGRA210_I2S_CG: + case TEGRA210_I2S_CTRL ... TEGRA210_I2S_CYA: + return true; + default: + return false; + }; +} + +static bool tegra210_i2s_rd_reg(struct device *dev, unsigned int reg) +{ + if (tegra210_i2s_wr_reg(dev, reg)) + return true; + + switch (reg) { + case TEGRA210_I2S_RX_STATUS: + case TEGRA210_I2S_RX_INT_STATUS: + case TEGRA210_I2S_RX_CIF_FIFO_STATUS: + case TEGRA210_I2S_TX_STATUS: + case TEGRA210_I2S_TX_INT_STATUS: + case TEGRA210_I2S_TX_CIF_FIFO_STATUS: + case TEGRA210_I2S_STATUS: + case TEGRA210_I2S_INT_STATUS: + return true; + default: + return false; + }; +} + +static bool tegra210_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_I2S_RX_STATUS: + case TEGRA210_I2S_RX_INT_STATUS: + case TEGRA210_I2S_RX_CIF_FIFO_STATUS: + case TEGRA210_I2S_TX_STATUS: + case TEGRA210_I2S_TX_INT_STATUS: + case TEGRA210_I2S_TX_CIF_FIFO_STATUS: + case TEGRA210_I2S_STATUS: + case TEGRA210_I2S_INT_STATUS: + case TEGRA210_I2S_RX_SOFT_RESET: + case TEGRA210_I2S_TX_SOFT_RESET: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra210_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_I2S_CYA, + .writeable_reg = tegra210_i2s_wr_reg, + .readable_reg = tegra210_i2s_rd_reg, + .volatile_reg = tegra210_i2s_volatile_reg, + .reg_defaults = tegra210_i2s_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_i2s_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int tegra210_i2s_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_i2s *i2s; + void __iomem *regs; + int err; + + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + i2s->rx_fifo_th = DEFAULT_I2S_RX_FIFO_THRESHOLD; + i2s->tx_mask = DEFAULT_I2S_SLOT_MASK; + i2s->rx_mask = DEFAULT_I2S_SLOT_MASK; + i2s->loopback = false; + + dev_set_drvdata(dev, i2s); + + i2s->clk_i2s = devm_clk_get(dev, "i2s"); + if (IS_ERR(i2s->clk_i2s)) { + dev_err(dev, "can't retrieve I2S bit clock\n"); + return PTR_ERR(i2s->clk_i2s); + } + + /* + * Not an error, as this clock is needed only when some other I/O + * requires input clock from current I2S instance, which is + * configurable from DT. + */ + i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); + if (IS_ERR(i2s->clk_sync_input)) + dev_dbg(dev, "can't retrieve I2S sync input clock\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + i2s->regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(i2s->regmap); + } + + regcache_cache_only(i2s->regmap, true); + + err = devm_snd_soc_register_component(dev, &tegra210_i2s_cmpnt, + tegra210_i2s_dais, + ARRAY_SIZE(tegra210_i2s_dais)); + if (err) { + dev_err(dev, "can't register I2S component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra210_i2s_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_i2s_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_i2s_runtime_suspend, + tegra210_i2s_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct of_device_id tegra210_i2s_of_match[] = { + { .compatible = "nvidia,tegra210-i2s" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_i2s_of_match); + +static struct platform_driver tegra210_i2s_driver = { + .driver = { + .name = "tegra210-i2s", + .of_match_table = tegra210_i2s_of_match, + .pm = &tegra210_i2s_pm_ops, + }, + .probe = tegra210_i2s_probe, + .remove = tegra210_i2s_remove, +}; +module_platform_driver(tegra210_i2s_driver) + +MODULE_AUTHOR("Songhee Baek "); +MODULE_DESCRIPTION("Tegra210 ASoC I2S driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h new file mode 100644 index 00000000..a98a8768 --- /dev/null +++ b/sound/soc/tegra/tegra210_i2s.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_i2s.h - Definitions for Tegra210 I2S driver + * + * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_I2S_H__ +#define __TEGRA210_I2S_H__ + +/* Register offsets from I2S*_BASE */ +#define TEGRA210_I2S_RX_ENABLE 0x0 +#define TEGRA210_I2S_RX_SOFT_RESET 0x4 +#define TEGRA210_I2S_RX_STATUS 0x0c +#define TEGRA210_I2S_RX_INT_STATUS 0x10 +#define TEGRA210_I2S_RX_INT_MASK 0x14 +#define TEGRA210_I2S_RX_INT_SET 0x18 +#define TEGRA210_I2S_RX_INT_CLEAR 0x1c +#define TEGRA210_I2S_RX_CIF_CTRL 0x20 +#define TEGRA210_I2S_RX_CTRL 0x24 +#define TEGRA210_I2S_RX_SLOT_CTRL 0x28 +#define TEGRA210_I2S_RX_CLK_TRIM 0x2c +#define TEGRA210_I2S_RX_CYA 0x30 +#define TEGRA210_I2S_RX_CIF_FIFO_STATUS 0x34 +#define TEGRA210_I2S_TX_ENABLE 0x40 +#define TEGRA210_I2S_TX_SOFT_RESET 0x44 +#define TEGRA210_I2S_TX_STATUS 0x4c +#define TEGRA210_I2S_TX_INT_STATUS 0x50 +#define TEGRA210_I2S_TX_INT_MASK 0x54 +#define TEGRA210_I2S_TX_INT_SET 0x58 +#define TEGRA210_I2S_TX_INT_CLEAR 0x5c +#define TEGRA210_I2S_TX_CIF_CTRL 0x60 +#define TEGRA210_I2S_TX_CTRL 0x64 +#define TEGRA210_I2S_TX_SLOT_CTRL 0x68 +#define TEGRA210_I2S_TX_CLK_TRIM 0x6c +#define TEGRA210_I2S_TX_CYA 0x70 +#define TEGRA210_I2S_TX_CIF_FIFO_STATUS 0x74 +#define TEGRA210_I2S_ENABLE 0x80 +#define TEGRA210_I2S_SOFT_RESET 0x84 +#define TEGRA210_I2S_CG 0x88 +#define TEGRA210_I2S_STATUS 0x8c +#define TEGRA210_I2S_INT_STATUS 0x90 +#define TEGRA210_I2S_CTRL 0xa0 +#define TEGRA210_I2S_TIMING 0xa4 +#define TEGRA210_I2S_SLOT_CTRL 0xa8 +#define TEGRA210_I2S_CLK_TRIM 0xac +#define TEGRA210_I2S_CYA 0xb0 + +/* Bit fields, shifts and masks */ +#define I2S_DATA_SHIFT 8 +#define I2S_CTRL_DATA_OFFSET_MASK (0x7ff << I2S_DATA_SHIFT) + +#define I2S_EN_SHIFT 0 +#define I2S_EN_MASK BIT(I2S_EN_SHIFT) +#define I2S_EN BIT(I2S_EN_SHIFT) + +#define I2S_FSYNC_WIDTH_SHIFT 24 +#define I2S_CTRL_FSYNC_WIDTH_MASK (0xff << I2S_FSYNC_WIDTH_SHIFT) + +#define I2S_POS_EDGE 0 +#define I2S_NEG_EDGE 1 +#define I2S_EDGE_SHIFT 20 +#define I2S_CTRL_EDGE_CTRL_MASK BIT(I2S_EDGE_SHIFT) +#define I2S_CTRL_EDGE_CTRL_POS_EDGE (I2S_POS_EDGE << I2S_EDGE_SHIFT) +#define I2S_CTRL_EDGE_CTRL_NEG_EDGE (I2S_NEG_EDGE << I2S_EDGE_SHIFT) + +#define I2S_FMT_LRCK 0 +#define I2S_FMT_FSYNC 1 +#define I2S_FMT_SHIFT 12 +#define I2S_CTRL_FRAME_FMT_MASK (7 << I2S_FMT_SHIFT) +#define I2S_CTRL_FRAME_FMT_LRCK_MODE (I2S_FMT_LRCK << I2S_FMT_SHIFT) +#define I2S_CTRL_FRAME_FMT_FSYNC_MODE (I2S_FMT_FSYNC << I2S_FMT_SHIFT) + +#define I2S_CTRL_MASTER_EN_SHIFT 10 +#define I2S_CTRL_MASTER_EN_MASK BIT(I2S_CTRL_MASTER_EN_SHIFT) +#define I2S_CTRL_MASTER_EN BIT(I2S_CTRL_MASTER_EN_SHIFT) + +#define I2S_CTRL_LRCK_POL_SHIFT 9 +#define I2S_CTRL_LRCK_POL_MASK BIT(I2S_CTRL_LRCK_POL_SHIFT) +#define I2S_CTRL_LRCK_POL_LOW (0 << I2S_CTRL_LRCK_POL_SHIFT) +#define I2S_CTRL_LRCK_POL_HIGH BIT(I2S_CTRL_LRCK_POL_SHIFT) + +#define I2S_CTRL_LPBK_SHIFT 8 +#define I2S_CTRL_LPBK_MASK BIT(I2S_CTRL_LPBK_SHIFT) +#define I2S_CTRL_LPBK_EN BIT(I2S_CTRL_LPBK_SHIFT) + +#define I2S_BITS_8 1 +#define I2S_BITS_16 3 +#define I2S_BITS_24 5 +#define I2S_BITS_32 7 +#define I2S_CTRL_BIT_SIZE_MASK 0x7 + +#define I2S_TIMING_CH_BIT_CNT_MASK 0x7ff +#define I2S_TIMING_CH_BIT_CNT_SHIFT 0 + +#define I2S_SOFT_RESET_SHIFT 0 +#define I2S_SOFT_RESET_MASK BIT(I2S_SOFT_RESET_SHIFT) +#define I2S_SOFT_RESET_EN BIT(I2S_SOFT_RESET_SHIFT) + +#define I2S_RX_FIFO_DEPTH 64 +#define DEFAULT_I2S_RX_FIFO_THRESHOLD 3 + +#define DEFAULT_I2S_SLOT_MASK 0xffff + +enum tegra210_i2s_path { + I2S_RX_PATH, + I2S_TX_PATH, + I2S_PATHS, +}; + +struct tegra210_i2s { + struct clk *clk_i2s; + struct clk *clk_sync_input; + struct regmap *regmap; + unsigned int stereo_to_mono[I2S_PATHS]; + unsigned int mono_to_stereo[I2S_PATHS]; + unsigned int audio_ch_override[I2S_PATHS]; + unsigned int audio_fmt_override[I2S_PATHS]; + /* Client overrides are common for TX and RX paths */ + unsigned int client_ch_override; + unsigned int client_fmt_override; + unsigned int srate_override; + unsigned int dai_fmt; + unsigned int fsync_width; + unsigned int bclk_ratio; + unsigned int tx_mask; + unsigned int rx_mask; + unsigned int rx_fifo_th; + bool loopback; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_iqc.c b/sound/soc/tegra/tegra210_iqc.c index e680bdfb..5ad3d4d0 100644 --- a/sound/soc/tegra/tegra210_iqc.c +++ b/sound/soc/tegra/tegra210_iqc.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -// SPDX-FileCopyrightText: Copyright (c) 2014-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // tegra210_iqc.c - Tegra210 IQC driver +// +// Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved. #include #include @@ -19,6 +20,7 @@ #include +#include "tegra210_ahub.h" #include "tegra210_iqc.h" static const struct reg_default tegra210_iqc_reg_defaults[] = { diff --git a/sound/soc/tegra/tegra210_mbdrc.c b/sound/soc/tegra/tegra210_mbdrc.c new file mode 100644 index 00000000..66bf6207 --- /dev/null +++ b/sound/soc/tegra/tegra210_mbdrc.c @@ -0,0 +1,912 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_mbdrc.c - Tegra210 MBDRC driver +// +// Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra210_ahub.h" +#include "tegra210_mbdrc.h" +#include "tegra210_ope.h" + +#define MBDRC_FILTER_REG(reg, id) \ + (reg + (id * TEGRA210_MBDRC_FILTER_PARAM_STRIDE)) +#define MBDRC_FILTER_REG_DEFAULTS(id) \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_IIR_CONFIG, id), 0x00000005}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_ATTACK, id), 0x3e48590c}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_RELEASE, id), 0x08414e9f}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_ATTACK, id), 0x7fffffff}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_THRESHOLD, id), 0x06145082}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_OUT_THRESHOLD, id), 0x060d379b}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_1ST, id), 0x0000a000}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_2ND, id), 0x00002000}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_3RD, id), 0x00000b33}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_4TH, id), 0x00000800}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_5TH, id), 0x0000019a}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_MAKEUP_GAIN, id), 0x00000002}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_INIT_GAIN, id), 0x00066666}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_ATTACK, id), 0x00d9ba0e}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_RELEASE, id), 0x3e48590c}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_RELEASE, id), 0x7ffff26a}, \ + { MBDRC_FILTER_REG(TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL, id), 0x4000} + +static const struct reg_default tegra210_mbdrc_reg_defaults[] = { + { TEGRA210_MBDRC_CONFIG, 0x0030de51}, + { TEGRA210_MBDRC_CHANNEL_MASK, 0x00000003}, + { TEGRA210_MBDRC_FAST_FACTOR, 0x30000800}, + + MBDRC_FILTER_REG_DEFAULTS(0), + MBDRC_FILTER_REG_DEFAULTS(1), + MBDRC_FILTER_REG_DEFAULTS(2) +}; + +/* Default MBDRC parameters */ +static const struct tegra210_mbdrc_config mbdrc_init_config = { + .mode = 0, /* bypass */ + .rms_off = 48, + .peak_rms_mode = 1, /* PEAK */ + .fliter_structure = 0, /* All-pass tree */ + .shift_ctrl = 30, + .frame_size = 32, + .channel_mask = 0x3, + .fa_factor = 2048, + .fr_factor = 14747, + .band_params[MBDRC_LOW_BAND] = { + .band = MBDRC_LOW_BAND, + .iir_stages = 5, + .in_attack_tc = 1044928780, + .in_release_tc = 138497695, + .fast_attack_tc = 2147483647, + .in_threshold = {130, 80, 20, 6}, + .out_threshold = {155, 55, 13, 6}, + .ratio = {40960, 8192, 2867, 2048, 410}, + .makeup_gain = 4, + .gain_init = 419430, + .gain_attack_tc = 14268942, + .gain_release_tc = 1440547090, + .fast_release_tc = 2147480170, + .biquad_params = { + /* Gains : b0, b1, a0, a1, a2 */ + 961046798, -2030431983, 1073741824, 2030431983, -961046798, /* band-0 */ + 1030244425, -2099481453, 1073741824, 2099481453, -1030244425, /* band-1 */ + 1067169294, -2136327263, 1073741824, 2136327263, -1067169294, /* band-2 */ + 434951949, -1306567134, 1073741824, 1306567134, -434951949, /* band-3 */ + 780656019, -1605955641, 1073741824, 1605955641, -780656019, /* band-4 */ + 1024497031, -1817128152, 1073741824, 1817128152, -1024497031, /* band-5 */ + 1073741824, 0, 0, 0, 0, /* band-6 */ + 1073741824, 0, 0, 0, 0, /* band-7 */ + } + }, + .band_params[MBDRC_MID_BAND] = { + .band = MBDRC_MID_BAND, + .iir_stages = 5, + .in_attack_tc = 1581413104, + .in_release_tc = 35494783, + .fast_attack_tc = 2147483647, + .in_threshold = {130, 50, 30, 6}, + .out_threshold = {106, 50, 30, 13}, + .ratio = {40960, 2867, 4096, 2867, 410}, + .makeup_gain = 6, + .gain_init = 419430, + .gain_attack_tc = 4766887, + .gain_release_tc = 1044928780, + .fast_release_tc = 2147480170, + .biquad_params = { + /* Gains : b0, b1, a0, a1, a2 */ + -1005668963, 1073741824, 0, 1005668963, 0, /* band-0 */ + 998437058, -2067742187, 1073741824, 2067742187, -998437058, /* band-1 */ + 1051963422, -2121153948, 1073741824, 2121153948, -1051963422, /* band-2 */ + 434951949, -1306567134, 1073741824, 1306567134, -434951949, /* band-3 */ + 780656019, -1605955641, 1073741824, 1605955641, -780656019, /* band-4 */ + 1024497031, -1817128152, 1073741824, 1817128152, -1024497031, /* band-5 */ + 1073741824, 0, 0, 0, 0, /* band-6 */ + 1073741824, 0, 0, 0, 0, /* band-7 */ + } + }, + .band_params[MBDRC_HIGH_BAND] = { + .band = MBDRC_HIGH_BAND, + .iir_stages = 5, + .in_attack_tc = 2144750688, + .in_release_tc = 70402888, + .fast_attack_tc = 2147483647, + .in_threshold = {130, 50, 30, 6}, + .out_threshold = {106, 50, 30, 13}, + .ratio = {40960, 2867, 4096, 2867, 410}, + .makeup_gain = 6, + .gain_init = 419430, + .gain_attack_tc = 4766887, + .gain_release_tc = 1044928780, + .fast_release_tc = 2147480170, + .biquad_params = { + /* Gains : b0, b1, a0, a1, a2 */ + 1073741824, 0, 0, 0, 0, /* band-0 */ + 1073741824, 0, 0, 0, 0, /* band-1 */ + 1073741824, 0, 0, 0, 0, /* band-2 */ + -619925131, 1073741824, 0, 619925131, 0, /* band-3 */ + 606839335, -1455425976, 1073741824, 1455425976, -606839335, /* band-4 */ + 917759617, -1724690840, 1073741824, 1724690840, -917759617, /* band-5 */ + 1073741824, 0, 0, 0, 0, /* band-6 */ + 1073741824, 0, 0, 0, 0, /* band-7 */ + } + } +}; + +static int tegra210_mbdrc_get(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_ope *ope = snd_soc_component_get_drvdata(cmpnt); + unsigned long long fls_val = 1ULL << fls(mc->max); + unsigned int mask = fls_val - 1; + unsigned int val; + + regmap_read(ope->mbdrc_regmap, mc->reg, &val); + ucontrol->value.integer.value[0] = (val >> mc->shift) & mask; + if (mc->invert) + ucontrol->value.integer.value[0] = + mc->max - ucontrol->value.integer.value[0]; + + return 0; +} + +static int tegra210_mbdrc_put(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_ope *ope = snd_soc_component_get_drvdata(cmpnt); + unsigned long long fls_val = 1ULL << fls(mc->max); + unsigned int mask = fls_val - 1; + unsigned int val; + + val = (ucontrol->value.integer.value[0] & mask); + if (mc->invert) + val = mc->max - val; + val = val << mc->shift; + + return regmap_update_bits(ope->mbdrc_regmap, mc->reg, + (mask << mc->shift), val); +} + +static int tegra210_mbdrc_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + + regmap_read(ope->mbdrc_regmap, e->reg, &val); + ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask; + + return 0; +} + +static int tegra210_mbdrc_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned int mask; + + if (ucontrol->value.enumerated.item[0] > e->items - 1) + return -EINVAL; + + val = ucontrol->value.enumerated.item[0] << e->shift_l; + mask = e->mask << e->shift_l; + + return regmap_update_bits(ope->mbdrc_regmap, e->reg, mask, val); +} + +static int tegra210_mbdrc_band_params_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + u32 *data = (u32 *)ucontrol->value.bytes.data; + u32 regs = params->soc.base; + u32 mask = params->soc.mask; + u32 shift = params->shift; + int i; + + for (i = 0; i < params->soc.num_regs; i++, + regs += cmpnt->val_bytes) { + regmap_read(ope->mbdrc_regmap, regs, &data[i]); + data[i] = ((data[i] & mask) >> shift); + } + + return 0; +} + +static int tegra210_mbdrc_band_params_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + u32 *data = (u32 *)ucontrol->value.bytes.data; + u32 regs = params->soc.base; + u32 mask = params->soc.mask; + u32 shift = params->shift; + int i; + + for (i = 0; i < params->soc.num_regs; i++, + regs += cmpnt->val_bytes) + regmap_update_bits(ope->mbdrc_regmap, regs, mask, + data[i] << shift); + + return 0; +} + +static int tegra210_mbdrc_threshold_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + u32 *data = (u32 *)ucontrol->value.bytes.data; + u32 regs = params->soc.base; + u32 num_regs = params->soc.num_regs; + u32 val; + int i; + + for (i = 0; i < num_regs; i += 4, regs += cmpnt->val_bytes) { + regmap_read(ope->mbdrc_regmap, regs, &val); + + data[i] = (val & TEGRA210_MBDRC_THRESH_1ST_MASK) >> + TEGRA210_MBDRC_THRESH_1ST_SHIFT; + data[i + 1] = (val & TEGRA210_MBDRC_THRESH_2ND_MASK) >> + TEGRA210_MBDRC_THRESH_2ND_SHIFT; + data[i + 2] = (val & TEGRA210_MBDRC_THRESH_3RD_MASK) >> + TEGRA210_MBDRC_THRESH_3RD_SHIFT; + data[i + 3] = (val & TEGRA210_MBDRC_THRESH_4TH_MASK) >> + TEGRA210_MBDRC_THRESH_4TH_SHIFT; + } + + return 0; +} + +static int tegra210_mbdrc_threshold_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + u32 *data = (u32 *)ucontrol->value.bytes.data; + u32 regs = params->soc.base; + u32 num_regs = params->soc.num_regs; + int i; + + for (i = 0; i < num_regs; i += 4, regs += cmpnt->val_bytes) { + data[i] = (((data[i] >> TEGRA210_MBDRC_THRESH_1ST_SHIFT) & + TEGRA210_MBDRC_THRESH_1ST_MASK) | + ((data[i + 1] >> TEGRA210_MBDRC_THRESH_2ND_SHIFT) & + TEGRA210_MBDRC_THRESH_2ND_MASK) | + ((data[i + 2] >> TEGRA210_MBDRC_THRESH_3RD_SHIFT) & + TEGRA210_MBDRC_THRESH_3RD_MASK) | + ((data[i + 3] >> TEGRA210_MBDRC_THRESH_4TH_SHIFT) & + TEGRA210_MBDRC_THRESH_4TH_MASK)); + regmap_update_bits(ope->mbdrc_regmap, regs, + 0xffffffff, data[i]); + } + + return 0; +} + +static int tegra210_mbdrc_biquad_coeffs_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + u32 *data = (u32 *)ucontrol->value.bytes.data; + + memset(data, 0, params->soc.num_regs * cmpnt->val_bytes); + return 0; +} + +static int tegra210_mbdrc_biquad_coeffs_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + u32 reg_ctrl = params->soc.base; + u32 reg_data = reg_ctrl + cmpnt->val_bytes; + u32 *data = (u32 *)ucontrol->value.bytes.data; + + tegra210_ahub_write_ram(ope->mbdrc_regmap, reg_ctrl, reg_data, + params->shift, data, params->soc.num_regs); + + return 0; +} + +static int tegra210_mbdrc_param_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_bytes *params = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = params->num_regs; + + return 0; +} + +static int tegra210_mbdrc_vol_get(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_ope *ope = snd_soc_component_get_drvdata(cmpnt); + unsigned long long fls_val = 1ULL << fls(mc->max); + unsigned int mask = fls_val - 1; + int val; + + regmap_read(ope->mbdrc_regmap, mc->reg, &val); + + ucontrol->value.integer.value[0] = ((val >> mc->shift) - + TEGRA210_MBDRC_MASTER_VOL_MIN) & mask; + + return 0; +} + +static int tegra210_mbdrc_vol_put(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_ope *ope = snd_soc_component_get_drvdata(cmpnt); + unsigned long long fls_val = 1ULL << fls(mc->max); + unsigned int mask = fls_val - 1; + unsigned int val; + + val = (ucontrol->value.integer.value[0] & mask); + val = val + TEGRA210_MBDRC_MASTER_VOL_MIN; + val = val << mc->shift; + + return regmap_write(ope->mbdrc_regmap, mc->reg, val); +} + +static const char * const tegra210_mbdrc_mode_text[] = { + "bypass", "fullband", "dualband", "multiband" +}; + +static const struct soc_enum tegra210_mbdrc_mode_enum = + SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT, 4, + tegra210_mbdrc_mode_text); + +static const char * const tegra210_mbdrc_peak_rms_text[] = { + "peak", "rms" +}; + +static const struct soc_enum tegra210_mbdrc_peak_rms_enum = + SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT, 2, + tegra210_mbdrc_peak_rms_text); + +static const char * const tegra210_mbdrc_filter_structure_text[] = { + "all-pass-tree", "flexible" +}; + +static const struct soc_enum tegra210_mbdrc_filter_structure_enum = + SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT, 2, + tegra210_mbdrc_filter_structure_text); + +static const char * const tegra210_mbdrc_frame_size_text[] = { + "N1", "N2", "N4", "N8", "N16", "N32", "N64" +}; + +static const struct soc_enum tegra210_mbdrc_frame_size_enum = + SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT, 7, + tegra210_mbdrc_frame_size_text); + +#define TEGRA_MBDRC_BYTES_EXT(xname, xbase, xregs, xshift, xmask, xinfo) \ + TEGRA_SOC_BYTES_EXT(xname, xbase, xregs, xshift, xmask, \ + tegra210_mbdrc_band_params_get, tegra210_mbdrc_band_params_put, \ + tegra210_mbdrc_param_info) + +#define TEGRA_MBDRC_BAND_BYTES_EXT(xname, xbase, xshift, xmask, xinfo) \ + TEGRA_MBDRC_BYTES_EXT(xname, xbase, TEGRA210_MBDRC_FILTER_COUNT, \ + xshift, xmask, xinfo) + +static const DECLARE_TLV_DB_MINMAX(mdbrc_vol_tlv, -25600, 25500); + +static const struct snd_kcontrol_new tegra210_mbdrc_controls[] = { + SOC_ENUM_EXT("MBDRC Peak RMS Mode", tegra210_mbdrc_peak_rms_enum, + tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum), + SOC_ENUM_EXT("MBDRC Filter Structure", + tegra210_mbdrc_filter_structure_enum, + tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum), + SOC_ENUM_EXT("MBDRC Frame Size", tegra210_mbdrc_frame_size_enum, + tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum), + SOC_ENUM_EXT("MBDRC Mode", tegra210_mbdrc_mode_enum, + tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum), + + SOC_SINGLE_EXT("MBDRC RMS Offset", TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT, 0x1ff, 0, + tegra210_mbdrc_get, tegra210_mbdrc_put), + SOC_SINGLE_EXT("MBDRC Shift Control", TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT, 0x1f, 0, + tegra210_mbdrc_get, tegra210_mbdrc_put), + SOC_SINGLE_EXT("MBDRC Fast Attack Factor", TEGRA210_MBDRC_FAST_FACTOR, + TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT, 0xffff, 0, + tegra210_mbdrc_get, tegra210_mbdrc_put), + SOC_SINGLE_EXT("MBDRC Fast Release Factor", TEGRA210_MBDRC_FAST_FACTOR, + TEGRA210_MBDRC_FAST_FACTOR_RELEASE_SHIFT, 0xffff, 0, + tegra210_mbdrc_get, tegra210_mbdrc_put), + + SOC_SINGLE_RANGE_EXT_TLV("MBDRC Master Volume", TEGRA210_MBDRC_MASTER_VOLUME, + TEGRA210_MBDRC_MASTER_VOLUME_SHIFT, TEGRA210_MBDRC_MASTER_VOL_MIN, + TEGRA210_MBDRC_MASTER_VOL_MAX, 0, + tegra210_mbdrc_vol_get, tegra210_mbdrc_vol_put, mdbrc_vol_tlv), + + TEGRA_SOC_BYTES_EXT("MBDRC IIR Stages", TEGRA210_MBDRC_IIR_CONFIG, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT, + TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC In Attack Time Const", TEGRA210_MBDRC_IN_ATTACK, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT, + TEGRA210_MBDRC_IN_ATTACK_TC_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC In Release Time Const", TEGRA210_MBDRC_IN_RELEASE, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT, + TEGRA210_MBDRC_IN_RELEASE_TC_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Fast Attack Time Const", TEGRA210_MBDRC_FAST_ATTACK, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT, + TEGRA210_MBDRC_FAST_ATTACK_TC_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + + TEGRA_SOC_BYTES_EXT("MBDRC In Threshold", TEGRA210_MBDRC_IN_THRESHOLD, + TEGRA210_MBDRC_FILTER_COUNT * 4, 0, 0xffffffff, + tegra210_mbdrc_threshold_get, tegra210_mbdrc_threshold_put, tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Out Threshold", TEGRA210_MBDRC_OUT_THRESHOLD, + TEGRA210_MBDRC_FILTER_COUNT * 4, 0, 0xffffffff, + tegra210_mbdrc_threshold_get, tegra210_mbdrc_threshold_put, tegra210_mbdrc_param_info), + + TEGRA_SOC_BYTES_EXT("MBDRC Ratio", TEGRA210_MBDRC_RATIO_1ST, + TEGRA210_MBDRC_FILTER_COUNT * 5, + TEGRA210_MBDRC_RATIO_1ST_SHIFT, TEGRA210_MBDRC_RATIO_1ST_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Makeup Gain", TEGRA210_MBDRC_MAKEUP_GAIN, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT, + TEGRA210_MBDRC_MAKEUP_GAIN_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Init Gain", TEGRA210_MBDRC_INIT_GAIN, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_INIT_GAIN_SHIFT, + TEGRA210_MBDRC_INIT_GAIN_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Attack Gain", TEGRA210_MBDRC_GAIN_ATTACK, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_GAIN_ATTACK_SHIFT, + TEGRA210_MBDRC_GAIN_ATTACK_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Release Gain", TEGRA210_MBDRC_GAIN_RELEASE, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_GAIN_RELEASE_SHIFT, + TEGRA210_MBDRC_GAIN_RELEASE_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Fast Release Gain", + TEGRA210_MBDRC_FAST_RELEASE, + TEGRA210_MBDRC_FILTER_COUNT, + TEGRA210_MBDRC_FAST_RELEASE_SHIFT, + TEGRA210_MBDRC_FAST_RELEASE_MASK, + tegra210_mbdrc_band_params_get, + tegra210_mbdrc_band_params_put, + tegra210_mbdrc_param_info), + + TEGRA_SOC_BYTES_EXT("MBDRC Low Band Biquad Coeffs", + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL, + TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5, 0, 0xffffffff, + tegra210_mbdrc_biquad_coeffs_get, + tegra210_mbdrc_biquad_coeffs_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC Mid Band Biquad Coeffs", + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL + + TEGRA210_MBDRC_FILTER_PARAM_STRIDE, + TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5, 0, 0xffffffff, + tegra210_mbdrc_biquad_coeffs_get, + tegra210_mbdrc_biquad_coeffs_put, + tegra210_mbdrc_param_info), + TEGRA_SOC_BYTES_EXT("MBDRC High Band Biquad Coeffs", + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL + + (TEGRA210_MBDRC_FILTER_PARAM_STRIDE * 2), + TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5, 0, 0xffffffff, + tegra210_mbdrc_biquad_coeffs_get, + tegra210_mbdrc_biquad_coeffs_put, + tegra210_mbdrc_param_info), +}; + +static bool tegra210_mbdrc_wr_reg(struct device *dev, unsigned int reg) +{ + if (reg >= TEGRA210_MBDRC_IIR_CONFIG) + reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) % + (TEGRA210_MBDRC_FILTER_PARAM_STRIDE * + TEGRA210_MBDRC_FILTER_COUNT)); + + switch (reg) { + case TEGRA210_MBDRC_CG: + case TEGRA210_MBDRC_SOFT_RESET: + case TEGRA210_MBDRC_CONFIG: + case TEGRA210_MBDRC_CHANNEL_MASK: + case TEGRA210_MBDRC_MASTER_VOLUME: + case TEGRA210_MBDRC_FAST_FACTOR: + case TEGRA210_MBDRC_IIR_CONFIG: + case TEGRA210_MBDRC_IN_ATTACK: + case TEGRA210_MBDRC_IN_RELEASE: + case TEGRA210_MBDRC_FAST_ATTACK: + case TEGRA210_MBDRC_IN_THRESHOLD: + case TEGRA210_MBDRC_OUT_THRESHOLD: + case TEGRA210_MBDRC_RATIO_1ST: + case TEGRA210_MBDRC_RATIO_2ND: + case TEGRA210_MBDRC_RATIO_3RD: + case TEGRA210_MBDRC_RATIO_4TH: + case TEGRA210_MBDRC_RATIO_5TH: + case TEGRA210_MBDRC_MAKEUP_GAIN: + case TEGRA210_MBDRC_INIT_GAIN: + case TEGRA210_MBDRC_GAIN_ATTACK: + case TEGRA210_MBDRC_GAIN_RELEASE: + case TEGRA210_MBDRC_FAST_RELEASE: + case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL: + case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_mbdrc_rd_reg(struct device *dev, unsigned int reg) +{ + if (reg >= TEGRA210_MBDRC_IIR_CONFIG) + reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) % + (TEGRA210_MBDRC_FILTER_PARAM_STRIDE * + TEGRA210_MBDRC_FILTER_COUNT)); + switch (reg) { + case TEGRA210_MBDRC_CG: + case TEGRA210_MBDRC_SOFT_RESET: + case TEGRA210_MBDRC_STATUS: + case TEGRA210_MBDRC_CONFIG: + case TEGRA210_MBDRC_CHANNEL_MASK: + case TEGRA210_MBDRC_MASTER_VOLUME: + case TEGRA210_MBDRC_FAST_FACTOR: + case TEGRA210_MBDRC_IIR_CONFIG: + case TEGRA210_MBDRC_IN_ATTACK: + case TEGRA210_MBDRC_IN_RELEASE: + case TEGRA210_MBDRC_FAST_ATTACK: + case TEGRA210_MBDRC_IN_THRESHOLD: + case TEGRA210_MBDRC_OUT_THRESHOLD: + case TEGRA210_MBDRC_RATIO_1ST: + case TEGRA210_MBDRC_RATIO_2ND: + case TEGRA210_MBDRC_RATIO_3RD: + case TEGRA210_MBDRC_RATIO_4TH: + case TEGRA210_MBDRC_RATIO_5TH: + case TEGRA210_MBDRC_MAKEUP_GAIN: + case TEGRA210_MBDRC_INIT_GAIN: + case TEGRA210_MBDRC_GAIN_ATTACK: + case TEGRA210_MBDRC_GAIN_RELEASE: + case TEGRA210_MBDRC_FAST_RELEASE: + case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL: + case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_mbdrc_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg >= TEGRA210_MBDRC_IIR_CONFIG) + reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) % + (TEGRA210_MBDRC_FILTER_PARAM_STRIDE * + TEGRA210_MBDRC_FILTER_COUNT)); + + switch (reg) { + case TEGRA210_MBDRC_SOFT_RESET: + case TEGRA210_MBDRC_STATUS: + case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL: + case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_mbdrc_precious_reg(struct device *dev, unsigned int reg) +{ + if (reg >= TEGRA210_MBDRC_IIR_CONFIG) + reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) % + (TEGRA210_MBDRC_FILTER_PARAM_STRIDE * + TEGRA210_MBDRC_FILTER_COUNT)); + + switch (reg) { + case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA: + return true; + default: + return false; + } +} + +static const struct regmap_config tegra210_mbdrc_regmap_config = { + .name = "mbdrc", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_MBDRC_MAX_REG, + .writeable_reg = tegra210_mbdrc_wr_reg, + .readable_reg = tegra210_mbdrc_rd_reg, + .volatile_reg = tegra210_mbdrc_volatile_reg, + .precious_reg = tegra210_mbdrc_precious_reg, + .reg_defaults = tegra210_mbdrc_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_mbdrc_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +int tegra210_mbdrc_hw_params(struct snd_soc_component *cmpnt) +{ + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + const struct tegra210_mbdrc_config *conf = &mbdrc_init_config; + u32 val = 0; + int i; + + regmap_read(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, &val); + if (val & TEGRA210_MBDRC_CONFIG_MBDRC_MODE_BYPASS) + return 0; + + for (i = 0; i < MBDRC_NUM_BAND; i++) { + const struct tegra210_mbdrc_band_params *params = + &conf->band_params[i]; + u32 reg_off = i * TEGRA210_MBDRC_FILTER_PARAM_STRIDE; + + tegra210_ahub_write_ram(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL, + reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA, 0, + (u32 *)¶ms->biquad_params[0], + TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5); + } + return 0; +} +EXPORT_SYMBOL_GPL(tegra210_mbdrc_hw_params); + +int tegra210_mbdrc_codec_init(struct snd_soc_component *cmpnt) +{ + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + const struct tegra210_mbdrc_config *conf = &mbdrc_init_config; + u32 val; + int i; + + pm_runtime_get_sync(cmpnt->dev); + /* Initialize MBDRC registers and ahub-ram with default params */ + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_MBDRC_MODE_MASK, + conf->mode << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_RMS_OFFSET_MASK, + conf->rms_off << TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_PEAK_RMS_MASK, + conf->peak_rms_mode << TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_MASK, + conf->fliter_structure << + TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_MASK, + conf->shift_ctrl << TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, + TEGRA210_MBDRC_CONFIG_FRAME_SIZE_MASK, + __ffs(conf->frame_size) << + TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CHANNEL_MASK, + TEGRA210_MBDRC_CHANNEL_MASK_MASK, + conf->channel_mask << TEGRA210_MBDRC_CHANNEL_MASK_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_FAST_FACTOR, + TEGRA210_MBDRC_FAST_FACTOR_ATTACK_MASK, + conf->fa_factor << TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_FAST_FACTOR, + TEGRA210_MBDRC_FAST_FACTOR_ATTACK_MASK, + conf->fr_factor << TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT); + + for (i = 0; i < MBDRC_NUM_BAND; i++) { + const struct tegra210_mbdrc_band_params *params = + &conf->band_params[i]; + u32 reg_off = i * TEGRA210_MBDRC_FILTER_PARAM_STRIDE; + + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_IIR_CONFIG, + TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_MASK, + params->iir_stages << + TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_IN_ATTACK, + TEGRA210_MBDRC_IN_ATTACK_TC_MASK, + params->in_attack_tc << + TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_IN_RELEASE, + TEGRA210_MBDRC_IN_RELEASE_TC_MASK, + params->in_release_tc << + TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_FAST_ATTACK, + TEGRA210_MBDRC_FAST_ATTACK_TC_MASK, + params->fast_attack_tc << + TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT); + + val = (((params->in_threshold[0] >> + TEGRA210_MBDRC_THRESH_1ST_SHIFT) & + TEGRA210_MBDRC_THRESH_1ST_MASK) | + ((params->in_threshold[1] >> + TEGRA210_MBDRC_THRESH_2ND_SHIFT) & + TEGRA210_MBDRC_THRESH_2ND_MASK) | + ((params->in_threshold[2] >> + TEGRA210_MBDRC_THRESH_3RD_SHIFT) & + TEGRA210_MBDRC_THRESH_3RD_MASK) | + ((params->in_threshold[3] >> + TEGRA210_MBDRC_THRESH_4TH_SHIFT) & + TEGRA210_MBDRC_THRESH_4TH_MASK)); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_IN_THRESHOLD, 0xffffffff, val); + + val = (((params->out_threshold[0] >> + TEGRA210_MBDRC_THRESH_1ST_SHIFT) & + TEGRA210_MBDRC_THRESH_1ST_MASK) | + ((params->out_threshold[1] >> + TEGRA210_MBDRC_THRESH_2ND_SHIFT) & + TEGRA210_MBDRC_THRESH_2ND_MASK) | + ((params->out_threshold[2] >> + TEGRA210_MBDRC_THRESH_3RD_SHIFT) & + TEGRA210_MBDRC_THRESH_3RD_MASK) | + ((params->out_threshold[3] >> + TEGRA210_MBDRC_THRESH_4TH_SHIFT) & + TEGRA210_MBDRC_THRESH_4TH_MASK)); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_OUT_THRESHOLD, + 0xffffffff, val); + + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_RATIO_1ST, + TEGRA210_MBDRC_RATIO_1ST_MASK, + params->ratio[0] << TEGRA210_MBDRC_RATIO_1ST_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_RATIO_2ND, + TEGRA210_MBDRC_RATIO_2ND_MASK, + params->ratio[1] << TEGRA210_MBDRC_RATIO_2ND_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_RATIO_3RD, + TEGRA210_MBDRC_RATIO_3RD_MASK, + params->ratio[2] << TEGRA210_MBDRC_RATIO_3RD_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_RATIO_4TH, + TEGRA210_MBDRC_RATIO_4TH_MASK, + params->ratio[3] << TEGRA210_MBDRC_RATIO_4TH_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_RATIO_5TH, + TEGRA210_MBDRC_RATIO_5TH_MASK, + params->ratio[4] << TEGRA210_MBDRC_RATIO_5TH_SHIFT); + + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_MAKEUP_GAIN, + TEGRA210_MBDRC_MAKEUP_GAIN_MASK, + params->makeup_gain << + TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_INIT_GAIN, + TEGRA210_MBDRC_INIT_GAIN_MASK, + params->gain_init << + TEGRA210_MBDRC_INIT_GAIN_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_GAIN_ATTACK, + TEGRA210_MBDRC_GAIN_ATTACK_MASK, + params->gain_attack_tc << + TEGRA210_MBDRC_GAIN_ATTACK_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_GAIN_RELEASE, + TEGRA210_MBDRC_GAIN_RELEASE_MASK, + params->gain_release_tc << + TEGRA210_MBDRC_GAIN_RELEASE_SHIFT); + regmap_update_bits(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_FAST_RELEASE, + TEGRA210_MBDRC_FAST_RELEASE_MASK, + params->fast_release_tc << + TEGRA210_MBDRC_FAST_RELEASE_SHIFT); + + tegra210_ahub_write_ram(ope->mbdrc_regmap, + reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL, + reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA, 0, + (u32 *)¶ms->biquad_params[0], + TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5); + } + pm_runtime_put_sync(cmpnt->dev); + + snd_soc_add_component_controls(cmpnt, tegra210_mbdrc_controls, + ARRAY_SIZE(tegra210_mbdrc_controls)); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra210_mbdrc_codec_init); + +int tegra210_mbdrc_regmap_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_ope *ope = dev_get_drvdata(dev); + struct device_node *child; + struct resource mem; + void __iomem *regs; + int err; + + child = of_get_child_by_name(dev->of_node, "dynamic-range-compressor"); + if (!child) + return -ENODEV; + + err = of_address_to_resource(child, 0, &mem); + of_node_put(child); + if (err < 0) { + dev_err(dev, "fail to get MBDRC resource\n"); + return err; + } + + mem.flags = IORESOURCE_MEM; + regs = devm_ioremap_resource(&pdev->dev, &mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + ope->mbdrc_regmap = + devm_regmap_init_mmio(&pdev->dev, regs, + &tegra210_mbdrc_regmap_config); + if (IS_ERR(ope->mbdrc_regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(ope->mbdrc_regmap); + } + + regcache_cache_only(ope->mbdrc_regmap, true); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra210_mbdrc_regmap_init); + +MODULE_AUTHOR("Sumit Bhattacharya "); +MODULE_DESCRIPTION("Tegra210 MBDRC module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra210_mbdrc.h b/sound/soc/tegra/tegra210_mbdrc.h new file mode 100644 index 00000000..fd6b14bc --- /dev/null +++ b/sound/soc/tegra/tegra210_mbdrc.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_mbdrc.h - Definitions for Tegra210 MBDRC driver + * + * Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_MBDRC_H__ +#define __TEGRA210_MBDRC_H__ + +/* Order of these enums are same as the order of band specific hw registers */ +enum { + MBDRC_LOW_BAND, + MBDRC_MID_BAND, + MBDRC_HIGH_BAND, + MBDRC_NUM_BAND, +}; + +/* Register offsets from TEGRA210_MBDRC*_BASE */ +#define TEGRA210_MBDRC_SOFT_RESET 0x4 +#define TEGRA210_MBDRC_CG 0x8 +#define TEGRA210_MBDRC_STATUS 0xc +#define TEGRA210_MBDRC_CONFIG 0x28 +#define TEGRA210_MBDRC_CHANNEL_MASK 0x2c +#define TEGRA210_MBDRC_MASTER_VOLUME 0x30 +#define TEGRA210_MBDRC_FAST_FACTOR 0x34 + +#define TEGRA210_MBDRC_FILTER_COUNT 3 +#define TEGRA210_MBDRC_FILTER_PARAM_STRIDE 0x4 + +#define TEGRA210_MBDRC_IIR_CONFIG 0x38 +#define TEGRA210_MBDRC_IN_ATTACK 0x44 +#define TEGRA210_MBDRC_IN_RELEASE 0x50 +#define TEGRA210_MBDRC_FAST_ATTACK 0x5c +#define TEGRA210_MBDRC_IN_THRESHOLD 0x68 +#define TEGRA210_MBDRC_OUT_THRESHOLD 0x74 +#define TEGRA210_MBDRC_RATIO_1ST 0x80 +#define TEGRA210_MBDRC_RATIO_2ND 0x8c +#define TEGRA210_MBDRC_RATIO_3RD 0x98 +#define TEGRA210_MBDRC_RATIO_4TH 0xa4 +#define TEGRA210_MBDRC_RATIO_5TH 0xb0 +#define TEGRA210_MBDRC_MAKEUP_GAIN 0xbc +#define TEGRA210_MBDRC_INIT_GAIN 0xc8 +#define TEGRA210_MBDRC_GAIN_ATTACK 0xd4 +#define TEGRA210_MBDRC_GAIN_RELEASE 0xe0 +#define TEGRA210_MBDRC_FAST_RELEASE 0xec +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL 0xf8 +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA 0x104 + +#define TEGRA210_MBDRC_MAX_REG (TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA + \ + (TEGRA210_MBDRC_FILTER_PARAM_STRIDE * \ + (TEGRA210_MBDRC_FILTER_COUNT - 1))) + +/* Fields for TEGRA210_MBDRC_CONFIG */ +#define TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT 16 +#define TEGRA210_MBDRC_CONFIG_RMS_OFFSET_MASK (0x1ff << TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT) + +#define TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT 14 +#define TEGRA210_MBDRC_CONFIG_PEAK_RMS_MASK (0x1 << TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT) +#define TEGRA210_MBDRC_CONFIG_PEAK (1 << TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT) + +#define TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT 13 +#define TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_MASK (0x1 << TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_FLEX (1 << TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT) + +#define TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT 8 +#define TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_MASK (0x1f << TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT) + +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT 4 +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_MASK (0xf << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N1 (0 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N2 (1 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N4 (2 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N8 (3 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N16 (4 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N32 (5 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N64 (6 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT) + +#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT 0 +#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_MASK (0x3 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_BYPASS (0 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_FULLBAND (1 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_DUALBAND (2 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT) +#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_MULTIBAND (3 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT) + +/* Fields for TEGRA210_MBDRC_CHANNEL_MASK */ +#define TEGRA210_MBDRC_CHANNEL_MASK_SHIFT 0 +#define TEGRA210_MBDRC_CHANNEL_MASK_MASK (0xff << TEGRA210_MBDRC_CHANNEL_MASK_SHIFT) + +/* Fields for TEGRA210_MBDRC_MASTER_VOLUME */ +#define TEGRA210_MBDRC_MASTER_VOLUME_SHIFT 23 +#define TEGRA210_MBDRC_MASTER_VOL_MIN -256 +#define TEGRA210_MBDRC_MASTER_VOL_MAX 256 + +/* Fields for TEGRA210_MBDRC_FAST_FACTOR */ +#define TEGRA210_MBDRC_FAST_FACTOR_RELEASE_SHIFT 16 +#define TEGRA210_MBDRC_FAST_FACTOR_RELEASE_MASK (0xffff << TEGRA210_MBDRC_FAST_FACTOR_RELEASE_SHIFT) + +#define TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT 0 +#define TEGRA210_MBDRC_FAST_FACTOR_ATTACK_MASK (0xffff << TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT) + +/* Fields for TEGRA210_MBDRC_IIR_CONFIG */ +#define TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT 0 +#define TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_MASK (0xf << TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT) + +/* Fields for TEGRA210_MBDRC_IN_ATTACK */ +#define TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT 0 +#define TEGRA210_MBDRC_IN_ATTACK_TC_MASK (0xffffffff << TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT) + +/* Fields for TEGRA210_MBDRC_IN_RELEASE */ +#define TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT 0 +#define TEGRA210_MBDRC_IN_RELEASE_TC_MASK (0xffffffff << TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT) + +/* Fields for TEGRA210_MBDRC_FAST_ATTACK */ +#define TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT 0 +#define TEGRA210_MBDRC_FAST_ATTACK_TC_MASK (0xffffffff << TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT) + +/* Fields for TEGRA210_MBDRC_IN_THRESHOLD / TEGRA210_MBDRC_OUT_THRESHOLD */ +#define TEGRA210_MBDRC_THRESH_4TH_SHIFT 24 +#define TEGRA210_MBDRC_THRESH_4TH_MASK (0xff << TEGRA210_MBDRC_THRESH_4TH_SHIFT) + +#define TEGRA210_MBDRC_THRESH_3RD_SHIFT 16 +#define TEGRA210_MBDRC_THRESH_3RD_MASK (0xff << TEGRA210_MBDRC_THRESH_3RD_SHIFT) + +#define TEGRA210_MBDRC_THRESH_2ND_SHIFT 8 +#define TEGRA210_MBDRC_THRESH_2ND_MASK (0xff << TEGRA210_MBDRC_THRESH_2ND_SHIFT) + +#define TEGRA210_MBDRC_THRESH_1ST_SHIFT 0 +#define TEGRA210_MBDRC_THRESH_1ST_MASK (0xff << TEGRA210_MBDRC_THRESH_1ST_SHIFT) + +/* Fields for TEGRA210_MBDRC_RATIO_1ST */ +#define TEGRA210_MBDRC_RATIO_1ST_SHIFT 0 +#define TEGRA210_MBDRC_RATIO_1ST_MASK (0xffff << TEGRA210_MBDRC_RATIO_1ST_SHIFT) + +/* Fields for TEGRA210_MBDRC_RATIO_2ND */ +#define TEGRA210_MBDRC_RATIO_2ND_SHIFT 0 +#define TEGRA210_MBDRC_RATIO_2ND_MASK (0xffff << TEGRA210_MBDRC_RATIO_2ND_SHIFT) + +/* Fields for TEGRA210_MBDRC_RATIO_3RD */ +#define TEGRA210_MBDRC_RATIO_3RD_SHIFT 0 +#define TEGRA210_MBDRC_RATIO_3RD_MASK (0xffff << TEGRA210_MBDRC_RATIO_3RD_SHIFT) + +/* Fields for TEGRA210_MBDRC_RATIO_4TH */ +#define TEGRA210_MBDRC_RATIO_4TH_SHIFT 0 +#define TEGRA210_MBDRC_RATIO_4TH_MASK (0xffff << TEGRA210_MBDRC_RATIO_4TH_SHIFT) + +/* Fields for TEGRA210_MBDRC_RATIO_5TH */ +#define TEGRA210_MBDRC_RATIO_5TH_SHIFT 0 +#define TEGRA210_MBDRC_RATIO_5TH_MASK (0xffff << TEGRA210_MBDRC_RATIO_5TH_SHIFT) + +/* Fields for TEGRA210_MBDRC_MAKEUP_GAIN */ +#define TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT 0 +#define TEGRA210_MBDRC_MAKEUP_GAIN_MASK (0x3f << TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT) + +/* Fields for TEGRA210_MBDRC_INIT_GAIN */ +#define TEGRA210_MBDRC_INIT_GAIN_SHIFT 0 +#define TEGRA210_MBDRC_INIT_GAIN_MASK (0xffffffff << TEGRA210_MBDRC_INIT_GAIN_SHIFT) + +/* Fields for TEGRA210_MBDRC_GAIN_ATTACK */ +#define TEGRA210_MBDRC_GAIN_ATTACK_SHIFT 0 +#define TEGRA210_MBDRC_GAIN_ATTACK_MASK (0xffffffff << TEGRA210_MBDRC_GAIN_ATTACK_SHIFT) + +/* Fields for TEGRA210_MBDRC_GAIN_RELEASE */ +#define TEGRA210_MBDRC_GAIN_RELEASE_SHIFT 0 +#define TEGRA210_MBDRC_GAIN_RELEASE_MASK (0xffffffff << TEGRA210_MBDRC_GAIN_RELEASE_SHIFT) + +/* Fields for TEGRA210_MBDRC_FAST_RELEASE */ +#define TEGRA210_MBDRC_FAST_RELEASE_SHIFT 0 +#define TEGRA210_MBDRC_FAST_RELEASE_MASK (0xffffffff << TEGRA210_MBDRC_FAST_RELEASE_SHIFT) + +/* Fields in TEGRA210_mbdrc ram ctrl */ +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_SHIFT 31 +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_SHIFT) +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_SHIFT) + +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_READ_COUNT_SHIFT 16 +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_READ_COUNT_MASK (0xff << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_READ_COUNT_SHIFT) + +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT 14 +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT) +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_READ (0 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT) +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_WRITE (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT) + +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13 +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_SHIFT) +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_SHIFT) + +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12 +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT) +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT) + +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT 0 +#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_MASK (0x1ff << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT) +/* MBDRC register definition ends here */ + +/* order and size of each structure element for following structures should not + be altered size order of elements and their size are based on PEQ + co-eff ram and shift ram layout. +*/ +#define TEGRA210_MBDRC_THRESHOLD_NUM 4 +#define TEGRA210_MBDRC_RATIO_NUM (TEGRA210_MBDRC_THRESHOLD_NUM + 1) +#define TEGRA210_MBDRC_MAX_BIQUAD_STAGES 8 + +#define TEGRA210_MBDRC_BIQ_PARAMS_PER_STAGE 5 + +struct tegra210_mbdrc_band_params { + u32 band; + u32 iir_stages; + u32 in_attack_tc; + u32 in_release_tc; + u32 fast_attack_tc; + u32 in_threshold[TEGRA210_MBDRC_THRESHOLD_NUM]; + u32 out_threshold[TEGRA210_MBDRC_THRESHOLD_NUM]; + u32 ratio[TEGRA210_MBDRC_RATIO_NUM]; + u32 makeup_gain; + u32 gain_init; + u32 gain_attack_tc; + u32 gain_release_tc; + u32 fast_release_tc; + /* For biquad_params[][5] order of coeff is b0, b1, a0, a1, a2 */ + u32 biquad_params[TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5]; +}; + +struct tegra210_mbdrc_config { + unsigned int mode; + unsigned int rms_off; + unsigned int peak_rms_mode; + unsigned int fliter_structure; + unsigned int shift_ctrl; + unsigned int frame_size; + unsigned int channel_mask; + unsigned int fa_factor; /* Fast attack factor */ + unsigned int fr_factor; /* Fast release factor */ + struct tegra210_mbdrc_band_params band_params[MBDRC_NUM_BAND]; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c new file mode 100644 index 00000000..021c28a5 --- /dev/null +++ b/sound/soc/tegra/tegra210_mixer.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_mixer.c - Tegra210 MIXER driver +// +// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra210_ahub.h" +#include "tegra210_mixer.h" + +#define MIXER_RX_REG(reg, id) (reg + (id * TEGRA210_MIXER_RX_STRIDE)) +#define MIXER_TX_REG(reg, id) (reg + (id * TEGRA210_MIXER_TX_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_RX_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700}, \ + { MIXER_RX_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823}, \ + { MIXER_RX_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0} + +#define MIXER_TX_REG_DEFAULTS(id) \ + { MIXER_TX_REG(TEGRA210_MIXER_TX1_INT_MASK, id), 0x00000001}, \ + { MIXER_TX_REG(TEGRA210_MIXER_TX1_CIF_CTRL, id), 0x00007700} + +static const struct reg_default tegra210_mixer_reg_defaults[] = { + 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), + + 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}, +}; + +static int 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 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_put_format(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); + int value = ucontrol->value.integer.value[0]; + + if (strstr(kcontrol->id.name, "Audio Channels")) { + if (value >= 0 && value <= 8) + mixer->channels_via_control[mc->reg - 1] = value; + else + return -EINVAL; + } + + return 0; +} + +static int tegra210_mixer_get_format(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); + + if (strstr(kcontrol->id.name, "Audio Channels")) + ucontrol->value.integer.value[0] = + mixer->channels_via_control[mc->reg - 1]; + + return 0; +} +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, i; + int err; + + pm_runtime_get_sync(cmpnt->dev); + /* write default gain config poly coefficients */ + for (i = 0; i < 10; i++) + tegra210_mixer_write_ram(mixer, reg + i, mixer->gain_coeff[i]); + + /* set duration parameter */ + if (strstr(kcontrol->id.name, "Instant")) { + for (; i < 14; i++) + tegra210_mixer_write_ram(mixer, reg + i, 1); + } else { + for (; i < 14; i++) + tegra210_mixer_write_ram(mixer, reg + i, + mixer->gain_coeff[i]); + } + + /* write new gain and trigger config */ + err = tegra210_mixer_write_ram(mixer, reg + 0x09, + ucontrol->value.integer.value[0]); + err |= tegra210_mixer_write_ram(mixer, reg + 0x0f, + ucontrol->value.integer.value[0]); + pm_runtime_put(cmpnt->dev); + + /* save gain */ + i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) / + TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE; + mixer->gain_value[i] = ucontrol->value.integer.value[0]; + + return err; +} + +static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer, + struct snd_pcm_hw_params *params, + unsigned int reg, + unsigned int id) +{ + int channels, audio_bits; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + + if (mixer->channels_via_control[id]) + channels = mixer->channels_via_control[id]; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + 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, &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, i; + + err = tegra210_mixer_set_audio_cif(mixer, params, + TEGRA210_MIXER_RX1_CIF_CTRL + + (dai->id * TEGRA210_MIXER_RX_STRIDE), + dai->id); + + /* write the gain config poly coefficients */ + for (i = 0; i < 14; i++) { + tegra210_mixer_write_ram(mixer, + MIXER_GAIN_CFG_RAM_ADDR(dai->id) + i, + mixer->gain_coeff[i]); + } + + /* write saved gain */ + err = tegra210_mixer_write_ram(mixer, + MIXER_GAIN_CFG_RAM_ADDR(dai->id) + 0x09, + mixer->gain_value[dai->id]); + + /* trigger the polynomial configuration */ + tegra210_mixer_write_ram(mixer, + MIXER_GAIN_CFG_RAM_ADDR(dai->id) + 0xf, + 0x01); + + return err; +} + +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); + int err; + + err = tegra210_mixer_set_audio_cif(mixer, params, + TEGRA210_MIXER_TX1_CIF_CTRL + + ((dai->id-10) * TEGRA210_MIXER_TX_STRIDE), + dai->id); + + return err; +} + +static struct snd_soc_dai_ops tegra210_mixer_out_dai_ops = { + .hw_params = tegra210_mixer_out_hw_params, +}; + +static struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = { + .hw_params = tegra210_mixer_in_hw_params, +}; + +#define IN_DAI(sname, id, dai_ops) \ + { \ + .name = #sname #id, \ + .playback = { \ + .stream_name = #sname #id " Receive", \ + .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_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = dai_ops, \ + } + +#define OUT_DAI(sname, id, dai_ops) \ + { \ + .name = #sname #id, \ + .capture = { \ + .stream_name = #sname #id " Transmit", \ + .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_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = dai_ops, \ + } + +static struct snd_soc_dai_driver tegra210_mixer_dais[] = { + IN_DAI(RX, 1, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 2, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 3, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 4, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 5, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 6, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 7, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 8, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 9, &tegra210_mixer_in_dai_ops), + IN_DAI(RX, 10, &tegra210_mixer_in_dai_ops), + OUT_DAI(TX, 1, &tegra210_mixer_out_dai_ops), + OUT_DAI(TX, 2, &tegra210_mixer_out_dai_ops), + OUT_DAI(TX, 3, &tegra210_mixer_out_dai_ops), + OUT_DAI(TX, 4, &tegra210_mixer_out_dai_ops), + OUT_DAI(TX, 5, &tegra210_mixer_out_dai_ops), +}; + +#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); + +static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = { \ + SOC_SINGLE_EXT("RX1 Gain", MIXER_GAIN_CFG_RAM_ADDR(0), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX2 Gain", MIXER_GAIN_CFG_RAM_ADDR(1), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX3 Gain", MIXER_GAIN_CFG_RAM_ADDR(2), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX4 Gain", MIXER_GAIN_CFG_RAM_ADDR(3), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX5 Gain", MIXER_GAIN_CFG_RAM_ADDR(4), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX6 Gain", MIXER_GAIN_CFG_RAM_ADDR(5), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX7 Gain", MIXER_GAIN_CFG_RAM_ADDR(6), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX8 Gain", MIXER_GAIN_CFG_RAM_ADDR(7), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX9 Gain", MIXER_GAIN_CFG_RAM_ADDR(8), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX10 Gain", MIXER_GAIN_CFG_RAM_ADDR(9), 0, 0x20000, 0, + tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX1 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(0), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX2 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(1), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX3 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(2), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX4 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(3), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX5 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(4), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX6 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(5), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX7 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(6), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX8 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(7), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX9 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(8), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX10 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(9), 0, + 0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain), + SOC_SINGLE_EXT("RX1 Audio Channels", 1, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX2 Audio Channels", 2, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX3 Audio Channels", 3, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX4 Audio Channels", 4, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX5 Audio Channels", 5, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX6 Audio Channels", 6, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX7 Audio Channels", 7, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX8 Audio Channels", 8, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX9 Audio Channels", 9, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("RX10 Audio Channels", 10, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("TX1 Audio Channels", 11, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("TX2 Audio Channels", 12, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("TX3 Audio Channels", 13, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("TX4 Audio Channels", 14, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE_EXT("TX5 Audio Channels", 15, 0, 8, 0, + tegra210_mixer_get_format, tegra210_mixer_put_format), + SOC_SINGLE("Mixer Enable", TEGRA210_MIXER_ENABLE, 0, 1, 0), +}; + +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 MIXER_ROUTES(name, id) \ + {name, "RX1", "RX1",}, \ + {name, "RX2", "RX2",}, \ + {name, "RX3", "RX3",}, \ + {name, "RX4", "RX4",}, \ + {name, "RX5", "RX5",}, \ + {name, "RX6", "RX6",}, \ + {name, "RX7", "RX7",}, \ + {name, "RX8", "RX8",}, \ + {name, "RX9", "RX9",}, \ + {name, "RX10", "RX10"}, \ + {"TX"#id, NULL, name} + +static const struct snd_soc_dapm_route tegra210_mixer_routes[] = { + { "RX1", NULL, "RX1 Receive" }, + { "RX2", NULL, "RX2 Receive" }, + { "RX3", NULL, "RX3 Receive" }, + { "RX4", NULL, "RX4 Receive" }, + { "RX5", NULL, "RX5 Receive" }, + { "RX6", NULL, "RX6 Receive" }, + { "RX7", NULL, "RX7 Receive" }, + { "RX8", NULL, "RX8 Receive" }, + { "RX9", NULL, "RX9 Receive" }, + { "RX10", NULL, "RX10 Receive" }, + /* route between MIXER RXs and TXs */ + MIXER_ROUTES("Adder1", 1), + MIXER_ROUTES("Adder2", 2), + MIXER_ROUTES("Adder3", 3), + MIXER_ROUTES("Adder4", 4), + MIXER_ROUTES("Adder5", 5), + { "TX1 Transmit", NULL, "TX1" }, + { "TX2 Transmit", NULL, "TX2" }, + { "TX3 Transmit", NULL, "TX3" }, + { "TX4 Transmit", NULL, "TX4" }, + { "TX5 Transmit", NULL, "TX5" }, +}; + +static 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), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static bool tegra210_mixer_wr_reg(struct device *dev, + unsigned int reg) +{ + if (reg < TEGRA210_MIXER_RX_LIMIT) + reg %= TEGRA210_MIXER_RX_STRIDE; + else if (reg < TEGRA210_MIXER_TX_LIMIT) + reg = (reg % TEGRA210_MIXER_TX_STRIDE) + + 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 %= TEGRA210_MIXER_RX_STRIDE; + else if (reg < TEGRA210_MIXER_TX_LIMIT) + reg = (reg % TEGRA210_MIXER_TX_STRIDE) + + 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 %= TEGRA210_MIXER_RX_STRIDE; + else if (reg < TEGRA210_MIXER_TX_LIMIT) + reg = (reg % TEGRA210_MIXER_TX_STRIDE) + + 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; + + mixer->gain_coeff[0] = 0; + mixer->gain_coeff[1] = 0; + mixer->gain_coeff[2] = 0; + mixer->gain_coeff[3] = 0; + mixer->gain_coeff[4] = 0; + mixer->gain_coeff[5] = 0; + mixer->gain_coeff[6] = 0; + mixer->gain_coeff[7] = 0x1000000; + mixer->gain_coeff[8] = 0; + mixer->gain_coeff[9] = 0x10000; + mixer->gain_coeff[10] = 0; + mixer->gain_coeff[11] = 0; + mixer->gain_coeff[12] = 0x400; + mixer->gain_coeff[13] = 0x8000000; + dev_set_drvdata(dev, mixer); + + for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++) + mixer->gain_value[i] = 0x10000; + + 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 "); +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 00000000..c6fe2fcc --- /dev/null +++ b/sound/soc/tegra/tegra210_mixer.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_mixer.h - Definitions for Tegra210 MIXER driver + * + * Copyright (c) 2015-2021, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_MIXER_H__ +#define __TEGRA210_MIXER_H__ + +#define TEGRA210_MIXER_RX_STRIDE 0x40 +#define TEGRA210_MIXER_RX_MAX 10 +#define TEGRA210_MIXER_RX_LIMIT (TEGRA210_MIXER_RX_MAX * TEGRA210_MIXER_RX_STRIDE) + +/* AXBAR_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 +#define TEGRA210_MIXER_RX1_CYA 0x34 +#define TEGRA210_MIXER_RX1_DBG0 0x38 +#define TEGRA210_MIXER_RX1_DBG1 0x3c + +#define TEGRA210_MIXER_TX_STRIDE 0x40 +#define TEGRA210_MIXER_TX_MAX 5 +#define TEGRA210_MIXER_TX_LIMIT (TEGRA210_MIXER_RX_LIMIT + (TEGRA210_MIXER_TX_MAX * TEGRA210_MIXER_TX_STRIDE)) + +/* AXBAR_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 +#define TEGRA210_MIXER_TX1_CYA 0x2ac +#define TEGRA210_MIXER_TX1_DBG0 0x2b0 +#define TEGRA210_MIXER_TX1_DBG1 0x2b4 + +/* 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_CYA 0x440 +#define TEGRA210_MIXER_DBG 0x448 + +#define TEGRA210_MIXER_TX_ENABLE_SHIFT 0 +#define TEGRA210_MIXER_TX_EN (1 << TEGRA210_MIXER_TX_ENABLE_SHIFT) + +/* TODO Add fields for MIXER_TX1_ADDER_CONFIG register */ +#define TEGRA210_MIXER_TX2_ADDER_CONFIG (TEGRA210_MIXER_TX1_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE) +#define TEGRA210_MIXER_TX3_ADDER_CONFIG (TEGRA210_MIXER_TX2_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE) +#define TEGRA210_MIXER_TX4_ADDER_CONFIG (TEGRA210_MIXER_TX3_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE) +#define TEGRA210_MIXER_TX5_ADDER_CONFIG (TEGRA210_MIXER_TX4_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE) + +#define TEGRA210_MIXER_TX2_ENABLE (TEGRA210_MIXER_TX1_ENABLE + TEGRA210_MIXER_TX_STRIDE) +#define TEGRA210_MIXER_TX3_ENABLE (TEGRA210_MIXER_TX2_ENABLE + TEGRA210_MIXER_TX_STRIDE) +#define TEGRA210_MIXER_TX4_ENABLE (TEGRA210_MIXER_TX3_ENABLE + TEGRA210_MIXER_TX_STRIDE) +#define TEGRA210_MIXER_TX5_ENABLE (TEGRA210_MIXER_TX4_ENABLE + TEGRA210_MIXER_TX_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_CTRL */ +#define TEGRA210_MIXER_CTRL_ENABLE_BYPASS_MODE 1 +#define TEGRA210_MIXER_CTRL_DISABLE_BYPASS_MODE 0 + +/* 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_TOTAL_PATH (TEGRA210_MIXER_RX_MAX + TEGRA210_MIXER_TX_MAX) + +struct tegra210_mixer { + struct regmap *regmap; + int gain_coeff[14]; + int gain_value[TEGRA210_MIXER_RX_MAX]; + unsigned int channels_via_control[TEGRA210_MIXER_TOTAL_PATH]; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c new file mode 100644 index 00000000..53e130d9 --- /dev/null +++ b/sound/soc/tegra/tegra210_mvc.c @@ -0,0 +1,779 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_mvc.c - Tegra210 MVC driver +// +// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra210_ahub.h" +#include "tegra210_mvc.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 int 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 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 int tegra210_mvc_write_ram(struct tegra210_mvc *mvc, + unsigned int addr, unsigned int coef) +{ + unsigned int reg, val; + int err; + + err = regmap_read_poll_timeout(mvc->regmap, + TEGRA210_MVC_CFG_RAM_CTRL, + val, !(val & 0x80000000), 10, 10000); + if (err < 0) + return err; + + reg = (addr << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT) & + TEGRA210_MVC_CFG_RAM_CTRL_ADDR_MASK; + reg |= TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN; + reg |= TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE; + reg |= TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN; + + regmap_write(mvc->regmap, TEGRA210_MVC_CFG_RAM_CTRL, reg); + regmap_write(mvc->regmap, TEGRA210_MVC_CFG_RAM_DATA, + coef); + + return 0; +} + +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); + unsigned int reg = mc->reg; + + if (reg == TEGRA210_MVC_CTRL) { + u32 val; + u8 mute_mask; + + 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; + + if (strstr(kcontrol->id.name, "Per Chan Mute Mask")) { + /* + * If per channel control is enabled, then return + * exact mute/unmute setting of all channels. + * + * Else report setting based on CH0 bit to reflect + * the correct HW state. + */ + if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) { + ucontrol->value.integer.value[0] = mute_mask; + } else { + if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN) + ucontrol->value.integer.value[0] = + TEGRA210_MUTE_MASK_EN; + else + ucontrol->value.integer.value[0] = 0; + } + } else { + /* + * If per channel control is disabled, then return + * master mute/unmute setting based on CH0 bit. + * + * Else report settings based on state of all + * channels. + */ + if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) { + ucontrol->value.integer.value[0] = + mute_mask & TEGRA210_MVC_CH0_MUTE_EN; + } else { + if (mute_mask == TEGRA210_MUTE_MASK_EN) + ucontrol->value.integer.value[0] = + TEGRA210_MVC_CH0_MUTE_EN; + else + ucontrol->value.integer.value[0] = 0; + } + } + } else { + u8 chan = (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 void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val) +{ + /* Volume control read from mixer ctl 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_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; + int err, i; + + 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; + + if (reg == TEGRA210_MVC_CTRL) { + u32 reg_mask; + u8 mute_mask; + + mute_mask = ucontrol->value.integer.value[0]; + + if (strstr(kcontrol->id.name, "Per Chan Mute Mask")) { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + TEGRA210_MVC_PER_CHAN_CTRL_EN); + + reg_mask = TEGRA210_MVC_MUTE_MASK; + + } else { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + 0); + + reg_mask = TEGRA210_MVC_CH0_MUTE_MASK; + } + + regmap_update_bits(mvc->regmap, reg, reg_mask, + mute_mask << TEGRA210_MVC_MUTE_SHIFT); + } else { + u8 chan; + + chan = (reg - TEGRA210_MVC_TARGET_VOL)/REG_SIZE; + tegra210_mvc_conv_vol(mvc, chan, + ucontrol->value.integer.value[0]); + + /* Config init vol same as target vol */ + if (strstr(kcontrol->id.name, "Channel")) { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + TEGRA210_MVC_PER_CHAN_CTRL_EN); + + regmap_write(mvc->regmap, + TEGRA210_MVC_REG_OFFSET( + TEGRA210_MVC_INIT_VOL, chan), + mvc->volume[chan]); + + regmap_write(mvc->regmap, reg, mvc->volume[chan]); + } else { + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, + 0); + + for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) + mvc->volume[i] = mvc->volume[0]; + + regmap_write(mvc->regmap, TEGRA210_MVC_INIT_VOL, + mvc->volume[0]); + + regmap_write(mvc->regmap, TEGRA210_MVC_TARGET_VOL, + mvc->volume[0]); + } + } + + regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, + TEGRA210_MVC_VOLUME_SWITCH_MASK, + TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); + +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 the volume for channels in MVC */ + 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 0; +} + +static int tegra210_mvc_get_audio_bits(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); + + if (mvc->audio_bits > 0) + ucontrol->value.integer.value[0] = (mvc->audio_bits + 1) * 4; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int tegra210_mvc_put_audio_bits(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); + unsigned int val; + + val = ucontrol->value.integer.value[0]; + if ((val >= 8) && (val <= 32) && (val%4 == 0)) + mvc->audio_bits = val/4 - 1; + else if (val == 0) + mvc->audio_bits = 0; + else + return -EINVAL; + + return 0; +} +static int tegra210_mvc_get_format(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); + + /* get the format control flag */ + if (strstr(kcontrol->id.name, "Audio Bit Format")) + ucontrol->value.integer.value[0] = mvc->format_in; + else if (strstr(kcontrol->id.name, "Audio Channels")) + ucontrol->value.integer.value[0] = mvc->cif_channels; + else if (strstr(kcontrol->id.name, "Bypass")) + ucontrol->value.integer.value[0] = mvc->bypass_mode; + + return 0; +} + +static int tegra210_mvc_put_format(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); + unsigned int value = ucontrol->value.integer.value[0]; + + /* set the format control flag */ + if (strstr(kcontrol->id.name, "Audio Bit Format")) + mvc->format_in = value; + else if (strstr(kcontrol->id.name, "Audio Channels")) + mvc->cif_channels = value; + else if (strstr(kcontrol->id.name, "Bypass")) + mvc->bypass_mode = value; + + return 0; +} + +static const int tegra210_mvc_fmt_values[] = { + 0, + TEGRA_ACIF_BITS_16, + TEGRA_ACIF_BITS_32, +}; + +static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + int channels, audio_bits; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + if (mvc->cif_channels > 0) + channels = mvc->cif_channels; + + if (channels > 8) + return -EINVAL; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EINVAL; + } + + if (mvc->audio_bits > 0) + audio_bits = mvc->audio_bits; + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = audio_bits; + + /* Override input format to MVC, if set */ + if (mvc->format_in && (reg == TEGRA210_MVC_RX_CIF_CTRL)) + cif_conf.audio_bits = tegra210_mvc_fmt_values[mvc->format_in]; + + 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 i, err, val; + + /* SW reset */ + 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 and TX 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; + } + 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; + } + + /* program the poly coefficients */ + for (i = 0; i < 9; i++) { + err = tegra210_mvc_write_ram(mvc, i, mvc->poly_coeff[i]); + if (err < 0) { + dev_err(dai->dev, "failed to write coefs, err = %d\n", + err); + return err; + } + } + + /* program poly_n1, poly_n2, duration */ + regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, mvc->poly_n1); + regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, mvc->poly_n2); + regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, mvc->duration); + + /* program duration_inv */ + regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, mvc->duration_inv); + + /* set bypass mode */ + regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, + TEGRA210_MVC_BYPASS_MODE_MASK, + mvc->bypass_mode << TEGRA210_MVC_BYPASS_MODE_SHIFT); + + return err; +} + +static struct snd_soc_dai_ops tegra210_mvc_dai_ops = { + .hw_params = tegra210_mvc_hw_params, +}; + +static const unsigned int tegra210_mvc_curve_type_values[] = { + CURVE_POLY, + CURVE_LINEAR, +}; + +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); + +static const char * const tegra210_mvc_format_text[] = { + "None", + "16", + "32", +}; + +static const struct soc_enum tegra210_mvc_format_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(tegra210_mvc_format_text), + tegra210_mvc_format_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[] = { + 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), + SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0, + tegra210_mvc_get_vol, tegra210_mvc_put_vol), + SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0, + tegra210_mvc_get_vol, tegra210_mvc_put_vol), + SOC_SINGLE_EXT("Per Chan Mute Mask", TEGRA210_MVC_CTRL, 0, + TEGRA210_MUTE_MASK_EN, 0, tegra210_mvc_get_vol, + tegra210_mvc_put_vol), + SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl, + tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), + SOC_SINGLE_EXT("Bits", 0, 0, 32, 0, + tegra210_mvc_get_audio_bits, tegra210_mvc_put_audio_bits), + SOC_SINGLE_EXT("Audio Channels", 0, 0, 8, 0, + tegra210_mvc_get_format, tegra210_mvc_put_format), + SOC_ENUM_EXT("Audio Bit Format", tegra210_mvc_format_enum, + tegra210_mvc_get_format, tegra210_mvc_put_format), + SOC_SINGLE_EXT("Bypass", TEGRA210_MVC_CTRL, 0, 1, 0, + tegra210_mvc_get_format, tegra210_mvc_put_format), +}; + +static struct snd_soc_dai_driver tegra210_mvc_dais[] = { + { + .name = "MVC IN", + .playback = { + .stream_name = "MVC Receive", + .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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, + { + .name = "MVC OUT", + .capture = { + .stream_name = "MVC Transmit", + .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_S24_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("MVC RX", NULL, 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("MVC TX", NULL, 0, TEGRA210_MVC_ENABLE, + TEGRA210_MVC_EN_SHIFT, 0), +}; + +static const struct snd_soc_dapm_route tegra210_mvc_routes[] = { + { "MVC RX", NULL, "MVC Receive" }, + { "MVC TX", NULL, "MVC RX" }, + { "MVC Transmit", NULL, "MVC TX" }, +}; + +static 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), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_DBG: + 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_CYA: + case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CYA: + case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG: + case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA: + case TEGRA210_MVC_CYA: + 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_CYA, + .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->poly_n1 = 16; + mvc->poly_n2 = 63; + mvc->duration = 150; + mvc->duration_inv = 14316558; + mvc->poly_coeff[0] = 23738319; + mvc->poly_coeff[1] = 659403; + mvc->poly_coeff[2] = -3680; + mvc->poly_coeff[3] = 15546680; + mvc->poly_coeff[4] = 2530732; + mvc->poly_coeff[5] = -120985; + mvc->poly_coeff[6] = 12048422; + mvc->poly_coeff[7] = 5527252; + mvc->poly_coeff[8] = -785042; + 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 "); +MODULE_DESCRIPTION("Tegra210 MVC ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h new file mode 100644 index 00000000..cce8cb62 --- /dev/null +++ b/sound/soc/tegra/tegra210_mvc.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_mvc.h - Definitions for Tegra210 MVC driver + * + * Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_MVC_H__ +#define __TEGRA210_MVC_H__ + +/* + * MVC_RX registers are with respect to AXBAR. + * The data is coming from AXBAR to MVC for playback. + */ +#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 +#define TEGRA210_MVC_RX_CYA 0x24 +#define TEGRA210_MVC_RX_DBG 0x28 + +/* + * MVC_TX registers are with respect to AXBAR. + * The data is going out of MVC for playback. + */ +#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 +#define TEGRA210_MVC_TX_CYA 0x64 +#define TEGRA210_MVC_TX_DBG 0x68 + +/* 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 +#define TEGRA210_MVC_CYA 0x130 +#define TEGRA210_MVC_DBG 0x138 + +/* 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_CH0_MUTE_EN 1 +#define TEGRA210_MVC_CH0_MUTE_MASK (TEGRA210_MVC_CH0_MUTE_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 + +#define TEGRA210_MVC_BYPASS_MODE_SHIFT 31 +#define TEGRA210_MVC_BYPASS_MODE_MASK (1 << TEGRA210_MVC_BYPASS_MODE_SHIFT) + +/* 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)) + +enum { + CURVE_POLY, + CURVE_LINEAR, +}; + +struct tegra210_mvc { + struct regmap *regmap; + int poly_coeff[9]; + int poly_n1, poly_n2, duration, duration_inv; + int volume[TEGRA210_MVC_MAX_CHAN_COUNT]; + unsigned int curve_type; + unsigned int ctrl_value; + unsigned int cif_channels; + unsigned int audio_bits; + unsigned int format_in; + bool bypass_mode; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_ope.c b/sound/soc/tegra/tegra210_ope.c new file mode 100644 index 00000000..b09cea59 --- /dev/null +++ b/sound/soc/tegra/tegra210_ope.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_ope.c - Tegra210 OPE driver +// +// Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra210_ahub.h" +#include "tegra210_ope.h" + +static const struct reg_default tegra210_ope_reg_defaults[] = { + { TEGRA210_OPE_AXBAR_RX_INT_MASK, 0x00000001}, + { TEGRA210_OPE_AXBAR_RX_CIF_CTRL, 0x00007700}, + { TEGRA210_OPE_AXBAR_TX_INT_MASK, 0x00000001}, + { TEGRA210_OPE_AXBAR_TX_CIF_CTRL, 0x00007700}, + { TEGRA210_OPE_CG, 0x1}, +}; + +static int tegra210_ope_runtime_suspend(struct device *dev) +{ + struct tegra210_ope *ope = dev_get_drvdata(dev); + + tegra210_peq_save(ope); + + regcache_cache_only(ope->mbdrc_regmap, true); + regcache_cache_only(ope->peq_regmap, true); + regcache_cache_only(ope->regmap, true); + regcache_mark_dirty(ope->regmap); + regcache_mark_dirty(ope->peq_regmap); + regcache_mark_dirty(ope->mbdrc_regmap); + + return 0; +} + +static int tegra210_ope_runtime_resume(struct device *dev) +{ + struct tegra210_ope *ope = dev_get_drvdata(dev); + + regcache_cache_only(ope->regmap, false); + regcache_cache_only(ope->peq_regmap, false); + regcache_cache_only(ope->mbdrc_regmap, false); + regcache_sync(ope->regmap); + regcache_sync(ope->peq_regmap); + regcache_sync(ope->mbdrc_regmap); + tegra210_peq_restore(ope); + + return 0; +} + +static int tegra210_ope_set_audio_cif(struct tegra210_ope *ope, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + int channels, audio_bits; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + if (channels < 2) + return -EINVAL; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + 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(ope->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra210_ope_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_ope *ope = snd_soc_dai_get_drvdata(dai); + int err; + + /* set RX cif and TX cif */ + err = tegra210_ope_set_audio_cif(ope, params, + TEGRA210_OPE_AXBAR_RX_CIF_CTRL); + if (err) { + dev_err(dev, "Can't set OPE RX CIF: %d\n", err); + return err; + } + + err = tegra210_ope_set_audio_cif(ope, params, + TEGRA210_OPE_AXBAR_TX_CIF_CTRL); + if (err) { + dev_err(dev, "Can't set OPE TX CIF: %d\n", err); + return err; + } + + tegra210_mbdrc_hw_params(dai->component); + + return err; +} + +static int tegra210_ope_codec_probe(struct snd_soc_component *cmpnt) +{ + struct tegra210_ope *ope = dev_get_drvdata(cmpnt->dev); + + tegra210_peq_codec_init(cmpnt); + tegra210_mbdrc_codec_init(cmpnt); + + /* + * The OPE, PEQ and MBDRC functionalities are combined under one + * device registered by OPE driver. However there are separate + * regmap interfaces for each of these. ASoC core depends on + * dev_get_regmap() to populate the regmap field for a given ASoC + * component. Due to multiple regmap interfaces, it always uses + * the last registered interface in probe(). The DAPM routes in + * the current driver depend on OPE regmap. So to avoid dependency + * on probe order and to allow DAPM paths to use correct regmap + * below explicit assignment is done. + */ + snd_soc_component_init_regmap(cmpnt, ope->regmap); + + return 0; +} + +static struct snd_soc_dai_ops tegra210_ope_dai_ops = { + .hw_params = tegra210_ope_hw_params, +}; + +static struct snd_soc_dai_driver tegra210_ope_dais[] = { + { + .name = "OPE IN", + .playback = { + .stream_name = "OPE Receive", + .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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, + { + .name = "OPE OUT", + .capture = { + .stream_name = "OPE Transmit", + .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_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra210_ope_dai_ops, + } +}; + +static const struct snd_soc_dapm_widget tegra210_ope_widgets[] = { + SND_SOC_DAPM_AIF_IN("OPE RX", NULL, 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("OPE TX", NULL, 0, TEGRA210_OPE_ENABLE, + TEGRA210_OPE_EN_SHIFT, 0), +}; + +static const struct snd_soc_dapm_route tegra210_ope_routes[] = { + { "OPE RX", NULL, "OPE Receive" }, + { "OPE TX", NULL, "OPE RX" }, + { "OPE Transmit", NULL, "OPE TX" }, +}; + +static const struct snd_kcontrol_new tegra210_ope_controls[] = { + SOC_SINGLE("direction peq to mbdrc", TEGRA210_OPE_DIRECTION, + TEGRA210_OPE_DIRECTION_SHIFT, 1, 0), +}; + +static struct snd_soc_component_driver tegra210_ope_cmpnt = { + .probe = tegra210_ope_codec_probe, + .dapm_widgets = tegra210_ope_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_ope_widgets), + .dapm_routes = tegra210_ope_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_ope_routes), + .controls = tegra210_ope_controls, + .num_controls = ARRAY_SIZE(tegra210_ope_controls), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +static bool tegra210_ope_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_OPE_AXBAR_RX_INT_MASK: + case TEGRA210_OPE_AXBAR_RX_INT_SET: + case TEGRA210_OPE_AXBAR_RX_INT_CLEAR: + case TEGRA210_OPE_AXBAR_RX_CIF_CTRL: + + case TEGRA210_OPE_AXBAR_TX_INT_MASK: + case TEGRA210_OPE_AXBAR_TX_INT_SET: + case TEGRA210_OPE_AXBAR_TX_INT_CLEAR: + case TEGRA210_OPE_AXBAR_TX_CIF_CTRL: + + case TEGRA210_OPE_ENABLE: + case TEGRA210_OPE_SOFT_RESET: + case TEGRA210_OPE_CG: + case TEGRA210_OPE_DIRECTION: + return true; + default: + return false; + } +} + +static bool tegra210_ope_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_OPE_AXBAR_RX_STATUS: + case TEGRA210_OPE_AXBAR_RX_INT_STATUS: + case TEGRA210_OPE_AXBAR_RX_INT_MASK: + case TEGRA210_OPE_AXBAR_RX_INT_SET: + case TEGRA210_OPE_AXBAR_RX_INT_CLEAR: + case TEGRA210_OPE_AXBAR_RX_CIF_CTRL: + + case TEGRA210_OPE_AXBAR_TX_STATUS: + case TEGRA210_OPE_AXBAR_TX_INT_STATUS: + case TEGRA210_OPE_AXBAR_TX_INT_MASK: + case TEGRA210_OPE_AXBAR_TX_INT_SET: + case TEGRA210_OPE_AXBAR_TX_INT_CLEAR: + case TEGRA210_OPE_AXBAR_TX_CIF_CTRL: + + case TEGRA210_OPE_ENABLE: + case TEGRA210_OPE_SOFT_RESET: + case TEGRA210_OPE_CG: + case TEGRA210_OPE_STATUS: + case TEGRA210_OPE_INT_STATUS: + case TEGRA210_OPE_DIRECTION: + return true; + default: + return false; + } +} + +static bool tegra210_ope_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_OPE_AXBAR_RX_STATUS: + case TEGRA210_OPE_AXBAR_RX_INT_SET: + case TEGRA210_OPE_AXBAR_RX_INT_STATUS: + + case TEGRA210_OPE_AXBAR_TX_STATUS: + case TEGRA210_OPE_AXBAR_TX_INT_SET: + case TEGRA210_OPE_AXBAR_TX_INT_STATUS: + + case TEGRA210_OPE_SOFT_RESET: + case TEGRA210_OPE_STATUS: + case TEGRA210_OPE_INT_STATUS: + return true; + default: + return false; + } +} + +static const struct regmap_config tegra210_ope_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_OPE_DIRECTION, + .writeable_reg = tegra210_ope_wr_reg, + .readable_reg = tegra210_ope_rd_reg, + .volatile_reg = tegra210_ope_volatile_reg, + .reg_defaults = tegra210_ope_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_ope_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id tegra210_ope_of_match[] = { + { .compatible = "nvidia,tegra210-ope" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra210_ope_of_match); + +static int tegra210_ope_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_ope *ope; + void __iomem *regs; + int err; + + ope = devm_kzalloc(dev, sizeof(*ope), GFP_KERNEL); + if (!ope) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + ope->regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_ope_regmap_config); + if (IS_ERR(ope->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(ope->regmap); + } + + regcache_cache_only(ope->regmap, true); + + dev_set_drvdata(dev, ope); + + err = tegra210_peq_regmap_init(pdev); + if (err < 0) { + dev_err(dev, "PEQ init failed\n"); + return err; + } + + err = tegra210_mbdrc_regmap_init(pdev); + if (err < 0) { + dev_err(dev, "MBDRC init failed\n"); + return err; + } + + err = devm_snd_soc_register_component(dev, &tegra210_ope_cmpnt, + tegra210_ope_dais, + ARRAY_SIZE(tegra210_ope_dais)); + if (err) { + dev_err(dev, "can't register OPE component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra210_ope_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra210_ope_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_ope_runtime_suspend, + tegra210_ope_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra210_ope_driver = { + .driver = { + .name = "tegra210-ope", + .of_match_table = tegra210_ope_of_match, + .pm = &tegra210_ope_pm_ops, + }, + .probe = tegra210_ope_platform_probe, + .remove = tegra210_ope_platform_remove, +}; +module_platform_driver(tegra210_ope_driver) + +MODULE_AUTHOR("Sumit Bhattacharya "); +MODULE_DESCRIPTION("Tegra210 OPE ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra210_ope.h b/sound/soc/tegra/tegra210_ope.h new file mode 100644 index 00000000..ae1e44f2 --- /dev/null +++ b/sound/soc/tegra/tegra210_ope.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_ope.h - Definitions for Tegra210 OPE driver + * + * Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_OPE_H__ +#define __TEGRA210_OPE_H__ + +#include "tegra210_peq.h" + +/* Register offsets from TEGRA210_OPE*_BASE */ +/* + * OPE_AXBAR_RX registers are with respect to AXBAR. + * The data is coming from AXBAR to OPE for playback. + */ +#define TEGRA210_OPE_AXBAR_RX_STATUS 0xc +#define TEGRA210_OPE_AXBAR_RX_INT_STATUS 0x10 +#define TEGRA210_OPE_AXBAR_RX_INT_MASK 0x14 +#define TEGRA210_OPE_AXBAR_RX_INT_SET 0x18 +#define TEGRA210_OPE_AXBAR_RX_INT_CLEAR 0x1c +#define TEGRA210_OPE_AXBAR_RX_CIF_CTRL 0x20 + +/* + * OPE_AXBAR_TX registers are with respect to AXBAR. + * The data is going out of OPE for playback. + */ +#define TEGRA210_OPE_AXBAR_TX_STATUS 0x4c +#define TEGRA210_OPE_AXBAR_TX_INT_STATUS 0x50 +#define TEGRA210_OPE_AXBAR_TX_INT_MASK 0x54 +#define TEGRA210_OPE_AXBAR_TX_INT_SET 0x58 +#define TEGRA210_OPE_AXBAR_TX_INT_CLEAR 0x5c +#define TEGRA210_OPE_AXBAR_TX_CIF_CTRL 0x60 + +/* OPE Gloabal registers */ +#define TEGRA210_OPE_ENABLE 0x80 +#define TEGRA210_OPE_SOFT_RESET 0x84 +#define TEGRA210_OPE_CG 0x88 +#define TEGRA210_OPE_STATUS 0x8c +#define TEGRA210_OPE_INT_STATUS 0x90 +#define TEGRA210_OPE_DIRECTION 0x94 + +/* Fields for TEGRA210_OPE_ENABLE */ +#define TEGRA210_OPE_EN_SHIFT 0 +#define TEGRA210_OPE_EN (1 << TEGRA210_OPE_EN_SHIFT) + +/* Fields for TEGRA210_OPE_SOFT_RESET */ +#define TEGRA210_OPE_SOFT_RESET_SHIFT 0 +#define TEGRA210_OPE_SOFT_RESET_EN (1 << TEGRA210_OPE_SOFT_RESET_SHIFT) + +/* Fields for TEGRA210_OPE_DIRECTION */ +#define TEGRA210_OPE_DIRECTION_SHIFT 0 +#define TEGRA210_OPE_DIRECTION_MASK (1 << TEGRA210_OPE_DIRECTION_SHIFT) +#define TEGRA210_OPE_DIRECTION_MBDRC_TO_PEQ (0 << TEGRA210_OPE_DIRECTION_SHIFT) +#define TEGRA210_OPE_DIRECTION_PEQ_TO_MBDRC (1 << TEGRA210_OPE_DIRECTION_SHIFT) +/* OPE register definitions end here */ + +#define TEGRA210_PEQ_IORESOURCE_MEM 1 +#define TEGRA210_MBDRC_IORESOURCE_MEM 2 + +struct tegra210_ope { + struct regmap *regmap; + struct regmap *peq_regmap; + struct regmap *mbdrc_regmap; + u32 peq_biquad_gains[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH]; + u32 peq_biquad_shifts[TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH]; +}; + +extern int tegra210_peq_regmap_init(struct platform_device *pdev); +extern int tegra210_peq_codec_init(struct snd_soc_component *cmpnt); +extern void tegra210_peq_restore(struct tegra210_ope *ope); +extern void tegra210_peq_save(struct tegra210_ope *ope); +extern int tegra210_mbdrc_regmap_init(struct platform_device *pdev); +extern int tegra210_mbdrc_codec_init(struct snd_soc_component *cmpnt); +extern int tegra210_mbdrc_hw_params(struct snd_soc_component *cmpnt); + +/* Extension of soc_bytes structure defined in sound/soc.h */ +struct tegra_soc_bytes { + struct soc_bytes soc; + u32 shift; /* Used as offset for ahub ram related programing */ +}; + +/* Utility structures for using mixer control of type snd_soc_bytes */ +#define TEGRA_SOC_BYTES_EXT(xname, xbase, xregs, xshift, xmask, \ + xhandler_get, xhandler_put, xinfo) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = xinfo, \ + .get = xhandler_get, \ + .put = xhandler_put, \ + .private_value = ((unsigned long)&(struct tegra_soc_bytes) \ + { \ + .soc.base = xbase, \ + .soc.num_regs = xregs, \ + .soc.mask = xmask, \ + .shift = xshift \ + }) \ +} + +#endif diff --git a/sound/soc/tegra/tegra210_peq.c b/sound/soc/tegra/tegra210_peq.c new file mode 100644 index 00000000..f816b07d --- /dev/null +++ b/sound/soc/tegra/tegra210_peq.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_peq.c - Tegra210 PEQ driver +// +// Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra210_ahub.h" +#include "tegra210_ope.h" +#include "tegra210_peq.h" + +static const struct reg_default tegra210_peq_reg_defaults[] = { + { TEGRA210_PEQ_CONFIG, 0x00000013}, + { TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL, 0x00004000}, + { TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL, 0x00004000}, +}; + +/* Default PEQ filter parameters for a 5-stage biquad*/ +static const int biquad_init_stage = 5; +static const u32 biquad_init_gains[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH] = { + 1495012349, /* pre-gain */ + /* Gains : b0, b1, a0, a1, a2 */ + 536870912, -1073741824, 536870912, 2143508246, -1069773768, /* band-0 */ + 134217728, -265414508, 131766272, 2140402222, -1071252997, /* band-1 */ + 268435456, -233515765, -33935948, 1839817267, -773826124, /* band-2 */ + 536870912, -672537913, 139851540, 1886437554, -824433167, /* band-3 */ + 268435456, -114439279, 173723964, 205743566, 278809729, /* band-4 */ + 1, 0, 0, 0, 0, /* band-5 */ + 1, 0, 0, 0, 0, /* band-6 */ + 1, 0, 0, 0, 0, /* band-7 */ + 1, 0, 0, 0, 0, /* band-8 */ + 1, 0, 0, 0, 0, /* band-9 */ + 1, 0, 0, 0, 0, /* band-10 */ + 1, 0, 0, 0, 0, /* band-11 */ + 963423114, /* post-gain */ +}; + +static const u32 biquad_init_shifts[TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH] = { + 23, /* pre-shift */ + 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, /* shift for bands */ + 28, /* post-shift */ +}; + +static s32 biquad_coeff_buffer[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH]; + +static int tegra210_peq_get(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_ope *ope = snd_soc_component_get_drvdata(cmpnt); + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val; + + regmap_read(ope->peq_regmap, mc->reg, &val); + ucontrol->value.integer.value[0] = (val >> mc->shift) & mask; + if (mc->invert) + ucontrol->value.integer.value[0] = + mc->max - ucontrol->value.integer.value[0]; + + return 0; +} + +static int tegra210_peq_put(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_ope *ope = snd_soc_component_get_drvdata(cmpnt); + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val; + + val = (ucontrol->value.integer.value[0] & mask); + if (mc->invert) + val = mc->max - val; + val = val << mc->shift; + + return regmap_update_bits(ope->peq_regmap, mc->reg, + (mask << mc->shift), val); +} + +static int tegra210_peq_ahub_ram_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + u32 i, reg_ctrl = params->soc.base; + u32 reg_data = reg_ctrl + cmpnt->val_bytes; + s32 *data = (s32 *)biquad_coeff_buffer; + + pm_runtime_get_sync(cmpnt->dev); + tegra210_ahub_read_ram(ope->peq_regmap, reg_ctrl, reg_data, + params->shift, data, params->soc.num_regs); + pm_runtime_put_sync(cmpnt->dev); + + for (i = 0; i < params->soc.num_regs; i++) + ucontrol->value.integer.value[i] = (long)data[i]; + + return 0; +} + +static int tegra210_peq_ahub_ram_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + u32 i, reg_ctrl = params->soc.base; + u32 reg_data = reg_ctrl + cmpnt->val_bytes; + s32 *data = (s32 *)biquad_coeff_buffer; + + for (i = 0; i < params->soc.num_regs; i++) + data[i] = (s32)ucontrol->value.integer.value[i]; + + pm_runtime_get_sync(cmpnt->dev); + tegra210_ahub_write_ram(ope->peq_regmap, reg_ctrl, reg_data, + params->shift, data, params->soc.num_regs); + pm_runtime_put_sync(cmpnt->dev); + + return 0; +} + +static int tegra210_peq_param_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_bytes *params = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = -0x7fffffff; + uinfo->value.integer.max = 0x7fffffff; + uinfo->count = params->num_regs; + + return 0; +} + +#define TEGRA210_PEQ_GAIN_PARAMS_CTRL(chan) \ + TEGRA_SOC_BYTES_EXT("PEQ Channel-" #chan " biquad gain params", \ + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL, \ + TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH, \ + (TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH * chan), 0xffffffff, \ + tegra210_peq_ahub_ram_get, tegra210_peq_ahub_ram_put, \ + tegra210_peq_param_info) + +#define TEGRA210_PEQ_SHIFT_PARAMS_CTRL(chan) \ + TEGRA_SOC_BYTES_EXT("PEQ Channel-" #chan " biquad shift params", \ + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL, \ + TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH, \ + (TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH * chan), 0x1f, \ + tegra210_peq_ahub_ram_get, tegra210_peq_ahub_ram_put, \ + tegra210_peq_param_info) + +static const struct snd_kcontrol_new tegra210_peq_controls[] = { + SOC_SINGLE_EXT("PEQ Active", TEGRA210_PEQ_CONFIG, + TEGRA210_PEQ_CONFIG_MODE_SHIFT, 1, 0, + tegra210_peq_get, tegra210_peq_put), + SOC_SINGLE_EXT("PEQ Biquad Stages", TEGRA210_PEQ_CONFIG, + TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT, + TEGRA210_PEQ_MAX_BIQUAD_STAGES - 1, 0, + tegra210_peq_get, tegra210_peq_put), + + TEGRA210_PEQ_GAIN_PARAMS_CTRL(0), + TEGRA210_PEQ_GAIN_PARAMS_CTRL(1), + TEGRA210_PEQ_GAIN_PARAMS_CTRL(2), + TEGRA210_PEQ_GAIN_PARAMS_CTRL(3), + TEGRA210_PEQ_GAIN_PARAMS_CTRL(4), + TEGRA210_PEQ_GAIN_PARAMS_CTRL(5), + TEGRA210_PEQ_GAIN_PARAMS_CTRL(6), + TEGRA210_PEQ_GAIN_PARAMS_CTRL(7), + + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(0), + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(1), + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(2), + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(3), + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(4), + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(5), + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(6), + TEGRA210_PEQ_SHIFT_PARAMS_CTRL(7), +}; + +static bool tegra210_peq_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_PEQ_SOFT_RESET: + case TEGRA210_PEQ_CG: + case TEGRA210_PEQ_CONFIG: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_peq_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_PEQ_SOFT_RESET: + case TEGRA210_PEQ_CG: + case TEGRA210_PEQ_STATUS: + case TEGRA210_PEQ_CONFIG: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_peq_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_PEQ_SOFT_RESET: + case TEGRA210_PEQ_STATUS: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA: + return true; + default: + return false; + } +} + +static bool tegra210_peq_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA: + case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA: + return true; + default: + return false; + } +} + +static const struct regmap_config tegra210_peq_regmap_config = { + .name = "peq", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA, + .writeable_reg = tegra210_peq_wr_reg, + .readable_reg = tegra210_peq_rd_reg, + .volatile_reg = tegra210_peq_volatile_reg, + .precious_reg = tegra210_peq_precious_reg, + .reg_defaults = tegra210_peq_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra210_peq_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +void tegra210_peq_restore(struct tegra210_ope *ope) +{ + unsigned int i; + + for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) { + tegra210_ahub_write_ram(ope->peq_regmap, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA, + (i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH), + (u32 *)&ope->peq_biquad_gains, + TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH); + + tegra210_ahub_write_ram(ope->peq_regmap, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA, + (i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH), + (u32 *)&ope->peq_biquad_shifts, + TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH); + + } +} +EXPORT_SYMBOL_GPL(tegra210_peq_restore); + +void tegra210_peq_save(struct tegra210_ope *ope) +{ + unsigned int i; + + for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) { + tegra210_ahub_read_ram(ope->peq_regmap, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA, + (i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH), + (u32 *)&ope->peq_biquad_gains, + TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH); + + tegra210_ahub_read_ram(ope->peq_regmap, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA, + (i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH), + (u32 *)&ope->peq_biquad_shifts, + TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH); + + } +} +EXPORT_SYMBOL_GPL(tegra210_peq_save); + +int tegra210_peq_codec_init(struct snd_soc_component *cmpnt) +{ + struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); + int i = 0; + + pm_runtime_get_sync(cmpnt->dev); + regmap_update_bits(ope->peq_regmap, TEGRA210_PEQ_CONFIG, + TEGRA210_PEQ_CONFIG_MODE_MASK, + 0 << TEGRA210_PEQ_CONFIG_MODE_SHIFT); + regmap_update_bits(ope->peq_regmap, TEGRA210_PEQ_CONFIG, + TEGRA210_PEQ_CONfIG_BIQUAD_STAGES_MASK, + (biquad_init_stage - 1) << + TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT); + + /* Initialize PEQ AHUB RAM with default params */ + for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) { + /* Set default gain params */ + tegra210_ahub_write_ram(ope->peq_regmap, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA, + (i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH), + (u32 *)&biquad_init_gains, + TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH); + + /* Set default shift params */ + tegra210_ahub_write_ram(ope->peq_regmap, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL, + TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA, + (i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH), + (u32 *)&biquad_init_shifts, + TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH); + } + pm_runtime_put_sync(cmpnt->dev); + + snd_soc_add_component_controls(cmpnt, tegra210_peq_controls, + ARRAY_SIZE(tegra210_peq_controls)); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra210_peq_codec_init); + +int tegra210_peq_regmap_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra210_ope *ope = dev_get_drvdata(dev); + struct device_node *child; + struct resource mem; + void __iomem *regs; + int err; + + child = of_get_child_by_name(dev->of_node, "equalizer"); + if (!child) + return -ENODEV; + + err = of_address_to_resource(child, 0, &mem); + of_node_put(child); + if (err < 0) { + dev_err(dev, "fail to get PEQ resource\n"); + return err; + } + + mem.flags = IORESOURCE_MEM; + regs = devm_ioremap_resource(dev, &mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); + ope->peq_regmap = devm_regmap_init_mmio(dev, regs, + &tegra210_peq_regmap_config); + if (IS_ERR(ope->peq_regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(ope->peq_regmap); + } + + regcache_cache_only(ope->peq_regmap, true); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra210_peq_regmap_init); + +MODULE_AUTHOR("Sumit Bhattacharya "); +MODULE_DESCRIPTION("Tegra210 PEQ module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra210_peq.h b/sound/soc/tegra/tegra210_peq.h new file mode 100644 index 00000000..11086738 --- /dev/null +++ b/sound/soc/tegra/tegra210_peq.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_peq.h - Definitions for Tegra210 PEQ driver + * + * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_PEQ_H__ +#define __TEGRA210_PEQ_H__ + +/* Register offsets from TEGRA210_PEQ*_BASE */ +#define TEGRA210_PEQ_SOFT_RESET 0x0 +#define TEGRA210_PEQ_CG 0x4 +#define TEGRA210_PEQ_STATUS 0x8 +#define TEGRA210_PEQ_CONFIG 0xc +#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL 0x10 +#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA 0x14 +#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL 0x18 +#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA 0x1c + +/* Fields in TEGRA210_PEQ_CONFIG */ +#define TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT 2 +#define TEGRA210_PEQ_CONfIG_BIQUAD_STAGES_MASK (0xf << TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT) +#define TEGRA210_PEQ_CONFIG_BIAS_SHIFT 1 +#define TEGRA210_PEQ_CONFIG_BIAS_MASK (0x1 << TEGRA210_PEQ_CONFIG_BIAS_SHIFT) +#define TEGRA210_PEQ_CONFIG_UNBIAS (1 << TEGRA210_PEQ_CONFIG_BIAS_SHIFT) + +#define TEGRA210_PEQ_CONFIG_MODE_SHIFT 0 +#define TEGRA210_PEQ_CONFIG_MODE_MASK (0x1 << TEGRA210_PEQ_CONFIG_MODE_SHIFT) +#define TEGRA210_PEQ_CONFIG_MODE_ACTIVE (1 << TEGRA210_PEQ_CONFIG_MODE_SHIFT) + +/* PEQ register definition ends here */ +#define TEGRA210_PEQ_MAX_BIQUAD_STAGES 12 + +#define TEGRA210_PEQ_MAX_CHANNELS 8 + +#define TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH \ + (2 + TEGRA210_PEQ_MAX_BIQUAD_STAGES * 5) +#define TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH \ + (2 + TEGRA210_PEQ_MAX_BIQUAD_STAGES) + +#endif diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c new file mode 100644 index 00000000..b14c1590 --- /dev/null +++ b/sound/soc/tegra/tegra210_sfc.c @@ -0,0 +1,3520 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra210_sfc.c - Tegra210 SFC driver +// +// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tegra210_ahub.h" +#include "tegra210_sfc.h" + +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 int tegra210_sfc_rates[] = { + 8000, + 11025, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 64000, + 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 /* ouptut 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 +}; + +/* Below table has coefficients for conversion for every combination + * of sample rates defined in the driver(8000, 11025, 16000, 22050, + * 24000, 32000, 44100, 48000, 64000, 88100, 96000,176400, 192000). + * First row has coefficients for conversion from 8k to all above + * rates, in the same sequence as listed, second row has + * coefficients for conversion from 11.025k to all above rates and + * so on + */ +static u32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { + {NULL, coef_8to11, coef_8to16, coef_8to22, coef_8to24, coef_8to32, + coef_8to44, coef_8to48, NULL, coef_8to88, coef_8to96, NULL, NULL}, + {coef_11to8, NULL, coef_11to16, coef_11to22, coef_11to24, + coef_11to32, coef_11to44, coef_11to48, NULL, coef_11to88, + coef_11to96, NULL, NULL}, + {coef_16to8, coef_16to11, NULL, coef_16to22, coef_16to24, + coef_16to32, coef_16to44, coef_16to48, NULL, coef_16to88, + coef_16to96, coef_16to176, coef_16to192}, + {coef_22to8, coef_22to11, coef_22to16, NULL, coef_22to24, + coef_22to32, coef_22to44, coef_22to48, NULL, coef_22to88, + coef_22to96, coef_22to176, coef_22to192}, + {coef_24to8, coef_24to11, coef_24to16, coef_24to22, NULL, + coef_24to32, coef_24to44, coef_24to48, NULL, coef_24to88, + coef_24to96, coef_24to176, coef_24to192}, + {coef_32to8, coef_32to11, coef_32to16, coef_32to22, coef_32to24, + NULL, coef_32to44, coef_32to48, NULL, coef_32to88, coef_32to96, + coef_32to176, coef_32to192}, + {coef_44to8, coef_44to11, coef_44to16, coef_44to22, coef_44to24, + coef_44to32, NULL, coef_44to48, NULL, coef_44to88, coef_44to96, + coef_44to176, coef_44to192}, + {coef_48to8, coef_48to11, coef_48to16, coef_48to22, coef_48to24, + coef_48to32, coef_48to44, NULL, NULL, coef_48to88, coef_48to96, + coef_48to176, coef_48to192}, + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL}, + {coef_88to8, coef_88to11, coef_88to16, coef_88to22, coef_88to24, + coef_88to32, coef_88to44, coef_88to48, NULL, NULL, coef_88to96, + coef_88to176, coef_88to192}, + {coef_96to8, coef_96to11, coef_96to16, coef_96to22, coef_96to24, + coef_96to32, coef_96to44, coef_96to48, NULL, coef_96to88, NULL, + coef_96to176, coef_96to192}, + {NULL, NULL, coef_176to16, coef_176to22, coef_176to24, coef_176to32, + coef_176to44, coef_176to48, NULL, coef_176to88, coef_176to96, NULL, + coef_176to192}, + {NULL, NULL, coef_192to16, coef_192to22, coef_192to24, coef_192to32, + coef_192to44, coef_192to48, NULL, coef_192to88, coef_192to96, + coef_192to176, NULL}, +}; + +static int tegra210_sfc_rate_to_index(int rate) +{ + int index; + for (index = 0; index < ARRAY_SIZE(tegra210_sfc_rates); index++) { + if (rate == tegra210_sfc_rates[index]) + return index; + } + return -EINVAL; +} + +static int 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 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 int tegra210_sfc_write_coeff_ram(struct tegra210_sfc *sfc) +{ + u32 *coeff_ram = NULL; + + coeff_ram = coef_addr_table[sfc->srate_in][sfc->srate_out]; + if (!coeff_ram) + return -EINVAL; + + if (coeff_ram) { + tegra210_ahub_write_ram(sfc->regmap, + TEGRA210_SFC_CFG_RAM_CTRL, + TEGRA210_SFC_CFG_RAM_DATA, + 0, coeff_ram, TEGRA210_SFC_COEF_RAM_DEPTH); + + regmap_update_bits(sfc->regmap, + TEGRA210_SFC_COEF_RAM, + TEGRA210_SFC_COEF_RAM_EN, + TEGRA210_SFC_COEF_RAM_EN); + } + + return 0; +} + +static const int tegra210_sfc_fmt_values[] = { + 0, + TEGRA_ACIF_BITS_16, + TEGRA_ACIF_BITS_32, +}; + +static int tegra210_sfc_set_audio_cif(struct tegra210_sfc *sfc, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + 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_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EINVAL; + } + + path = (reg == TEGRA210_SFC_RX_CIF_CTRL) ? + SFC_RX_PATH : SFC_TX_PATH; + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + + if (sfc->audio_ch_override[path]) + cif_conf.audio_ch = sfc->audio_ch_override[path]; + + if (sfc->client_ch_override) + cif_conf.client_ch = sfc->client_ch_override; + + cif_conf.stereo_conv = sfc->stereo_to_mono[path]; + cif_conf.mono_conv = sfc->mono_to_stereo[path]; + + cif_conf.audio_bits = audio_bits; + if (sfc->format_in && (reg == TEGRA210_SFC_RX_CIF_CTRL)) + cif_conf.audio_bits = tegra210_sfc_fmt_values[sfc->format_in]; + if (sfc->format_out && (reg == TEGRA210_SFC_TX_CIF_CTRL)) + cif_conf.audio_bits = tegra210_sfc_fmt_values[sfc->format_out]; + cif_conf.client_bits = TEGRA_ACIF_BITS_32; + + tegra_set_cif(sfc->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra210_sfc_soft_reset(struct tegra210_sfc *sfc) +{ + u32 val; + int err; + + /* SW reset */ + regmap_update_bits(sfc->regmap, TEGRA210_SFC_SOFT_RESET, + TEGRA210_SFC_SOFT_RESET_EN, 1); + + err = regmap_read_poll_timeout(sfc->regmap, TEGRA210_SFC_SOFT_RESET, + val, !(val & TEGRA210_SFC_SOFT_RESET_EN), + 10, 10000); + if (err < 0) + 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 device *dev = dai->dev; + 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(dev, "failed to reset SFC in %s, err = %d\n", + __func__, err); + + return err; + } + + err = tegra210_sfc_set_audio_cif(sfc, params, + TEGRA210_SFC_RX_CIF_CTRL); + if (err) { + dev_err(dev, "Can't set SFC RX CIF: %d\n", err); + return err; + } + memcpy(&sfc->in_hw_params, params, sizeof(struct snd_pcm_hw_params)); + + regmap_write(sfc->regmap, TEGRA210_SFC_RX_FREQ, sfc->srate_in); + + if (sfc->srate_in != sfc->srate_out) { + err = tegra210_sfc_write_coeff_ram(sfc); + if (err) + dev_err(dev, "Conversion from %d to %d is not supported\n", + sfc->srate_in, sfc->srate_out); + } + + 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 device *dev = dai->dev; + struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai); + int err; + + err = tegra210_sfc_set_audio_cif(sfc, params, + TEGRA210_SFC_TX_CIF_CTRL); + if (err) { + dev_err(dev, "Can't set SFC TX CIF: %d\n", err); + return err; + } + memcpy(&sfc->out_hw_params, params, sizeof(struct snd_pcm_hw_params)); + + if (sfc->srate_out < 0) { + dev_err(dev, "SFC%d output rate not set: %d\n", + dev->id, -EINVAL); + return -EINVAL; + } + + regmap_write(sfc->regmap, TEGRA210_SFC_TX_FREQ, sfc->srate_out); + return err; +} + +static int tegra210_sfc_init(struct snd_soc_component *cmpnt, int init) +{ + struct tegra210_sfc *sfc = snd_soc_component_get_drvdata(cmpnt); + int is_enabled, err; + + if (!init) + return 0; + + dev_dbg(cmpnt->dev, "%s: inrate %d outrate %d\n", + __func__, sfc->srate_in, sfc->srate_out); + + err = pm_runtime_get_sync(cmpnt->dev); + if (err < 0) { + dev_err(cmpnt->dev, "RPM get_sync() failed: %d\n", err); + return err; + } + + regmap_read(sfc->regmap, TEGRA210_SFC_ENABLE, &is_enabled); + + if (is_enabled) { + u32 val; + + regmap_write(sfc->regmap, TEGRA210_SFC_ENABLE, 0); + + err = regmap_read_poll_timeout(sfc->regmap, + TEGRA210_SFC_STATUS, val, + !(val & 0x1), 10, 10000); + if (err < 0) { + dev_err(cmpnt->dev, + "failed to disable SFC, err = %d\n", err); + return 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(cmpnt->dev, + "failed to reset SFC in %s, err = %d\n", + __func__, err); + goto exit; + } + + err = tegra210_sfc_set_audio_cif(sfc, &sfc->in_hw_params, + TEGRA210_SFC_RX_CIF_CTRL); + if (err) { + dev_err(cmpnt->dev, "Can't set SFC RX CIF: %d\n", err); + goto exit; + } + + err = tegra210_sfc_set_audio_cif(sfc, &sfc->out_hw_params, + TEGRA210_SFC_TX_CIF_CTRL); + if (err) { + dev_err(cmpnt->dev, "Can't set SFC TX CIF: %d\n", err); + goto exit; + } + + regmap_write(sfc->regmap, TEGRA210_SFC_RX_FREQ, sfc->srate_in); + regmap_write(sfc->regmap, TEGRA210_SFC_TX_FREQ, sfc->srate_out); + + if (sfc->srate_in != sfc->srate_out) { + err = tegra210_sfc_write_coeff_ram(sfc); + if (err) { + dev_err(cmpnt->dev, "Conversion from %d to %d is not supported\n", + sfc->srate_in, sfc->srate_out); + goto exit; + } + } + + regmap_write(sfc->regmap, TEGRA210_SFC_ENABLE, 1); + } +exit: + pm_runtime_put(cmpnt->dev); + + return err; +} + +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 Sample Rate")) + ucontrol->value.integer.value[0] = + tegra210_sfc_rates[sfc->srate_in]; + else if (strstr(kcontrol->id.name, "Output Sample Rate")) + ucontrol->value.integer.value[0] = + tegra210_sfc_rates[sfc->srate_out]; + else if (strstr(kcontrol->id.name, "Input Audio Bit Format")) + ucontrol->value.integer.value[0] = sfc->format_in; + else if (strstr(kcontrol->id.name, "Output Audio Bit Format")) + ucontrol->value.integer.value[0] = sfc->format_out; + else if (strstr(kcontrol->id.name, "Input Audio Channels")) + ucontrol->value.integer.value[0] = + sfc->audio_ch_override[SFC_RX_PATH]; + else if (strstr(kcontrol->id.name, "Output Audio Channels")) + ucontrol->value.integer.value[0] = + sfc->audio_ch_override[SFC_TX_PATH]; + else if (strstr(kcontrol->id.name, "Client Channels")) + ucontrol->value.integer.value[0] = sfc->client_ch_override; + else 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 Sample Rate")) { + int srate = tegra210_sfc_rate_to_index(value); + + if (srate < 0) + return -EINVAL; + + sfc->srate_in = srate; + } else if (strstr(kcontrol->id.name, "Output Sample Rate")) { + int srate = tegra210_sfc_rate_to_index(value); + + if (srate < 0) + return -EINVAL; + + sfc->srate_out = srate; + } else if (strstr(kcontrol->id.name, "Init")) { + return tegra210_sfc_init(cmpnt, value); + } else if (strstr(kcontrol->id.name, "Input Audio Bit Format")) { + sfc->format_in = value; + } else if (strstr(kcontrol->id.name, "Output Audio Bit Format")) { + sfc->format_out = value; + } else if (strstr(kcontrol->id.name, "Input Audio Channels")) { + sfc->audio_ch_override[SFC_RX_PATH] = value; + } else if (strstr(kcontrol->id.name, "Output Audio Channels")) { + sfc->audio_ch_override[SFC_TX_PATH] = value; + } else if (strstr(kcontrol->id.name, "Client Channels")) { + sfc->client_ch_override = value; + } else 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; + } + + return 0; +} + +static struct snd_soc_dai_ops tegra210_sfc_in_dai_ops = { + .hw_params = tegra210_sfc_in_hw_params, +}; + +static 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 = "CIF", + .playback = { + .stream_name = "SFC Receive", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tegra210_sfc_in_dai_ops, + }, + { + .name = "DAP", + .capture = { + .stream_name = "SFC Transmit", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_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("SFC RX", NULL, 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("SFC TX", NULL, 0, TEGRA210_SFC_ENABLE, + TEGRA210_SFC_EN_SHIFT, 0), +}; + +static const struct snd_soc_dapm_route tegra210_sfc_routes[] = { + { "SFC RX", NULL, "SFC Receive" }, + { "SFC TX", NULL, "SFC RX" }, + { "SFC Transmit", NULL, "SFC TX" }, +}; + +static const char * const tegra210_sfc_format_text[] = { + "None", + "16", + "32", +}; + +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_format_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(tegra210_sfc_format_text), + tegra210_sfc_format_text); + +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_SINGLE_EXT("Input Sample Rate", 0, 0, 192000, 0, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_SINGLE_EXT("Output Sample Rate", 0, 0, 192000, 0, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_ENUM_EXT("Input Audio Bit Format", tegra210_sfc_format_enum, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_ENUM_EXT("Output Audio Bit Format", tegra210_sfc_format_enum, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_SINGLE_EXT("Input Audio Channels", 0, 0, 2, 0, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_SINGLE_EXT("Output Audio Channels", 0, 0, 2, 0, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_SINGLE_EXT("Client Channels", 0, 0, 2, 0, + tegra210_sfc_get_control, tegra210_sfc_put_control), + SOC_SINGLE_EXT("Init", 0, 0, 1, 0, + tegra210_sfc_get_control, tegra210_sfc_put_control), + 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 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), +#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */ + .non_legacy_dai_naming = 1, +#endif +}; + +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 "); +MODULE_DESCRIPTION("Tegra210 SFC ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra210_sfc.h b/sound/soc/tegra/tegra210_sfc.h new file mode 100644 index 00000000..1c0cd2c6 --- /dev/null +++ b/sound/soc/tegra/tegra210_sfc.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra210_sfc.h - Definitions for Tegra210 SFC driver + * + * Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA210_SFC_H__ +#define __TEGRA210_SFC_H__ + +/* + * SFC_RX registers are with respect to AXBAR. + * The data is coming from AXBAR to SFC for playback. + */ +#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 AXBAR. + * The data is going out of SFC for playback. + */ +#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 13 + +/* Fields in TEGRA210_SFC_COEF_RAM */ +#define TEGRA210_SFC_COEF_RAM_EN BIT(0) + +#define TEGRA210_SFC_SOFT_RESET_EN BIT(0) + +/* SRC coefficients */ +#define TEGRA210_SFC_COEF_RAM_DEPTH 64 + +enum tegra210_sfc_path { + SFC_RX_PATH, + SFC_TX_PATH, + SFC_PATHS, +}; + +struct tegra210_sfc { + int srate_in; + int srate_out; + int format_in; + int format_out; + struct regmap *regmap; + struct snd_pcm_hw_params in_hw_params; + struct snd_pcm_hw_params out_hw_params; + int audio_ch_override[SFC_PATHS]; + int client_ch_override; /* common for both TX and RX */ + int stereo_to_mono[SFC_PATHS]; + int mono_to_stereo[SFC_PATHS]; +}; + +#endif diff --git a/sound/soc/tegra/tegra_machine_driver.c b/sound/soc/tegra/tegra_machine_driver.c index 1f2f7674..c1c0ccb9 100644 --- a/sound/soc/tegra/tegra_machine_driver.c +++ b/sound/soc/tegra/tegra_machine_driver.c @@ -12,6 +12,7 @@ #include #include #include +#include "tegra210_ahub.h" #include "tegra_asoc_machine.h" #include "tegra_codecs.h"