[alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver

Sameer Pujar spujar at nvidia.com
Tue Jan 21 15:21:00 CET 2020



On 1/21/2020 10:45 AM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 20.01.2020 17:23, Sameer Pujar пишет:
>> The Inter-IC Sound (I2S) controller implements full-duplex, bi-directional
>> and single direction point to point serial interface. It can interface
>> with I2S compatible devices. Tegra I2S controller can operate as both
>> master and slave.
>>
>> This patch registers I2S controller with ASoC framework. The component
>> driver exposes DAPM widgets, routes and kcontrols for the device. The DAI
>> driver exposes I2S interfaces, which can be used to connect different
>> components in the ASoC layer. Makefile and Kconfig support is added to
>> allow to build the driver. The I2S devices can be enabled in the DT via
>> "nvidia,tegra210-i2s" compatible binding.
>>
>> Signed-off-by: Sameer Pujar <spujar at nvidia.com>
>> ---
>>   sound/soc/tegra/Kconfig        |  10 +
>>   sound/soc/tegra/Makefile       |   2 +
>>   sound/soc/tegra/tegra210_i2s.c | 941 +++++++++++++++++++++++++++++++++++++++++
>>   sound/soc/tegra/tegra210_i2s.h | 132 ++++++
>>   4 files changed, 1085 insertions(+)
>>   create mode 100644 sound/soc/tegra/tegra210_i2s.c
>>   create mode 100644 sound/soc/tegra/tegra210_i2s.h
>>
>> diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
>> index 2bde1e6..157fa7a 100644
>> --- a/sound/soc/tegra/Kconfig
>> +++ b/sound/soc/tegra/Kconfig
>> @@ -73,6 +73,16 @@ config SND_SOC_TEGRA210_DMIC
>>             PDM receiver.
>>             Say Y or M if you want to add support for Tegra210 DMIC module.
>>
>> +config SND_SOC_TEGRA210_I2S
>> +        tristate "Tegra210 I2S module"
>> +        depends on SND_SOC_TEGRA
>> +        help
>> +          Config to enable the Inter-IC Sound (I2S) Controller which
>> +          implements full-duplex and bidirectional and single direction
>> +          point-to-point serial interfaces. It can interface with I2S
>> +          compatible devices.
>> +          Say Y or M if you want to add support for Tegra210 I2S module.
>> +
>>   config SND_SOC_TEGRA_RT5640
>>        tristate "SoC Audio support for Tegra boards using an RT5640 codec"
>>        depends on SND_SOC_TEGRA && I2C && GPIOLIB
>> diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
>> index 9e78fe4..1c4457d 100644
>> --- a/sound/soc/tegra/Makefile
>> +++ b/sound/soc/tegra/Makefile
>> @@ -10,6 +10,7 @@ snd-soc-tegra30-ahub-objs := tegra30_ahub.o
>>   snd-soc-tegra30-i2s-objs := tegra30_i2s.o
>>   snd-soc-tegra-cif-objs := tegra_cif.o
>>   snd-soc-tegra210-dmic-objs := tegra210_dmic.o
>> +snd-soc-tegra210-i2s-objs := tegra210_i2s.o
>>
>>   obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
>>   obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
>> @@ -21,6 +22,7 @@ obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o
>>   obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o
>>   obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
>>   obj-$(CONFIG_SND_SOC_TEGRA210_DMIC) += snd-soc-tegra210-dmic.o
>> +obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
>>
>>   # Tegra machine Support
>>   snd-soc-tegra-rt5640-objs := tegra_rt5640.o
>> diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
>> new file mode 100644
>> index 0000000..36e8a7c
>> --- /dev/null
>> +++ b/sound/soc/tegra/tegra210_i2s.c
>> @@ -0,0 +1,941 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * tegra210_i2s.c - Tegra210 I2S driver
>> + *
>> + * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
>> + *
>> + */
>> +
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/of_device.h>
>> +#include <linux/device.h>
>> +#include <linux/module.h>
>> +#include <linux/regmap.h>
>> +#include <linux/clk.h>
>> +#include <sound/pcm_params.h>
>> +#include <sound/core.h>
>> +#include <sound/soc.h>
>> +#include "tegra210_i2s.h"
>> +#include "tegra_cif.h"
> Headers should be sorted in alphabet order to avoid confusion about how
> to insert a new header.

I had tried using inverted-waterfall order to make it appear cleaner.
But I see that other Tegra source files are using alphabetical order, 
will follow the same here.
>> +#define DRV_NAME "tegra210-i2s"
>> +
>> +static const struct reg_default tegra210_i2s_reg_defaults[] = {
>> +     { TEGRA210_I2S_RX_INT_MASK, 0x00000003},
>> +     { TEGRA210_I2S_RX_CIF_CTRL, 0x00007700},
>> +     { TEGRA210_I2S_TX_INT_MASK, 0x00000003},
>> +     { TEGRA210_I2S_TX_CIF_CTRL, 0x00007700},
>> +     { TEGRA210_I2S_CG, 0x1},
>> +     { TEGRA210_I2S_TIMING, 0x0000001f},
>> +     { TEGRA210_I2S_ENABLE, 0x1},
>> +     /*
>> +      * Below update does not have any effect on Tegra186 and Tegra194.
>> +      * On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update
>> +      * is required to select i2s4b for it to be functional for I2S
>> +      * operation.
>> +      */
>> +     { TEGRA210_I2S_CYA, 0x1},
>> +};
>> +
>> +static void tegra210_i2s_set_slot_ctrl(struct regmap *regmap,
>> +                                    unsigned int total_slots,
>> +                                    unsigned int tx_slot_mask,
>> +                                    unsigned int rx_slot_mask)
>> +{
>> +     regmap_write(regmap, TEGRA210_I2S_SLOT_CTRL, total_slots - 1);
>> +     regmap_write(regmap, TEGRA210_I2S_TX_SLOT_CTRL, tx_slot_mask);
>> +     regmap_write(regmap, TEGRA210_I2S_RX_SLOT_CTRL, rx_slot_mask);
>> +}
>> +
>> +static int tegra210_i2s_set_clock_rate(struct device *dev,
>> +                                    unsigned int clock_rate)
>> +{
>> +     struct tegra210_i2s *i2s = dev_get_drvdata(dev);
>> +     unsigned int val;
>> +     int ret;
>> +
>> +     regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
>> +
>> +     /* No need to set rates if I2S is being operated in slave */
>> +     if (!(val & I2S_CTRL_MASTER_EN))
>> +             return 0;
>> +
>> +     ret = clk_set_rate(i2s->clk_i2s, clock_rate);
>> +     if (ret) {
>> +             dev_err(dev, "failed to set I2S bit clock rate %u, err: %d\n",
>> +                     clock_rate, ret);
>> +             return ret;
>> +     }
>> +
>> +     if (!IS_ERR(i2s->clk_sync_input)) {
>> +             /*
>> +              * Other I/O modules in AHUB can use i2s bclk as reference
>> +              * clock. Below sets sync input clock rate as per bclk,
>> +              * which can be used as input to other I/O modules.
>> +              */
>> +             ret = clk_set_rate(i2s->clk_sync_input, clock_rate);
>> +             if (ret) {
>> +                     dev_err(dev,
>> +                             "failed to set sync input rate %u, err = %d\n",
>> +                             clock_rate, ret);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_sw_reset(struct snd_soc_component *compnt,
>> +                              bool is_playback)
>> +{
>> +     struct device *dev = compnt->dev;
>> +     struct tegra210_i2s *i2s = dev_get_drvdata(dev);
>> +     unsigned int reset_mask = I2S_SOFT_RESET_MASK;
>> +     unsigned int reset_en = I2S_SOFT_RESET_EN;
>> +     unsigned int reset_reg, cif_reg, stream_reg;
>> +     unsigned int cif_ctrl, stream_ctrl, i2s_ctrl, val;
>> +     int ret;
>> +
>> +     if (is_playback) {
>> +             reset_reg = TEGRA210_I2S_RX_SOFT_RESET;
>> +             cif_reg = TEGRA210_I2S_RX_CIF_CTRL;
>> +             stream_reg = TEGRA210_I2S_RX_CTRL;
>> +     } else {
>> +             reset_reg = TEGRA210_I2S_TX_SOFT_RESET;
>> +             cif_reg = TEGRA210_I2S_TX_CIF_CTRL;
>> +             stream_reg = TEGRA210_I2S_TX_CTRL;
>> +     }
>> +
>> +     /* Store */
>> +     regmap_read(i2s->regmap, cif_reg, &cif_ctrl);
>> +     regmap_read(i2s->regmap, stream_reg, &stream_ctrl);
>> +     regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &i2s_ctrl);
>> +
>> +     /* Reset */
>> +     regmap_update_bits(i2s->regmap, reset_reg, reset_mask, reset_en);
>> +
>> +     ret = regmap_read_poll_timeout(i2s->regmap, reset_reg, val,
>> +                                    !(val & reset_mask & reset_en),
>> +                                    10, 10000);
>> +     if (ret < 0) {
>> +             dev_err(dev, "timeout: failed to reset I2S for %s\n",
>> +                     is_playback ? "playback" : "capture");
>> +             return ret;
>> +     }
>> +
>> +     /* Restore */
>> +     regmap_write(i2s->regmap, cif_reg, cif_ctrl);
>> +     regmap_write(i2s->regmap, stream_reg, stream_ctrl);
>> +     regmap_write(i2s->regmap, TEGRA210_I2S_CTRL, i2s_ctrl);
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_init(struct snd_soc_dapm_widget *w,
>> +                          struct snd_kcontrol *kcontrol, int event)
>> +{
>> +     struct snd_soc_component *compnt = snd_soc_dapm_to_component(w->dapm);
>> +     struct device *dev = compnt->dev;
>> +     struct tegra210_i2s *i2s = dev_get_drvdata(dev);
>> +     unsigned int val, status_reg;
>> +     bool is_playback;
>> +     int ret;
>> +
>> +     switch (w->reg) {
>> +     case TEGRA210_I2S_RX_ENABLE:
>> +             is_playback = true;
>> +             status_reg = TEGRA210_I2S_RX_STATUS;
>> +             break;
>> +     case TEGRA210_I2S_TX_ENABLE:
>> +             is_playback = false;
>> +             status_reg = TEGRA210_I2S_TX_STATUS;
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Ensure I2S is in disabled state before new session */
>> +     ret = regmap_read_poll_timeout(i2s->regmap, status_reg, val,
>> +                                    !(val & I2S_EN_MASK & I2S_EN),
>> +                                    10, 10000);
>> +     if (ret < 0) {
>> +             dev_err(dev, "timeout: previous I2S %s is still active\n",
>> +                     is_playback ? "playback" : "capture");
>> +             return ret;
>> +     }
>> +
>> +     /* SW reset */
> Comments should amend code with useful information.

Will add more details if needed or will get rid of it.
>
>> +     return tegra210_i2s_sw_reset(compnt, is_playback);
>> +}
>> +
>> +static int tegra210_i2s_runtime_suspend(struct device *dev)
>> +{
>> +     struct tegra210_i2s *i2s = dev_get_drvdata(dev);
>> +
>> +     regcache_cache_only(i2s->regmap, true);
>> +     regcache_mark_dirty(i2s->regmap);
>> +
>> +     clk_disable_unprepare(i2s->clk_i2s);
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_runtime_resume(struct device *dev)
>> +{
>> +     struct tegra210_i2s *i2s = dev_get_drvdata(dev);
>> +     int ret;
>> +
>> +     ret = clk_prepare_enable(i2s->clk_i2s);
>> +     if (ret) {
>> +             dev_err(dev, "failed to enable I2S bit clock, err: %d\n", ret);
>> +             return ret;
>> +     }
>> +
>> +     regcache_cache_only(i2s->regmap, false);
>> +     regcache_sync(i2s->regmap);
>> +
>> +     return 0;
>> +}
>> +
>> +static void tegra210_i2s_set_data_offset(struct tegra210_i2s *i2s,
>> +                                      unsigned int data_offset)
>> +{
>> +     unsigned int mask = I2S_CTRL_DATA_OFFSET_MASK;
>> +     unsigned int shift = I2S_DATA_SHIFT;
>> +     unsigned int reg;
>> +
>> +     reg = TEGRA210_I2S_TX_CTRL;
>> +     regmap_update_bits(i2s->regmap, reg, mask, data_offset << shift);
>> +
>> +     reg = TEGRA210_I2S_RX_CTRL;
>> +     regmap_update_bits(i2s->regmap, reg, mask, data_offset << shift);
>> +}
>> +
>> +static int tegra210_i2s_set_fmt(struct snd_soc_dai *dai,
>> +                             unsigned int fmt)
>> +{
>> +     struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
>> +     unsigned int mask, val;
>> +
>> +     mask = I2S_CTRL_MASTER_EN_MASK;
>> +     switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
>> +     case SND_SOC_DAIFMT_CBS_CFS:
>> +             val = 0;
>> +             break;
>> +     case SND_SOC_DAIFMT_CBM_CFM:
>> +             val = I2S_CTRL_MASTER_EN;
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     mask |= I2S_CTRL_FRAME_FMT_MASK | I2S_CTRL_LRCK_POL_MASK;
>> +     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
>> +     case SND_SOC_DAIFMT_DSP_A:
>> +             val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE;
>> +             val |= I2S_CTRL_LRCK_POL_HIGH;
>> +             tegra210_i2s_set_data_offset(i2s, 1);
>> +             break;
>> +     case SND_SOC_DAIFMT_DSP_B:
>> +             val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE;
>> +             val |= I2S_CTRL_LRCK_POL_HIGH;
>> +             tegra210_i2s_set_data_offset(i2s, 0);
>> +             break;
>> +     /* I2S mode has data offset of 1 */
>> +     case SND_SOC_DAIFMT_I2S:
>> +             val |= I2S_CTRL_FRAME_FMT_LRCK_MODE;
>> +             val |= I2S_CTRL_LRCK_POL_LOW;
>> +             tegra210_i2s_set_data_offset(i2s, 1);
>> +             break;
>> +     /*
>> +      * For RJ mode data offset is dependent on the sample size
>> +      * and the bclk ratio, and so is set when hw_params is called.
>> +      */
>> +     case SND_SOC_DAIFMT_RIGHT_J:
>> +             val |= I2S_CTRL_FRAME_FMT_LRCK_MODE;
>> +             val |= I2S_CTRL_LRCK_POL_HIGH;
>> +             break;
>> +     case SND_SOC_DAIFMT_LEFT_J:
>> +             val |= I2S_CTRL_FRAME_FMT_LRCK_MODE;
>> +             val |= I2S_CTRL_LRCK_POL_HIGH;
>> +             tegra210_i2s_set_data_offset(i2s, 0);
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     mask |= I2S_CTRL_EDGE_CTRL_MASK;
>> +     switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
>> +     case SND_SOC_DAIFMT_NB_NF:
>> +             val |= I2S_CTRL_EDGE_CTRL_POS_EDGE;
>> +             break;
>> +     case SND_SOC_DAIFMT_NB_IF:
>> +             val |= I2S_CTRL_EDGE_CTRL_POS_EDGE;
>> +             val ^= I2S_CTRL_LRCK_POL_MASK;
>> +             break;
>> +     case SND_SOC_DAIFMT_IB_NF:
>> +             val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE;
>> +             break;
>> +     case SND_SOC_DAIFMT_IB_IF:
>> +             val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE;
>> +             val ^= I2S_CTRL_LRCK_POL_MASK;
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, mask, val);
>> +
>> +     i2s->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_set_tdm_slot(struct snd_soc_dai *dai,
>> +                                  unsigned int tx_mask, unsigned int rx_mask,
>> +                                  int slots, int slot_width)
>> +{
>> +     struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
>> +
>> +     /* Copy the required tx and rx mask */
>> +     i2s->tx_mask = (tx_mask > DEFAULT_I2S_SLOT_MASK) ?
>> +                    DEFAULT_I2S_SLOT_MASK : tx_mask;
>> +     i2s->rx_mask = (rx_mask > DEFAULT_I2S_SLOT_MASK) ?
>> +                    DEFAULT_I2S_SLOT_MASK : rx_mask;
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai,
>> +                                        unsigned int ratio)
>> +{
>> +     struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
>> +
>> +     i2s->bclk_ratio = ratio;
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_get_control(struct snd_kcontrol *kcontrol,
>> +                                 struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol);
>> +     struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt);
>> +     long *uctl_val = &ucontrol->value.integer.value[0];
>> +
>> +     if (strstr(kcontrol->id.name, "Loopback"))
>> +             *uctl_val = i2s->loopback;
>> +     else if (strstr(kcontrol->id.name, "Sample Rate"))
>> +             *uctl_val = i2s->srate_override;
>> +     else if (strstr(kcontrol->id.name, "FSYNC Width"))
>> +             *uctl_val = i2s->fsync_width;
>> +     else if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
>> +             *uctl_val = i2s->audio_fmt_override[I2S_RX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Capture Audio Bit Format"))
>> +             *uctl_val = i2s->audio_fmt_override[I2S_TX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Client Bit Format"))
>> +             *uctl_val = i2s->client_fmt_override;
>> +     else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
>> +             *uctl_val = i2s->audio_ch_override[I2S_RX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
>> +             *uctl_val = i2s->audio_ch_override[I2S_TX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Client Channels"))
>> +             *uctl_val = i2s->client_ch_override;
>> +     else if (strstr(kcontrol->id.name, "Capture Stereo To Mono"))
>> +             *uctl_val = i2s->stereo_to_mono[I2S_TX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Capture Mono To Stereo"))
>> +             *uctl_val = i2s->mono_to_stereo[I2S_TX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Playback Stereo To Mono"))
>> +             *uctl_val = i2s->stereo_to_mono[I2S_RX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Playback Mono To Stereo"))
>> +             *uctl_val = i2s->mono_to_stereo[I2S_RX_PATH];
>> +     else if (strstr(kcontrol->id.name, "Playback FIFO Threshold"))
>> +             *uctl_val = i2s->rx_fifo_th;
>> +     else if (strstr(kcontrol->id.name, "BCLK Ratio"))
>> +             *uctl_val = i2s->bclk_ratio;
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_put_control(struct snd_kcontrol *kcontrol,
>> +     struct snd_ctl_elem_value *ucontrol)
> Checkpatch should complain about the wrong indentation here.

I had run checkpatch before sending the patch, below is the result.
-----
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#70:
new file mode 100644

total: 0 errors, 1 warnings, 1103 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
       mechanically convert to the typical style using --fix or 
--fix-inplace.
-----

>
>> +{
>> +     struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol);
>> +     struct tegra210_i2s *i2s = snd_soc_component_get_drvdata(compnt);
>> +     int value = ucontrol->value.integer.value[0];
>> +
>> +     if (strstr(kcontrol->id.name, "Loopback")) {
>> +             i2s->loopback = value;
>> +
>> +             regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL,
>> +                                I2S_CTRL_LPBK_MASK,
>> +                                i2s->loopback << I2S_CTRL_LPBK_SHIFT);
>> +
>> +     } else if (strstr(kcontrol->id.name, "Sample Rate"))
>> +             i2s->srate_override = value;
>> +     else if (strstr(kcontrol->id.name, "FSYNC Width")) {
>> +             /*
>> +              * Frame sync width is used only for FSYNC modes and not
>> +              * applicable for LRCK modes. Reset value for this field is "0",
>> +              * which means the width is one bit clock wide.
>> +              * The width requirement may depend on the codec and in such
>> +              * cases mixer control is used to update custom values. A value
>> +              * of "N" here means, width is "N + 1" bit clock wide.
>> +              */
>> +             i2s->fsync_width = value;
>> +
>> +             regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL,
>> +                                I2S_CTRL_FSYNC_WIDTH_MASK,
>> +                                i2s->fsync_width << I2S_FSYNC_WIDTH_SHIFT);
>> +
>> +     } else if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
>> +             i2s->audio_fmt_override[I2S_RX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Capture Audio Bit Format"))
>> +             i2s->audio_fmt_override[I2S_TX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Client Bit Format"))
>> +             i2s->client_fmt_override = value;
>> +     else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
>> +             i2s->audio_ch_override[I2S_RX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
>> +             i2s->audio_ch_override[I2S_TX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Client Channels"))
>> +             i2s->client_ch_override = value;
>> +     else if (strstr(kcontrol->id.name, "Capture Stereo To Mono"))
>> +             i2s->stereo_to_mono[I2S_TX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Capture Mono To Stereo"))
>> +             i2s->mono_to_stereo[I2S_TX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Playback Stereo To Mono"))
>> +             i2s->stereo_to_mono[I2S_RX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Playback Mono To Stereo"))
>> +             i2s->mono_to_stereo[I2S_RX_PATH] = value;
>> +     else if (strstr(kcontrol->id.name, "Playback FIFO Threshold"))
>> +             i2s->rx_fifo_th = value;
>> +     else if (strstr(kcontrol->id.name, "BCLK Ratio"))
>> +             i2s->bclk_ratio = value;
> I'm pretty sure that checkpatch should complain about the missing
> brackets, they should make code's indentation uniform and thus easier to
> read. Same for all other occurrences in the code.

same as above
>> +     return 0;
>> +}
>> +
>> +static const char * const tegra210_i2s_format_text[] = {
>> +     "None",
>> +     "16",
>> +     "32",
>> +};
>> +
>> +static const int tegra210_cif_fmt[] = {
>> +     0,
>> +     TEGRA_ACIF_BITS_16,
>> +     TEGRA_ACIF_BITS_32,
>> +};
>> +
>> +static const int tegra210_i2s_bit_fmt[] = {
>> +     0,
>> +     I2S_BITS_16,
>> +     I2S_BITS_32,
>> +};
>> +
>> +static const int tegra210_i2s_sample_size[] = {
>> +     0,
>> +     16,
>> +     32,
>> +};
>> +
>> +static const struct soc_enum tegra210_i2s_format_enum =
>> +     SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_format_text),
>> +                     tegra210_i2s_format_text);
>> +
>> +static int tegra210_i2s_set_timing_params(struct device *dev,
>> +                                       unsigned int sample_size,
>> +                                       unsigned int srate,
>> +                                       unsigned int channels)
>> +{
>> +     struct tegra210_i2s *i2s = dev_get_drvdata(dev);
>> +     unsigned int val, bit_count, bclk_rate, num_bclk = sample_size;
>> +     int ret;
>> +
>> +     if (i2s->bclk_ratio)
>> +             num_bclk *= i2s->bclk_ratio;
>> +
>> +     if (i2s->dai_fmt == SND_SOC_DAIFMT_RIGHT_J)
>> +             tegra210_i2s_set_data_offset(i2s, num_bclk - sample_size);
>> +
>> +     /* I2S bit clock rate */
>> +     bclk_rate = srate * channels * num_bclk;
>> +
>> +     ret = tegra210_i2s_set_clock_rate(dev, bclk_rate);
>> +     if (ret) {
>> +             dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n",
>> +                     bclk_rate, ret);
>> +             return ret;
>> +     }
>> +
>> +     regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
>> +
>> +     /*
>> +      * For LRCK mode, channel bit count depends on number of bit clocks
>> +      * on the left channel, where as for FSYNC mode bit count depends on
>> +      * the number of bit clocks in both left and right channels for DSP
>> +      * mode or the number of bit clocks in one TDM frame.
>> +      *
>> +      */
>> +     switch (val & I2S_CTRL_FRAME_FMT_MASK) {
>> +     case I2S_CTRL_FRAME_FMT_LRCK_MODE:
>> +             bit_count = (bclk_rate / (srate * 2)) - 1;
>> +             break;
>> +     case I2S_CTRL_FRAME_FMT_FSYNC_MODE:
>> +             bit_count = (bclk_rate / srate) - 1;
>> +
>> +             tegra210_i2s_set_slot_ctrl(i2s->regmap, channels,
>> +                                        i2s->tx_mask, i2s->rx_mask);
>> +             break;
>> +     default:
>> +             dev_err(dev, "invalid I2S mode\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (bit_count > I2S_TIMING_CH_BIT_CNT_MASK) {
>> +             dev_err(dev, "invalid channel bit count %u\n", bit_count);
>> +             return -EINVAL;
>> +     }
>> +
>> +     regmap_write(i2s->regmap, TEGRA210_I2S_TIMING,
>> +                  bit_count << I2S_TIMING_CH_BIT_CNT_SHIFT);
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
>> +                               struct snd_pcm_hw_params *params,
>> +                               struct snd_soc_dai *dai)
>> +{
>> +     struct device *dev = dai->dev;
>> +     struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
>> +     unsigned int sample_size, channels, srate, val, reg, path;
>> +     struct tegra_cif_conf cif_conf;
>> +     int max_th;
>> +
>> +     memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
>> +
>> +     channels = params_channels(params);
>> +     if (channels < 1) {
>> +             dev_err(dev, "invalid %d channel configuration\n", channels);
>> +             return -EINVAL;
>> +     }
>> +
>> +     cif_conf.audio_ch = channels;
>> +     cif_conf.client_ch = channels;
>> +
>> +     switch (params_format(params)) {
>> +     case SNDRV_PCM_FORMAT_S8:
>> +             val = I2S_BITS_8;
>> +             sample_size = 8;
>> +             cif_conf.audio_bits = TEGRA_ACIF_BITS_8;
>> +             cif_conf.client_bits = TEGRA_ACIF_BITS_8;
>> +             break;
>> +     case SNDRV_PCM_FORMAT_S16_LE:
>> +             val = I2S_BITS_16;
>> +             sample_size = 16;
>> +             cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
>> +             cif_conf.client_bits = TEGRA_ACIF_BITS_16;
>> +             break;
>> +     case SNDRV_PCM_FORMAT_S32_LE:
>> +             val = I2S_BITS_32;
>> +             sample_size = 32;
>> +             cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
>> +             cif_conf.client_bits = TEGRA_ACIF_BITS_32;
>> +             break;
>> +     default:
>> +             dev_err(dev, "unsupported format!\n");
>> +             return -ENOTSUPP;
>> +     }
>> +
>> +     if (i2s->client_fmt_override) {
>> +             val = tegra210_i2s_bit_fmt[i2s->client_fmt_override];
>> +             sample_size =
>> +                     tegra210_i2s_sample_size[i2s->client_fmt_override];
>> +             cif_conf.client_bits =
>> +                     tegra210_cif_fmt[i2s->client_fmt_override];
>> +     }
>> +
>> +     /* Program sample size */
>> +     regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL,
>> +                        I2S_CTRL_BIT_SIZE_MASK, val);
>> +
>> +     srate = params_rate(params);
>> +
>> +     /* Override rate, channel and audio bit params as applicable */
>> +     if (i2s->srate_override)
>> +             srate = i2s->srate_override;
>> +
>> +     /*
>> +      * For playback I2S RX-CIF and for capture TX-CIF is used.
>> +      * With reference to AHUB, for I2S, SNDRV_PCM_STREAM_CAPTURE stream is
>> +      * actually for playback.
>> +      */
>> +     path = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
>> +            I2S_RX_PATH : I2S_TX_PATH;
>> +
>> +     if (i2s->audio_ch_override[path])
>> +             cif_conf.audio_ch = i2s->audio_ch_override[path];
>> +
>> +     if (i2s->client_ch_override)
>> +             cif_conf.client_ch = i2s->client_ch_override;
>> +
>> +     if (i2s->audio_fmt_override[path])
>> +             cif_conf.audio_bits =
>> +                     tegra210_cif_fmt[i2s->audio_fmt_override[path]];
>> +
>> +     if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
>> +             reg = TEGRA210_I2S_RX_CIF_CTRL;
>> +
>> +             /* FIFO threshold in terms of frames */
>> +             max_th = (I2S_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;
>> +             if (max_th < 0)
>> +                     return -EINVAL;
>> +
>> +             if (i2s->rx_fifo_th > max_th)
>> +                     i2s->rx_fifo_th = max_th;
>> +
>> +             cif_conf.threshold = i2s->rx_fifo_th;
>> +     } else
>> +             reg = TEGRA210_I2S_TX_CIF_CTRL;
>> +
>> +     cif_conf.mono_conv = i2s->mono_to_stereo[path];
>> +     cif_conf.stereo_conv = i2s->stereo_to_mono[path];
>> +
>> +     tegra_set_cif(i2s->regmap, reg, &cif_conf);
>> +
>> +     return tegra210_i2s_set_timing_params(dev, sample_size, srate,
>> +                                           cif_conf.client_ch);
>> +}
>> +
>> +static struct snd_soc_dai_ops tegra210_i2s_dai_ops = {
> const?

will add

>
>> +     .set_fmt        = tegra210_i2s_set_fmt,
>> +     .hw_params      = tegra210_i2s_hw_params,
>> +     .set_bclk_ratio = tegra210_i2s_set_dai_bclk_ratio,
>> +     .set_tdm_slot   = tegra210_i2s_set_tdm_slot,
>> +};
>> +
>> +/*
>> + * Three DAIs are exposed
>> + * 1. "CIF" DAI for connecting with XBAR
>> + * 2. "DAP" DAI for connecting with CODEC
>> + * 3. "DUMMY" can be used when no external codec connection is
>> + *    available. In such case "DAP" is connected with "DUMMY".
>> + * Order of these DAIs should not be changed, since DAI links in DT refer
>> + * to these DAIs depending on the index.
>> + */
>> +static struct snd_soc_dai_driver tegra210_i2s_dais[] = {
>> +     {
>> +             .name = "CIF",
>> +             .playback = {
>> +                     .stream_name = "CIF Receive",
>> +                     .channels_min = 1,
>> +                     .channels_max = 16,
>> +                     .rates = SNDRV_PCM_RATE_8000_192000,
>> +                     .formats = SNDRV_PCM_FMTBIT_S8 |
>> +                             SNDRV_PCM_FMTBIT_S16_LE |
>> +                             SNDRV_PCM_FMTBIT_S32_LE,
>> +             },
>> +             .capture = {
>> +                     .stream_name = "CIF Transmit",
>> +                     .channels_min = 1,
>> +                     .channels_max = 16,
>> +                     .rates = SNDRV_PCM_RATE_8000_192000,
>> +                     .formats = SNDRV_PCM_FMTBIT_S8 |
>> +                             SNDRV_PCM_FMTBIT_S16_LE |
>> +                             SNDRV_PCM_FMTBIT_S32_LE,
>> +             },
>> +     },
>> +     {
>> +             .name = "DAP",
>> +             .playback = {
>> +                     .stream_name = "DAP Receive",
>> +                     .channels_min = 1,
>> +                     .channels_max = 16,
>> +                     .rates = SNDRV_PCM_RATE_8000_192000,
>> +                     .formats = SNDRV_PCM_FMTBIT_S8 |
>> +                             SNDRV_PCM_FMTBIT_S16_LE |
>> +                             SNDRV_PCM_FMTBIT_S32_LE,
>> +             },
>> +             .capture = {
>> +                     .stream_name = "DAP Transmit",
>> +                     .channels_min = 1,
>> +                     .channels_max = 16,
>> +                     .rates = SNDRV_PCM_RATE_8000_192000,
>> +                     .formats = SNDRV_PCM_FMTBIT_S8 |
>> +                             SNDRV_PCM_FMTBIT_S16_LE |
>> +                             SNDRV_PCM_FMTBIT_S32_LE,
>> +             },
>> +             .ops = &tegra210_i2s_dai_ops,
>> +             .symmetric_rates = 1,
>> +     },
>> +     {
>> +             .name = "DUMMY",
>> +             .playback = {
>> +                     .stream_name = "Dummy Playback",
>> +                     .channels_min = 1,
>> +                     .channels_max = 16,
>> +                     .rates = SNDRV_PCM_RATE_8000_192000,
>> +                     .formats = SNDRV_PCM_FMTBIT_S8 |
>> +                             SNDRV_PCM_FMTBIT_S16_LE |
>> +                             SNDRV_PCM_FMTBIT_S32_LE,
>> +             },
>> +             .capture = {
>> +                     .stream_name = "Dummy Capture",
>> +                     .channels_min = 1,
>> +                     .channels_max = 16,
>> +                     .rates = SNDRV_PCM_RATE_8000_192000,
>> +                     .formats = SNDRV_PCM_FMTBIT_S8 |
>> +                             SNDRV_PCM_FMTBIT_S16_LE |
>> +                             SNDRV_PCM_FMTBIT_S32_LE,
>> +             },
>> +     },
>> +};
>> +
>> +static const char * const tegra210_i2s_stereo_conv_text[] = {
>> +     "CH0", "CH1", "AVG",
>> +};
>> +
>> +static const char * const tegra210_i2s_mono_conv_text[] = {
>> +     "ZERO", "COPY",
>> +};
>> +
>> +static const struct soc_enum tegra210_i2s_mono_conv_enum =
>> +     SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_mono_conv_text),
>> +                     tegra210_i2s_mono_conv_text);
>> +
>> +static const struct soc_enum tegra210_i2s_stereo_conv_enum =
>> +     SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_i2s_stereo_conv_text),
>> +                     tegra210_i2s_stereo_conv_text);
>> +
>> +static const struct snd_kcontrol_new tegra210_i2s_controls[] = {
>> +     SOC_SINGLE_EXT("Loopback", 0, 0, 1, 0, tegra210_i2s_get_control,
>> +                    tegra210_i2s_put_control),
>> +     SOC_SINGLE_EXT("FSYNC Width", 0, 0, 255, 0, tegra210_i2s_get_control,
>> +                    tegra210_i2s_put_control),
>> +     SOC_SINGLE_EXT("Sample Rate", 0, 0, 192000, 0, tegra210_i2s_get_control,
>> +                    tegra210_i2s_put_control),
>> +     SOC_ENUM_EXT("Playback Audio Bit Format", tegra210_i2s_format_enum,
>> +                  tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_ENUM_EXT("Capture Audio Bit Format", tegra210_i2s_format_enum,
>> +                  tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_ENUM_EXT("Client Bit Format", tegra210_i2s_format_enum,
>> +                  tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_SINGLE_EXT("Playback Audio Channels", 0, 0, 16, 0,
>> +                    tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_SINGLE_EXT("Capture Audio Channels", 0, 0, 16, 0,
>> +                    tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_SINGLE_EXT("Client Channels", 0, 0, 16, 0,
>> +                    tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_ENUM_EXT("Capture Stereo To Mono", tegra210_i2s_stereo_conv_enum,
>> +                  tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_ENUM_EXT("Capture Mono To Stereo", tegra210_i2s_mono_conv_enum,
>> +                  tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_ENUM_EXT("Playback Stereo To Mono", tegra210_i2s_stereo_conv_enum,
>> +                  tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_ENUM_EXT("Playback Mono To Stereo", tegra210_i2s_mono_conv_enum,
>> +                  tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_SINGLE_EXT("Playback FIFO Threshold", 0, 0, I2S_RX_FIFO_DEPTH - 1,
>> +                    0, tegra210_i2s_get_control, tegra210_i2s_put_control),
>> +     SOC_SINGLE_EXT("BCLK Ratio", 0, 0, INT_MAX, 0, tegra210_i2s_get_control,
>> +                    tegra210_i2s_put_control),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = {
>> +     SND_SOC_DAPM_AIF_IN("CIF RX", NULL, 0, SND_SOC_NOPM, 0, 0),
>> +     SND_SOC_DAPM_AIF_OUT("CIF TX", NULL, 0, SND_SOC_NOPM, 0, 0),
>> +     SND_SOC_DAPM_AIF_IN_E("DAP RX", NULL, 0, TEGRA210_I2S_TX_ENABLE,
>> +                           0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU),
>> +     SND_SOC_DAPM_AIF_OUT_E("DAP TX", NULL, 0, TEGRA210_I2S_RX_ENABLE,
>> +                            0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU),
>> +     SND_SOC_DAPM_MIC("Dummy Input", NULL),
>> +     SND_SOC_DAPM_SPK("Dummy Output", NULL),
>> +};
>> +
>> +static const struct snd_soc_dapm_route tegra210_i2s_routes[] = {
>> +     { "CIF RX",       NULL, "CIF Receive" },
>> +     { "DAP TX",       NULL, "CIF RX" },
>> +     { "DAP Transmit", NULL, "DAP TX" },
>> +
>> +     { "DAP RX",       NULL, "DAP Receive" },
>> +     { "CIF TX",       NULL, "DAP RX" },
>> +     { "CIF Transmit", NULL, "CIF TX" },
>> +
>> +     { "Dummy Capture", NULL, "Dummy Input" },
>> +     { "Dummy Output", NULL, "Dummy Playback" },
>> +};
>> +
>> +static const struct snd_soc_component_driver tegra210_i2s_cmpnt = {
>> +     .dapm_widgets = tegra210_i2s_widgets,
>> +     .num_dapm_widgets = ARRAY_SIZE(tegra210_i2s_widgets),
>> +     .dapm_routes = tegra210_i2s_routes,
>> +     .num_dapm_routes = ARRAY_SIZE(tegra210_i2s_routes),
>> +     .controls = tegra210_i2s_controls,
>> +     .num_controls = ARRAY_SIZE(tegra210_i2s_controls),
>> +     .non_legacy_dai_naming = 1,
>> +};
>> +
>> +static bool tegra210_i2s_wr_reg(struct device *dev, unsigned int reg)
>> +{
>> +     switch (reg) {
>> +     case TEGRA210_I2S_RX_ENABLE ... TEGRA210_I2S_RX_SOFT_RESET:
>> +     case TEGRA210_I2S_RX_INT_MASK ... TEGRA210_I2S_RX_CLK_TRIM:
>> +     case TEGRA210_I2S_TX_ENABLE ... TEGRA210_I2S_TX_SOFT_RESET:
>> +     case TEGRA210_I2S_TX_INT_MASK ... TEGRA210_I2S_TX_CLK_TRIM:
>> +     case TEGRA210_I2S_ENABLE ... TEGRA210_I2S_CG:
>> +     case TEGRA210_I2S_CTRL ... TEGRA210_I2S_CYA:
>> +             return true;
>> +     default:
>> +             return false;
>> +     };
>> +}
>> +
>> +static bool tegra210_i2s_rd_reg(struct device *dev, unsigned int reg)
>> +{
>> +     if (tegra210_i2s_wr_reg(dev, reg))
>> +             return true;
>> +
>> +     switch (reg) {
>> +     case TEGRA210_I2S_RX_STATUS:
>> +     case TEGRA210_I2S_RX_INT_STATUS:
>> +     case TEGRA210_I2S_RX_CIF_FIFO_STATUS:
>> +     case TEGRA210_I2S_TX_STATUS:
>> +     case TEGRA210_I2S_TX_INT_STATUS:
>> +     case TEGRA210_I2S_TX_CIF_FIFO_STATUS:
>> +     case TEGRA210_I2S_STATUS:
>> +     case TEGRA210_I2S_INT_STATUS:
>> +             return true;
>> +     default:
>> +             return false;
>> +     };
>> +}
>> +
>> +static bool tegra210_i2s_volatile_reg(struct device *dev, unsigned int reg)
>> +{
>> +     switch (reg) {
>> +     case TEGRA210_I2S_RX_STATUS:
>> +     case TEGRA210_I2S_RX_INT_STATUS:
>> +     case TEGRA210_I2S_RX_CIF_FIFO_STATUS:
>> +     case TEGRA210_I2S_TX_STATUS:
>> +     case TEGRA210_I2S_TX_INT_STATUS:
>> +     case TEGRA210_I2S_TX_CIF_FIFO_STATUS:
>> +     case TEGRA210_I2S_STATUS:
>> +     case TEGRA210_I2S_INT_STATUS:
>> +     case TEGRA210_I2S_RX_SOFT_RESET:
>> +     case TEGRA210_I2S_TX_SOFT_RESET:
>> +             return true;
>> +     default:
>> +             return false;
>> +     };
>> +}
>> +
>> +static const struct regmap_config tegra210_i2s_regmap_config = {
>> +     .reg_bits               = 32,
>> +     .reg_stride             = 4,
>> +     .val_bits               = 32,
>> +     .max_register           = TEGRA210_I2S_CYA,
>> +     .writeable_reg          = tegra210_i2s_wr_reg,
>> +     .readable_reg           = tegra210_i2s_rd_reg,
>> +     .volatile_reg           = tegra210_i2s_volatile_reg,
>> +     .reg_defaults           = tegra210_i2s_reg_defaults,
>> +     .num_reg_defaults       = ARRAY_SIZE(tegra210_i2s_reg_defaults),
>> +     .cache_type             = REGCACHE_FLAT,
>> +};
>> +
>> +static const struct of_device_id tegra210_i2s_of_match[] = {
>> +     { .compatible = "nvidia,tegra210-i2s" },
>> +     {},
>> +};
>> +
>> +static int tegra210_i2s_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct tegra210_i2s *i2s;
>> +     void __iomem *regs;
>> +     int ret = 0;
> Please do not initialize variables if it's not really needed, to keep
> code cleaner. Same for all other occurrences in the code.

initialization can be avoided, will remove.
>
>> +     i2s = devm_kcalloc(dev, 1, sizeof(*i2s), GFP_KERNEL);
>> +     if (!i2s)
>> +             return -ENOMEM;
>> +
>> +     i2s->rx_fifo_th = DEFAULT_I2S_RX_FIFO_THRESHOLD;
>> +     i2s->tx_mask = i2s->rx_mask = DEFAULT_I2S_SLOT_MASK;
>> +     i2s->loopback = false;
>> +
>> +     dev_set_drvdata(dev, i2s);
>> +
>> +     i2s->clk_i2s = devm_clk_get(dev, "i2s");
>> +     if (IS_ERR(i2s->clk_i2s)) {
>> +             dev_err(dev, "can't retrieve I2S bit clock\n");
>> +             return PTR_ERR(i2s->clk_i2s);
>> +     }
>> +
>> +     /*
>> +      * Not an error, as this clock is needed only when some other I/O
>> +      * requires input clock from current I2S instance, which is
>> +      * configurable from DT.
>> +      */
>> +     i2s->clk_sync_input = devm_clk_get(dev, "sync_input");
>> +     if (IS_ERR(i2s->clk_sync_input))
>> +             dev_dbg(dev, "can't retrieve I2S sync input clock\n");
>> +
>> +     regs = devm_platform_ioremap_resource(pdev, 0);
>> +     if (IS_ERR(regs))
>> +             return PTR_ERR(regs);
>> +
>> +     i2s->regmap = devm_regmap_init_mmio(dev, regs,
>> +                                         &tegra210_i2s_regmap_config);
>> +     if (IS_ERR(i2s->regmap)) {
>> +             dev_err(dev, "regmap init failed\n");
>> +             return PTR_ERR(i2s->regmap);
>> +     }
>> +
>> +     regcache_cache_only(i2s->regmap, true);
>> +
>> +     ret = devm_snd_soc_register_component(dev, &tegra210_i2s_cmpnt,
>> +                                           tegra210_i2s_dais,
>> +                                           ARRAY_SIZE(tegra210_i2s_dais));
>> +     if (ret != 0) {
>> +             dev_err(dev, "can't register I2S component, err: %d\n", ret);
>> +             return ret;
>> +     }
>> +
>> +     pm_runtime_enable(dev);
> Error checking?

return type for above is void()
>> +     return 0;
>> +}
>> +
>> +static int tegra210_i2s_remove(struct platform_device *pdev)
>> +{
>> +     pm_runtime_disable(&pdev->dev);
>> +     if (!pm_runtime_status_suspended(&pdev->dev))
>> +             tegra210_i2s_runtime_suspend(&pdev->dev);
> This breaks device's RPM refcounting if it was disabled in the active
> state. This code should be removed. At most you could warn about the
> unxpected RPM state here, but it shouldn't be necessary.

I guess this was added for safety and explicit suspend keeps clock disabled.
Not sure if ref-counting of the device matters when runtime PM is 
disabled and device is removed.
I see few drivers using this way.
>
>> +     return 0;
>> +}
>> +
>> +static const struct dev_pm_ops tegra210_i2s_pm_ops = {
>> +     SET_RUNTIME_PM_OPS(tegra210_i2s_runtime_suspend,
>> +                        tegra210_i2s_runtime_resume, NULL)
>> +     SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
>> +                                  pm_runtime_force_resume)
>> +};
>> +
>> +static struct platform_driver tegra210_i2s_driver = {
>> +     .driver = {
>> +             .name = DRV_NAME,
> Will be nicer to set the name directly here, thus to remove the DRV_NAME
> macro.

can be done.
>
>> +             .owner = THIS_MODULE,
> No need to explicitly set the owner, drivers core takes care about it.

OK, will remove.
>
>> +             .of_match_table = tegra210_i2s_of_match,
>> +             .pm = &tegra210_i2s_pm_ops,
>> +     },
>> +     .probe = tegra210_i2s_probe,
>> +     .remove = tegra210_i2s_remove,
>> +};
>> +module_platform_driver(tegra210_i2s_driver)
>> +
>> +MODULE_AUTHOR("Songhee Baek <sbaek at nvidia.com>");
>> +MODULE_DESCRIPTION("Tegra210 ASoC I2S driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DEVICE_TABLE(of, tegra210_i2s_of_match);
> What about to place MODULE_DEVICE_TABLE() right after the definition of
> tegra210_i2s_of_match like it's done by most of the drivers in kernel?

yes will move.
>
>> diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h
>> new file mode 100644
>> index 0000000..121dff7
>> --- /dev/null
>> +++ b/sound/soc/tegra/tegra210_i2s.h
>> @@ -0,0 +1,132 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * tegra210_i2s.h - Definitions for Tegra210 I2S driver
>> + *
>> + * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
>> + *
>> + */
>> +
>> +#ifndef __TEGRA210_I2S_H__
>> +#define __TEGRA210_I2S_H__
>> +
>> +/* Register offsets from I2S*_BASE */
>> +#define TEGRA210_I2S_RX_ENABLE                       0x0
>> +#define TEGRA210_I2S_RX_SOFT_RESET           0x4
>> +#define TEGRA210_I2S_RX_STATUS                       0x0c
>> +#define TEGRA210_I2S_RX_INT_STATUS           0x10
>> +#define TEGRA210_I2S_RX_INT_MASK             0x14
>> +#define TEGRA210_I2S_RX_INT_SET                      0x18
>> +#define TEGRA210_I2S_RX_INT_CLEAR            0x1c
>> +#define TEGRA210_I2S_RX_CIF_CTRL             0x20
>> +#define TEGRA210_I2S_RX_CTRL                 0x24
>> +#define TEGRA210_I2S_RX_SLOT_CTRL            0x28
>> +#define TEGRA210_I2S_RX_CLK_TRIM             0x2c
>> +#define TEGRA210_I2S_RX_CYA                  0x30
>> +#define TEGRA210_I2S_RX_CIF_FIFO_STATUS              0x34
>> +#define TEGRA210_I2S_TX_ENABLE                       0x40
>> +#define TEGRA210_I2S_TX_SOFT_RESET           0x44
>> +#define TEGRA210_I2S_TX_STATUS                       0x4c
>> +#define TEGRA210_I2S_TX_INT_STATUS           0x50
>> +#define TEGRA210_I2S_TX_INT_MASK             0x54
>> +#define TEGRA210_I2S_TX_INT_SET                      0x58
>> +#define TEGRA210_I2S_TX_INT_CLEAR            0x5c
>> +#define TEGRA210_I2S_TX_CIF_CTRL             0x60
>> +#define TEGRA210_I2S_TX_CTRL                 0x64
>> +#define TEGRA210_I2S_TX_SLOT_CTRL            0x68
>> +#define TEGRA210_I2S_TX_CLK_TRIM             0x6c
>> +#define TEGRA210_I2S_TX_CYA                  0x70
>> +#define TEGRA210_I2S_TX_CIF_FIFO_STATUS              0x74
>> +#define TEGRA210_I2S_ENABLE                  0x80
>> +#define TEGRA210_I2S_SOFT_RESET                      0x84
>> +#define TEGRA210_I2S_CG                              0x88
>> +#define TEGRA210_I2S_STATUS                  0x8c
>> +#define TEGRA210_I2S_INT_STATUS                      0x90
>> +#define TEGRA210_I2S_CTRL                    0xa0
>> +#define TEGRA210_I2S_TIMING                  0xa4
>> +#define TEGRA210_I2S_SLOT_CTRL                       0xa8
>> +#define TEGRA210_I2S_CLK_TRIM                        0xac
>> +#define TEGRA210_I2S_CYA                     0xb0
>> +
>> +/* Bit fields, shifts and masks */
>> +#define I2S_DATA_SHIFT                               8
>> +#define I2S_CTRL_DATA_OFFSET_MASK            (0x7ff << I2S_DATA_SHIFT)
>> +
>> +#define I2S_EN_SHIFT                         0
>> +#define I2S_EN_MASK                          (1 << I2S_EN_SHIFT)
>> +#define I2S_EN                                       (1 << I2S_EN_SHIFT)
>> +
>> +#define I2S_FSYNC_WIDTH_SHIFT                        24
>> +#define I2S_CTRL_FSYNC_WIDTH_MASK            (0xff << I2S_FSYNC_WIDTH_SHIFT)
>> +
>> +#define I2S_POS_EDGE                         0
>> +#define I2S_NEG_EDGE                         1
>> +#define I2S_EDGE_SHIFT                               20
>> +#define I2S_CTRL_EDGE_CTRL_MASK                      (1 << I2S_EDGE_SHIFT)
>> +#define I2S_CTRL_EDGE_CTRL_POS_EDGE          (I2S_POS_EDGE << I2S_EDGE_SHIFT)
>> +#define I2S_CTRL_EDGE_CTRL_NEG_EDGE          (I2S_NEG_EDGE << I2S_EDGE_SHIFT)
>> +
>> +#define I2S_FMT_LRCK                         0
>> +#define I2S_FMT_FSYNC                                1
>> +#define I2S_FMT_SHIFT                                12
>> +#define I2S_CTRL_FRAME_FMT_MASK                      (7 << I2S_FMT_SHIFT)
>> +#define I2S_CTRL_FRAME_FMT_LRCK_MODE         (I2S_FMT_LRCK << I2S_FMT_SHIFT)
>> +#define I2S_CTRL_FRAME_FMT_FSYNC_MODE                (I2S_FMT_FSYNC << I2S_FMT_SHIFT)
>> +
>> +#define I2S_CTRL_MASTER_EN_SHIFT             10
>> +#define I2S_CTRL_MASTER_EN_MASK                      (1 << I2S_CTRL_MASTER_EN_SHIFT)
>> +#define I2S_CTRL_MASTER_EN                   (1 << I2S_CTRL_MASTER_EN_SHIFT)
>> +
>> +#define I2S_CTRL_LRCK_POL_SHIFT                      9
>> +#define I2S_CTRL_LRCK_POL_MASK                       (1 << I2S_CTRL_LRCK_POL_SHIFT)
>> +#define I2S_CTRL_LRCK_POL_LOW                        (0 << I2S_CTRL_LRCK_POL_SHIFT)
>> +#define I2S_CTRL_LRCK_POL_HIGH                       (1 << I2S_CTRL_LRCK_POL_SHIFT)
>> +
>> +#define I2S_CTRL_LPBK_SHIFT                  8
>> +#define I2S_CTRL_LPBK_MASK                   (1 << I2S_CTRL_LPBK_SHIFT)
>> +#define I2S_CTRL_LPBK_EN                     (1 << I2S_CTRL_LPBK_SHIFT)
>> +
>> +#define I2S_BITS_8                           1
>> +#define I2S_BITS_16                          3
>> +#define I2S_BITS_32                          7
>> +#define I2S_CTRL_BIT_SIZE_MASK                       0x7
>> +
>> +#define I2S_TIMING_CH_BIT_CNT_MASK           0x7ff
>> +#define I2S_TIMING_CH_BIT_CNT_SHIFT          0
>> +
>> +#define I2S_SOFT_RESET_SHIFT                 0
>> +#define I2S_SOFT_RESET_MASK                  (1 << I2S_SOFT_RESET_SHIFT)
>> +#define I2S_SOFT_RESET_EN                    (1 << I2S_SOFT_RESET_SHIFT)
>> +
>> +#define I2S_RX_FIFO_DEPTH                    64
>> +#define DEFAULT_I2S_RX_FIFO_THRESHOLD                3
>> +
>> +#define DEFAULT_I2S_SLOT_MASK                        0xffff
>> +
>> +enum tegra210_i2s_path {
>> +     I2S_RX_PATH,
>> +     I2S_TX_PATH,
>> +     I2S_PATHS,
>> +};
>> +
>> +struct tegra210_i2s {
>> +     struct clk *clk_i2s;
>> +     struct clk *clk_sync_input;
>> +     struct regmap *regmap;
>> +     unsigned int stereo_to_mono[I2S_PATHS];
>> +     unsigned int mono_to_stereo[I2S_PATHS];
>> +     unsigned int audio_ch_override[I2S_PATHS];
>> +     unsigned int audio_fmt_override[I2S_PATHS];
>> +     /* Client overrides are common for TX and RX paths */
>> +     unsigned int client_ch_override;
>> +     unsigned int client_fmt_override;
>> +     unsigned int srate_override;
>> +     unsigned int dai_fmt;
>> +     unsigned int fsync_width;
>> +     unsigned int bclk_ratio;
>> +     unsigned int tx_mask;
>> +     unsigned int rx_mask;
>> +     int rx_fifo_th;
> Could rx_fifo_th be negative?

rx_fifo_th itself does not take negative values, explicit typecasting is 
avoided in "if" condition by declaring this as "int"
>> +     bool loopback;
>> +};
>> +
>> +#endif
>>



More information about the Alsa-devel mailing list