diff --git a/sound/soc/tegra-alt/Kconfig b/sound/soc/tegra-alt/Kconfig new file mode 100644 index 00000000..c4b34c91 --- /dev/null +++ b/sound/soc/tegra-alt/Kconfig @@ -0,0 +1,43 @@ +config SND_SOC_TEGRA_ALT_186 + def_bool y + depends on SND_SOC_TEGRA_ALT + depends on ARCH_TEGRA_APE + depends on ARCH_TEGRA_18x_SOC + +config SND_SOC_TEGRA186_ASRC_ALT + tristate "Tegra186 ASRC driver" + depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_186 + help + Say Y or M if you want to add support for Tegra186 ASRC module. + +config SND_SOC_TEGRA186_ARAD_ALT + tristate "Tegra186 ARAD driver" + depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_186 + help + Say Y or M if you want to add support for Tegra186 ARAD module. + +config SND_SOC_TEGRA_T186REF_ALT + tristate "SoC Audio support for T186Ref" + depends on SND_SOC_TEGRA_ALT + depends on ARCH_TEGRA_18x_SOC + select SND_SOC_TEGRA210_XBAR_ALT + select SND_SOC_TEGRA210_PCM_ALT + select SND_SOC_TEGRA210_ADMA_ALT + select SND_SOC_TEGRA210_ADMAIF_ALT + select SND_SOC_TEGRA210_I2S_ALT + select SND_SOC_TEGRA210_DMIC_ALT + select SND_SOC_TEGRA210_AMX_ALT + select SND_SOC_TEGRA210_ADX_ALT + select SND_SOC_TEGRA210_MIXER_ALT + select SND_SOC_TEGRA210_SFC_ALT + select SND_SOC_TEGRA210_AFC_ALT + select SND_SOC_TEGRA210_MVC_ALT + select SND_SOC_TEGRA210_OPE_ALT + select SND_SOC_TEGRA210_SPDIF_ALT + select SND_SOC_TEGRA186_ASRC_ALT + select SND_SOC_TEGRA186_ARAD_ALT + select SND_SOC_TEGRA_ASOC_HWDEP_ALT + select SND_SOC_SPDIF + select SND_SOC_AD193X + help + Say Y or M here. diff --git a/sound/soc/tegra-alt/Makefile b/sound/soc/tegra-alt/Makefile new file mode 100644 index 00000000..f111f0da --- /dev/null +++ b/sound/soc/tegra-alt/Makefile @@ -0,0 +1,16 @@ +KBUILD_CFLAGS += -I$(srctree)/../kernel-3.18/sound/soc/tegra-alt + +snd-soc-tegra186-alt-asrc-objs := tegra186_asrc_alt.o +snd-soc-tegra186-alt-arad-objs := tegra186_arad_alt.o +snd-soc-tegra186-alt-afc-objs := tegra186_afc_alt.o +snd-soc-tegra186-alt-xbar-objs := tegra186_xbar_alt.o + +obj-$(CONFIG_SND_SOC_TEGRA186_ASRC_ALT) += snd-soc-tegra186-alt-asrc.o +obj-$(CONFIG_SND_SOC_TEGRA186_ARAD_ALT) += snd-soc-tegra186-alt-arad.o +obj-$(CONFIG_SND_SOC_TEGRA210_AFC_ALT) += snd-soc-tegra186-alt-afc.o +obj-$(CONFIG_SND_SOC_TEGRA210_XBAR_ALT) += snd-soc-tegra186-alt-xbar.o + +snd-soc-tegra-alt-t186ref-objs := tegra_t186ref_alt.o tegra_asoc_machine_alt_t18x.o \ + ../../../../kernel-3.18/sound/soc/tegra-alt/tegra_asoc_machine_alt.o + +obj-$(CONFIG_SND_SOC_TEGRA_T186REF_ALT) += snd-soc-tegra-alt-t186ref.o diff --git a/sound/soc/tegra-alt/tegra186_arad_alt.c b/sound/soc/tegra-alt/tegra186_arad_alt.c new file mode 100644 index 00000000..99f08dc2 --- /dev/null +++ b/sound/soc/tegra-alt/tegra186_arad_alt.c @@ -0,0 +1,778 @@ +/* + * tegra186_arad_alt.c - Tegra186 ARAD driver + * + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra210_xbar_alt.h" +#include "tegra186_arad_alt.h" + +#define DRV_NAME "tegra186-arad" + +#define ARAD_LANE_NUMERATOR_MUX(id) \ + (TEGRA186_ARAD_LANE1_NUMERATOR_MUX_SEL + id*TEGRA186_ARAD_LANE_STRIDE) +#define ARAD_LANE_DENOMINATOR_MUX(id) \ + (TEGRA186_ARAD_LANE1_DENOMINATOR_MUX_SEL + id*TEGRA186_ARAD_LANE_STRIDE) + +#define ARAD_LANE_REG(reg, id) (reg + (id * TEGRA186_ARAD_LANE_STRIDE)) + +#define ASRC_STREAM_REG_DEFAULTS(id) \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_NUMERATOR_MUX_SEL, id), 0x0}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_RATIO_INTEGER_PART, id), 0x0}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_RATIO_FRACTIONAL_PART, id), 0x0}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_NUMERATOR_PRESCALAR, id), 0x0}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_DENOMINATOR_MUX_SEL, id), 0x0}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_DENOMINATOR_PRESCALAR, id), 0x0}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_SERVO_LOOP_CONFIG, id), 0xd5e7}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_LOCK_UNLOCK_DETECTOR_CONFIG, id), 0x840500}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_ERROR_LOCK_THRESHOLD, id), 0x400000}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_ERROR_UNLOCK_THRESHOLD, id), 0xa00000}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_RATIO_CALCULATOR_CONFIG, id), 0xf000006}, \ + { ARAD_LANE_REG(TEGRA186_ARAD_LANE1_CYA, id), 0x0} + +static const struct reg_default tegra186_arad_reg_defaults[] = { + { TEGRA186_ARAD_LANE_ENABLE, 0x0}, + { TEGRA186_ARAD_LANE_SOFT_RESET, 0x0}, + { TEGRA186_ARAD_LANE_INT_MASK, 0x0}, + { TEGRA186_ARAD_LANE_INT_SET, 0x0}, + { TEGRA186_ARAD_LANE_INT_CLEAR, 0x0}, + { TEGRA186_ARAD_LANE_INT_CLEAR, 0x0}, + { TEGRA186_ARAD_CG, 0x0}, + { TEGRA186_ARAD_CYA_GLOBAL, 0x0}, + + 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_ARAD_TX_CIF_CTRL, 0x115500}, +}; + +static int tegra186_arad_runtime_suspend(struct device *dev) +{ + struct tegra186_arad *arad = dev_get_drvdata(dev); + + regcache_cache_only(arad->regmap, true); + + pm_runtime_put_sync(dev->parent); + + return 0; +} + +static int tegra186_arad_runtime_resume(struct device *dev) +{ + struct tegra186_arad *arad = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_get_sync(dev->parent); + if (ret < 0) { + dev_err(dev, "parent get_sync failed: %d\n", ret); + return ret; + } + + regcache_cache_only(arad->regmap, false); + regcache_sync(arad->regmap); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra186_arad_suspend(struct device *dev) +{ + struct tegra186_arad *arad = dev_get_drvdata(dev); + + regcache_mark_dirty(arad->regmap); + + return 0; +} +#endif + +static int tegra186_arad_codec_probe(struct snd_soc_codec *codec) +{ + struct tegra186_arad *arad = snd_soc_codec_get_drvdata(codec); + + codec->control_data = arad->regmap; + + return 0; +} + +static int tegra186_arad_get_lane_lock_status( + struct tegra186_arad *arad, unsigned int lane_id) +{ + unsigned int val; + + regmap_read(arad->regmap, + TEGRA186_ARAD_LANE_STATUS, &val); + val = (val >> (16 + lane_id)) & 0x1; + + return val; +} + +static struct snd_soc_dai_ops tegra186_arad_out_dai_ops = { +}; + +static struct snd_soc_dai_driver tegra186_arad_dais[] = { + { + .name = "ARAD OUT", + .capture = { + .stream_name = "ARAD Transmit", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &tegra186_arad_out_dai_ops, + }, +}; + +static const int tegra186_arad_mux_value[] = { + -1, /* None */ + 0, 1, 2, 3, 4, 5, /* I2S1~6 */ + 12, 13, 14, 15, /* DMIC1~4 */ + 24, 25, /* DSPK1~2 */ + 26, 27, /* IQC1~2 */ + 28, 29, 30, 31, /* SPDIF_RX1,2 & SPDIF_TX1,2 */ +}; + +static const char * const tegra186_arad_mux_text[] = { + "None", + "I2S1", + "I2S2", + "I2S3", + "I2S4", + "I2S5", + "I2S6", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", + "DSPK1", + "DSPK2", + "IQC1", + "IQC2", + "SPDIF1_RX1", + "SPDIF1_RX2", + "SPDIF1_TX1", + "SPDIF1_TX2", +}; + +static int tegra186_arad_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *arad_private = + (struct soc_enum *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct tegra186_arad *arad = snd_soc_codec_get_drvdata(codec); + unsigned int val, loop = 0; + + regmap_read(arad->regmap, arad_private->reg, &val); + + if (val) { + for (loop = 1; loop < 19; loop++) + if (val & (1<values[loop])) { + val = loop; + break; + } + } + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tegra186_arad_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *arad_private = + (struct soc_enum *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct tegra186_arad *arad = snd_soc_codec_get_drvdata(codec); + unsigned int val = ucontrol->value.integer.value[0]; + + if (!val) + regmap_write(arad->regmap, arad_private->reg, 0); + else + regmap_write(arad->regmap, arad_private->reg, + 1 << arad_private->values[val]); + + return 0; +} + +static int tegra186_arad_get_ratio_int(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *arad_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_arad *arad = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + regmap_read(arad->regmap, arad_private->reg, &val); + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tegra186_arad_get_ratio_frac(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *arad_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_arad *arad = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + regmap_read(arad->regmap, arad_private->reg, &val); + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tegra186_arad_get_enable_lane(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *arad_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_arad *arad = snd_soc_codec_get_drvdata(codec); + unsigned int enable; + + regmap_read(arad->regmap, arad_private->reg, &enable); + enable = (enable >> arad_private->shift) & arad_private->max; + ucontrol->value.integer.value[0] = enable; + + return 0; +} + +static int tegra186_arad_put_enable_lane(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *arad_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_arad *arad = snd_soc_codec_get_drvdata(codec); + unsigned int enable = 0, lane_id = arad_private->shift; + int dcnt = 10; + + enable = ucontrol->value.integer.value[0]; + + regmap_update_bits(arad->regmap, + arad_private->reg, 1<shift, + enable<shift); + + if (enable) + while (!tegra186_arad_get_lane_lock_status(arad, lane_id) && + dcnt--) + udelay(100); + else { + regmap_update_bits(arad->regmap, + TEGRA186_ARAD_LANE_SOFT_RESET, 1<shift, + 1<shift); + while (tegra186_arad_get_lane_lock_status(arad, lane_id) && + dcnt--) + udelay(100); + } + + if (dcnt < 0) { + if (enable) + pr_err("ARAD Lane %d can't be locked\n", lane_id+1); + else + pr_err("ARAD Lane %d can't be unlocked\n", lane_id+1); + return -ETIMEDOUT; + } else + return 0; +} + +#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) \ + struct soc_enum name = SOC_VALUE_ENUM_WIDE(xreg, shift, \ + ARRAY_SIZE(xtexts), xtexts, xvalues) + +#define ARAD_MUX_ENUM_CTRL_DECL(ename, reg) \ + SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, reg, 0, \ + tegra186_arad_mux_text, tegra186_arad_mux_value); \ + static const struct snd_kcontrol_new ename##_ctrl = \ + SOC_DAPM_ENUM_EXT("Mux", ename##_enum, \ + tegra186_arad_mux_get, \ + tegra186_arad_mux_put) + +static ARAD_MUX_ENUM_CTRL_DECL(numerator1, + TEGRA186_ARAD_LANE1_NUMERATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(numerator2, + TEGRA186_ARAD_LANE2_NUMERATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(numerator3, + TEGRA186_ARAD_LANE3_NUMERATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(numerator4, + TEGRA186_ARAD_LANE4_NUMERATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(numerator5, + TEGRA186_ARAD_LANE5_NUMERATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(numerator6, + TEGRA186_ARAD_LANE6_NUMERATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(denominator1, + TEGRA186_ARAD_LANE1_DENOMINATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(denominator2, + TEGRA186_ARAD_LANE2_DENOMINATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(denominator3, + TEGRA186_ARAD_LANE3_DENOMINATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(denominator4, + TEGRA186_ARAD_LANE4_DENOMINATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(denominator5, + TEGRA186_ARAD_LANE5_DENOMINATOR_MUX_SEL); +static ARAD_MUX_ENUM_CTRL_DECL(denominator6, + TEGRA186_ARAD_LANE6_DENOMINATOR_MUX_SEL); + +#define ARAD_RATIO_GEN_WIDGETS(sname, ename) \ + SND_SOC_DAPM_AIF_OUT(sname " TX", NULL, 0, SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_MUX(sname " MUX", SND_SOC_NOPM, 0, 0, &ename##_ctrl) + +static const struct snd_soc_dapm_widget tegra186_arad_widgets[] = { + SND_SOC_DAPM_SIGGEN("I2S1 SIG"), + SND_SOC_DAPM_SIGGEN("I2S2 SIG"), + SND_SOC_DAPM_SIGGEN("I2S3 SIG"), + SND_SOC_DAPM_SIGGEN("I2S4 SIG"), + SND_SOC_DAPM_SIGGEN("I2S5 SIG"), + SND_SOC_DAPM_SIGGEN("I2S6 SIG"), + SND_SOC_DAPM_SIGGEN("DMIC1 SIG"), + SND_SOC_DAPM_SIGGEN("DMIC2 SIG"), + SND_SOC_DAPM_SIGGEN("DMIC3 SIG"), + SND_SOC_DAPM_SIGGEN("DMIC4 SIG"), + SND_SOC_DAPM_SIGGEN("DSPK1 SIG"), + SND_SOC_DAPM_SIGGEN("DSPK2 SIG"), + SND_SOC_DAPM_SIGGEN("IQC1 SIG"), + SND_SOC_DAPM_SIGGEN("IQC2 SIG"), + SND_SOC_DAPM_SIGGEN("SPDIF1_RX1 SIG"), + SND_SOC_DAPM_SIGGEN("SPDIF1_RX2 SIG"), + SND_SOC_DAPM_SIGGEN("SPDIF1_TX1 SIG"), + SND_SOC_DAPM_SIGGEN("SPDIF1_TX2 SIG"), + + ARAD_RATIO_GEN_WIDGETS("Numerator1", numerator1), + ARAD_RATIO_GEN_WIDGETS("Numerator2", numerator2), + ARAD_RATIO_GEN_WIDGETS("Numerator3", numerator3), + ARAD_RATIO_GEN_WIDGETS("Numerator4", numerator4), + ARAD_RATIO_GEN_WIDGETS("Numerator5", numerator5), + ARAD_RATIO_GEN_WIDGETS("Numerator6", numerator6), + ARAD_RATIO_GEN_WIDGETS("Denominator1", denominator1), + ARAD_RATIO_GEN_WIDGETS("Denominator2", denominator2), + ARAD_RATIO_GEN_WIDGETS("Denominator3", denominator3), + ARAD_RATIO_GEN_WIDGETS("Denominator4", denominator4), + ARAD_RATIO_GEN_WIDGETS("Denominator5", denominator5), + ARAD_RATIO_GEN_WIDGETS("Denominator6", denominator6), + + SND_SOC_DAPM_AIF_OUT("Packetizer", NULL, 0, 0, 0, 0), +}; + +#define RATIO_GEN_ROUTE(sname) \ + { sname " MUX", NULL, "I2S1 SIG" }, \ + { sname " MUX", NULL, "I2S2 SIG" }, \ + { sname " MUX", NULL, "I2S3 SIG" }, \ + { sname " MUX", NULL, "I2S4 SIG" }, \ + { sname " MUX", NULL, "I2S5 SIG" }, \ + { sname " MUX", NULL, "I2S6 SIG" }, \ + { sname " MUX", NULL, "DMIC1 SIG" }, \ + { sname " MUX", NULL, "DMIC2 SIG" }, \ + { sname " MUX", NULL, "DMIC3 SIG" }, \ + { sname " MUX", NULL, "DMIC4 SIG" }, \ + { sname " MUX", NULL, "IQC1 SIG" }, \ + { sname " MUX", NULL, "IQC2 SIG" }, \ + { sname " MUX", NULL, "SPDIF1_RX1 SIG" }, \ + { sname " MUX", NULL, "SPDIF1_RX2 SIG" }, \ + { sname " MUX", NULL, "SPDIF1_TX1 SIG" }, \ + { sname " MUX", NULL, "SPDIF1_TX2 SIG" } + +#define RATIO_GEN(sname) \ + RATIO_GEN_ROUTE(sname), \ + { sname " TX", NULL, sname " MUX" } + +#define PACKETIZER_ROUTE(sname) \ + { sname, "Numerator1 TX", "Numerator1 TX" }, \ + { sname, "Numerator2 TX", "Numerator2 TX" }, \ + { sname, "Numerator3 TX", "Numerator3 TX" }, \ + { sname, "Numerator4 TX", "Numerator4 TX" }, \ + { sname, "Numerator5 TX", "Numerator5 TX" }, \ + { sname, "Numerator6 TX", "Numerator6 TX" }, \ + { sname, "Denominator1 TX", "Denominator1 TX" }, \ + { sname, "Denominator2 TX", "Denominator2 TX" }, \ + { sname, "Denominator3 TX", "Denominator3 TX" }, \ + { sname, "Denominator4 TX", "Denominator4 TX" }, \ + { sname, "Denominator5 TX", "Denominator5 TX" }, \ + { sname, "Denominator6 TX", "Denominator6 TX" } + +static const struct snd_soc_dapm_route tegra186_arad_routes[] = { + RATIO_GEN("Numerator1"), + RATIO_GEN("Numerator2"), + RATIO_GEN("Numerator3"), + RATIO_GEN("Numerator4"), + RATIO_GEN("Numerator5"), + RATIO_GEN("Numerator6"), + RATIO_GEN("Denominator1"), + RATIO_GEN("Denominator2"), + RATIO_GEN("Denominator3"), + RATIO_GEN("Denominator4"), + RATIO_GEN("Denominator5"), + RATIO_GEN("Denominator6"), + + PACKETIZER_ROUTE("Packetizer"), + + { "ARAD Transmit", NULL, "Packetizer" }, +}; + +static const struct snd_kcontrol_new tegra186_arad_controls[] = { + SOC_SINGLE_EXT("Lane1 enable", TEGRA186_ARAD_LANE_ENABLE, 0, 1, 0, + tegra186_arad_get_enable_lane, tegra186_arad_put_enable_lane), + SOC_SINGLE_EXT("Lane2 enable", TEGRA186_ARAD_LANE_ENABLE, 1, 1, 0, + tegra186_arad_get_enable_lane, tegra186_arad_put_enable_lane), + SOC_SINGLE_EXT("Lane3 enable", TEGRA186_ARAD_LANE_ENABLE, 2, 1, 0, + tegra186_arad_get_enable_lane, tegra186_arad_put_enable_lane), + SOC_SINGLE_EXT("Lane4 enable", TEGRA186_ARAD_LANE_ENABLE, 3, 1, 0, + tegra186_arad_get_enable_lane, tegra186_arad_put_enable_lane), + SOC_SINGLE_EXT("Lane5 enable", TEGRA186_ARAD_LANE_ENABLE, 4, 1, 0, + tegra186_arad_get_enable_lane, tegra186_arad_put_enable_lane), + SOC_SINGLE_EXT("Lane6 enable", TEGRA186_ARAD_LANE_ENABLE, 5, 1, 0, + tegra186_arad_get_enable_lane, tegra186_arad_put_enable_lane), + + SOC_SINGLE_EXT("Lane1 Ratio Int", + TEGRA186_ARAD_LANE1_RATIO_INTEGER_PART, + 0, TEGRA186_ARAD_LANE_RATIO_INTEGER_PART_MASK, 0, + tegra186_arad_get_ratio_int, NULL), + SOC_SINGLE_EXT("Lane1 Ratio Frac", + TEGRA186_ARAD_LANE1_RATIO_FRACTIONAL_PART, + 0, TEGRA186_ARAD_LANE_RATIO_FRAC_PART_MASK, 0, + tegra186_arad_get_ratio_frac, NULL), + SOC_SINGLE_EXT("Lane2 Ratio Int", + TEGRA186_ARAD_LANE2_RATIO_INTEGER_PART, + 0, TEGRA186_ARAD_LANE_RATIO_INTEGER_PART_MASK, 0, + tegra186_arad_get_ratio_int, NULL), + SOC_SINGLE_EXT("Lane2 Ratio Frac", + TEGRA186_ARAD_LANE2_RATIO_FRACTIONAL_PART, + 0, TEGRA186_ARAD_LANE_RATIO_FRAC_PART_MASK, 0, + tegra186_arad_get_ratio_frac, NULL), + SOC_SINGLE_EXT("Lane3 Ratio Int", + TEGRA186_ARAD_LANE3_RATIO_INTEGER_PART, + 0, TEGRA186_ARAD_LANE_RATIO_INTEGER_PART_MASK, 0, + tegra186_arad_get_ratio_int, NULL), + SOC_SINGLE_EXT("Lane3 Ratio Frac", + TEGRA186_ARAD_LANE3_RATIO_FRACTIONAL_PART, + 0, TEGRA186_ARAD_LANE_RATIO_FRAC_PART_MASK, 0, + tegra186_arad_get_ratio_frac, NULL), + SOC_SINGLE_EXT("Lane4 Ratio Int", + TEGRA186_ARAD_LANE4_RATIO_INTEGER_PART, + 0, TEGRA186_ARAD_LANE_RATIO_INTEGER_PART_MASK, 0, + tegra186_arad_get_ratio_int, NULL), + SOC_SINGLE_EXT("Lane4 Ratio Frac", + TEGRA186_ARAD_LANE4_RATIO_FRACTIONAL_PART, + 0, TEGRA186_ARAD_LANE_RATIO_FRAC_PART_MASK, 0, + tegra186_arad_get_ratio_frac, NULL), + SOC_SINGLE_EXT("Lane5 Ratio Int", + TEGRA186_ARAD_LANE5_RATIO_INTEGER_PART, + 0, TEGRA186_ARAD_LANE_RATIO_INTEGER_PART_MASK, 0, + tegra186_arad_get_ratio_int, NULL), + SOC_SINGLE_EXT("Lane5 Ratio Frac", + TEGRA186_ARAD_LANE5_RATIO_FRACTIONAL_PART, + 0, TEGRA186_ARAD_LANE_RATIO_FRAC_PART_MASK, 0, + tegra186_arad_get_ratio_frac, NULL), + SOC_SINGLE_EXT("Lane6 Ratio Int", + TEGRA186_ARAD_LANE6_RATIO_INTEGER_PART, + 0, TEGRA186_ARAD_LANE_RATIO_INTEGER_PART_MASK, 0, + tegra186_arad_get_ratio_int, NULL), + SOC_SINGLE_EXT("Lane6 Ratio Frac", + TEGRA186_ARAD_LANE6_RATIO_FRACTIONAL_PART, + 0, TEGRA186_ARAD_LANE_RATIO_FRAC_PART_MASK, 0, + tegra186_arad_get_ratio_frac, NULL), +}; + +static struct snd_soc_codec_driver tegra186_arad_codec = { + .probe = tegra186_arad_codec_probe, + .dapm_widgets = tegra186_arad_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra186_arad_widgets), + .dapm_routes = tegra186_arad_routes, + .num_dapm_routes = ARRAY_SIZE(tegra186_arad_routes), + .controls = tegra186_arad_controls, + .num_controls = ARRAY_SIZE(tegra186_arad_controls), + .idle_bias_off = 1, +}; + +static bool tegra186_arad_wr_reg(struct device *dev, unsigned int reg) +{ + if ((reg >= TEGRA186_ARAD_LANE_START) && + (reg <= TEGRA186_ARAD_LANE_LIMIT)) { + reg -= TEGRA186_ARAD_LANE_START; + reg %= TEGRA186_ARAD_LANE_STRIDE; + reg += TEGRA186_ARAD_LANE_START; + } + + switch (reg) { + case TEGRA186_ARAD_LANE_ENABLE: + case TEGRA186_ARAD_LANE_SOFT_RESET: + case TEGRA186_ARAD_LANE_INT_MASK: + case TEGRA186_ARAD_LANE_INT_SET: + case TEGRA186_ARAD_LANE_INT_CLEAR: + case TEGRA186_ARAD_GLOBAL_SOFT_RESET: + case TEGRA186_ARAD_SEND_RATIO: + case TEGRA186_ARAD_CG: + case TEGRA186_ARAD_CYA_GLOBAL: + + case TEGRA186_ARAD_LANE1_NUMERATOR_MUX_SEL: + case TEGRA186_ARAD_LANE1_NUMERATOR_PRESCALAR: + case TEGRA186_ARAD_LANE1_DENOMINATOR_MUX_SEL: + case TEGRA186_ARAD_LANE1_DENOMINATOR_PRESCALAR: + case TEGRA186_ARAD_LANE1_SERVO_LOOP_CONFIG: + case TEGRA186_ARAD_LANE1_LOCK_UNLOCK_DETECTOR_CONFIG: + case TEGRA186_ARAD_LANE1_ERROR_LOCK_THRESHOLD: + case TEGRA186_ARAD_LANE1_ERROR_UNLOCK_THRESHOLD: + case TEGRA186_ARAD_LANE1_RATIO_CALCULATOR_CONFIG: + case TEGRA186_ARAD_LANE1_CYA: + return true; + default: + return false; + }; +} + +static bool tegra186_arad_rd_reg(struct device *dev, unsigned int reg) +{ + if ((reg >= TEGRA186_ARAD_LANE_START) && + (reg <= TEGRA186_ARAD_LANE_LIMIT)) { + reg -= TEGRA186_ARAD_LANE_START; + reg %= TEGRA186_ARAD_LANE_STRIDE; + reg += TEGRA186_ARAD_LANE_START; + } + + switch (reg) { + case TEGRA186_ARAD_LANE_ENABLE: + case TEGRA186_ARAD_LANE_STATUS: + case TEGRA186_ARAD_LANE_SOFT_RESET: + case TEGRA186_ARAD_LANE_INT_STATUS: + case TEGRA186_ARAD_LANE_INT_MASK: + case TEGRA186_ARAD_LANE_INT_SET: + case TEGRA186_ARAD_LANE_INT_CLEAR: + case TEGRA186_ARAD_GLOBAL_SOFT_RESET: + case TEGRA186_ARAD_CG: + case TEGRA186_ARAD_STATUS: + case TEGRA186_ARAD_CYA_GLOBAL: + + case TEGRA186_ARAD_LANE1_NUMERATOR_MUX_SEL: + case TEGRA186_ARAD_LANE1_NUMERATOR_PRESCALAR: + case TEGRA186_ARAD_LANE1_DENOMINATOR_MUX_SEL: + case TEGRA186_ARAD_LANE1_DENOMINATOR_PRESCALAR: + case TEGRA186_ARAD_LANE1_RATIO_INTEGER_PART: + case TEGRA186_ARAD_LANE1_RATIO_FRACTIONAL_PART: + case TEGRA186_ARAD_LANE1_PERIOD_COUNT: + case TEGRA186_ARAD_LANE1_SERVO_LOOP_CONFIG: + case TEGRA186_ARAD_LANE1_LOCK_UNLOCK_DETECTOR_CONFIG: + case TEGRA186_ARAD_LANE1_ERROR_LOCK_THRESHOLD: + case TEGRA186_ARAD_LANE1_ERROR_UNLOCK_THRESHOLD: + case TEGRA186_ARAD_LANE1_RATIO_CALCULATOR_CONFIG: + case TEGRA186_ARAD_LANE1_CYA: + return true; + default: + return false; + }; +} + +static bool tegra186_arad_volatile_reg(struct device *dev, unsigned int reg) +{ + if ((reg >= TEGRA186_ARAD_LANE_START) && + (reg <= TEGRA186_ARAD_LANE_LIMIT)) { + reg -= TEGRA186_ARAD_LANE_START; + reg %= TEGRA186_ARAD_LANE_STRIDE; + reg += TEGRA186_ARAD_LANE_START; + } + + switch (reg) { + case TEGRA186_ARAD_LANE_STATUS: + case TEGRA186_ARAD_LANE_INT_STATUS: + case TEGRA186_ARAD_STATUS: + + case TEGRA186_ARAD_LANE1_RATIO_INTEGER_PART: + case TEGRA186_ARAD_LANE1_RATIO_FRACTIONAL_PART: + case TEGRA186_ARAD_LANE1_PERIOD_COUNT: + return true; + default: + return false; + }; +} + +static const struct regmap_config tegra186_arad_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA186_ARAD_TX_CIF_CTRL, + .writeable_reg = tegra186_arad_wr_reg, + .readable_reg = tegra186_arad_rd_reg, + .volatile_reg = tegra186_arad_volatile_reg, + .reg_defaults = tegra186_arad_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra186_arad_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct tegra186_arad_soc_data soc_data_tegra186 = { + .set_audio_cif = tegra210_xbar_set_cif, +}; + +static const struct of_device_id tegra186_arad_of_match[] = { + { .compatible = "nvidia,tegra186-arad", .data = &soc_data_tegra186 }, + {}, +}; + +static int tegra186_arad_platform_probe(struct platform_device *pdev) +{ + struct tegra186_arad *arad; + struct resource *mem, *memregion; + void __iomem *regs; + int ret = 0; + const struct of_device_id *match; + struct tegra186_arad_soc_data *soc_data; + + match = of_match_device(tegra186_arad_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + ret = -ENODEV; + goto err; + } + soc_data = (struct tegra186_arad_soc_data *)match->data; + + arad = devm_kzalloc(&pdev->dev, + sizeof(struct tegra186_arad), GFP_KERNEL); + if (!arad) { + dev_err(&pdev->dev, "Can't allocate tegra210_arad\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, arad); + + arad->soc_data = soc_data; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err; + } + + arad->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra186_arad_regmap_config); + if (IS_ERR(arad->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(arad->regmap); + goto err; + } + regcache_cache_only(arad->regmap, true); + + if (of_property_read_u32(pdev->dev.of_node, + "nvidia,ahub-arad-id", + &pdev->dev.id) < 0) { + dev_err(&pdev->dev, + "Missing property nvidia,ahub-arad-id\n"); + ret = -ENODEV; + goto err; + } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra186_arad_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_codec(&pdev->dev, &tegra186_arad_codec, + tegra186_arad_dais, + ARRAY_SIZE(tegra186_arad_dais)); + if (ret != 0) { + dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret); + goto err_suspend; + } + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra186_arad_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err: + return ret; +} + +static int tegra186_arad_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra186_arad_runtime_suspend(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra186_arad_pm_ops = { + SET_RUNTIME_PM_OPS(tegra186_arad_runtime_suspend, + tegra186_arad_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra186_arad_suspend, NULL) +}; + +static struct platform_driver tegra186_arad_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra186_arad_of_match, + .pm = &tegra186_arad_pm_ops, + }, + .probe = tegra186_arad_platform_probe, + .remove = tegra186_arad_platform_remove, +}; +module_platform_driver(tegra186_arad_driver) + +MODULE_AUTHOR("Junghyun Kim "); +MODULE_DESCRIPTION("Tegra186 ARAD ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra186_arad_of_match); diff --git a/sound/soc/tegra-alt/tegra186_asrc_alt.c b/sound/soc/tegra-alt/tegra186_asrc_alt.c new file mode 100644 index 00000000..d833bac2 --- /dev/null +++ b/sound/soc/tegra-alt/tegra186_asrc_alt.c @@ -0,0 +1,937 @@ +/* + * tegra186_asrc_alt.c - Tegra186 ASRC driver + * + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra210_xbar_alt.h" +#include "tegra186_asrc_alt.h" + +#define DRV_NAME "tegra186-asrc" + +#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), 0x7700}, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_TX_CIF_CTRL, id), 0x7700}, \ + { 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 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, 0}, + { 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_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) +{ + int dcnt = 10; + + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS, lane_id), 0); + + while (tegra186_asrc_get_ratio_lock_status(asrc, lane_id) && dcnt--) + udelay(100); + + regmap_write(asrc->regmap, + ASRC_STREAM_REG( + TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS, lane_id), 1); +} + +static int tegra186_asrc_runtime_suspend(struct device *dev) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + + regcache_cache_only(asrc->regmap, true); + + pm_runtime_put_sync(dev->parent); + + return 0; +} + +static int tegra186_asrc_runtime_resume(struct device *dev) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + int ret, lane_id; + + ret = pm_runtime_get_sync(dev->parent); + if (ret < 0) { + dev_err(dev, "parent get_sync failed: %d\n", ret); + return ret; + } + + regcache_cache_only(asrc->regmap, false); + regcache_sync(asrc->regmap); + + 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); + } + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra186_asrc_suspend(struct device *dev) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + + regcache_mark_dirty(asrc->regmap); + return 0; +} +#endif + +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 tegra210_xbar_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra210_xbar_cif_conf)); + + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA210_AUDIOCIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + audio_bits = TEGRA210_AUDIOCIF_BITS_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA210_AUDIOCIF_BITS_32; + break; + default: + return -EINVAL; + } + + cif_conf.audio_channels = channels; + cif_conf.client_channels = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = audio_bits; + + asrc->soc_data->set_audio_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; + + /* set threshold */ + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RX_THRESHOLD, dai->id), + 0x00201002); + + 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 */ + 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), + 0xaaaa); + + /* 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), + 0x00201002); + + 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + 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_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + 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_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + unsigned int id = asrc_private->reg / 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_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + unsigned int id = asrc_private->reg / 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_codec_probe(struct snd_soc_codec *codec) +{ + struct tegra186_asrc *asrc = snd_soc_codec_get_drvdata(codec); + + codec->control_data = asrc->regmap; + + return 0; +} + +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), +}; + +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_IN("RX7", NULL, 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA186_ASRC_STREAM1_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA186_ASRC_STREAM2_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA186_ASRC_STREAM3_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA186_ASRC_STREAM4_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0, TEGRA186_ASRC_STREAM5_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("TX6", NULL, 0, TEGRA186_ASRC_STREAM6_ENABLE, + TEGRA186_ASRC_STREAM_EN_SHIFT, 0), +}; + +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); + +static const struct snd_kcontrol_new tegra186_asrc_controls[] = { + SOC_SINGLE_EXT("Ratio1 Int", 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("Ratio1 Frac", TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, 0, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio2 Int", 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("Ratio2 Frac", TEGRA186_ASRC_STREAM2_RATIO_FRAC_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, 0, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio3 Int", 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("Ratio3 Frac", TEGRA186_ASRC_STREAM3_RATIO_FRAC_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, 0, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio4 Int", 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("Ratio4 Frac", TEGRA186_ASRC_STREAM4_RATIO_FRAC_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, 0, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio5 Int", 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("Ratio5 Frac", TEGRA186_ASRC_STREAM5_RATIO_FRAC_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, 0, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + SOC_SINGLE_EXT("Ratio6 Int", 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("Ratio6 Frac", TEGRA186_ASRC_STREAM6_RATIO_FRAC_PART, + 0, TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, 0, + tegra186_asrc_get_ratio_frac, tegra186_asrc_put_ratio_frac), + + SOC_ENUM_EXT("Ratio1 SRC", src_select1, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio2 SRC", src_select2, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio3 SRC", src_select3, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio4 SRC", src_select4, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio5 SRC", src_select5, + tegra186_asrc_get_ratio_source, tegra186_asrc_put_ratio_source), + SOC_ENUM_EXT("Ratio6 SRC", 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), +}; + +static struct snd_soc_codec_driver tegra186_asrc_codec = { + .probe = tegra186_asrc_codec_probe, + .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), + .idle_bias_off = 1, +}; + +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_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_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_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_RATIO_UPD_RX_STATUS: + + 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; + }; +} + +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, +}; + +static const struct tegra186_asrc_soc_data soc_data_tegra186 = { + .set_audio_cif = tegra210_xbar_set_cif, +}; + +static const struct of_device_id tegra186_asrc_of_match[] = { + { .compatible = "nvidia,tegra186-asrc", .data = &soc_data_tegra186 }, + {}, +}; + +static int tegra186_asrc_platform_probe(struct platform_device *pdev) +{ + struct tegra186_asrc *asrc; + struct resource *mem, *memregion; + void __iomem *regs; + int ret = 0; + const struct of_device_id *match; + struct tegra186_asrc_soc_data *soc_data; + unsigned int i = 0; + + match = of_match_device(tegra186_asrc_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + ret = -ENODEV; + goto err; + } + soc_data = (struct tegra186_asrc_soc_data *)match->data; + + asrc = devm_kzalloc(&pdev->dev, + sizeof(struct tegra186_asrc), GFP_KERNEL); + if (!asrc) { + dev_err(&pdev->dev, "Can't allocate asrc\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, asrc); + + asrc->soc_data = soc_data; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), pdev->name); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err; + } + + asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra186_asrc_regmap_config); + if (IS_ERR(asrc->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(asrc->regmap); + goto err; + } + regcache_cache_only(asrc->regmap, true); + + if (of_property_read_u32(pdev->dev.of_node, + "nvidia,ahub-asrc-id", + &pdev->dev.id) < 0) { + dev_err(&pdev->dev, + "Missing property nvidia,ahub-asrc-id\n"); + ret = -ENODEV; + goto err; + } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra186_asrc_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_CONFIG, + TEGRA186_ASRC_GLOBAL_CONFIG_FRAC_32BIT_PRECISION); + + /* Set global starting address of the buffer in ARAM */ + regmap_write(asrc->regmap, + TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR, 0x3F808000); + + /* set global enable */ + regmap_write(asrc->regmap, + TEGRA186_ASRC_GLOBAL_ENB, TEGRA186_ASRC_GLOBAL_EN); + + /* 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; + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, i), 1, 1); + } + + ret = snd_soc_register_codec(&pdev->dev, &tegra186_asrc_codec, + tegra186_asrc_dais, + ARRAY_SIZE(tegra186_asrc_dais)); + if (ret != 0) { + dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret); + goto err_suspend; + } + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra186_asrc_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err: + return ret; +} + +static int tegra186_asrc_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra186_asrc_runtime_suspend(&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_SYSTEM_SLEEP_PM_OPS(tegra186_asrc_suspend, NULL) +}; + +static struct platform_driver tegra186_asrc_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .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"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra186_asrc_of_match);