162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// tegra210_i2s.c - Tegra210 I2S driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of_device.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci#include <sound/core.h> 1562306a36Sopenharmony_ci#include <sound/pcm_params.h> 1662306a36Sopenharmony_ci#include <sound/soc.h> 1762306a36Sopenharmony_ci#include "tegra210_i2s.h" 1862306a36Sopenharmony_ci#include "tegra_cif.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic const struct reg_default tegra210_i2s_reg_defaults[] = { 2162306a36Sopenharmony_ci { TEGRA210_I2S_RX_INT_MASK, 0x00000003 }, 2262306a36Sopenharmony_ci { TEGRA210_I2S_RX_CIF_CTRL, 0x00007700 }, 2362306a36Sopenharmony_ci { TEGRA210_I2S_TX_INT_MASK, 0x00000003 }, 2462306a36Sopenharmony_ci { TEGRA210_I2S_TX_CIF_CTRL, 0x00007700 }, 2562306a36Sopenharmony_ci { TEGRA210_I2S_CG, 0x1 }, 2662306a36Sopenharmony_ci { TEGRA210_I2S_TIMING, 0x0000001f }, 2762306a36Sopenharmony_ci { TEGRA210_I2S_ENABLE, 0x1 }, 2862306a36Sopenharmony_ci /* 2962306a36Sopenharmony_ci * Below update does not have any effect on Tegra186 and Tegra194. 3062306a36Sopenharmony_ci * On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update 3162306a36Sopenharmony_ci * is required to select i2s4b for it to be functional for I2S 3262306a36Sopenharmony_ci * operation. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci { TEGRA210_I2S_CYA, 0x1 }, 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void tegra210_i2s_set_slot_ctrl(struct regmap *regmap, 3862306a36Sopenharmony_ci unsigned int total_slots, 3962306a36Sopenharmony_ci unsigned int tx_slot_mask, 4062306a36Sopenharmony_ci unsigned int rx_slot_mask) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci regmap_write(regmap, TEGRA210_I2S_SLOT_CTRL, total_slots - 1); 4362306a36Sopenharmony_ci regmap_write(regmap, TEGRA210_I2S_TX_SLOT_CTRL, tx_slot_mask); 4462306a36Sopenharmony_ci regmap_write(regmap, TEGRA210_I2S_RX_SLOT_CTRL, rx_slot_mask); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int tegra210_i2s_set_clock_rate(struct device *dev, 4862306a36Sopenharmony_ci unsigned int clock_rate) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct tegra210_i2s *i2s = dev_get_drvdata(dev); 5162306a36Sopenharmony_ci unsigned int val; 5262306a36Sopenharmony_ci int err; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* No need to set rates if I2S is being operated in slave */ 5762306a36Sopenharmony_ci if (!(val & I2S_CTRL_MASTER_EN)) 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci err = clk_set_rate(i2s->clk_i2s, clock_rate); 6162306a36Sopenharmony_ci if (err) { 6262306a36Sopenharmony_ci dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n", 6362306a36Sopenharmony_ci clock_rate, err); 6462306a36Sopenharmony_ci return err; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!IS_ERR(i2s->clk_sync_input)) { 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Other I/O modules in AHUB can use i2s bclk as reference 7062306a36Sopenharmony_ci * clock. Below sets sync input clock rate as per bclk, 7162306a36Sopenharmony_ci * which can be used as input to other I/O modules. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci err = clk_set_rate(i2s->clk_sync_input, clock_rate); 7462306a36Sopenharmony_ci if (err) { 7562306a36Sopenharmony_ci dev_err(dev, 7662306a36Sopenharmony_ci "can't set I2S sync input rate %u, err = %d\n", 7762306a36Sopenharmony_ci clock_rate, err); 7862306a36Sopenharmony_ci return err; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int tegra210_i2s_sw_reset(struct snd_soc_component *compnt, 8662306a36Sopenharmony_ci bool is_playback) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct device *dev = compnt->dev; 8962306a36Sopenharmony_ci struct tegra210_i2s *i2s = dev_get_drvdata(dev); 9062306a36Sopenharmony_ci unsigned int reset_mask = I2S_SOFT_RESET_MASK; 9162306a36Sopenharmony_ci unsigned int reset_en = I2S_SOFT_RESET_EN; 9262306a36Sopenharmony_ci unsigned int reset_reg, cif_reg, stream_reg; 9362306a36Sopenharmony_ci unsigned int cif_ctrl, stream_ctrl, i2s_ctrl, val; 9462306a36Sopenharmony_ci int err; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (is_playback) { 9762306a36Sopenharmony_ci reset_reg = TEGRA210_I2S_RX_SOFT_RESET; 9862306a36Sopenharmony_ci cif_reg = TEGRA210_I2S_RX_CIF_CTRL; 9962306a36Sopenharmony_ci stream_reg = TEGRA210_I2S_RX_CTRL; 10062306a36Sopenharmony_ci } else { 10162306a36Sopenharmony_ci reset_reg = TEGRA210_I2S_TX_SOFT_RESET; 10262306a36Sopenharmony_ci cif_reg = TEGRA210_I2S_TX_CIF_CTRL; 10362306a36Sopenharmony_ci stream_reg = TEGRA210_I2S_TX_CTRL; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Store CIF and I2S control values */ 10762306a36Sopenharmony_ci regmap_read(i2s->regmap, cif_reg, &cif_ctrl); 10862306a36Sopenharmony_ci regmap_read(i2s->regmap, stream_reg, &stream_ctrl); 10962306a36Sopenharmony_ci regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &i2s_ctrl); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Reset to make sure the previous transactions are clean */ 11262306a36Sopenharmony_ci regmap_update_bits(i2s->regmap, reset_reg, reset_mask, reset_en); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci err = regmap_read_poll_timeout(i2s->regmap, reset_reg, val, 11562306a36Sopenharmony_ci !(val & reset_mask & reset_en), 11662306a36Sopenharmony_ci 10, 10000); 11762306a36Sopenharmony_ci if (err) { 11862306a36Sopenharmony_ci dev_err(dev, "timeout: failed to reset I2S for %s\n", 11962306a36Sopenharmony_ci is_playback ? "playback" : "capture"); 12062306a36Sopenharmony_ci return err; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Restore CIF and I2S control values */ 12462306a36Sopenharmony_ci regmap_write(i2s->regmap, cif_reg, cif_ctrl); 12562306a36Sopenharmony_ci regmap_write(i2s->regmap, stream_reg, stream_ctrl); 12662306a36Sopenharmony_ci regmap_write(i2s->regmap, TEGRA210_I2S_CTRL, i2s_ctrl); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int tegra210_i2s_init(struct snd_soc_dapm_widget *w, 13262306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_dapm_to_component(w->dapm); 13562306a36Sopenharmony_ci struct device *dev = compnt->dev; 13662306a36Sopenharmony_ci struct tegra210_i2s *i2s = dev_get_drvdata(dev); 13762306a36Sopenharmony_ci unsigned int val, status_reg; 13862306a36Sopenharmony_ci bool is_playback; 13962306a36Sopenharmony_ci int err; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci switch (w->reg) { 14262306a36Sopenharmony_ci case TEGRA210_I2S_RX_ENABLE: 14362306a36Sopenharmony_ci is_playback = true; 14462306a36Sopenharmony_ci status_reg = TEGRA210_I2S_RX_STATUS; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case TEGRA210_I2S_TX_ENABLE: 14762306a36Sopenharmony_ci is_playback = false; 14862306a36Sopenharmony_ci status_reg = TEGRA210_I2S_TX_STATUS; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci default: 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Ensure I2S is in disabled state before new session */ 15562306a36Sopenharmony_ci err = regmap_read_poll_timeout(i2s->regmap, status_reg, val, 15662306a36Sopenharmony_ci !(val & I2S_EN_MASK & I2S_EN), 15762306a36Sopenharmony_ci 10, 10000); 15862306a36Sopenharmony_ci if (err) { 15962306a36Sopenharmony_ci dev_err(dev, "timeout: previous I2S %s is still active\n", 16062306a36Sopenharmony_ci is_playback ? "playback" : "capture"); 16162306a36Sopenharmony_ci return err; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return tegra210_i2s_sw_reset(compnt, is_playback); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int __maybe_unused tegra210_i2s_runtime_suspend(struct device *dev) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct tegra210_i2s *i2s = dev_get_drvdata(dev); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci regcache_cache_only(i2s->regmap, true); 17262306a36Sopenharmony_ci regcache_mark_dirty(i2s->regmap); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci clk_disable_unprepare(i2s->clk_i2s); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int __maybe_unused tegra210_i2s_runtime_resume(struct device *dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct tegra210_i2s *i2s = dev_get_drvdata(dev); 18262306a36Sopenharmony_ci int err; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci err = clk_prepare_enable(i2s->clk_i2s); 18562306a36Sopenharmony_ci if (err) { 18662306a36Sopenharmony_ci dev_err(dev, "failed to enable I2S bit clock, err: %d\n", err); 18762306a36Sopenharmony_ci return err; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci regcache_cache_only(i2s->regmap, false); 19162306a36Sopenharmony_ci regcache_sync(i2s->regmap); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void tegra210_i2s_set_data_offset(struct tegra210_i2s *i2s, 19762306a36Sopenharmony_ci unsigned int data_offset) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci /* Capture path */ 20062306a36Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA210_I2S_TX_CTRL, 20162306a36Sopenharmony_ci I2S_CTRL_DATA_OFFSET_MASK, 20262306a36Sopenharmony_ci data_offset << I2S_DATA_SHIFT); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Playback path */ 20562306a36Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA210_I2S_RX_CTRL, 20662306a36Sopenharmony_ci I2S_CTRL_DATA_OFFSET_MASK, 20762306a36Sopenharmony_ci data_offset << I2S_DATA_SHIFT); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int tegra210_i2s_set_fmt(struct snd_soc_dai *dai, 21162306a36Sopenharmony_ci unsigned int fmt) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); 21462306a36Sopenharmony_ci unsigned int mask, val; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci mask = I2S_CTRL_MASTER_EN_MASK; 21762306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 21862306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 21962306a36Sopenharmony_ci val = 0; 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 22262306a36Sopenharmony_ci val = I2S_CTRL_MASTER_EN; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci default: 22562306a36Sopenharmony_ci return -EINVAL; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci mask |= I2S_CTRL_FRAME_FMT_MASK | I2S_CTRL_LRCK_POL_MASK; 22962306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 23062306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 23162306a36Sopenharmony_ci val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE; 23262306a36Sopenharmony_ci val |= I2S_CTRL_LRCK_POL_HIGH; 23362306a36Sopenharmony_ci tegra210_i2s_set_data_offset(i2s, 1); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 23662306a36Sopenharmony_ci val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE; 23762306a36Sopenharmony_ci val |= I2S_CTRL_LRCK_POL_HIGH; 23862306a36Sopenharmony_ci tegra210_i2s_set_data_offset(i2s, 0); 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci /* I2S mode has data offset of 1 */ 24162306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 24262306a36Sopenharmony_ci val |= I2S_CTRL_FRAME_FMT_LRCK_MODE; 24362306a36Sopenharmony_ci val |= I2S_CTRL_LRCK_POL_LOW; 24462306a36Sopenharmony_ci tegra210_i2s_set_data_offset(i2s, 1); 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * For RJ mode data offset is dependent on the sample size 24862306a36Sopenharmony_ci * and the bclk ratio, and so is set when hw_params is called. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 25162306a36Sopenharmony_ci val |= I2S_CTRL_FRAME_FMT_LRCK_MODE; 25262306a36Sopenharmony_ci val |= I2S_CTRL_LRCK_POL_HIGH; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 25562306a36Sopenharmony_ci val |= I2S_CTRL_FRAME_FMT_LRCK_MODE; 25662306a36Sopenharmony_ci val |= I2S_CTRL_LRCK_POL_HIGH; 25762306a36Sopenharmony_ci tegra210_i2s_set_data_offset(i2s, 0); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci default: 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci mask |= I2S_CTRL_EDGE_CTRL_MASK; 26462306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 26562306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 26662306a36Sopenharmony_ci val |= I2S_CTRL_EDGE_CTRL_POS_EDGE; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 26962306a36Sopenharmony_ci val |= I2S_CTRL_EDGE_CTRL_POS_EDGE; 27062306a36Sopenharmony_ci val ^= I2S_CTRL_LRCK_POL_MASK; 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 27362306a36Sopenharmony_ci val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 27662306a36Sopenharmony_ci val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE; 27762306a36Sopenharmony_ci val ^= I2S_CTRL_LRCK_POL_MASK; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci default: 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, mask, val); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci i2s->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int tegra210_i2s_set_tdm_slot(struct snd_soc_dai *dai, 29162306a36Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, 29262306a36Sopenharmony_ci int slots, int slot_width) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Copy the required tx and rx mask */ 29762306a36Sopenharmony_ci i2s->tx_mask = (tx_mask > DEFAULT_I2S_SLOT_MASK) ? 29862306a36Sopenharmony_ci DEFAULT_I2S_SLOT_MASK : tx_mask; 29962306a36Sopenharmony_ci i2s->rx_mask = (rx_mask > DEFAULT_I2S_SLOT_MASK) ? 30062306a36Sopenharmony_ci DEFAULT_I2S_SLOT_MASK : rx_mask; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int tegra210_i2s_get_loopback(struct snd_kcontrol *kcontrol, 30662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 30962306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = i2s->loopback; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int tegra210_i2s_put_loopback(struct snd_kcontrol *kcontrol, 31762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 32062306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 32162306a36Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (value == i2s->loopback) 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci i2s->loopback = value; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, I2S_CTRL_LPBK_MASK, 32962306a36Sopenharmony_ci i2s->loopback << I2S_CTRL_LPBK_SHIFT); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return 1; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int tegra210_i2s_get_fsync_width(struct snd_kcontrol *kcontrol, 33562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 33862306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ucontrol->value.integer.value[0] = i2s->fsync_width; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int tegra210_i2s_put_fsync_width(struct snd_kcontrol *kcontrol, 34662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 34962306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 35062306a36Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (value == i2s->fsync_width) 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci i2s->fsync_width = value; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * Frame sync width is used only for FSYNC modes and not 35962306a36Sopenharmony_ci * applicable for LRCK modes. Reset value for this field is "0", 36062306a36Sopenharmony_ci * which means the width is one bit clock wide. 36162306a36Sopenharmony_ci * The width requirement may depend on the codec and in such 36262306a36Sopenharmony_ci * cases mixer control is used to update custom values. A value 36362306a36Sopenharmony_ci * of "N" here means, width is "N + 1" bit clock wide. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, 36662306a36Sopenharmony_ci I2S_CTRL_FSYNC_WIDTH_MASK, 36762306a36Sopenharmony_ci i2s->fsync_width << I2S_FSYNC_WIDTH_SHIFT); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 1; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int tegra210_i2s_cget_stereo_to_mono(struct snd_kcontrol *kcontrol, 37362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 37662306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = i2s->stereo_to_mono[I2S_TX_PATH]; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int tegra210_i2s_cput_stereo_to_mono(struct snd_kcontrol *kcontrol, 38462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 38762306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 38862306a36Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (value == i2s->stereo_to_mono[I2S_TX_PATH]) 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci i2s->stereo_to_mono[I2S_TX_PATH] = value; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 1; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int tegra210_i2s_cget_mono_to_stereo(struct snd_kcontrol *kcontrol, 39962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 40262306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = i2s->mono_to_stereo[I2S_TX_PATH]; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int tegra210_i2s_cput_mono_to_stereo(struct snd_kcontrol *kcontrol, 41062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 41362306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 41462306a36Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (value == i2s->mono_to_stereo[I2S_TX_PATH]) 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci i2s->mono_to_stereo[I2S_TX_PATH] = value; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 1; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int tegra210_i2s_pget_stereo_to_mono(struct snd_kcontrol *kcontrol, 42562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 42862306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = i2s->stereo_to_mono[I2S_RX_PATH]; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int tegra210_i2s_pput_stereo_to_mono(struct snd_kcontrol *kcontrol, 43662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 43962306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 44062306a36Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (value == i2s->stereo_to_mono[I2S_RX_PATH]) 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci i2s->stereo_to_mono[I2S_RX_PATH] = value; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 1; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int tegra210_i2s_pget_mono_to_stereo(struct snd_kcontrol *kcontrol, 45162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 45462306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = i2s->mono_to_stereo[I2S_RX_PATH]; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int tegra210_i2s_pput_mono_to_stereo(struct snd_kcontrol *kcontrol, 46262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 46562306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 46662306a36Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (value == i2s->mono_to_stereo[I2S_RX_PATH]) 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci i2s->mono_to_stereo[I2S_RX_PATH] = value; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return 1; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int tegra210_i2s_pget_fifo_th(struct snd_kcontrol *kcontrol, 47762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 48062306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = i2s->rx_fifo_th; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int tegra210_i2s_pput_fifo_th(struct snd_kcontrol *kcontrol, 48862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 49162306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 49262306a36Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (value == i2s->rx_fifo_th) 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci i2s->rx_fifo_th = value; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 1; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int tegra210_i2s_get_bclk_ratio(struct snd_kcontrol *kcontrol, 50362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 50662306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = i2s->bclk_ratio; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int tegra210_i2s_put_bclk_ratio(struct snd_kcontrol *kcontrol, 51462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol); 51762306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt); 51862306a36Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (value == i2s->bclk_ratio) 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci i2s->bclk_ratio = value; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return 1; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int tegra210_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, 52962306a36Sopenharmony_ci unsigned int ratio) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci i2s->bclk_ratio = ratio; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int tegra210_i2s_set_timing_params(struct device *dev, 53962306a36Sopenharmony_ci unsigned int sample_size, 54062306a36Sopenharmony_ci unsigned int srate, 54162306a36Sopenharmony_ci unsigned int channels) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct tegra210_i2s *i2s = dev_get_drvdata(dev); 54462306a36Sopenharmony_ci unsigned int val, bit_count, bclk_rate, num_bclk = sample_size; 54562306a36Sopenharmony_ci int err; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (i2s->bclk_ratio) 54862306a36Sopenharmony_ci num_bclk *= i2s->bclk_ratio; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (i2s->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) 55162306a36Sopenharmony_ci tegra210_i2s_set_data_offset(i2s, num_bclk - sample_size); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* I2S bit clock rate */ 55462306a36Sopenharmony_ci bclk_rate = srate * channels * num_bclk; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci err = tegra210_i2s_set_clock_rate(dev, bclk_rate); 55762306a36Sopenharmony_ci if (err) { 55862306a36Sopenharmony_ci dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n", 55962306a36Sopenharmony_ci bclk_rate, err); 56062306a36Sopenharmony_ci return err; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* 56662306a36Sopenharmony_ci * For LRCK mode, channel bit count depends on number of bit clocks 56762306a36Sopenharmony_ci * on the left channel, where as for FSYNC mode bit count depends on 56862306a36Sopenharmony_ci * the number of bit clocks in both left and right channels for DSP 56962306a36Sopenharmony_ci * mode or the number of bit clocks in one TDM frame. 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci switch (val & I2S_CTRL_FRAME_FMT_MASK) { 57362306a36Sopenharmony_ci case I2S_CTRL_FRAME_FMT_LRCK_MODE: 57462306a36Sopenharmony_ci bit_count = (bclk_rate / (srate * 2)) - 1; 57562306a36Sopenharmony_ci break; 57662306a36Sopenharmony_ci case I2S_CTRL_FRAME_FMT_FSYNC_MODE: 57762306a36Sopenharmony_ci bit_count = (bclk_rate / srate) - 1; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci tegra210_i2s_set_slot_ctrl(i2s->regmap, channels, 58062306a36Sopenharmony_ci i2s->tx_mask, i2s->rx_mask); 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci default: 58362306a36Sopenharmony_ci dev_err(dev, "invalid I2S frame format\n"); 58462306a36Sopenharmony_ci return -EINVAL; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (bit_count > I2S_TIMING_CH_BIT_CNT_MASK) { 58862306a36Sopenharmony_ci dev_err(dev, "invalid I2S channel bit count %u\n", bit_count); 58962306a36Sopenharmony_ci return -EINVAL; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci regmap_write(i2s->regmap, TEGRA210_I2S_TIMING, 59362306a36Sopenharmony_ci bit_count << I2S_TIMING_CH_BIT_CNT_SHIFT); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int tegra210_i2s_hw_params(struct snd_pcm_substream *substream, 59962306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 60062306a36Sopenharmony_ci struct snd_soc_dai *dai) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct device *dev = dai->dev; 60362306a36Sopenharmony_ci struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); 60462306a36Sopenharmony_ci unsigned int sample_size, channels, srate, val, reg, path; 60562306a36Sopenharmony_ci struct tegra_cif_conf cif_conf; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci channels = params_channels(params); 61062306a36Sopenharmony_ci if (channels < 1) { 61162306a36Sopenharmony_ci dev_err(dev, "invalid I2S %d channel configuration\n", 61262306a36Sopenharmony_ci channels); 61362306a36Sopenharmony_ci return -EINVAL; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci cif_conf.audio_ch = channels; 61762306a36Sopenharmony_ci cif_conf.client_ch = channels; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci switch (params_format(params)) { 62062306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 62162306a36Sopenharmony_ci val = I2S_BITS_8; 62262306a36Sopenharmony_ci sample_size = 8; 62362306a36Sopenharmony_ci cif_conf.audio_bits = TEGRA_ACIF_BITS_8; 62462306a36Sopenharmony_ci cif_conf.client_bits = TEGRA_ACIF_BITS_8; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 62762306a36Sopenharmony_ci val = I2S_BITS_16; 62862306a36Sopenharmony_ci sample_size = 16; 62962306a36Sopenharmony_ci cif_conf.audio_bits = TEGRA_ACIF_BITS_16; 63062306a36Sopenharmony_ci cif_conf.client_bits = TEGRA_ACIF_BITS_16; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 63362306a36Sopenharmony_ci val = I2S_BITS_32; 63462306a36Sopenharmony_ci sample_size = 32; 63562306a36Sopenharmony_ci cif_conf.audio_bits = TEGRA_ACIF_BITS_32; 63662306a36Sopenharmony_ci cif_conf.client_bits = TEGRA_ACIF_BITS_32; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci default: 63962306a36Sopenharmony_ci dev_err(dev, "unsupported format!\n"); 64062306a36Sopenharmony_ci return -EOPNOTSUPP; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Program sample size */ 64462306a36Sopenharmony_ci regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, 64562306a36Sopenharmony_ci I2S_CTRL_BIT_SIZE_MASK, val); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci srate = params_rate(params); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* For playback I2S RX-CIF and for capture TX-CIF is used */ 65062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 65162306a36Sopenharmony_ci path = I2S_RX_PATH; 65262306a36Sopenharmony_ci else 65362306a36Sopenharmony_ci path = I2S_TX_PATH; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 65662306a36Sopenharmony_ci unsigned int max_th; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* FIFO threshold in terms of frames */ 65962306a36Sopenharmony_ci max_th = (I2S_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (i2s->rx_fifo_th > max_th) 66262306a36Sopenharmony_ci i2s->rx_fifo_th = max_th; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci cif_conf.threshold = i2s->rx_fifo_th; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci reg = TEGRA210_I2S_RX_CIF_CTRL; 66762306a36Sopenharmony_ci } else { 66862306a36Sopenharmony_ci reg = TEGRA210_I2S_TX_CIF_CTRL; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci cif_conf.mono_conv = i2s->mono_to_stereo[path]; 67262306a36Sopenharmony_ci cif_conf.stereo_conv = i2s->stereo_to_mono[path]; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci tegra_set_cif(i2s->regmap, reg, &cif_conf); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return tegra210_i2s_set_timing_params(dev, sample_size, srate, 67762306a36Sopenharmony_ci cif_conf.client_ch); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tegra210_i2s_dai_ops = { 68162306a36Sopenharmony_ci .set_fmt = tegra210_i2s_set_fmt, 68262306a36Sopenharmony_ci .hw_params = tegra210_i2s_hw_params, 68362306a36Sopenharmony_ci .set_bclk_ratio = tegra210_i2s_set_dai_bclk_ratio, 68462306a36Sopenharmony_ci .set_tdm_slot = tegra210_i2s_set_tdm_slot, 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic struct snd_soc_dai_driver tegra210_i2s_dais[] = { 68862306a36Sopenharmony_ci { 68962306a36Sopenharmony_ci .name = "I2S-CIF", 69062306a36Sopenharmony_ci .playback = { 69162306a36Sopenharmony_ci .stream_name = "CIF-Playback", 69262306a36Sopenharmony_ci .channels_min = 1, 69362306a36Sopenharmony_ci .channels_max = 16, 69462306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 69562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 69662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 69762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 69862306a36Sopenharmony_ci }, 69962306a36Sopenharmony_ci .capture = { 70062306a36Sopenharmony_ci .stream_name = "CIF-Capture", 70162306a36Sopenharmony_ci .channels_min = 1, 70262306a36Sopenharmony_ci .channels_max = 16, 70362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 70462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 70562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 70662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 70762306a36Sopenharmony_ci }, 70862306a36Sopenharmony_ci }, 70962306a36Sopenharmony_ci { 71062306a36Sopenharmony_ci .name = "I2S-DAP", 71162306a36Sopenharmony_ci .playback = { 71262306a36Sopenharmony_ci .stream_name = "DAP-Playback", 71362306a36Sopenharmony_ci .channels_min = 1, 71462306a36Sopenharmony_ci .channels_max = 16, 71562306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 71662306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 71762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 71862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 71962306a36Sopenharmony_ci }, 72062306a36Sopenharmony_ci .capture = { 72162306a36Sopenharmony_ci .stream_name = "DAP-Capture", 72262306a36Sopenharmony_ci .channels_min = 1, 72362306a36Sopenharmony_ci .channels_max = 16, 72462306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 72562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 72662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 72762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 72862306a36Sopenharmony_ci }, 72962306a36Sopenharmony_ci .ops = &tegra210_i2s_dai_ops, 73062306a36Sopenharmony_ci .symmetric_rate = 1, 73162306a36Sopenharmony_ci }, 73262306a36Sopenharmony_ci}; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic const char * const tegra210_i2s_stereo_conv_text[] = { 73562306a36Sopenharmony_ci "CH0", "CH1", "AVG", 73662306a36Sopenharmony_ci}; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic const char * const tegra210_i2s_mono_conv_text[] = { 73962306a36Sopenharmony_ci "Zero", "Copy", 74062306a36Sopenharmony_ci}; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic const struct soc_enum tegra210_i2s_mono_conv_enum = 74362306a36Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_mono_conv_text), 74462306a36Sopenharmony_ci tegra210_i2s_mono_conv_text); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic const struct soc_enum tegra210_i2s_stereo_conv_enum = 74762306a36Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_stereo_conv_text), 74862306a36Sopenharmony_ci tegra210_i2s_stereo_conv_text); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic const struct snd_kcontrol_new tegra210_i2s_controls[] = { 75162306a36Sopenharmony_ci SOC_SINGLE_EXT("Loopback", 0, 0, 1, 0, tegra210_i2s_get_loopback, 75262306a36Sopenharmony_ci tegra210_i2s_put_loopback), 75362306a36Sopenharmony_ci SOC_SINGLE_EXT("FSYNC Width", 0, 0, 255, 0, 75462306a36Sopenharmony_ci tegra210_i2s_get_fsync_width, 75562306a36Sopenharmony_ci tegra210_i2s_put_fsync_width), 75662306a36Sopenharmony_ci SOC_ENUM_EXT("Capture Stereo To Mono", tegra210_i2s_stereo_conv_enum, 75762306a36Sopenharmony_ci tegra210_i2s_cget_stereo_to_mono, 75862306a36Sopenharmony_ci tegra210_i2s_cput_stereo_to_mono), 75962306a36Sopenharmony_ci SOC_ENUM_EXT("Capture Mono To Stereo", tegra210_i2s_mono_conv_enum, 76062306a36Sopenharmony_ci tegra210_i2s_cget_mono_to_stereo, 76162306a36Sopenharmony_ci tegra210_i2s_cput_mono_to_stereo), 76262306a36Sopenharmony_ci SOC_ENUM_EXT("Playback Stereo To Mono", tegra210_i2s_stereo_conv_enum, 76362306a36Sopenharmony_ci tegra210_i2s_pget_mono_to_stereo, 76462306a36Sopenharmony_ci tegra210_i2s_pput_mono_to_stereo), 76562306a36Sopenharmony_ci SOC_ENUM_EXT("Playback Mono To Stereo", tegra210_i2s_mono_conv_enum, 76662306a36Sopenharmony_ci tegra210_i2s_pget_stereo_to_mono, 76762306a36Sopenharmony_ci tegra210_i2s_pput_stereo_to_mono), 76862306a36Sopenharmony_ci SOC_SINGLE_EXT("Playback FIFO Threshold", 0, 0, I2S_RX_FIFO_DEPTH - 1, 76962306a36Sopenharmony_ci 0, tegra210_i2s_pget_fifo_th, tegra210_i2s_pput_fifo_th), 77062306a36Sopenharmony_ci SOC_SINGLE_EXT("BCLK Ratio", 0, 0, INT_MAX, 0, 77162306a36Sopenharmony_ci tegra210_i2s_get_bclk_ratio, 77262306a36Sopenharmony_ci tegra210_i2s_put_bclk_ratio), 77362306a36Sopenharmony_ci}; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = { 77662306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN_E("RX", NULL, 0, TEGRA210_I2S_RX_ENABLE, 77762306a36Sopenharmony_ci 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU), 77862306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT_E("TX", NULL, 0, TEGRA210_I2S_TX_ENABLE, 77962306a36Sopenharmony_ci 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU), 78062306a36Sopenharmony_ci SND_SOC_DAPM_MIC("MIC", NULL), 78162306a36Sopenharmony_ci SND_SOC_DAPM_SPK("SPK", NULL), 78262306a36Sopenharmony_ci}; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_cistatic const struct snd_soc_dapm_route tegra210_i2s_routes[] = { 78562306a36Sopenharmony_ci /* Playback route from XBAR */ 78662306a36Sopenharmony_ci { "XBAR-Playback", NULL, "XBAR-TX" }, 78762306a36Sopenharmony_ci { "CIF-Playback", NULL, "XBAR-Playback" }, 78862306a36Sopenharmony_ci { "RX", NULL, "CIF-Playback" }, 78962306a36Sopenharmony_ci { "DAP-Playback", NULL, "RX" }, 79062306a36Sopenharmony_ci { "SPK", NULL, "DAP-Playback" }, 79162306a36Sopenharmony_ci /* Capture route to XBAR */ 79262306a36Sopenharmony_ci { "XBAR-RX", NULL, "XBAR-Capture" }, 79362306a36Sopenharmony_ci { "XBAR-Capture", NULL, "CIF-Capture" }, 79462306a36Sopenharmony_ci { "CIF-Capture", NULL, "TX" }, 79562306a36Sopenharmony_ci { "TX", NULL, "DAP-Capture" }, 79662306a36Sopenharmony_ci { "DAP-Capture", NULL, "MIC" }, 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic const struct snd_soc_component_driver tegra210_i2s_cmpnt = { 80062306a36Sopenharmony_ci .dapm_widgets = tegra210_i2s_widgets, 80162306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tegra210_i2s_widgets), 80262306a36Sopenharmony_ci .dapm_routes = tegra210_i2s_routes, 80362306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tegra210_i2s_routes), 80462306a36Sopenharmony_ci .controls = tegra210_i2s_controls, 80562306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(tegra210_i2s_controls), 80662306a36Sopenharmony_ci}; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic bool tegra210_i2s_wr_reg(struct device *dev, unsigned int reg) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci switch (reg) { 81162306a36Sopenharmony_ci case TEGRA210_I2S_RX_ENABLE ... TEGRA210_I2S_RX_SOFT_RESET: 81262306a36Sopenharmony_ci case TEGRA210_I2S_RX_INT_MASK ... TEGRA210_I2S_RX_CLK_TRIM: 81362306a36Sopenharmony_ci case TEGRA210_I2S_TX_ENABLE ... TEGRA210_I2S_TX_SOFT_RESET: 81462306a36Sopenharmony_ci case TEGRA210_I2S_TX_INT_MASK ... TEGRA210_I2S_TX_CLK_TRIM: 81562306a36Sopenharmony_ci case TEGRA210_I2S_ENABLE ... TEGRA210_I2S_CG: 81662306a36Sopenharmony_ci case TEGRA210_I2S_CTRL ... TEGRA210_I2S_CYA: 81762306a36Sopenharmony_ci return true; 81862306a36Sopenharmony_ci default: 81962306a36Sopenharmony_ci return false; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic bool tegra210_i2s_rd_reg(struct device *dev, unsigned int reg) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci if (tegra210_i2s_wr_reg(dev, reg)) 82662306a36Sopenharmony_ci return true; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci switch (reg) { 82962306a36Sopenharmony_ci case TEGRA210_I2S_RX_STATUS: 83062306a36Sopenharmony_ci case TEGRA210_I2S_RX_INT_STATUS: 83162306a36Sopenharmony_ci case TEGRA210_I2S_RX_CIF_FIFO_STATUS: 83262306a36Sopenharmony_ci case TEGRA210_I2S_TX_STATUS: 83362306a36Sopenharmony_ci case TEGRA210_I2S_TX_INT_STATUS: 83462306a36Sopenharmony_ci case TEGRA210_I2S_TX_CIF_FIFO_STATUS: 83562306a36Sopenharmony_ci case TEGRA210_I2S_STATUS: 83662306a36Sopenharmony_ci case TEGRA210_I2S_INT_STATUS: 83762306a36Sopenharmony_ci return true; 83862306a36Sopenharmony_ci default: 83962306a36Sopenharmony_ci return false; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic bool tegra210_i2s_volatile_reg(struct device *dev, unsigned int reg) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci switch (reg) { 84662306a36Sopenharmony_ci case TEGRA210_I2S_RX_STATUS: 84762306a36Sopenharmony_ci case TEGRA210_I2S_RX_INT_STATUS: 84862306a36Sopenharmony_ci case TEGRA210_I2S_RX_CIF_FIFO_STATUS: 84962306a36Sopenharmony_ci case TEGRA210_I2S_TX_STATUS: 85062306a36Sopenharmony_ci case TEGRA210_I2S_TX_INT_STATUS: 85162306a36Sopenharmony_ci case TEGRA210_I2S_TX_CIF_FIFO_STATUS: 85262306a36Sopenharmony_ci case TEGRA210_I2S_STATUS: 85362306a36Sopenharmony_ci case TEGRA210_I2S_INT_STATUS: 85462306a36Sopenharmony_ci case TEGRA210_I2S_RX_SOFT_RESET: 85562306a36Sopenharmony_ci case TEGRA210_I2S_TX_SOFT_RESET: 85662306a36Sopenharmony_ci return true; 85762306a36Sopenharmony_ci default: 85862306a36Sopenharmony_ci return false; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic const struct regmap_config tegra210_i2s_regmap_config = { 86362306a36Sopenharmony_ci .reg_bits = 32, 86462306a36Sopenharmony_ci .reg_stride = 4, 86562306a36Sopenharmony_ci .val_bits = 32, 86662306a36Sopenharmony_ci .max_register = TEGRA210_I2S_CYA, 86762306a36Sopenharmony_ci .writeable_reg = tegra210_i2s_wr_reg, 86862306a36Sopenharmony_ci .readable_reg = tegra210_i2s_rd_reg, 86962306a36Sopenharmony_ci .volatile_reg = tegra210_i2s_volatile_reg, 87062306a36Sopenharmony_ci .reg_defaults = tegra210_i2s_reg_defaults, 87162306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(tegra210_i2s_reg_defaults), 87262306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 87362306a36Sopenharmony_ci}; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int tegra210_i2s_probe(struct platform_device *pdev) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 87862306a36Sopenharmony_ci struct tegra210_i2s *i2s; 87962306a36Sopenharmony_ci void __iomem *regs; 88062306a36Sopenharmony_ci int err; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); 88362306a36Sopenharmony_ci if (!i2s) 88462306a36Sopenharmony_ci return -ENOMEM; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci i2s->rx_fifo_th = DEFAULT_I2S_RX_FIFO_THRESHOLD; 88762306a36Sopenharmony_ci i2s->tx_mask = DEFAULT_I2S_SLOT_MASK; 88862306a36Sopenharmony_ci i2s->rx_mask = DEFAULT_I2S_SLOT_MASK; 88962306a36Sopenharmony_ci i2s->loopback = false; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci dev_set_drvdata(dev, i2s); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci i2s->clk_i2s = devm_clk_get(dev, "i2s"); 89462306a36Sopenharmony_ci if (IS_ERR(i2s->clk_i2s)) { 89562306a36Sopenharmony_ci dev_err(dev, "can't retrieve I2S bit clock\n"); 89662306a36Sopenharmony_ci return PTR_ERR(i2s->clk_i2s); 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* 90062306a36Sopenharmony_ci * Not an error, as this clock is needed only when some other I/O 90162306a36Sopenharmony_ci * requires input clock from current I2S instance, which is 90262306a36Sopenharmony_ci * configurable from DT. 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ci i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); 90562306a36Sopenharmony_ci if (IS_ERR(i2s->clk_sync_input)) 90662306a36Sopenharmony_ci dev_dbg(dev, "can't retrieve I2S sync input clock\n"); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 90962306a36Sopenharmony_ci if (IS_ERR(regs)) 91062306a36Sopenharmony_ci return PTR_ERR(regs); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci i2s->regmap = devm_regmap_init_mmio(dev, regs, 91362306a36Sopenharmony_ci &tegra210_i2s_regmap_config); 91462306a36Sopenharmony_ci if (IS_ERR(i2s->regmap)) { 91562306a36Sopenharmony_ci dev_err(dev, "regmap init failed\n"); 91662306a36Sopenharmony_ci return PTR_ERR(i2s->regmap); 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci regcache_cache_only(i2s->regmap, true); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci err = devm_snd_soc_register_component(dev, &tegra210_i2s_cmpnt, 92262306a36Sopenharmony_ci tegra210_i2s_dais, 92362306a36Sopenharmony_ci ARRAY_SIZE(tegra210_i2s_dais)); 92462306a36Sopenharmony_ci if (err) { 92562306a36Sopenharmony_ci dev_err(dev, "can't register I2S component, err: %d\n", err); 92662306a36Sopenharmony_ci return err; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci pm_runtime_enable(dev); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return 0; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic void tegra210_i2s_remove(struct platform_device *pdev) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic const struct dev_pm_ops tegra210_i2s_pm_ops = { 94062306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra210_i2s_runtime_suspend, 94162306a36Sopenharmony_ci tegra210_i2s_runtime_resume, NULL) 94262306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 94362306a36Sopenharmony_ci pm_runtime_force_resume) 94462306a36Sopenharmony_ci}; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic const struct of_device_id tegra210_i2s_of_match[] = { 94762306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-i2s" }, 94862306a36Sopenharmony_ci {}, 94962306a36Sopenharmony_ci}; 95062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra210_i2s_of_match); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic struct platform_driver tegra210_i2s_driver = { 95362306a36Sopenharmony_ci .driver = { 95462306a36Sopenharmony_ci .name = "tegra210-i2s", 95562306a36Sopenharmony_ci .of_match_table = tegra210_i2s_of_match, 95662306a36Sopenharmony_ci .pm = &tegra210_i2s_pm_ops, 95762306a36Sopenharmony_ci }, 95862306a36Sopenharmony_ci .probe = tegra210_i2s_probe, 95962306a36Sopenharmony_ci .remove_new = tegra210_i2s_remove, 96062306a36Sopenharmony_ci}; 96162306a36Sopenharmony_cimodule_platform_driver(tegra210_i2s_driver) 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ciMODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>"); 96462306a36Sopenharmony_ciMODULE_DESCRIPTION("Tegra210 ASoC I2S driver"); 96562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 966